支援不同的語言和文化

應用程式可內含特定文化專屬的資源。舉例來說,應用程式可以加入文化專屬字串,並翻譯成目前語言代碼的語言。

建議您將文化專屬資源與應用程式的其餘部分區隔開來。Android 會根據系統語言代碼設定來解析語言和文化專屬資源。您可以使用 Android 專案中的資源目錄,為不同語言代碼提供支援。

您可以針對使用自家應用程式的使用者,指定專為相關文化量身打造的資源。您可提供任何適用於使用者語言和文化的資源類型。例如,以下螢幕截圖顯示應用程式根據裝置預設 (en_US) 語言代碼和西班牙文 (es_ES) 語言代碼分別顯示的字串及可繪製資源。

應用程式會根據目前的語言代碼顯示不同文字和圖示

圖 1 應用程式會根據目前的語言代碼使用不同資源。

使用 Android SDK 工具建立專案時,這些工具會在專案頂層產生 res/ 目錄。這個 res/ 目錄提供各種資源類型的子目錄,還有一些用來保存字串值的預設檔案 (例如 res/values/strings.xml)。

支援不同語言並不侷限於使用語言代碼專屬的資源。部分使用者會選擇使用從右至左 (RTL) 書寫系統的語言 (例如阿拉伯文或希伯來文),做為使用者介面語言代碼。其他使用者就算設定使用從左至右 (LTR) 書寫系統的語言 (例如英文) 做為使用者介面語言代碼,仍會透過使用 RTL 書寫系統的語言查看或產生內容。為了同時支援這兩類使用者,您的應用程式必須執行下列步驟:

  • 針對 RTL 語言代碼部署 RTL UI 版面配置。
  • 偵測及宣告格式化訊息內的文字資料方向。如本指南所述,通常可呼叫方法來決定文字資料方向。

建立語言代碼目錄和資源檔案

如要新增對更多語言代碼的支援功能,請在 res/ 中建立其他目錄,每個目錄的名稱都必須採用以下格式:

<resource type>-b+<language code>[+<country code>]

例如,values-b+es/ 包含使用 es 語言代碼的地區專用字串資源。同理,mipmap-b+es+ES/ 會包含使用 es 語言代碼和 ES 國家/地區代碼的地區專用圖示。

Android 系統會根據裝置在執行階段的語言代碼設定載入適用的資源。詳情請參閱「提供替代資源」一文。

決定要支援的語言代碼之後,請建立資源子目錄和檔案。例如:

MyProject/
    res/
       values/
           strings.xml
       values-b+es/
           strings.xml
       mipmap/
           country_flag.png
       mipmap-b+es+ES/
           country_flag.png

將本地化資源填入資源檔案。以下是本地化字串和圖片資源檔案的範例:

/values/strings.xml 中的英文字串 (預設語言代碼):

<resources>
    <string name="hello_world">Hello World!</string>
</resources>

/values-b+es/strings.xml 中的西班牙文字串 (es 語言代碼):

<resources>
    <string name="hello_world">¡Hola Mundo!</string>
</resources>

/mipmap/country_flag.png 中的美國國旗圖示 (預設語言代碼):

美國的國旗圖示

圖 2. 用於預設 (en_US) 語言代碼的圖示

/mipmap-b+es+ES/country_flag.png 中的西班牙國旗圖示 (es_ES 語言代碼):

西班牙的國旗圖示

圖 3. 用於 es_ES 語言代碼的圖示。

注意:您可以針對任何資源類型使用設定限定詞,例如語言代碼限定詞。舉例來說,您可能想要提供點陣圖可繪項目的本地化版本。詳情請參閱「將應用程式本地化」。

使用應用程式中的資源

您可以使用每項資源的 name 屬性 (R.<resource type>.<resource name>) 參照原始碼及其他 XML 檔案中的資源。系統提供各種透過此方式接受資源的方法,如以下範例所示:

Kotlin

