個別應用程式語言偏好

系統設定中的個別應用程式語言

在許多情況下,多語言使用者會將系統語言設為某種語言 (例如英文),卻想在特定應用程式中選擇使用其他語言,例如荷蘭文、中文或北印度文。為了讓這類使用者在應用程式中享有更優質的體驗,Android 13 針對支援多種語言的應用程式推出下列功能:

  • 系統設定:集中管理各項設定的頁面,使用者可在此為各應用程式選取偏好的語言。

    您可以設定應用程式,自動產生支援個別應用程式語言偏好所需的檔案,並顯示在系統設定中。詳情請參閱啟用自動支援個別應用程式語言功能的操作說明。

  • 其他 API:利用這類公用 API (例如 LocaleManager 中的 setApplicationLocales()getApplicationLocales() 方法),應用程式可在執行階段設定與系統語言不同的語言。

    這些 API 會自動與系統設定保持同步;因此,使用這些 API 建立自訂應用程式內語言選單的應用程式,無論使用者選擇的語言偏好設定為何,都能為使用者提供一致的使用者體驗。此外,公用 API 也有助於減少樣板程式碼的數量,可支援分割 APK,並支援應用程式自動備份功能,儲存應用程式層級的使用者語言設定。

    為了提供與 Android 舊版本的回溯相容性,AndroidX 中也提供同等的 API。但若是 Android 12 (API 級別 32) 以下版本,回溯相容的 API 是與 AppCompatActivity 結構定義搭配運作,而非應用程式結構定義。請使用 Appcompat 1.6.0 以上版本存取回溯相容的 API。

功能實作方式總覽

下表根據不同用途提供建議的實作方式。

用途 建議實作方式
應用程式未提供應用程式內語言選單
  1. 啟用自動支援個別應用程式語言功能,產生 LocaleConfig 檔案並將應用程式語言加入系統設定。
  2. 在要新增應用程式內語言選單時選用:使用 AndroidX 程式庫並選擇啟用 API 實作方式,透過 autoStoreLocales 支援回溯相容性。
應用程式已提供應用程式內語言選單
  1. 啟用自動支援個別應用程式語言功能,產生 LocaleConfig 檔案並將應用程式語言加入系統設定。
  2. 將應用程式的自訂邏輯改用公用 API,確保使用者享有一致的體驗。
  3. 處理下列極端情況:
    1. 當應用程式初次在搭載 Android 13 的裝置上執行時,呼叫 AppCompatDelegate.setApplicationLocales()
    2. 在下列情況下呼叫 AppCompatDelegate.setApplicationLocales(),為系統提供原有的使用者要求語言代碼:

使用者的系統設定

從 Android 13 開始,Android 針對個別應用程式語言偏好,在系統設定中加入集中管理這類設定的頁面。為確保在搭載 Android 13 以上版本的裝置上,可透過系統設定指定應用程式語言,請啟用自動支援個別應用程式語言功能 (建議做法) 或手動設定支援功能

啟用自動支援個別應用程式語言功能

從 Android Studio Giraffe 和 AGP 8.1 開始,您可以將應用程式設為自動支援個別應用程式語言偏好。AGP 會根據專案資源產生 LocaleConfig 檔案,並在最終資訊清單檔案中加入該檔案的參照,為您省去手動操作的麻煩。AGP 會使用應用程式模組 res 資料夾中的資源和任何程式庫模組依附元件,決定要加入 LocaleConfig 檔案的語言代碼。也就是說,如果要在應用程式中加入新的語言資源,不必擔心如何更新 LocaleConfig 檔案。

請注意,自動個別應用程式語言功能支援在 Android 13 (API 級別 33) 以上版本中執行的應用程式。如要使用這項功能,必須將 compileSdkVersion 設為 33 以上。如要為先前的 Android 版本設定個別應用程式語言偏好,還是需要使用 API 和應用程式內語言選單

如要啟用自動支援個別應用程式語言功能,請按照下列步驟操作:

  1. 如要開啟這項功能,請在模組層級 build.gradle.kts 檔案 (若使用 Groovy 則為 build.gradle 檔案) 的 androidResources {} 區塊中使用 generateLocaleConfig 設定。這項功能預設為關閉。

    Kotlin

        android {
          androidResources {
            generateLocaleConfig = true
          }
        }
        

    Groovy

        android {
          androidResources {
            generateLocaleConfig true
          }
        }
        
  2. 指定預設語言代碼:
    1. 在應用程式模組的 res 資料夾中,建立名為 resources.properties 的新檔案。
    2. resources.properties 檔案中,使用 unqualifiedResLocale 標籤設定預設語言代碼。如要設定語言代碼名稱的格式,請參閱「如何設定語言代碼名稱格式」一節。

