Skip to main content

Android 應用程式本地化完整指南

從 strings.xml 到 Play Store 元數據:使用 Kotlin、Jetpack Compose、Fastlane 和自動化 AI 翻譯來本地化 Android 應用程式。

1

設定 Android 項目以支援本地化

Android 使用基於資料夾的本地化約定。預設字串位於 res/values/strings.xml,譯文則放在 res/values-de/、res/values-ja/ 等特定語言資料夾中。

Android Project Structure
// Android project structure for localization:
// res/
// ├── values/              ← Default (fallback) locale
// │   └── strings.xml
// ├── values-de/           ← German
// │   └── strings.xml
// ├── values-ja/           ← Japanese
// │   └── strings.xml
// └── values-es/           ← Spanish
//     └── strings.xml
//
// Folder naming: values-{language} or values-{language}-r{Region}
// Examples: values-pt-rBR, values-zh-rCN, values-zh-rTW
res/values/ 資料夾是回退語言。如果特定語言資料夾中缺少字串,Android 會從預設資料夾載入。但如果預設檔案中缺少字串,應用在不受支援的語言環境中就會崩潰。
2

建立 strings.xml

Android 字串資源使用 XML,在 '<resources>' 根元素內包含 '<string>' 元素。字串預留位置使用 %s,整數使用 %d,允許譯員調整順序的位置參數則使用 %1$s/%2$s。

res/values/strings.xml
<!-- res/values/strings.xml -->
<resources>
    <string name="welcome_title">Welcome to MyApp</string>
    <string name="login_button">Sign In</string>
    <string name="settings_label">Settings</string>
    <string name="greeting">Hello, %s!</string>        <!-- %s = string -->
    <string name="item_count">%d items</string>          <!-- %d = integer -->
    <string name="app_name" translatable="false">MyApp</string>
</resources>
未轉義的撇號會導致 XML 解析器無提示崩潰。請使用 \',或用雙引號包裹值。另外,預設 values/strings.xml 中缺少字串會直接導致崩潰,不會像 iOS 那樣平穩回退。
Common strings.xml Mistakes
<!-- ❌ Common mistakes in strings.xml: -->

<!-- Unescaped apostrophe — crashes XML parser silently -->
<string name="message">It's a great day</string>

<!-- Missing from default values/strings.xml — app crashes -->
<!-- (only exists in values-de/strings.xml) -->

<!-- ✅ Correct versions: -->
<string name="message">It\'s a great day</string>
<!-- Or wrap in double quotes: -->
<string name="message">"It's a great day"</string>
3

處理複數

Android 使用帶 quantity 屬性的 '&lt;plurals&gt;' 元素:zero、one、two、few、many、other。每種目標語言需要的類別可能不同,阿拉伯語使用全部 6 類,俄語需要 few/many,日語只使用 other。

res/values/strings.xml
<!-- res/values/strings.xml -->
<resources>
    <plurals name="items_count">
        <item quantity="zero">No items</item>
        <item quantity="one">%d item</item>
        <item quantity="other">%d items</item>
    </plurals>
</resources>

<!-- Usage in Kotlin: -->
<!-- val text = resources.getQuantityString(
    R.plurals.items_count,
    count,    // selects plural form
    count     // format argument
) -->
quantity 屬性會根據設備語言的 CLDR 規則選擇複數形式。務必包含 'other' 作為回退,它是每種語言都保證存在的唯一類別。
4

在程式碼中使用:Kotlin 與 Jetpack Compose

傳統 Android 使用 getString(R.string.key) 和 resources.getQuantityString(),Jetpack Compose 使用 stringResource(R.string.key) 和 pluralStringResource()。兩者都會在執行階段根據設備語言解析正確譯文。

WelcomeScreen.kt
// Traditional Android (Activity/Fragment)
val title = getString(R.string.welcome_title)
val greeting = getString(R.string.greeting, userName)
val items = resources.getQuantityString(
    R.plurals.items_count, count, count
)

// Jetpack Compose
@Composable
fun WelcomeScreen(userName: String, itemCount: Int) {
    // ✅ stringResource — Compose-aware, triggers recomposition
    Text(text = stringResource(R.string.welcome_title))

    // ✅ With format arguments
    Text(text = stringResource(R.string.greeting, userName))

    // ✅ Plurals — count passed TWICE
    Text(text = pluralStringResource(
        R.plurals.items_count,
        itemCount,    // selects plural form
        itemCount     // format argument
    ))
}
pluralStringResource(R.plurals.items, count, count) 中的 count 參數會傳入兩次:第一次選擇複數形式,第二次作為格式參數。缺少第二個 count 是最常見的 Compose 複數錯誤。
5

