字串資源

字串資源提供應用程式所用的文字字串,並可選擇文字樣式和格式。有三種類型的資源可為應用程式提供字串:

String
提供單一字串的 XML 資源。
字串陣列
提供字串陣列的 XML 資源。
數量字串 (複數)
XML 資源,包含複數形式使用的不同字串。

所有字串都可以套用樣式標記和格式化引數。如需樣式和格式化字串的相關資訊,請參閱「格式化和樣式」一節。

字串

可從應用程式程式碼 (例如可組合函式) 或其他資源檔案參照的單一字串。

檔案位置:
res/values/filename.xml
您可以使用任意檔案名稱。<string> 元素的 name 會做資源 ID 使用。
編譯資源資料類型:
String 的資源指標。
資源參照:
Kotlin:R.string.string_name
XML:@string/string_name
語法:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
元素:
<resources>
必要。這必須是根節點。

沒有任何屬性。

<string>
字串,可包含樣式標記。請注意,您必須逸出格號和引號。如要進一步瞭解如何正確設定字串的樣式及格式,請參閱下方的「格式化和樣式」。

屬性:

name
「字串」。字串的名稱。系統會把此名稱當做資源 ID。
例如:
XML 檔案儲存在 res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

此應用程式程式碼會使用 stringResource() 從可組合函式內擷取字串:

@Composable
fun Greeting() {
    Text(text = stringResource(R.string.hello))
}

注意:如要在可組合函式外部擷取字串,請使用 context.getString(R.string.hello)

您也可以從其他 XML 檔案 (例如 AndroidManifest.xml) 參照字串資源:
<activity
    android:name=".MainActivity"
    android:label="@string/hello" />

字串陣列

可從應用程式參照的字串陣列。

檔案位置:
res/values/filename.xml
您可以使用任意檔案名稱。<string-array> 元素的 name 會做資源 ID 使用。
編譯資源資料類型:
String 陣列的資源指標。
資源參照:
Kotlin:R.array.string_array_name
XML:@[package:]array/string_array_name
語法:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
元素:
<resources>
必要。這必須是根節點。

沒有任何屬性。

<string-array>
定義字串陣列。包含一或多個 <item> 元素。

屬性:

name
「字串」。陣列的名稱。系統會把此名稱當做資源 ID,藉此參照陣列。
<item>
字串,可包含樣式標記。此值可以是另一個字串資源的參照。必須是 <string-array> 元素的子項。請注意,您必須逸出格號和引號。請參閱下方的「格式化和樣式」,以瞭解如何正確設定字串的樣式及格式。

沒有任何屬性。

例如:
XML 檔案儲存在 res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="planets_array">
        <item>Mercury</item>
        <item>Venus</item>
        <item>Earth</item>
        <item>Mars</item>
    </string-array>
</resources>

此應用程式程式碼會從可組合函式內擷取字串陣列 (使用 stringArrayResource()):

@Composable
fun PlanetList() {
    val planets: Array =
        stringArrayResource(R.array.planets_array)
    // Render the array, e.g. inside a LazyColumn.
}

注意:如要在可組合函式外部擷取字串陣列,請使用 context.resources.getStringArray(R.array.planets_array)

數量字串 (複數)

不同的語言,對於數量的文法一致性有不同的規則。以英文為例,數量 1 是一種特殊情況。我們會寫「1 Book」,但如果是其他數量,則會寫「n Books」。單數與複數的區別非常重要,但其他語言則需採用更精細的方式。Android 支援的完整組合為 zeroonetwofewmanyother

決定特定語言和數量的使用方式規則可能會很複雜,因此 Android 提供 pluralStringResource() 等方式,協助您選擇合適的資源。

雖然過去稱為「數量字串」(在 API 中依然如此稱呼),但數量字串「只能」用於複數形式。舉例來說,您不應該使用數量字串實作如 Gmail 的「Inbox」(收件匣) 與有未讀郵件時顯示的「Inbox (12)」這類的項目。使用數量字串而不是 if 陳述式,也許看起來很方便,但一定要注意的一點是某些語言 (例如中文) 就完全沒有單複數的文法差別,因此一律會取得 other 字串。