AGP 會使用 res 資料夾中的 values-* 目錄,將這個預設語言代碼和所有指定替代語言代碼加進自動產生的 LocaleConfig 檔案。

如何設定語言代碼名稱格式

如要設定語言代碼名稱的格式,請將語言代碼與選用的指令碼和區碼結合,並以連字號分隔。

舉例來說,假設預設語言代碼是美式英文:

unqualifiedResLocale=en-US

使用 android:localeConfig 在系統設定中新增支援的語言

您可以手動設定應用程式,確保在搭載 Android 13 以上版本的裝置上,可透過系統設定指定應用程式語言。方法是建立 locales_config XML 檔案,並使用 android:localeConfig 屬性將該檔案加入應用程式資訊清單。如果省略 android:localeConfig 資訊清單項目,表示使用者無法在系統設定中,為應用程式設定與系統語言不同的語言。

如何在使用者的系統設定中手動新增應用程式支援的語言:

  1. 建立名為 res/xml/locales_config.xml 的檔案,並指定應用程式的語言,其中包括應用程式的最終備用語言代碼,也就是 res/values/strings.xml 中指定的語言代碼。

    如要瞭解格式規定,請參閱「如何設定語言代碼名稱格式」一節。 如需最常用的語言代碼清單,另請參閱 locale_config.xml 範例檔案

    例如,假設應用程式支援下列語言,則 locales_config.xml 檔案應採用如下格式:

    • 英文 (美國) 做為最終備用語言代碼
    • 英文 (英國)
    • 法文
    • 日文
    • 中文 (簡體、澳門)
    • 中文 (繁體、澳門)
    <?xml version="1.0" encoding="utf-8"?>
    <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
       <locale android:name="en-US"/>
       <locale android:name="en-GB"/>
       <locale android:name="fr"/>
       <locale android:name="ja"/>
       <locale android:name="zh-Hans-MO"/>
       <locale android:name="zh-Hant-MO"/>
    </locale-config>
    
  2. 在資訊清單中新增一行,指向這個新檔案:

    <manifest>
        ...
        <application
            ...
            android:localeConfig="@xml/locales_config">
        </application>
    </manifest>
    

您可以使用 LocaleManager.setOverrideLocaleConfig 動態更新應用程式的 localeConfig,自訂 Android「設定」中每個應用程式語言清單顯示的語言組合。這樣您就可以自訂各區域的語言清單、執行 A/B 實驗,以及提供更新的語言代碼 (如果應用程式使用伺服器端本地化推送作業,如以下範例所示):

Kotlin

//For setOverrideLocaleConfig
val localeManager = applicationContext
    .getSystemService(LocaleManager::class.java)
localeManager.overrideLocaleConfig = LocaleConfig(
LocaleList.forLanguageTags("en-US,ja-JP,zh-Hans-SG")
)

//For getOverrideLocaleConfig
// The app calls the API to get the override LocaleConfig
val overrideLocaleConfig = localeManager.overrideLocaleConfig
// If the returned overrideLocaleConfig isn't equal to NULL, then the app calls the API to get the supported Locales
val supportedLocales = overrideLocaleConfig.supportedLocales()

Java

//For setOverrideLocaleConfig
mContext.getSystemService(LocaleManager.class).setOverrideLocaleConfig(new LocaleConfig(LocaleList.forLanguageTags("en-US,ja-JP,zh-Hans-SG")));

//For getOverrideLocaleConfig
// The app calls the API to get the override LocaleConfig
LocaleConfig overrideLocaleConfig = mContext.getSystemService(LocaleManager.class).getOverrideLocaleConfig();
// If the returned overrideLocaleConfig isn't equal to NULL, then the app calls the API to get the supported Locales
LocaleList supportedLocales = overrideLocaleConfig.getSupportedLocales();

此外,輸入法編輯器現在可以使用 LocaleManager.getApplicationLocales 瞭解目前應用程式的 UI 語言,以便更新鍵盤語言,如下所示:

Kotlin

val currentAppLocales: LocaleList = applicationContext.getSystemService(LocaleManager::class.java).getApplicationLocales(appPackageName)

Java

LocaleList currentAppLocales =
    mContext.getSystemService(LocaleManager.class).getApplicationLocales(appPackageName);

