Android Language

来源:https://www.cnblogs.com/android-rui/archive/2018/08/13/9463769.html
-Advertisement-
Play Games

至最新的Android P, Google已經提供了世界絕大多數語言的支持。但是對許多發往海外的項目,可能還是會有一些國家的語言google預設沒有支持。下麵將介紹下對於某一特定語言如何判斷Google是否支持這種語言、如何添加一種語言(如果google預設沒有支持)。 還有,自Android N以 ...


       至最新的Android P, Google已經提供了世界絕大多數語言的支持。但是對許多發往海外的項目,可能還是會有一些國家的語言google預設沒有支持。下麵將介紹下對於某一特定語言如何判斷Google是否支持這種語言、如何添加一種語言(如果google預設沒有支持)。 還有,自Android N以來至Android P,首次開機時系統語言不再隨SIM卡自適應,即SIM卡是法國的,首次開機時系統語言卻不是法國。對此,可以參考下麵提供的Feature去設計。

 

一、如何判斷Google是否支持某種語言

      整個過程可以分為下麵幾步:

      1. 先要確定這種語言對應的country code和language code(如中文簡體, country code是cn, language code是zh; 美國英語,country code是us, language code是en), 確定好country code和language code之後再進行2以下步驟;

      language code可以從這個網址去查詢:https://zh.wikipedia.org/wiki/ISO_639-1

      country code可以從這個網址去查詢:https://zh.wikipedia.org/wiki/ISO_3166-1

      2. 是否有這種語言對應語言的icu資源(前面確定好了language code和country code, 如中文簡體是zh_CN)。可以看到external\icu\icu4c\source\data下coll、curr、lang、locales、region,zone這些子文件夾中都有zh.txt和zh_CN.txt.  這些目錄下記載的對應的都是這種語言對應顯示的region, zone, locale, language, dateformat...這些信息。

      3. 是否有這種語言對應的字型檔。Google已經提供了約大多數語言的字型檔,很多語言是共用同一字型檔。所以基本不需要再添加字型檔。如需添加,得找對應字型檔公司購買。

      4. 在frameworks/base/core/res/res/下是否有對應的values-xx-rYY的文件夾(xx是language code, YY是country code,下同). 即要有這種語言對應的resource, 不然語言顯示的信息從何而來呢。

      5. 每個app對應的res目錄下麵是否有values-xx文件夾或values-xx-rYY文件夾.   app下沒有這個語言對應的resource, 所帶來的影響會是這個app不能顯示這種語言。如果需要顯示,得手動去添加對應的resource.

      6. 如果是複雜語言,還需看看是否有對應的字體引擎。 複雜語言一般有這些特性: 閱讀順序從右到左、某幾個字元相鄰時會發生變形或替換等。對於複雜語言,要去external\harfbuzz_ng\src\hb-old\harfbuzz-shaper.cpp中添加對應的引擎。

      如果以上幾點都是YES, 那就說明這種語言已經支持。

 

二、Google不支持的語言,如何添加這種語言呢

      上面介紹瞭如何判斷是否支持某一種語言,對於某一種不支持的語言,是不是做到滿足上面幾點就能支持了呢? YES!  當然,答案是肯定的。

      可以參考下麵步驟:

      1. 修改編譯配置文件。 一般是在alps\device\公司名字\項目名字\full_項目名字.mk  中的  PRODUCT_LOCALES巨集 下添加需要添加的語言代碼。在此添加的目的是為系統打開這種語言。原因是雖然Android系統支持這麼多的語言,但對於絕大多數的用戶來說,並不需要這麼多的語言,所以一般情況下系統只需打開對應的幾種語言即可。如添加中文簡體,那就將zh_CN添加到PRODUCT_LOCALES下麵。 如果想要系統的預設語言(即首次開機時的語言)就是zh_CN, 那可以將zh_CN添加到PRODUCT_LOCALES下的第一個位置。

      2. 添加ICU資源。 ICU資源是開源組織提供的, 可以網路搜索對應的ICU資源添加(上面判斷是否支持一種語言中有介紹需要哪些ICU資源).

      3. 如缺少這種語言對應的字型檔,則還需要添加字型檔

      4. 在frameworks/base/core/res/res/下添加這種語言對應的resource. 如添加values-zh-rCN.

      5. 複雜語言需要添加對應的引擎

 

三、 首次開機隨Sim卡自適應語言的feature Design

      如下所示:

 /frameworks/opt/telephony/src/java/com/android/internal/telephony/MccTable.java