選擇要使用的字串時,這完全是根據文法的「必要性」而決定。對英文來說,即使數量為 0,也會忽略 zero 的字串,這是因為 0 與 2,或是任何其他除了 1 以外的數字並沒有文法上的差異 (「Zero Books」、「One Book」、「Two Books」等等)。反之,在韓文中,「只會」用到 other 字串。

不過,別誤以為 two 只能套用至數量為 2 的情況:在某種語言中,有可能必須將 2、12、102 等數量視為同一種情況,並採取與其他數量不同的處理方式。如要瞭解語言規則的差異,請諮詢翻譯人員。

如果訊息中不含數量,就可能不適合使用複數。以立陶宛語為例,1 和 101 都使用單數形式,因此「1 本書」會翻譯為「1 knyga」,「101 本書」會翻譯為「101 knyga」。而「一本書」是「knyga」,「許多本書」則是「daug knygų」。如果英文複數訊息包含「a book」(單數) 和「many books」(複數),但沒有實際數字,則可翻譯為「knyga」(一本書)/「daug knygų」(許多書),但根據立陶宛文規則,如果數字剛好是 101,則會顯示「knyga」(一本書)。

通常來說,使用「Books: 1」等數量中立的寫法,就可以避免使用數量字串。如果應用程式可以接受使用這種樣式,您和您的翻譯人員都會輕鬆很多。

在 API 24 以上版本中,您可以改用功能更強大的 ICU MessageFormat 類別。

檔案位置:
res/values/filename.xml
您可以使用任意檔案名稱。<plurals> 元素的 name 會做資源 ID 使用。
資源參照:
在 Kotlin 中:R.plurals.plural_name
語法:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals
        name="plural_name">
        <item
            quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
            >text_string</item>
    </plurals>
</resources>
元素:
<resources>
必要。這必須是根節點。

沒有任何屬性。

<plurals>
字串集合,其中一個字串會根據數量提供。包含一或多個 <item> 元素。

屬性:

name
「字串」。一對字串的名稱。系統會把此名稱當做資源 ID。
<item>
複數或單數字串。此值可以是另一個字串資源的參照。必須是 <plurals> 元素的子項。請注意,您必須逸出格號和引號。請參閱下方的「格式化和樣式」,以瞭解如何正確設定字串的樣式及格式。

屬性:

quantity
「關鍵字」。表示此字串使用時機的值。有效的值,僅在括號中列舉部分範例:
說明
zero當語言需要以特別方式處理數字 0 時 (如阿拉伯文)。
one當語言需要以特別的方式處理「一」等數字時 (如英文中的數字 1 以及大多數其他語言的數字 1;如果是俄文,任何以 1 結尾、結尾為 11 的號碼都屬於此類別)。
two當語言需要以特別的方式處理「二」等數字時 (如威爾斯語的 2,或斯洛維尼亞語的 102)。
few當語言需要以特別的方式處理「小」數字時 (如捷克文的 2、3 和 4;或波蘭文中以 2、3 或 4 結尾,但並非 12、13 或 14 的數字)。
many當語言需要以特別的方式處理「大」數字時 (如馬爾他文結尾為 11-99 的數字)。
other當語言不需以特別的方式處理任何數量時 (如中文的所有數字,或英文中的 42)。
例如:

XML 檔案儲存在 res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals name="numberOfSongsAvailable">
        <!--
             As a developer, you should always supply "one" and "other"
             strings. Your translators will know which strings are actually
             needed for their language. Always include %d in "one" because
             translators will need to use %d for languages where "one"
             doesn't mean 1 (as explained above).
          -->
        <item quantity="one">%d song found.</item>
        <item quantity="other">%d songs found.</item>
    </plurals>
</resources>