// Get a string resource
val hello = resources.getString(R.string.hello_world)

// Or supply a string resource to a method that requires a string
TextView(this).apply {
    setText(R.string.hello_world)
}

Java

// Get a string resource
String hello = getResources().getString(R.string.hello_world);

// Or supply a string resource to a method that requires a string
TextView textView = new TextView(this);
textView.setText(R.string.hello_world);

在 XML 檔案中,只要 XML 屬性接受相容的值,您就可以使用 @<resource type>/<resource name> 語法參照資源,如以下範例所示:

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@mipmap/country_flag" />

注意:如要確保系統優先採用使用者語言設定,請使用 resConfigs 屬性指定應用程式支援的語言。詳情請參閱「指定應用程式支援的語言」。

設定訊息的文字格式

應用程式最常見的工作之一是設定文字格式。本地化的訊息會經過格式化,在適當位置插入文字和數字資料。但在處理 RTL 使用者介面或 RTL 資料時,簡單的格式化可能會顯示錯誤或甚至無法閱讀的輸出文字。

如阿拉伯文、希伯來文、波斯文和烏都文等語言都是以 RTL 方向書寫,但部分元素 (例如數字和嵌入式的 LTR 文字) 則是以 LTR 方向書寫,夾雜在其他 RTL 文字中。此外,使用 LTR 書寫系統的語言 (包括英文) 也是雙向語言,因為這類語言可能包含必須以 RTL 方向顯示的 RTL 嵌入式文字。

應用程式經常會產生這種方向相反的嵌入式文字,例如將任意語言及任意文字方向的文字資料插入本地化訊息時,就可能發生這種情況。這種方向混合的情況通常不會明確指出反方向文字的開始和結束位置,因此應用程式產生的文字可能會導致使用者體驗不佳。

雖然系統對雙向文字的預設處理方式通常可正常顯示文字,但當應用程式將文字插入本地化訊息時,系統仍可能無法正確顯示文字。以下列舉一些文字可能無法正確顯示的情況:

  • 在訊息開頭插入的文字:

    PERSON_NAME 來電

  • 開頭為數字的文字,例如地址或電話號碼:

    987 654-3210

  • 開頭為標點符號的文字,例如電話號碼:

    +19876543210

  • 結尾為標點符號的文字:

    確定嗎?

  • 已同時包含兩個方向的文字:

    בננה 這個字詞是希伯來文的香蕉。

範例

假設某個應用程式有時需要顯示「你是不是要輸入 %s?」訊息,並在執行階段插入地址來取代 %s。應用程式支援不同的使用者介面語言代碼,因此訊息是來自語言代碼專屬的資源,並在裝置設為 RTL 語言代碼時採用 RTL 方向。以希伯來文 UI 為例,訊息會如下所示:

האם התכוונת ל %s

但該建議地址可能來自資料庫,且當中未提供指定的語言代碼文字。舉例來說,假如相關地址是位於美國加州的某個地點,則該地址在資料庫中會顯示為英文。如果在 RTL 訊息中插入「15 Bay Street, Laurel, CA」這個地址,但未提供任何文字方向相關提示,則系統可能無法產生預期或正確的結果,例如以下所示:

האם התכוונת ל 15 Bay Street, Laurel, CA?

門牌號碼顯示在地址的右側,而不是如預期的左側,因此門牌號碼看起來更像奇怪的郵遞區號。如果在採用 LTR 文字方向的訊息中加入 RTL 文字,也可能會發生這種問題。

說明與解決方案

此範例中之所以會有這個問題,是因為文字格式設定工具並未將「15」指定為地址的一部分,因此系統無法判斷「15」是應置於地址之前的 RTL 文字,還是置於地址之後的 LTR 文字。

如要解決這個問題,請使用 BidiFormatter 類別的 unicodeWrap() 方法。此方法會偵測字串方向,並以宣告該方向的萬國碼 (Unicode) 格式字元包裝該字串。