字串陣列與格式化字串

有序列表(例如下拉選項、入門步驟)使用 '&lt;string-array&gt;'。格式化字串中使用位置格式參數(%1$s、%2$d),讓譯員可以調整詞序而不破壞句子結構。

res/values/strings.xml
<!-- res/values/strings.xml -->
<resources>
    <!-- String array for dropdown/list -->
    <string-array name="sort_options">
        <item>Most Recent</item>
        <item>Most Popular</item>
        <item>Price: Low to High</item>
        <item>Price: High to Low</item>
    </string-array>

    <!-- Positional format args for reordering -->
    <string name="welcome_message">
        Hello %1$s, you have %2$d new messages
    </string>
    <!-- Translators can reorder: -->
    <!-- %2$d neue Nachrichten für %1$s -->
</resources>
%1$s 等位置參數允許譯員自由調整參數順序。對於詞序不同的語言,'Hello %1$s, you have %2$d items' 可以改為 '%2$d items for %1$s',無需修改任何程式碼。
6

使用 Fastlane 本地化 Google Play 元數據

使用 Fastlane 的 supply 命令管理 Play Store 元數據,包括標題、簡短描述、完整描述和更新記錄,並以儲存庫中按語言組織的純文字檔案形式儲存。

Terminal
# Install Fastlane
$ gem install fastlane

# Initialize supply for Play Store metadata
$ fastlane supply init

# Directory structure created:
# fastlane/metadata/android/
# ├── en-US/
# │   ├── title.txt              # App name (50 chars)
# │   ├── short_description.txt  # Short desc (80 chars)
# │   ├── full_description.txt   # Full desc (4000 chars)
# │   └── changelogs/
# │       └── default.txt        # What's New
# ├── de-DE/
# │   └── ...
# └── ja-JP/
#     └── ...

# Push metadata to Play Store:
$ fastlane supply
本地化 Play Store 商品詳情可讓非英語市場的下載量提升 30% 以上。標題、簡短描述和完整描述都會納入搜尋索引,翻譯這些內容是投資回報率最高的本地化工作。
Google Play

自動完成 Play Store 商品詳情本地化

跳過手動複製粘貼。在遵守字元限制的同時,將 Play Store 標題、描述和發佈說明翻譯到 175 種以上語言環境。

瞭解 Google Play 整合
7

測試本地化

通過模擬器切換語言、使用自訂 LocaleList 的 Compose 預覽,以及開發者選項中的偽語言來測試。使用 Gradle 中的 resConfigs 刪除第三方庫不需要的語言資源。

Testing Localization
// 1. Emulator: Settings > System > Language > Add language

// 2. Compose Preview with locale:
@Preview
@Composable
fun WelcomePreview() {
    val config = Configuration(resources.configuration).apply {
        setLocale(Locale("de"))
    }
    val localContext = LocalContext.current
    val localizedContext = localContext.createConfigurationContext(config)
    CompositionLocalProvider(
        LocalContext provides localizedContext
    ) {
        WelcomeScreen()
    }
}

// 3. Restrict library locales in build.gradle.kts:
android {
    defaultConfig {
        // Only include locales you actually translate
        resourceConfigurations += listOf("en", "de", "ja", "es", "fr")
    }
}

// 4. Enable pseudolocales in Developer Options:
// en-XA (accented) — detects hardcoded strings
// ar-XB (RTL) — tests layout mirroring
使用德語(文字約擴展 30%)和日語(約縮短 50%)進行測試,以發現佈局問題。在開發者選項中啟用偽語言(en-XA 用於重音字元,ar-XB 用於 RTL),無需真實譯文即可對佈局進行壓力測試。

自動保證翻譯質素

使用 i18n-validate 在發佈前發現缺失鍵、損壞的預留位置和複數問題。真實譯文完成前,可使用 i18n-pseudo 產生偽譯文來測試 UI。
8

自動翻譯

使用 AI 翻譯 strings.xml、複數、字串陣列和 Fastlane Supply 元數據。自動翻譯應用程式內字串和 Play Store 元數據,打造完整的本地化展示。

Terminal
# Translate strings.xml files
> Translate res/values/strings.xml
  to Japanese, German, and Spanish