XML 檔案儲存在 res/values-pl/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals name="numberOfSongsAvailable">
        <item quantity="one">Znaleziono %d piosenkę.</item>
        <item quantity="few">Znaleziono %d piosenki.</item>
        <item quantity="other">Znaleziono %d piosenek.</item>
    </plurals>
</resources>

此應用程式程式碼會從可組合函式內擷取複數字串 (使用 pluralStringResource()):

@Composable
fun SongCount(count: Int) {
    Text(
        text = pluralStringResource(
            R.plurals.numberOfSongsAvailable,
            count,
            count,
        )
    )
}

使用 pluralStringResource() 函式時,如果您的字串包含有數字的字串格式,您就必須傳遞 count 兩次。舉例來說,如果是 %d songs found 字串,第一個 count 參數會選取適當的複數字串,並在 %d 預留位置插入第二個 count 參數。如果複數字串不包含字串格式,則不需要傳遞第三個參數至 pluralStringResource

注意:如要在可組合函式外擷取複數字串,請使用 context.resources.getQuantityString(R.plurals.numberOfSongsAvailable, count, count)

格式與樣式

以下是有關正確設定字串資源格式和樣是的幾點重要注意事項。

處理特殊字元

如果字串包含 XML 中有特殊用途的字元,您就必須根據標準 XML/HTML 逸出規則逸出字元。如果需要逸出 Android 中具有特殊意義的字元,則應於前面加上反斜線。

根據預設,Android 會將空白字元的序列收合在單一空格中。使用雙引號括住字串的相關部分,就可以避免出現此情況。在這種情況下,所有空白字元 (包括換行符號) 都會保留在引號內的區域中。雙引號也可讓您使用一般的單一未逸出引號。