下列程式碼片段說明如何使用 unicodeWrap()

Kotlin

val mySuggestion = "15 Bay Street, Laurel, CA"
val myBidiFormatter: BidiFormatter = BidiFormatter.getInstance()

// The "did_you_mean" localized string resource includes
// a "%s" placeholder for the suggestion.
String.format(getString(R.string.did_you_mean), myBidiFormatter.unicodeWrap(mySuggestion))

Java

String mySuggestion = "15 Bay Street, Laurel, CA";
BidiFormatter myBidiFormatter = BidiFormatter.getInstance();

// The "did_you_mean" localized string resource includes
// a "%s" placeholder for the suggestion.
String.format(getString(R.string.did_you_mean),
        myBidiFormatter.unicodeWrap(mySuggestion));

由於目前「15」位於宣告為 LTR 的文字內容中,因此它會顯示在正確的位置:

האם התכוונת ל 15 Bay Street, Laurel, CA?

請針對插入本地化訊息的每段文字使用 unicodeWrap() 方法,但以下任一情況除外:

  • 正在將文字插入機器可讀取的字串中,如 URI 或 SQL 查詢。
  • 您知道該段文字已適當包裝。

注意:如果應用程式指定的是 Android 4.3 (API 級別 18) 以上版本,請使用 Android Framework 所提供的 BidiFormatter 版本。否則,請使用支援資料庫所提供的 BidiFormatter 版本。

設定數字格式

請使用格式字串 (而非方法呼叫),將數字轉換為應用程式邏輯中的字串:

Kotlin

var myIntAsString = "$myInt"

Java

String myIntAsString = String.format("%d", myInt);

系統會針對您的語言代碼適當設定數字格式,可能包括使用一組不同的數字。

如果您在某個裝置上使用 String.format() 建立 SQL 查詢,且該裝置設定的語言代碼使用的是自己的數字集 (例如波斯文和大部分的阿拉伯語言代碼),若查詢有任何參數為數字,就會發生問題。這是因為這組數字是採用該語言代碼的數字格式,而這些數字在 SQL 中無效。

如要保留 ASCII 格式的數字,並且讓 SQL 查詢繼續有效,您必須改用 String.format() 的超載版本,並在當中加入語言代碼做為第一個參數。請使用語言代碼引數 Locale.US

支援版面配置鏡像功能

使用 RTL 指令碼的使用者會偏好 RTL 使用者介面,其中包括靠右對齊的選單、靠右對齊的文字,以及指向左側的向前箭頭。

圖 4 顯示「設定」應用程式中 LTR 版本的畫面與其 RTL 對應項目之間的對比:

通知區域位於畫面右上角附近靠右對齊,應用程式列內的選單按鈕位於左上角附近,畫面主要區域中的內容靠左對齊且會從左至右顯示,而返回按鈕則位於左下角附近並指向左側。 通知區域位於畫面左上角附近靠左對齊,應用程式列內的選單按鈕位於右上角附近,畫面主要區域中的內容靠右對齊且會從右至左顯示,而返回按鈕則位於右下角附近並指向右側。
圖 4. 設定畫面的 LTR 和 RTL 變化版本。

在應用程式中新增 RTL 支援時,請留意以下要點:

  • 應用程式只有在搭載 Android 4.2 (API 級別 17) 以上版本的裝置上執行時,才支援 RTL 文字鏡像功能。如要瞭解如何在舊版裝置上支援文字鏡像功能,請參閱本指南中的「提供對舊版應用程式的支援」一節。
  • 如要測試應用程式是否支援 RTL 文字方向,請按照本指南的說明使用開發人員選項進行測試,並邀請採用 RTL 書寫系統的使用者來使用您的應用程式。

注意:如要查看與版面配置鏡像相關的其他設計指南 (包括適合及不適合建立鏡像的元素清單),請參閱雙向性 Material Design 指南。

