Skip to main content

Повний посібник із локалізації застосунків Android

Від strings.xml до метаданих Play Store: локалізуйте свій застосунок Android за допомогою Kotlin, Jetpack Compose, Fastlane й автоматизованого перекладу на основі ШІ.

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 з елементами '<string>' усередині кореневого елемента '<resources>'. Використовуйте %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 використовує елементи '&lt;plurals&gt;' з атрибутами quantity: 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

Локалізуйте метадані Google Play за допомогою Fastlane

Використовуйте команду supply з Fastlane, щоб керувати метаданими 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

Перевірте локалізацію

Перевіряйте локалізацію, перемикаючи локаль емулятора, використовуючи попередні перегляди Compose з власним LocaleList і псевдолокалі в параметрах розробника. Використовуйте resConfigs у Gradle, щоб вилучати непотрібні ресурси локалей зі сторонніх бібліотек.

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 ще до появи справжніх перекладів.
8

Автоматизуйте переклад

Перекладайте 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 правильно екранує XML Android, зберігає маркери translatable="false", дотримується категорій множини CLDR для кожної цільової мови та не змінює позиційних аргументів форматування.
JetBrains

Доступний плагін для Android Studio

Перекладайте XML-ресурси Android безпосередньо зі свого IDE за допомогою плагіна i18n Agent для IntelliJ / Android Studio.

Install
+

Додатково: розумний резервний вибір локалі з LocaleChain

Резервним вибором ресурсів Android керує операційна система. Коли перекладів pt-BR немає, Android повністю пропускає pt-PT і показує англійську. LocaleChain перехоплює пошук рядків і проходить налаштовуваний ланцюжок резервного вибору, щоб користувачі регіональних локалей бачили найближчий доступний переклад.

LocaleChain для 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 аварійно завершує роботу з ResourceNotFoundException, якщо рядка немає в стандартному res/values/strings.xml. Завжди перевіряйте наявність кожного ключа у стандартному файлі.

Поділ App Bundle за мовами порушує перемикання мови в застосунку

Google Play App Bundles поділяють APK за мовами — користувачі отримують лише рядки мовою свого пристрою. Якщо Ви пропонуєте перемикання мови в застосунку, додайте bundle '{ language { enableSplit = false } }' до свого build.gradle.kts.

Порушення компонування RTL

Причинами можуть бути використання left/right замість start/end у компонуваннях або відсутність android:supportsRtl="true" в AndroidManifest.xml. Скористайтеся командою Refactor > Add RTL Support в Android Studio, щоб автоматично перетворити наявні компонування.

Забруднення ресурсами бібліотек

Сторонні бібліотеки містять власні файли values-XX/strings.xml, через що Android вважає, ніби Ваш застосунок підтримує мови, яких насправді не підтримує. Використовуйте resConfigs у build.gradle.kts, щоб обмежити включені локалі лише тими, для яких Ви справді створюєте переклад.

Рекомендована структура файлів

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