# General Translation Knowledge Base: 如何使用 strings.xml 本地化 Android 应用？
URL: https://generaltranslation.com/zh/kb/localize-android-app.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 将所有默认字符串放在 res/values/strings.xml 中，在带区域设置限定符的文件夹（如 values-es 或 values-b+zh+Hant+TW）下为每种语言各添加一个 strings.xml，并通过 stringResource 或 getString 读取文本。翻译可以手动完成，也可以在 Play Console 中完成，或者使用 General Translation 的 CLI——它会生成每个 values 文件夹，并为每种目标语言重建复数形式。
Category: 国际化
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

Android 会在运行时根据设备的区域设置，从对应名称的文件夹中选取资源。默认文本位于 `res/values/strings.xml` 中，每种语言的译文则是另一个 `strings.xml`，资源名称保持一致，放在 `res/values-es/` 这类文件夹下。因此，你需要完成两项工作：把所有面向用户的字符串都放进 `strings.xml`，并为每个语言文件夹提供高质量的译文。

## 设置字符串资源

### 将所有字符串都放入 res/values/strings.xml

默认文件必须包含应用用到的所有字符串：根据 Android 的本地化指南，只要缺少任何一个，应用就会&quot;无法运行并显示错误&quot;。翻译后的文件可以只包含其中的一个子集，缺少的字符串会回退到默认文件 ([Android: Localize your app](https://developer.android.com/guide/topics/resources/localization)) 。

下面是一个默认文件，包含了你需要用到的各种模式，并逐一附有注释：

```xml title="res/values/strings.xml"
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <!-- 为购物车中的商品发起付款的按钮。 -->
    <string name="checkout">Checkout</string>

    <!-- 使用位置参数，译文就可以调整各个值的顺序。 -->
    <plurals name="welcome">
        <item quantity="one">Hi %1$s, you have %2$d new message.</item>
        <item quantity="other">Hi %1$s, you have %2$d new messages.</item>
    </plurals>

    <!-- 必须转义撇号，否则构建会失败。 -->
    <string name="cart_empty">Your cart\'s empty</string>

    <!-- xliff:g 用于标记不得翻译的文本。 -->
    <string name="countdown"><xliff:g id="time" example="5 days">%1$s</xliff:g> until your trial ends</string>

    <plurals name="items_in_cart">
        <item quantity="one">%d item</item>
        <item quantity="other">%d items</item>
    </plurals>

    <string-array name="sort_options">
        <item>Newest</item>
        <item>Price: low to high</item>
    </string-array>
</resources>
```

在 Compose 中，使用 `stringResource`、`pluralStringResource` 和 `stringArrayResource` 读取这些字符串；在 Views 中，则使用 `context.getString` 和 `resources.getQuantityString` ([Android：字符串资源](https://developer.android.com/guide/topics/resources/string-resource)) 。处理复数形式时，先传入数量以选择对应的形式，再传入各占位符的值 (包括数量本身) 。

```kotlin
val greeting = pluralStringResource(R.plurals.welcome, count, name, count)
val cart = pluralStringResource(R.plurals.items_in_cart, count, count)
val sortOptions = stringArrayResource(R.array.sort_options)
```

React Native 应用中同样有这个文件：模板自带的 `strings.xml` 位于 `android/app/src/main/res/values/` 下，其中定义了 `app_name`，即 Android 显示在图标下方的应用名称；JavaScript UI 则需要另行翻译。 (参见[如何翻译 React Native 应用？](/kb/translate-react-native-app))

### 为每种语言命名文件夹

语言文件夹的名称由 `values-` 加上资源限定符构成。语言使用 ISO 639-1 代码，地区需加小写 `r` 前缀；凡是包含文字体系或数字地区代码的，一律使用 BCP 47 形式，即 `b+` 后接以 `+` 连接的子标签。该形式自 Android 7.0 (API 24) 起受支持 ([Android：应用资源概览](https://developer.android.com/guide/topics/resources/providing-resources)) ：

| 语言           | 区域设置代码       | 文件夹                        |
| ------------ | ------------ | -------------------------- |
| 西班牙语         | `es`         | `res/values-es/`           |
| 葡萄牙语 (巴西)    | `pt-BR`      | `res/values-pt-rBR/`       |
| 西班牙语 (拉丁美洲)  | `es-419`     | `res/values-b+es+419/`     |
| 中文 (繁体，台湾)   | `zh-Hant-TW` | `res/values-b+zh+Hant+TW/` |

区域文件夹只需包含存在差异的字符串。以 Android 官方示例为例，所有英语内容都放在 `values/` 中，`values-en-rGB/` 中只放英式拼写，其余内容均自动回退。 (参见[什么是区域设置代码？](/kb/locale-codes)。)

### 编写不会被译者改坏的字符串

以下规则来自 Android 的[字符串资源指南](https://developer.android.com/guide/topics/resources/string-resource)，可避免翻译导致构建失败或语法出错：

* **复数形式遵循各语言的语法。** 数量类别包括 `zero`、`one`、`two`、`few`、`many` 和 `other`，但每种语言只使用其语法所需的类别：英语即使数量为 0 也不使用 `zero`，而中文和韩语始终只使用 `other`。不要把 `<plurals>` 当作 `if` 语句来处理“没有项目”的情况，而应单独编写一个字符串。
* **使用位置参数。** 借助 `%1$s` 和 `%2$d`，译者可以在目标语言需要时把名称移到数量之后。
* **转义撇号和引号。** 写成 `\'`，或用双引号包裹整个字符串；字面引号则写成 `\"`。开头的 `@` 或 `?` 需转义为 `\@` 和 `\?`，否则 Android 会将该值解析为资源引用。
* **保护不应改动的内容。** 用带有 `id` 和 `example` 的 `<xliff:g>` 包裹占位符和 URL，倒计时字符串就是这样做的 ([Android：本地化您的应用](https://developer.android.com/guide/topics/resources/localization)) 。
* **标记无需翻译的资源。** 为应用名称等值添加 `translatable="false"`，或将其移到 `donottranslate.xml` 文件中。
* **添加评论。** 在资源上方添加评论，告诉译者该文本出现在何处、有多少可用空间。单看 “Checkout” 一词，它既可能是名词，也可能是动词。

## 让用户选择应用语言

从 Android 13 (API 33) 开始，只要应用声明了所支持的语言，用户就可以在系统设置中为该应用单独设置语言。启用 `generateLocaleConfig` 后，Android Gradle Plugin (AGP) 8.1 或更高版本会在构建时根据你的 `values-*` 文件夹自动生成语言列表；这要求 `compileSdk` 为 33 或更高，并提供一个 `res/resources.properties` 文件来指明默认文件夹的区域设置，例如 `unqualifiedResLocale=en-US`。如果想改为手动列出语言，请添加 `res/xml/locale_config.xml`，并在清单文件中将 `android:localeConfig` 指向该文件。两种方式不能同时使用，否则构建会失败 ([Android：按应用设置语言偏好](https://developer.android.com/guide/topics/resources/app-languages)) 。

```kotlin title="app/build.gradle.kts"
android {
    androidResources {
        generateLocaleConfig = true
    }
}
```

如需实现应用内语言选择器，请使用 AppCompat 1.6.0 或更高版本，并在主线程中调用 `setApplicationLocales`。该方法会重建 activity；若使用 Compose，activity 必须继承 `AppCompatActivity`：

```kotlin
// 请在主线程上调用；此操作可能会重启 Activity。
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags("es"))
```

在 Android 12 及更低版本上，只要在清单文件中添加 AppCompat 的 `AppLocalesMetadataHolderService` 并将 `autoStoreLocales` 设为 `true`，AppCompat 就能自动为你持久化保存用户的选择。 (关于选择器的设计，请参阅[语言切换器最佳实践](/kb/language-switcher)。)

## 发现缺失的字符串和硬编码字符串

Android Studio 的 **Translations Editor** 会以网格形式显示所有键，每种语言占一列，并用红色标出问题。右键点击 `strings.xml`，选择 **Open Translations Editor** 即可打开。点击地球图标可添加一种语言并创建对应的 `values-*` 文件夹；勾选 **Untranslatable** 复选框则会自动写入 `translatable="false"` ([Android: Translations Editor](https://developer.android.com/studio/write/translations-editor)) 。

Android Lint 可以在 CI 中执行同样的检查：

* [`MissingTranslation`](https://googlesamples.github.io/android-custom-lint-rules/checks/MissingTranslation.md.html) (error) 会标记某种语言中缺失的字符串。可以用 `tools:ignore="MissingTranslation"` 针对单个资源禁用该检查；同时应在默认文件的 `<resources>` 元素上设置 `tools:locale`，让 lint 知道该文件使用的语言。
* [`ExtraTranslation`](https://googlesamples.github.io/android-custom-lint-rules/checks/ExtraTranslation.md.html) (fatal) 会标记没有默认值的已翻译字符串，这类字符串可能导致应用在缺少该字符串的区域设置下崩溃。
* [`HardcodedText`](https://googlesamples.github.io/android-custom-lint-rules/checks/HardcodedText.md.html) (warning) 会标记布局 XML 中的字面文本，但无法检测 Compose 中的 `Text("...")` 调用。 (参见[如何查找未翻译的硬编码字符串？](/kb/find-hardcoded-strings))

## 测试每种语言

Android 内置了两个伪区域设置，可在译文就绪之前进行测试。`en-XA` 会添加重音符号、拉长文本，并用括号包住每条消息，从而暴露截断和句子拼接问题。`ar-XB` 则将文本切换为从右到左。在 debug 构建类型上设置 `isPseudoLocalesEnabled = true`，在设备上启用开发者选项，然后在设备的语言设置中选择伪区域设置 ([Android：使用伪区域设置进行测试](https://developer.android.com/guide/topics/resources/pseudolocales)) 。 (参见[什么是伪本地化？](/kb/pseudo-localization)。) 

对于阿拉伯语、希伯来语和波斯语，请在 `<application>` 元素上设置 `android:supportsRtl="true"`，并使用 `start` 和 `end` 属性 (如 `paddingStart`、`layout_marginEnd`) 代替 `left` 和 `right`，使布局能够镜像翻转。为带方向性的 drawable 添加 `android:autoMirrored="true"`，并用 `BidiFormatter.unicodeWrap()` 包裹插入句子中的所有值，确保街道地址或数字保持原有的文本方向 ([Android：支持不同的语言和文化](https://developer.android.com/training/basics/supporting-devices/languages)) 。在 Compose 中，可以用 `@Preview(locale = "ar")` 以从右到左的方式预览界面。 (Web 端的做法请参见[如何在 CSS 中支持 RTL (从右到左) 语言？](/kb/right-to-left-css)。) 

Google Play 商店详情需要单独翻译。在 Play Console 中打开商店详情，点击 **Manage translations**，然后为每种语言分别添加标题、简短说明、完整说明和本地化图片 ([Play Console 帮助：翻译和本地化你的应用](https://support.google.com/googleplay/android-developer/answer/9844778)) 。

## 选择翻译方式

以下几种方式都可以填充你的 `values-*` 文件夹：

| 方式                          | 由谁翻译                         | 译者能获得的上下文            | 能否在每次发布时重复执行                                                    | 留给你的工作                              |
| --------------------------- | ---------------------------- | -------------------- | --------------------------------------------------------------- | ----------------------------------- |
| 在 Translations Editor 中手动输入 | 你或懂双语的同事                     | 整个应用                 | 否                                                               | 手动翻译每种语言的每个字符串                      |
| Play Console 应用字符串翻译        | Play Console 中的 Gemini 模型，免费 | 你的 `strings.xml`     | 是，每次将应用包上传到草稿版本时执行                                              | 译文保存在 Play 中而非你的代码仓库中，并会覆盖所选语言的现有译文 |
| 将文件发送给译者或翻译管理系统 (TMS)       | 专业译者                         | 评论，以及你附加的截图          | 通过 TMS 的同步功能                                                    | 每次发布都要来回交接一轮，并等待结果                  |
| 编写脚本，对 XML 调用机器翻译或 LLM      | 模型或翻译 API                    | 取决于脚本发送的内容           | 是                                                               | 各语言的复数类别、转义、`<xliff:g>` 元素、格式参数以及审校 |
| General Translation CLI     | GT 的翻译服务                     | 资源评论，以及共享的词汇表和自定义提示词 | 在构建前运行 [`gt translate`](/docs/cli/reference/commands/translate) | 在仪表板或 Android Studio 中审校，然后测试应用     |

Play Console 也可以直接翻译应用：开启应用字符串翻译后，每次将应用程序包上传到草稿版本时，系统都会根据其最新的 `strings.xml` 生成译文，你可以在模拟器中预览，并在发布前进行编辑。选择某种语言后，该语言的现有译文将被替换；取消勾选 **Translate** 复选框即可排除个别字符串 ([Play Console 帮助](https://support.google.com/googleplay/android-developer/answer/9844778)) 。

如果只需支持一两种你掌握的语言，请使用 Translations Editor。如果你通过 Google Play 发布应用，且无需将译文保存在代码库中，请使用 Play Console 的翻译功能。如果翻译后的 `strings.xml` 文件需要随每次发布一起提交，并与你的 iOS 应用和网站共享术语，请选择 General Translation。如果翻译工作由专业译者或现有供应商负责，请使用 [TMS](/kb/what-is-a-translation-management-system)。

## 使用 General Translation 翻译 strings.xml

General Translation 的 CLI `gt` (2.19.0 或更高版本) 可通过 `androidStrings` 文件键翻译 Android 字符串资源。默认文件的路径中不含区域设置，因此需要将 `include` 指向该文件，并通过 `transform` 将各语言的译文分别写入对应的语言文件夹：

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["es", "pt-BR", "zh-Hant-TW"],
  "files": {
    "androidStrings": {
      "include": ["app/src/main/res/values/strings.xml"],
      "transform": {
        "match": "res/values/(.*)",
        "replace": "res/values-{locale}/$1"
      }
    }
  }
}
```

CLI 会将每个区域设置转换为 Android 所需的限定符，因此此配置会写入 `values-es/`、`values-pt-rBR/` 和 `values-b+zh+Hant+TW/`。标准项目不要使用 `values-[locale]` 模式：它会去查找 `values-en/` 文件夹，结果什么都匹配不到，也就什么都不会翻译。

CLI 需要 Node.js 20 或更高版本。请按照 [CLI 快速入门](/docs/cli/quickstart) 中的说明进行安装，使用 [`npx gt login`](/docs/cli/reference/commands/login) 登录，并设置 `GT_PROJECT_ID` ([CLI 凭据](/docs/cli/guides/configuring#credentials)) ；如果应用已有译文，请使用 [`npx gt upload`](/docs/cli/reference/commands/upload) 导入。然后运行 [`npx gt translate`](/docs/cli/reference/commands/translate)。在 CI 中，请将具有 **Files &gt; Write** 和 **Translation queue &gt; Enabled** 权限的项目 API 密钥存储为 `GT_API_KEY`。

翻译后的文件会保留资源名称、`<xliff:g>` 元素、格式参数和内联标记；每个翻译后的字符串都会经过检查，确保这些内容完好，且符合 Android 构建所要求的撇号和引号转义规则。`<plurals>` 会按目标语言的复数类别重新构建：波兰语为 `one`、`few`、`many` 和 `other`，阿拉伯语包含全部六种，中文仅有 `other`；该语言从不使用的类别 (包括源文件中的 `zero`) 会被移除。资源上方的评论会作为上下文一并发送。CLI 不会翻译标记为 `translatable="false"` 的资源、引用其他资源的值，以及 `<color>` 等非文本类型。 (参见 [strings.xml 参考](/docs/cli/reference/formats/android-strings-files)。) 

如果被包含的文件中有标记为 `translatable="false"` 的资源，它们会原样复制到每个翻译后的文件中，而 Android Lint 的 [`Untranslatable`](https://googlesamples.github.io/android-custom-lint-rules/checks/Untranslatable.md.html) 检查会对每一份副本发出警告。请将这些值放到 `res/values/donottranslate.xml` 中，并且只包含 `strings.xml`。

你可以在 [仪表板](/docs/platform/dashboard/guides/reviewing-translations) 或 Android Studio 中审校译文；如果在本地编辑了文件，请运行 [`npx gt save-local`](/docs/cli/reference/commands/save-local)，这样后续运行时会保留你的修改。为 Android、[iOS](/kb/localize-ios-app) 和 Web 项目分配同一个[上下文分组](/docs/platform/dashboard/guides/defining-context-for-translations)，方案名称等词汇表术语就能在各处保持一致的译法。Partiful 使用 General Translation 在 Web、iOS 和 Android 上支持 2 个以上的区域设置 ([生产环境部署](https://generaltranslation.com/enterprise)) 。

## 常见问题

### 英语需要 values-en 文件夹吗？

不需要。不带限定符的 `values/` 文件夹会作为所有未翻译区域设置的默认资源。启用 `generateLocaleConfig` 后，`resources.properties` 会告诉构建过程该文件夹对应哪个区域设置。

### 为什么翻译后的应用中仍有部分字符串显示为英文？

翻译后的 `strings.xml` 中缺少这些字符串名称，因此 Android 会回退到 `values/` 中的默认值。Lint 的 `MissingTranslation` 检查会按语言列出这些缺失项。

### Google Play 会自动翻译我的商店详情吗？

如果用户查看商店详情时使用的语言是你尚未翻译的，他们可以选择查看自动翻译版本。对于你在 **Manage translations** 下添加的每种语言，你自己提供的译文都会取代相应的自动翻译。

## 关键要点

* `res/values/strings.xml` 必须包含所有字符串；已翻译的文件夹可以只包含其中的子集，缺失的条目会回退到该文件。
* 文件夹名称使用限定符：地区用 `values-pt-rBR`，文字体系用 `values-b+zh+Hant+TW`。
* 使用 `%1$s` 等位置参数，转义撇号，并让每种语言自行定义 `<plurals>` 类别。
* 启用 `generateLocaleConfig` (AGP 8.1+) 以支持 Android 13 的按应用语言设置，并使用 `en-XA` 和 `ar-XB` 伪区域设置进行测试。
* General Translation 的 CLI 通过 `androidStrings` 和 `transform` 将 `strings.xml` 翻译到 `values-{locale}`，自动写入 Android 所需的限定符，并按语言重新生成复数形式。

## Related questions

- [如何使用 String Catalogs 本地化 iOS 应用？](https://generaltranslation.com/zh/kb/localize-ios-app.mdx): 在 Xcode 中添加语言，使用 SwiftUI Text 和 String(localized:) 等可本地化 API 编写面向用户的文本，然后执行构建，Xcode 会将这些字符串收集到 String Catalog（.xcstrings）中。翻译可以在 Xcode 中完成，也可以借助 XLIFF；如果希望目录随版本发布同步更新，并与你的网站和 Android 应用共享术语，可以使用 General Translation 的 CLI。
- [如何翻译 React Native 应用？](https://generaltranslation.com/zh/kb/translate-react-native-app.mdx): 在组件中标记文本，为每种语言生成一个翻译文件，添加 Intl 兼容性补丁，然后根据设备语言加载对应的文件。使用 General Translation 实验性的 gt-react-native 时，只需用 `<T>` 包裹文本，Babel 插件会自动添加 polyfills，npx gt translate 则负责生成翻译文件。
- [什么是伪本地化？](https://generaltranslation.com/zh/kb/pseudo-localization.mdx): 伪本地化是一种测试方法：将源文本替换为改写后的版本——加上重音符号、长度扩充约 40%，并用方括号包裹——这样在还没有任何翻译之前，硬编码字符串、截断、字体显示异常和句子拼接等问题就会暴露出来。Android 为此内置了 en-XA 和 ar-XB 两个伪区域设置。
- [如何找出未翻译的硬编码字符串？](https://generaltranslation.com/zh/kb/find-hardcoded-strings.mdx): 在 CI 中运行 linter，标记面向用户的字符串字面量，例如 JSX 可使用 eslint-plugin-i18next 的 no-literal-string 规则，Android 可使用 Android Lint 的 HardcodedText 检查，Rails 可使用 erb_lint 的 HardCodedString linter。然后切换到伪区域设置，逐一浏览应用，找出静态检查无法追踪到的文本。
- [如何在 CSS 中支持 RTL（从右到左）语言？](https://generaltranslation.com/zh/kb/right-to-left-css.mdx): 在 html 元素上同时设置 dir="rtl" 和 lang，因为仅设置 lang 并不会改变文本方向；然后将 margin-left 等物理 CSS 属性替换为 margin-inline-start 等逻辑属性。Flex 行、Grid 列和默认文本对齐会自动翻转，而 translateX()、left 和 right 不会。

## Previous and next questions

- Previous question: [如何为多语言网站创建站点地图？](https://generaltranslation.com/zh/kb/multilingual-sitemap.mdx)
- Next question: [如何在 .NET 应用中翻译 .resx 文件？](https://generaltranslation.com/zh/kb/translate-resx-files.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/zh/kb/sitemap.md) for every article.