在 Gradle 中指定支援的語言

如果您還沒有在應用程式模組層級的 build.gradle 檔案中提供 resourceConfigurations 屬性,請使用該屬性指定相同的語言:

android {
  ...
  defaultConfig {
    resourceConfigurations += ["en", "en-rGB", "fr", "ja", "b+zh+Hans+MO", "b+zh+Hant+MO"]
  }
}

如果已提供 resourceConfigurations 屬性,建構系統僅會將這些指定語言的語言資源納入 APK,以防止納入其他程式庫支援但您的應用程式不支援的語言翻譯字串。詳情請參閱指定應用程式支援的語言相關說明。

使用者如何在系統設定中選取應用程式語言

使用者可以透過系統設定,為各個應用程式選取偏好的語言。他們可以透過兩種方法存取這些設定:

  • 透過「系統」設定

    依序點選「設定」>「系統」>「語言與輸入設定」>「應用程式語言」> (選取所需應用程式)

  • 透過「應用程式」設定

    依序點選「設定」>「應用程式」> (選取所需應用程式) >「語言」

處理應用程式內語言選單

若應用程式已有應用程式內語言選單,或是想要使用如此的語言選單,請使用公用 API 來處理設定 (不要自訂應用程式邏輯) 和為您的應用程式取得使用者的偏好語言。如果您的應用程式內語言選單使用了公用 API,則裝置的系統設定會自動更新,以符合使用者透過應用程式內體驗選擇的任何語言。

為了提供與 Android 舊版本的回溯相容性,我們強烈建議在實作應用程式內語言選單時使用 AndroidX 支援資料庫。不過,您也可以視需要直接實作架構 API

使用 AndroidX 支援資料庫的實作方式

Appcompat 1.6.0 以上版本中,請使用 setApplicationLocales()getApplicationLocales() 方法。請注意,若是 Android 12 (API 級別 32) 以下版本,回溯相容的 API 是與 AppCompatActivity 結構定義搭配運作,而非應用程式結構定義。

舉例來說,如要設定使用者偏好的語言,您可以要求使用者在語言選單中選取所需語言代碼,然後在系統中設定該值:

Kotlin

val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)

Java

LocaleListCompat appLocale = LocaleListCompat.forLanguageTags("xx-YY");
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale);

請注意,除非應用程式會自行處理語言代碼設定變更,否則呼叫 setApplicationLocales() 會導致重建 Activity

使用 AppCompatDelegate.getApplicationLocales() 擷取使用者偏好的語言代碼。使用者可能已從系統設定或應用程式內語言選單中選取了想要的應用程式語言代碼。

支援 Android 12 以下版本

如要支援搭載 Android 12 (API 級別 32) 以下版本的裝置,請在應用程式的 AppLocalesMetadataHolderService 服務資訊清單項目中,將 autoStoreLocales 值設為 true,並將 android:enabled 設為 false,指示 AndroidX 處理語言代碼儲存空間,如以下程式碼片段所示:

<application
  ...
  <service
    android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
    android:enabled="false"
    android:exported="false">
    <meta-data
      android:name="autoStoreLocales"
      android:value="true" />
  </service>
  ...
</application>

請注意,將 autoStoreLocales 值設為 true 會導致主執行緒發生封阻式讀取情形,而如果您正在記錄執行緒違規事件,則可能造成 StrictMode diskReaddiskWrite 違規事件。詳情請參閱 AppCompatDelegate.setApplicationLocales() 相關說明。

處理自訂儲存空間

省略資訊清單項目或將 autoStoreLocales 設為 false 信號,即可自行處理儲存空間。在此情況下,您必須在活動生命週期的 onCreate 之前,以及在 Android 12 (API 級別 32) 以下版本中發出 AppCompatDelegate.setApplicationLocales() 入口呼叫之前,提供已儲存的語言代碼。

如果應用程式有自訂語言代碼儲存位置,建議您在自訂語言代碼儲存空間解決方案與 autoStoreLocales 之間進行一次性交遞,讓使用者能繼續以自己偏好的語言享用您的應用程式。如果裝置是在升級至 Android 13 後首次執行該應用程式,就特別適合使用這種做法。在此情況下,您可以從自訂儲存空間擷取語言代碼,並將語言代碼傳入 AppCompatDelegate.setApplicationLocales(),藉此提供使用者所要求的既有語言代碼。

使用 Android 架構 API 的實作方式