174 public static void updateMccMncConfiguration(Context context, String mccmnc,
175 boolean fromServiceState) {
176 Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc + "' fromServiceState=" + fromServiceState);
177
178 if (Build.IS_DEBUGGABLE) {
179 String overrideMcc = SystemProperties.get("persist.sys.override_mcc");
180 if (!TextUtils.isEmpty(overrideMcc)) {
181 mccmnc = overrideMcc;
182 Slog.d(LOG_TAG, "updateMccMncConfiguration overriding mccmnc='" + mccmnc + "'");
183 }
184 }
185
186 if (!TextUtils.isEmpty(mccmnc)) {
187 int mcc, mnc;
188
189 String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumeric();
190 Slog.d(LOG_TAG, "updateMccMncConfiguration defaultMccMnc=" + defaultMccMnc);
191 //Update mccmnc only for default subscription in case of MultiSim.
192// if (!defaultMccMnc.equals(mccmnc)) {
193// Slog.d(LOG_TAG, "Not a Default subscription, ignoring mccmnc config update.");
194// return;
195// }
196
197 try {
198 mcc = Integer.parseInt(mccmnc.substring(0,3));
199 mnc = Integer.parseInt(mccmnc.substring(3));
200 } catch (NumberFormatException e) {
201 Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc);
202 return;
203 }
204
205 Slog.d(LOG_TAG, "updateMccMncConfiguration: mcc=" + mcc + ", mnc=" + mnc);
Locale mccLocale = null; //添加這行
206 if (mcc != 0) {
207 setTimezoneFromMccIfNeeded(context, mcc);
mccLocale = getLocaleFromMcc(context, mcc); //添加這行
208 }
209 if (fromServiceState) {
210 setWifiCountryCodeFromMcc(context, mcc);
211 } else {
212 // from SIM
213 try {
214 Configuration config = new Configuration();
215 boolean updateConfig = false;
216 if (mcc != 0) {
217 config.mcc = mcc;
218 config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
219 updateConfig = true;
220 }
221
if (mccLocale != null) { //添加這行
Configuration conLocale = new Configuration(); //添加這行
conLocale = ActivityManagerNative.getDefault().getConfiguration(); //添加這行
LocaleList userLocale = conLocale.getLocales(); //添加這行
LocaleList newUserLocale = new LocaleList(mccLocale,userLocale); //添加這行
config.setLocales(newUserLocale); //添加這行
updateConfig = true; //添加這行
} //添加這行

222 if (updateConfig) {
223 Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
224 ActivityManagerNative.getDefault().updateConfiguration(config);
225 } else {
226 Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
227 }
228 } catch (RemoteException e) {
229 Slog.e(LOG_TAG, "Can't update configuration", e);
230 }
231 }
232 } else {
233 if (fromServiceState) {
234 // an empty mccmnc means no signal - tell wifi we don't know
235 setWifiCountryCodeFromMcc(context, 0);
236 }
237 }
238 }


//添加下麵函數
245 private static boolean canUpdateLocale(Context context) {
246 return !(userHasPersistedLocale() || isDeviceProvisioned(context));
247 }
248
249 private static boolean userHasPersistedLocale() {
250 String persistSysLanguage = SystemProperties.get("persist.sys.locale", "");
251 String persistSysCountry = SystemProperties.get("persist.sys.country", "");
252 return !(persistSysLanguage.isEmpty() && persistSysCountry.isEmpty());
253 }
254
255 private static boolean isDeviceProvisioned(Context context) {
256 try {
257 return Settings.Global.getInt(
258 context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED) != 0;
259 } catch (Settings.SettingNotFoundException e) {
260 return false;
261 }
262 }


284 private static Locale getLocaleForLanguageCountry(Context context, String language,
285 String country) {
286 if (language == null) {
287 Slog.d(LOG_TAG, "getLocaleForLanguageCountry: skipping no language");
288 return null; // no match possible
289 }
290 if (country == null) {
291 country = ""; // The Locale constructor throws if passed null.
292 }

if(!canUpdateLocale()){ //添加這行
return null; //添加這行
} //添加這行
293
294 final Locale target = new Locale(language, country);
295 try {
296 String[] localeArray = context.getAssets().getLocales();
297 List<String> locales = new ArrayList<>(Arrays.asList(localeArray));
298
299 // Even in developer mode, you don't want the pseudolocales.
300 locales.remove("ar-XB");
301 locales.remove("en-XA");
302
303 List<Locale> languageMatches = new ArrayList<>();
304 for (String locale : locales) {
305 final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
306
307 // Only consider locales with both language and country.
308 if (l == null || "und".equals(l.getLanguage()) ||
309 l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
310 continue;
311 }
312 if (l.getLanguage().equals(target.getLanguage())) {
313 // If we got a perfect match, we're done.
314 if (l.getCountry().equals(target.getCountry())) {
315 Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got perfect match: " +
316 l.toLanguageTag());
317 return l;
318 }
319
320 // We've only matched the language, not the country.
321 languageMatches.add(l);
322 }
323 }

325 Locale bestMatch = chooseBestFallback(target, languageMatches);
326 if (bestMatch != null) {
327 Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a language-only match: " +
328 bestMatch.toLanguageTag());
329 return bestMatch;
330 } else {
331 Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " +
332 language);
333 }
334 } catch (Exception e) {
335 Slog.d(LOG_TAG, "getLocaleForLanguageCountry: exception", e);
336 }
337
338 return null;
339 }
340

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