# Translate Play Store metadata too
> Translate fastlane/metadata/android/en-US/
  to de-DE, ja-JP, es-ES

✓ 6 files translated in 3.2s
i18n Agent 會處理 Android XML 轉義,保留 translatable="false" 標記,遵守每種目標語言的 CLDR 複數類別,並保持位置格式參數完整。
JetBrains

現已提供 Android Studio 外掛程式

使用適用於 IntelliJ / Android Studio 的 i18n Agent 外掛程式,直接從 IDE 翻譯 Android XML 資源。

Install
+

額外功能:使用 LocaleChain 實現智能語言回退

Android 的資源回退由操作系統控制。pt-BR 譯文缺失時,Android 會完全跳過 pt-PT 並顯示英語。LocaleChain 會攔截字串查找並遍歷可設定回退鏈,讓區域用戶看到最接近的可用譯文。

LocaleChain for Android 是開源 Kotlin 庫。在 GitHub 上查看

build.gradle.kts
// build.gradle.kts (app module)
dependencies {
    implementation("com.i18nagent:locale-chain-android:0.1.0")
}
MyApp.kt / BaseActivity.kt
// 1. Application.onCreate() — configure chains once
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        LocaleChain.configure()
    }
}

// 2. BaseActivity — wrap context per Activity
open class BaseActivity : AppCompatActivity() {
    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(LocaleChain.wrap(newBase))
    }
}

// pt-BR user with only pt-PT translations?
// → Shows Portuguese instead of falling back to English

// Custom overrides for your specific locales:
LocaleChain.configure(
    overrides = mapOf("es-MX" to listOf("es-419", "es"))
)

常見問題

缺少預設字串會導致崩潰

iOS 會顯示原始鍵,而 Android 在預設 res/values/strings.xml 中缺少字串時會拋出 ResourceNotFoundException 並崩潰。務必確保預設檔案中包含所有鍵。

App Bundle 語言拆分會破壞應用程式內切換

Google Play App Bundle 會按語言拆分 APK,用戶只會收到設備語言對應的字串。如果提供應用程式內語言切換,請在 build.gradle.kts 中新增 bundle '{ language { enableSplit = false } }'。

RTL 佈局失效

佈局中使用 left/right 而不是 start/end,或 AndroidManifest.xml 中缺少 android:supportsRtl="true"。請使用 Android Studio 的 Refactor > Add RTL Support 自動轉換現有佈局。

庫資源污染

第三方庫自帶 values-XX/strings.xml 檔案,會讓 Android 誤以為應用程式支援實際上未翻譯的語言。請在 build.gradle.kts 中使用 resConfigs,將包含的語言限制為實際已翻譯的語言。

推薦的檔案結構

Project Structure
MyApp/
├── app/
│   └── src/main/
│       ├── res/
│       │   ├── values/
│       │   │   ├── strings.xml          # Default (source) strings
│       │   │   └── plurals.xml          # Plural rules
│       │   ├── values-de/
│       │   │   └── strings.xml
│       │   ├── values-ja/
│       │   │   └── strings.xml
│       │   └── values-es/
│       │       └── strings.xml
│       ├── java/com/example/myapp/
│       └── AndroidManifest.xml
├── fastlane/
│   └── metadata/android/
│       ├── en-US/
│       │   ├── title.txt
│       │   ├── short_description.txt
│       │   ├── full_description.txt
│       │   └── changelogs/default.txt
│       ├── de-DE/
│       └── ja-JP/
├── build.gradle.kts
└── settings.gradle.kts

立即試用 i18n Agent

將翻譯檔案拖放到此處

JSON, YAML, PO, XML, CSV, Markdown, Properties

或點擊選擇檔案

目標語言

無需註冊即時估價

使用 locale-chain-android 實現語言回退

如果 pt-BR 等區域語言缺少翻譯鍵,Android 會直接跳到預設資源檔案夾,而不會先檢查父語言 pt。

Terminal
implementation("com.i18nagent:locale-chain-android:0.1.0")
Configuration
import com.i18nagent.localechain.LocaleChain

LocaleChain.configure(
    overrides = mapOf(
        "pt-BR" to listOf("pt", "en"),
        "zh-Hant-HK" to listOf("zh-Hant", "zh", "en"),
    )
)

查看語言回退指南,瞭解受支援框架的完整列表和 75 條內置回退鏈。 Learn more →

Android 本地化常見問題