雖然我們強烈建議使用 AndroidX 支援資料庫實作應用程式內語言選單,您還是可以針對搭載 Android 13 的裝置使用 Android 架構中的 setApplicationLocales()getApplicationLocales() 方法。

舉例來說,如要設定使用者偏好的語言,您可以要求使用者在語言選單中選取所需語言代碼,然後在系統中設定該值:

// 1. Inside an activity, in-app language picker gets an input locale "xx-YY"
// 2. App calls the API to set its locale
mContext.getSystemService(LocaleManager.class
    ).setApplicationLocales(new LocaleList(Locale.forLanguageTag("xx-YY")));
// 3. The system updates the locale and restarts the app, including any configuration updates
// 4. The app is now displayed in "xx-YY" language

如要取得目前的使用者偏好語言並顯示在語言選單中,應用程式可從系統中取回這個值:

// 1. App calls the API to get the preferred locale
LocaleList currentAppLocales =
    mContext.getSystemService(LocaleManager.class).getApplicationLocales();
// 2. App uses the returned LocaleList to display languages to the user

其他最佳做法

請謹記下列最佳做法。

在其他應用程式中叫用意圖時考慮語言

以語言為主的意圖或許能讓您指定叫用的應用程式要使用哪種語言。例如 Speech Recognizer API 的 EXTRA_LANGUAGE 功能。

考慮為 Chrome 自訂分頁使用 Accept-Language 標頭

叫用 Chrome 自訂分頁時,建議您透過 Browser.EXTRA_HEADERS 新增 Accept-Language 標頭,以應用程式的語言開啟網頁。

如果在系統設定中移除了個別應用程式語言偏好,請將應用程式語言代碼重設為系統語言代碼

如果您從系統設定移除應用程式的語言偏好設定,也就是從應用程式的 AndroidManifest.xml 移除 android:localeConfig,使用者便無法輕易將應用程式語言重設為系統預設值。

因此,您移除 android:localeConfig 後,請考慮使用 LocaleListCompat.getEmptyLocaleList()LocaleList.getEmptyLocaleList(),將應用程式語言代碼重設為系統語言代碼,如下列程式碼片段所示:

Kotlin

// Use the AndroidX APIs to reset to the system locale for backward and forward compatibility
AppCompatDelegate.setApplicationLocales(
  LocaleListCompat.getEmptyLocaleList()
)

// Or use the Framework APIs for Android 13 and above to reset to the system locale
val context = LocalContext.current
context.getSystemService(LocaleManager::class.java)
  .applicationLocales = LocaleList.getEmptyLocaleList()

Java

// Use the AndroidX APIs to reset to the system locale for backward and forward compatibility
AppCompatDelegate.setApplicationLocales(
  LocaleListCompat.getEmptyLocaleList()
);

// Or use the Framework APIs for Android 13 and above to reset to the system locale
mContext.getSystemService(LocaleManager.class)
  .setApplicationLocales(LocaleList.getEmptyLocaleList());

其他資源

如需其他資訊,請參閱程式碼範例、網誌文章和影片。

locale_config.xml 檔案範例

Android 開放原始碼計畫 (AOSP) 已預設為一組標準的最常用語言代碼提供系統層級的翻譯。本節所提供的 locale_config.xml 範例檔案逐一顯示了這些語言代碼的建議格式。您可以參考這個範例檔案,瞭解如何為應用程式支援的語言組合自行建構 locale_config.xml 檔案。