如要建立應用程式中 UI 版面配置的鏡像,讓它根據 RTL 語言代碼顯示從右至左的版面配置,請完成以下各節所述的步驟。

修改版本和資訊清單檔案

請修改應用程式模組的 build.gradle 檔案和應用程式資訊清單檔案,如下所示:

build.gradle (Module: app)

Groovy

android {
    ...
    defaultConfig {
        targetSdkVersion 17 // Or higher
        ...
    }
}

Kotlin

android {
    ...
    defaultConfig {
        targetSdkVersion(17) // Or higher
        ...
    }
}

AndroidManifest.xml

<manifest ... >
    ...
    <application ...
        android:supportsRtl="true">
    </application>
</manifest>

注意:如果應用程式指定的是 Android 4.1.1 (API 級別 16) 以下版本,系統會忽略 android:supportsRtl 屬性,以及應用程式版面配置檔案中的所有 startend 屬性值。在此情況下,應用程式不會自動建立 RTL 版面配置鏡像。

更新現有資源

在現有的版面配置資源檔案中,將 leftright 分別轉換成 startend。如此一來,架構就能依據使用者的語言設定,對齊應用程式的 UI 元素。

注意:更新資源之前,請先瞭解如何提供對舊版應用程式的支援,或是如何支援指定 Android 4.1.1 (API 級別 16) 以下版本的應用程式。

如要使用架構的 RTL 對齊功能,請在表 1 所列的版面配置檔案中變更屬性。

表 1. 應用程式支援多種文字方向時使用的屬性

僅支援 LTR 的屬性 支援 LTR 和 RTL 的屬性
android:gravity="left" android:gravity="start"
android:gravity="right" android:gravity="end"
android:layout_gravity="left" android:layout_gravity="start"
android:layout_gravity="right" android:layout_gravity="end"
android:paddingLeft android:paddingStart
android:paddingRight android:paddingEnd
android:drawableLeft android:drawableStart
android:drawableRight android:drawableEnd
android:layout_alignLeft android:layout_alignStart
android:layout_alignRight android:layout_alignEnd
android:layout_marginLeft android:layout_marginStart
android:layout_marginRight android:layout_marginEnd
android:layout_alignParentLeft android:layout_alignParentStart
android:layout_alignParentRight android:layout_alignParentEnd
android:layout_toLeftOf android:layout_toStartOf
android:layout_toRightOf android:layout_toEndOf

表 2 說明系統如何根據目標 SDK 版本、是否已定義 leftright 屬性,以及是否已定義 startend 屬性,處理 UI 對齊屬性。

表 2. 根據目標 SDK 版本和已定義的屬性決定 UI 元素對齊行為

指定 Android 4.2
(API 級別 17) 以上
已定義 Left 和 Right 屬性? 已定義 Start 和 End 屬性? 結果
可轉移 使用 startend 覆寫 leftright
使用 leftright
不可轉移 使用 startend
使用 leftright (忽略 startend)
使用 leftright
startend 會解析為 leftright

新增方向與語言專屬資源

這個步驟涉及新增特定版本版面配置、可繪項目和值的資源檔案,檔案中包含不同語言和文字方向的自訂值。

在 Android 4.2 (API 級別 17) 以上版本中,您可以使用 -ldrtl (layout-direction-right-to-left) 和 -ldltr (layout-direction-left-to-right) 資源限定詞。為了保有載入現有資源時的回溯相容性,舊版 Android 可以使用資源的語言限定詞來推斷正確的文字方向。

假如您要新增支援 RTL 書寫系統的特定版面配置檔案,例如希伯來文、阿拉伯文和波斯文等語言,方法是在 res/ 目錄中新增 layout-ldrtl/ 目錄,如以下範例所示:

res/
    layout/
        main.xml This layout file is loaded by default.
    layout-ldrtl/
        main.xml This layout file is loaded for languages using an
                 RTL text direction, including Arabic, Persian, and Hebrew.

如果您想新增專為阿拉伯文文字設計的版面配置特定版本,則目錄結構將如下所示:

