字符串资源

字符串资源用于为应用提供具有可选文本样式和格式设置的文本字符串。有三种类型的资源可为应用提供字符串:

String
提供单个字符串的 XML 资源。
字符串数组
提供字符串数组的 XML 资源。
数量字符串(复数)
XML 资源,包含各种复数形式的字符串。

所有字符串都能应用某些样式设置标记和格式设置参数。如需了解有关样式和格式设置字符串的信息,请参阅 格式和样式设置部分。

String

可从应用代码(例如可组合函数)或其他资源文件引用的单个字符串。

文件位置:
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。
示例:
保存在 res/values/strings.xml 的 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> 元素的子项。请注意,您必须对撇号和引号进行转义。如需了解有关如何正确设置字符串样式和格式的信息,请参阅下文的格式和样式设置

无属性。

示例:
保存在 res/values/strings.xml 的 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 book”被翻译为“1 knyga”,“101 books”被翻译为“101 knyga”,与此同时,“a book”被翻译“knyga”,“many books”则被翻译为“daug knygų”。如果英语复数消息包含“a book”(单数)和“many books”(复数),但却没有实际数字,则可翻译为“knyga”(a book)/“daug knygų”(many books),不过根据立陶宛语的规则,系统会在数字为 101 时,显示“knyga”(a single book)。

通常,您可以利用“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(如阿拉伯语中的 0)进行特殊处理时。
one当某种语言要求对 1 这类数字(如英语和大多数其他语言中的数字 1;在俄语中,任何末尾为 1 但非 11 的数字均属此类)进行特殊处理时。
two当某种语言要求对 2 这类数字(如威尔士语中的 2,或斯洛文尼亚语中的 102)进行特殊处理时。
few当某种语言要求对“小”数字(如捷克语中的 2、3 和 4;或波兰语中末尾为 2、3 或 4,但非 12、13 或 14 的数字)进行特殊处理时。
many当某种语言要求对“大”数字(如马耳他语中末尾为 11 至 99 的数字)进行特殊处理时。
other当某种语言未要求对给定数量(如中文中的所有数字,或英语中的 42)进行特殊处理时。
示例:

保存在 res/values/strings.xml 的 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>

保存在 res/values-pl/strings.xml 的 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参数会选择合适的复数字符串, 第二个count参数会插入%d占位符内。如果您的复数字符串没有字符串格式设置,则无需向 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 全角空格)会合拢为一个空格 (" "),因为它们在文件解析为 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> 元素。请注意,开括号使用 HTML 转义,使用 &lt; 表示法。

  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)
}

对于单语言应用或 Compose 中的静态文本,建议直接构建 AnnotatedString。不过,对于需要本地化的带样式文本,请参阅下一部分中详细介绍的 XML <annotation> 方法。

使用注解设置已翻译字符串的样式

对于需要自定义样式 翻译的字符串,请在每个语言区域的 strings.xml 中定义 <annotation> 标记。无论注解在句子中的位置如何,翻译人员都会保留该注解。使用 context.resources.getText() 读取字符串,遍历其 Annotation span,并将结果转换为 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> 标记保持不变。只有检索代码不同。翻译人员仍会移动标记,以在每种语言中封装正确的字词。

其他资源

如需详细了解字符串资源,请参阅以下其他资源:

文档

查看内容