<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
   <locale android:name="af"/> <!-- Afrikaans -->
   <locale android:name="am"/> <!-- Amharic -->
   <locale android:name="ar"/> <!-- Arabic -->
   <locale android:name="as"/> <!-- Assamese -->
   <locale android:name="az"/> <!-- Azerbaijani -->
   <locale android:name="be"/> <!-- Belarusian -->
   <locale android:name="bg"/> <!-- Bulgarian -->
   <locale android:name="bn"/> <!-- Bengali -->
   <locale android:name="bs"/> <!-- Bosnian -->
   <locale android:name="ca"/> <!-- Catalan -->
   <locale android:name="cs"/> <!-- Czech -->
   <locale android:name="da"/> <!-- Danish -->
   <locale android:name="de"/> <!-- German -->
   <locale android:name="el"/> <!-- Greek -->
   <locale android:name="en-AU"/> <!-- English (Australia) -->
   <locale android:name="en-CA"/> <!-- English (Canada) -->
   <locale android:name="en-GB"/> <!-- English (United Kingdom) -->
   <locale android:name="en-IN"/> <!-- English (India) -->
   <locale android:name="en-US"/> <!-- English (United States) -->
   <locale android:name="en-XA"/> <!-- English (Pseudo-Accents) -->
   <locale android:name="es"/> <!-- Spanish (Spain) -->
   <locale android:name="es-US"/> <!-- Spanish (United States) -->
   <locale android:name="et"/> <!-- Estonian -->
   <locale android:name="eu"/> <!-- Basque -->
   <locale android:name="fa"/> <!-- Farsi -->
   <locale android:name="fi"/> <!-- Finnish -->
   <locale android:name="fr"/> <!-- French (France) -->
   <locale android:name="fr-CA"/> <!-- French (Canada) -->
   <locale android:name="gl"/> <!-- Galician -->
   <locale android:name="gu"/> <!-- Gujarati -->
   <locale android:name="hi"/> <!-- Hindi -->
   <locale android:name="hr"/> <!-- Croatian -->
   <locale android:name="hu"/> <!-- Hungarian -->
   <locale android:name="hy"/> <!-- Armenian -->
   <locale android:name="in"/> <!-- Indonesian -->
   <locale android:name="is"/> <!-- Icelandic -->
   <locale android:name="it"/> <!-- Italian -->
   <locale android:name="iw"/> <!-- Hebrew -->
   <locale android:name="ja"/> <!-- Japanese -->
   <locale android:name="ka"/> <!-- Georgian -->
   <locale android:name="kk"/> <!-- Kazakh -->
   <locale android:name="km"/> <!-- Khmer -->
   <locale android:name="kn"/> <!-- Kannada -->
   <locale android:name="ko"/> <!-- Korean -->
   <locale android:name="ky"/> <!-- Kyrgyz -->
   <locale android:name="lo"/> <!-- Lao -->
   <locale android:name="lt"/> <!-- Lithuanian -->
   <locale android:name="lv"/> <!-- Latvian -->
   <locale android:name="mk"/> <!-- Macedonian -->
   <locale android:name="ml"/> <!-- Malayalam -->
   <locale android:name="mn"/> <!-- Mongolian -->
   <locale android:name="mr"/> <!-- Marathi -->
   <locale android:name="ms"/> <!-- Malay -->
   <locale android:name="my"/> <!-- Burmese -->
   <locale android:name="my-MM"/> <!-- Burmese (Myanmar) -->
   <locale android:name="nb"/> <!-- Norwegian -->
   <locale android:name="ne"/> <!-- Nepali -->
   <locale android:name="nl"/> <!-- Dutch -->
   <locale android:name="or"/> <!-- Odia -->
   <locale android:name="pa"/> <!-- Punjabi -->
   <locale android:name="pl"/> <!-- Polish -->
   <locale android:name="pt-BR"/> <!-- Portuguese (Brazil) -->
   <locale android:name="pt-PT"/> <!-- Portuguese (Portugal) -->
   <locale android:name="ro"/> <!-- Romanian -->
   <locale android:name="ru"/> <!-- Russian -->
   <locale android:name="si"/> <!-- Sinhala -->
   <locale android:name="sk"/> <!-- Slovak -->
   <locale android:name="sl"/> <!-- Slovenian -->
   <locale android:name="sq"/> <!-- Albanian -->
   <locale android:name="sr"/> <!-- Serbian (Cyrillic) -->
   <locale android:name="sr-Latn"/> <!-- Serbian (Latin) -->
   <locale android:name="sv"/> <!-- Swedish -->
   <locale android:name="sw"/> <!-- Swahili -->
   <locale android:name="ta"/> <!-- Tamil -->
   <locale android:name="te"/> <!-- Telugu -->
   <locale android:name="th"/> <!-- Thai -->
   <locale android:name="tl"/> <!-- Filipino -->
   <locale android:name="tr"/> <!-- Turkish -->
   <locale android:name="uk"/> <!-- Ukrainian -->
   <locale android:name="ur"/> <!-- Urdu -->
   <locale android:name="uz"/> <!-- Uzbek -->
   <locale android:name="vi"/> <!-- Vietnamese -->
   <locale android:name="zh-CN"/> <!-- Chinese (Simplified) -->
   <locale android:name="zh-HK"/> <!-- Chinese (Hong Kong) -->
   <locale android:name="zh-TW"/> <!-- Chinese (Traditional) -->
   <locale android:name="zu"/> <!-- Zulu -->
</locale-config>