res/
    layout/
        main.xml This layout file is loaded by default.
    layout-ar/
        main.xml This layout file is loaded for Arabic text.
    layout-ldrtl/
        main.xml This layout file is loaded only for non-Arabic
                 languages that use an RTL text direction.

注意:語言專屬資源的優先順序高於版面配置方向專屬資源,而版面配置方向專屬資源的優先順序則高於預設資源。

使用支援的小工具

自 Android 4.2 (API 級別 17) 起,多數架構 UI 元素都會自動支援 RTL 文字方向,但是仍有部分架構元素 (例如 ViewPager) 不支援 RTL 文字方向。

主畫面小工具支援 RTL 文字方向,但前提是相應的資訊清單檔案含有 android:supportsRtl="true" 屬性指派設定。

提供對舊版應用程式的支援

如果應用程式指定的是 Android 4.1.1 (API 級別 16) 以下版本,則除了 startend 之外,還必須加入 leftright 屬性。

如要檢查版面配置是否需要使用 RTL 文字方向,請運用以下邏輯:

Kotlin

private fun shouldUseLayoutRtl(): Boolean {
    return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
        View.LAYOUT_DIRECTION_RTL == layoutDirection
    } else {
        false
    }
}

Java

private boolean shouldUseLayoutRtl() {
    if (android.os.Build.VERSION.SDK_INT >=
            android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
        return View.LAYOUT_DIRECTION_RTL == getLayoutDirection();
    } else {
        return false;
    }
}

注意:為避免發生相容性問題,請使用 Android SDK Build Tools 23.0.1 以上版本。

使用開發人員選項進行測試

在搭載 Android 4.4 (API 級別 19) 以上版本的裝置上,您可以啟用裝置端開發人員選項中的「強制使用從右至左版面配置方向」。這項設定可讓您在 RTL 模式下查看使用 LTR 書寫系統的文字,例如英文文字。

更新應用程式邏輯

本節說明為處理多個文字方向而調整應用程式時,要更新的應用程式邏輯的特定部分。

屬性變動

如要處理任何 RTL 相關屬性的異動,例如版面配置方向、版面配置參數、邊框間距、文字方向、文字對齊方式或可繪項目的位置,可以使用 onRtlPropertiesChanged() 回呼。這個回呼可讓您取得目前的版面配置方向,並據此更新活動的 View 物件。

View

如果您建立的 UI 小工具並非直接屬於活動檢視區塊階層的一部分 (例如對話方塊或類似浮動式訊息的 UI 元素),請根據內容設定正確的版面配置方向。下列程式碼片段說明如何完成這項程序:

Kotlin

val config: Configuration = context.resources.configuration
view.layoutDirection = config.layoutDirection

Java

final Configuration config =
    getContext().getResources().getConfiguration();
view.setLayoutDirection(config.getLayoutDirection());

以下幾個 View 類別的方法需要額外考量:

onMeasure()
檢視區塊的測量數據可能會因文字方向而異。
onLayout()
如要建立自己的版面配置實作方式,您必須在自己的 onLayout() 版本中呼叫 super(),並調整自訂邏輯以支援 RTL 書寫系統。
onDraw()
如要實作自訂檢視區塊,或是在繪圖中加入進階功能,您必須更新程式碼以支援 RTL 書寫系統。請使用以下程式碼確認您的小工具是否處於 RTL 模式:

Kotlin

// On devices running Android 4.1.1 (API level 16) and lower,
// you can call the isLayoutRtl() system method directly.
fun isLayoutRtl(): Boolean = layoutDirection == LAYOUT_DIRECTION_RTL

Java

// On devices running Android 4.1.1 (API level 16) and lower,
// you can call the isLayoutRtl() system method directly.
public boolean isLayoutRtl() {
    return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}

可繪項目