字元 逸出形式
@ \@
? \?
換行 \n
Tab 鍵 \t
U+XXXX 萬國碼 (Unicode) 字元 \uXXXX
單引號 (')

下列任何項目:

  • \'
  • 以雙引號括住整個字串 (例如:"This'll work")
雙引號 (") \"

請注意,使用單引號括住字串並沒有效果。

以 XML 剖析資源檔案後,就會收合空白字元,且 Android 會逸出。也就是說,<string> &#32; &#8200; &#8195;</string> (空格、標點符號空格、萬國碼 (Unicode) Em 空格) 全部會收合成單一空格 (" "),這是因為以 XML 剖析檔案之後,這些項目都會是萬國碼 (Unicode) 空格。如要保留這些空格,您可以加上引號 (<string>" &#32; &#8200; &#8195;"</string>) 或使用 Android 逸出功能 (<string> \u0032 \u8200 \u8195</string>)。

格式化字串

如要格式化字串,可以在字串資源中加入格式引數,如下方範例資源所示。

<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>

此應用程式程式碼會將引數直接傳遞至 stringResource(),在可組合函式內格式化字串:

@Composable
fun WelcomeMessage(username: String, mailCount: Int) {
    Text(
        text = stringResource(
            R.string.welcome_messages,
            username,
            mailCount,
        )
    )
}

使用 HTML 標記設定樣式

您可以使用 HTML 標記,在字串中加入樣式。例如:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="welcome">Welcome to <b>Android</b>!</string>
</resources>

系統支援下列 HTML 元素:

  • 粗體:<b>
  • 斜體:<i><cite><dfn><em>
  • 文字放大 25%:<big>
  • 縮小 20% 的文字:<small>
  • 設定字型屬性:<font face="font_family" color="hex_color">。可能的字型系列範例包括 monospaceserifsans_serif
  • 設定等寬字型系列:<tt>
  • 刪除線:<s><strike><del>
  • 底線:<u>
  • 上標:<sup>
  • 下標:<sub>
  • 項目符號:<ul><li>
  • 換行符號:<br>
  • 除號:<div>
  • CSS 樣式:<span style="color|background_color|text-decoration">
  • 段落:<p dir="rtl | ltr" style="…">

在某些情況下,您也許會想建立也會做格式字串使用的樣式文字資源。一般而言,這並不會發揮作用,因為格式化方法 (例如 stringResource()) 會去除字串中的所有樣式資訊。解決方法是編寫有逸出實體的 HTML 標記,然後在格式化後使用 AnnotatedString.fromHtml() 進行復原。例如:

  1. 儲存樣式文字資源為 HTML 逸出字串:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    在此格式字串中,系統會加入 <b> 元素。請注意,左括號是使用 &lt; 標記法的 HTML 逸出。

  2. 然後依照一般方式格式化字串,不過也要呼叫 AnnotatedString.fromHtml(),將 HTML 文字轉換為樣式 Compose 字串。

由於 fromHtml() 會格式化所有 HTML 實體,因此請務必在搭配格式化文字的字串中使用 TextUtils.htmlEncode() 逸出任何可能的 HTML 字元。

import android.text.TextUtils
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.fromHtml

@Composable
fun WelcomeHtmlMessage(username: String, mailCount: Int) {
    // Escape the username in case it contains characters like "<" or "&"
    val escapedUsername = TextUtils.htmlEncode(username)

    val text = stringResource(
        R.string.welcome_messages,
        escapedUsername,
        mailCount,
    )

    Text(
        text = AnnotatedString.fromHtml(text)
    )
}

使用 AnnotatedString 設定樣式

AnnotatedString 是 Compose 文字物件,您可以使用顏色和字體粗細等屬性設定樣式。使用 buildAnnotatedStringwithStyle 以程式輔助方式建構樣式文字。

這段應用程式程式碼會建立具有混合樣式的單一文字元素:

@Composable
fun StyledGreeting() {
    val styled = buildAnnotatedString {
        append("Welcome to ")
        withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
            append("Android")
        }
        append("!")
    }
    Text(text = styled)
}

如要套用顏色、字型大小和文字裝飾,請使用 SpanStyle。如要套用段落層級的樣式 (例如對齊方式或行高),請使用 ParagraphStyle

@Composable
fun RichText() {
    val text = buildAnnotatedString {
        withStyle(ParagraphStyle(lineHeight = 24.sp, textAlign = TextAlign.Center)) {
            withStyle(SpanStyle(color = Color.Gray)) {
                append("Hello, ")
            }
            withStyle(
                SpanStyle(
                    fontWeight = FontWeight.Bold,
                    color = Color.Red,
                )
            ) {
                append("world")
            }
            append("!")
        }
    }
    Text(text = text)
}

建議您直接建構 AnnotatedString,適用於單一語言的應用程式,或 Compose 中的靜態文字。不過,如要翻譯需要本地化的樣式文字,請參閱下一節詳述的 XML <annotation> 方法。

使用註解設定翻譯字串的樣式

如要為字串自訂樣式翻譯,請在每個語言代碼的 strings.xml 中定義 <annotation> 標記。無論註解在句子中的位置為何,譯者都會保留註解。使用 context.resources.getText() 讀取字串、走訪其 Annotation 範圍,並將結果轉換為 AnnotatedString

@Composable
fun AnnotatedTitle() {
    val context = LocalContext.current
    val source = context.resources.getText(R.string.title) as SpannedString
    val text = buildAnnotatedString {
        append(source.toString())
        source.getSpans(0, source.length, Annotation::class.java)
            .forEach { annotation ->
                if (annotation.key == "font" &&
                    annotation.value == "title_emphasis") {
                    addStyle(
                        SpanStyle(
                            fontFamily = FontFamily(
                                Font(R.font.permanent_marker)
                            )
                        ),
                        source.getSpanStart(annotation),
                        source.getSpanEnd(annotation),
                    )
                }
            }
    }
    Text(text = text)
}

XML 中的 <annotation> 標記維持不變。只有取回代碼不同。翻譯人員仍須移動標記,在每種語言中包住正確的字詞。

其他資源

如要進一步瞭解字串資源,請參閱下列其他資源:

說明文件

Views content