19627 private boolean updateConfigurationLocked(Configuration values,
19628 ActivityRecord starting, boolean initLocale, boolean persistent, int userId) {
...
19643 if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { //刪除這行
if (!initLocale && !values.getLocales().isEmpty()) { //添加這行
19644 final LocaleList locales = values.getLocales();
19645 int bestLocaleIndex = 0;
19646 if (locales.size() > 1) {
19647 if (mSupportedSystemLocales == null) {
19648 mSupportedSystemLocales =
19649 Resources.getSystem().getAssets().getLocales();
19650 }
19651 bestLocaleIndex = Math.max(0,
19652 locales.getFirstMatchIndex(mSupportedSystemLocales));
19653 }
if(values.userSetLocale){
SystemProperties.set("persist.sys.locale", //添加這行
locales.get(bestLocaleIndex).toLanguageTag()); //添加這行
}else{ //添加這行
SystemProperties.set("persist.sys.simLocale", //添加這行
locales.get(bestLocaleIndex).toLanguageTag()); //添加這行
} //添加這行
19654 SystemProperties.set("persist.sys.locale", //刪除這行
19655 locales.get(bestLocaleIndex).toLanguageTag()); //刪除這行
19656 LocaleList.setDefault(locales, bestLocaleIndex);
19657 mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
19658 locales.get(bestLocaleIndex)));
19659 }
...
}


/frameworks/base/core/jni/AndroidRuntime.cpp 
414const std::string readLocale()
415{
416 const std::string locale = getProperty("persist.sys.locale", "");
417 if (!locale.empty()) {
418 return locale;
419 }
420
421 const std::string language = getProperty("persist.sys.language", "");
422 if (!language.empty()) {
423 const std::string country = getProperty("persist.sys.country", "");
424 const std::string variant = getProperty("persist.sys.localevar", "");
425
426 std::string out = language;
427 if (!country.empty()) {
428 out = out + "-" + country;
429 }
430
431 if (!variant.empty()) {
432 out = out + "-" + variant;
433 }
434
435 return out;
436 }
437
const std::string simLocale = getProperty("persist.sys.simLocale", ""); //添加這行
if (!simLocale.empty()) { //添加這行
return simLocale; //添加這行
} //添加這行

438 const std::string productLocale = getProperty("ro.product.locale", "");
439 if (!productLocale.empty()) {
440 return productLocale;
441 }
442
443 // If persist.sys.locale and ro.product.locale are missing,
444 // construct a locale value from the individual locale components.
445 const std::string productLanguage = getProperty("ro.product.locale.language", "en");
446 const std::string productRegion = getProperty("ro.product.locale.region", "US");
447
448 return productLanguage + "-" + productRegion;
449}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在MongoDB3.6引入的新feature中,change stream無疑是非常吸引人的。 Change streams allow applications to access real-time data changes without the complexity and risk of ...
  • 之前只用phpmyadmin登錄本地的mysql,管理另一個遠程資料庫的時候發現,單純用命令行處理字元串、換行符實在是不好使,所以配置了遠程登錄mysql,很簡單的問題結果沒有搜到合適的方法,所以記錄下我的配置方式。 phpmyadmin/libraries/config.default.php $ ...
  • 今天來介紹新手學習hadoop的入門註意事項。這篇文章一來談談hadoop核心知識學習。 首先hadoop分為hadoop1.X和hadoop2.X,並且還有hadoop生態系統,那麼下麵我們以hadoop2.x為例進行詳細介紹: Hadoop的核心是mapreduce和hdfs。 Mapreduc ...
  • 一、sql server日期時間函數Sql Server中的日期與時間函數 1. 當前系統日期、時間 select getdate() 2. dateadd 在向指定日期加上一段時間的基礎上,返回新的 datetime 值 例如:嚮日期加上2天 select dateadd(day,2,'2004- ...
  • 一. 概述 在sql server 備份與恢復系列的第一篇里,有講到大容量模式下備份與還原的相關知識。這篇重點來演示在大容量模式下常用的備份與還原模式“完整備份+差異備份+日誌備份”。 在大容量恢復模式下,特別要註意的是在什麼情況下會導致數據還原丟失風險,帶著這個問題,來進行演示說明。備份策略如下圖 ...
  • 線上上進行DDL操作時,相對於其可能帶來的系統負載,其實,我們最擔心的還是MDL其可能導致的阻塞問題。 一旦DDL操作因獲取不到MDL被阻塞,後續其它針對該表的其它操作都會被阻塞。典型如下,如阻塞稍久的話,我們會看到Threads_running飆升,CPU告警。 如果發生線上上,無疑會影響到業務。 ...
  • 眾所周知,MongoDB包括社區版和企業版,但不止如此,MongoDB公司還有MongoDB Atlas:Database as a Service. MongoDB Atlas delivers the world’s leading database for modern application ...
  • APK文件只能包含一個AndroidManifest.xml文件,但Android Studio項目可以包含多個文件(通過buildSrc、導入的庫引入)。因此,在構建應用時,Gradle構建會將所有清單文件合併到一個封裝的APK的清單文件中。 清單文件合併優先順序 清單合併工具 可以使用Merged ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...