如果您有需要針對 RTL 版面配置建立鏡像的可繪項目,請依據裝置搭載的 Android 版本完成以下任一步驟:

  • 在搭載 Android 4.3 (API 級別 18) 以下版本的裝置上,新增並定義 -ldrtl 資源檔案。
  • 在搭載 Android 4.4 (API 級別 19) 以上版本的裝置上,在定義可繪項目時使用 android:autoMirrored="true",讓系統為您處理 RTL 版面配置的鏡像作業。

    注意:android:autoMirrored 屬性僅適用於簡單的可繪項目,其雙向鏡像只是整個可繪項目的圖形鏡像。如果可繪項目包含多個元素,或者反映可繪項目會改變其解讀含義,您可以自行執行鏡像作業。請盡可能諮詢雙向專家,判斷鏡像可繪項目對使用者來說是否合理。

Gravity

如果應用程式的版面配置程式碼使用 Gravity.LEFTGravity.RIGHT,請分別將這些值變更為 Gravity.STARTGravity.END

如果您有依附於 Gravity.LEFTGravity.RIGHT 屬性的 Kotlin 或 Java 程式碼,可以將 absoluteGravity 設定為與 layoutDirection 一致,使程式碼能根據這項變更進行調整。

舉例來說,如果您使用的是以下程式碼:

Kotlin

when (gravity and Gravity.HORIZONTAL_GRAVITY_MASK) {
    Gravity.LEFT -> {
        // Handle objects that are left-aligned.
    }
    Gravity.RIGHT -> {
        // Handle objects that are right-aligned.
    }
}

Java

switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
    case Gravity.LEFT:
        // Handle objects that are left-aligned.
        break;
    case Gravity.RIGHT:
        // Handle objects that are right-aligned.
        break;
}

請變更為以下程式碼:

Kotlin

val absoluteGravity: Int = Gravity.getAbsoluteGravity(gravity, layoutDirection)
when (absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK) {
    Gravity.LEFT -> {
        // Handle objects that are left-aligned.
    }
    Gravity.RIGHT -> {
        // Handle objects that are right-aligned.
    }
}

Java

final int layoutDirection = getLayoutDirection();
final int absoluteGravity =
        Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
    case Gravity.LEFT:
        // Handle objects that are left-aligned.
        break;
    case Gravity.RIGHT:
        // Handle objects that are right-aligned.
        break;
}

這表示即使您針對 gravity 的值使用 startend,仍然可以保留現有程式碼,以處理靠左對齊與靠右對齊的值。

注意:套用 gravity 設定時,請使用含有 layoutDirection 引數的 Gravity.apply() 超載版本。

邊界和邊框間距

如要在應用程式中支援 RTL 書寫系統,請遵循下列邊界和邊框間距值相關的最佳做法:

支援個別應用程式語言偏好

在許多情況下,多語言使用者會將系統語言設為一種語言 (例如英文),但對於特定應用程式卻想選擇其他語言,例如荷蘭文、中文或北印度文。為協助應用程式為這些使用者提供更優質的體驗,Android 13 針對支援多種語言的應用程式提供以下功能:

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

    應用程式必須在其資訊清單中宣告 android:localeConfig 屬性,以告知系統支援多種語言。詳情請參閱這份操作說明,瞭解如何建立資源檔案,並在應用程式的資訊清單檔案中宣告資源檔案。

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

    如果應用程式採用自訂應用程式內語言選單,可以透過這些 API 讓使用者無論在何處選取語言偏好設定,都能獲得一致的使用者體驗。公用 API 也有助於減少樣板程式碼的數量,且支援分割 APK。此外,也支援透過應用程式自動備份功能儲存應用程式層級的使用者語言設定。

    為了提供與 Android 舊版本的回溯相容性,AndroidX 中也提供同等的 API。建議使用 Appcompat 1.6.0-beta01 以上版本。

    詳情請參閱導入新版 API 的操作說明。

另請參閱

其他資源

如要進一步瞭解如何支援舊版裝置,請參閱下列資源:

網誌文章