Skip to main content

Kotlin Multiplatform i18n: bendras lokalizavimas visose platformose

Parašykite vertimus vieną kartą bendrame Kotlin kode. Išleiskite juos Android, iOS ir internete su tinkamomis atsarginių lokalių grandinėmis.

1

Sukonfigūruoti Gradle KMP i18n

Pridėkite i18n priklausomybes prie bendro modulio commonMain šaltinių rinkinio. Galite rinktis moko-resources XML pagrįstoms eilutėms, Lyricist tipų požiūriu saugioms Compose eilutėms arba abu. kmp-localechain prie bet kurio prideda išmanią atsarginę lokalę.

build.gradle.kts
// build.gradle.kts (shared module)
plugins {
    kotlin("multiplatform")
    id("com.android.library")
}

kotlin {
    androidTarget()
    iosArm64()
    iosSimulatorArm64()
    js(IR) { browser(); nodejs() }

    sourceSets {
        val commonMain by getting {
            dependencies {
                // Option A: moko-resources (code-gen from XML)
                implementation("dev.icerock.moko:resources:0.24.4")

                // Option B: Lyricist (type-safe Compose strings)
                implementation("cafe.adriel.lyricist:lyricist:1.7.0")

                // Locale fallback chains (works with any library)
                implementation("com.i18nagent:locale-chain-kmp:0.1.0")
            }
        }
    }
}
Visos trys bibliotekos skelbiamos Maven Central. Pridėkite jas prie commonMain priklausomybių, kad būtų prieinamos kiekvienoje tikslinėje platformoje (Android, iOS, JS).
2

Apibrėžti bendrus eilučių tipus

Sukurkite Kotlin duomenų klases commonMain, kuriose laikomos visos verstinos eilutės. Tai vienintelis patikimas šaltinis: kiekviena platforma nuskaito tas pačias tipų požiūriu saugias apibrėžtis. Jokių pasikartojančių eilučių failų ar nukrypimo tarp platformų.

commonMain/.../Strings.kt
// commonMain/kotlin/com/myapp/Strings.kt
package com.myapp.i18n

/**
 * Shared string definitions — the single source of truth.
 * Each platform reads from the same keys.
 */
data class AppStrings(
    val greeting: String,
    val farewell: String,
    val itemCount: (count: Int) -> String,
    val nav: NavStrings,
)

data class NavStrings(
    val home: String,
    val settings: String,
    val about: String,
)

// English defaults
val EnStrings = AppStrings(
    greeting = "Hello!",
    farewell = "Goodbye!",
    itemCount = { count ->
        if (count == 1) "$count item" else "$count items"
    },
    nav = NavStrings(
        home = "Home",
        settings = "Settings",
        about = "About",
    ),
)

// Japanese
val JaStrings = AppStrings(
    greeting = "こんにちは!",
    farewell = "さようなら!",
    itemCount = { count -> "${count}個のアイテム" },
    nav = NavStrings(
        home = "ホーム",
        settings = "設定",
        about = "概要",
    ),
)

// Add more locales following the same pattern: DeStrings, EsStrings, etc.
Daugiskaitai naudokite lambda ypatybes, o ne atskirus vienaskaitos ir daugiskaitos raktus. Lambda gauna skaičių ir grąžina tinkamą formą. Taip daugiskaitos logika lieka Kotlin kode, kuriame ją gali patikrinti kompiliatorius.
3

Prijungti Android

androidMain aplinkoje įgyvendinkite expect/actual šabloną įrenginio lokalei nuskaityti per java.util.Locale. Android gali naudoti bendras Kotlin eilutes verslo logikai kartu su standartiniu values/strings.xml sistemos UI elementams, pavyzdžiui, pranešimams ir valdikliams.

androidMain/.../StringProvider.kt
// androidMain/kotlin/com/myapp/StringProvider.kt
package com.myapp.i18n

import java.util.Locale

actual fun currentLocale(): String =
    Locale.getDefault().toLanguageTag()  // e.g. "pt-BR"

// Android can also use standard resources/values-*/strings.xml
// alongside the shared Kotlin definitions.
// Use shared strings for business logic, XML for system UI.

// In your Activity or Compose screen:
@Composable
fun GreetingScreen() {
    val strings = rememberStrings()  // resolves via locale
    Text(text = strings.greeting)
    Text(text = strings.itemCount(cartSize))
}
Locale.getDefault().toLanguageTag() grąžina IETF žymas, pavyzdžiui, „pt-BR“, tačiau kai kurios Android versijos iš senesnių API grąžina „pt-rBR“. Nuosekliems rezultatams visada naudokite toLanguageTag() (API 21+).
4

Prijungti iOS

iosMain aplinkoje įgyvendinkite currentLocale() naudodami NSLocale iš Foundation. Bendra KMP sistema eksportuoja eilučių apibrėžtis į Swift, todėl SwiftUI rodiniai gali jas tiesiogiai iškviesti per sugeneruotą Kotlin sistemą.

iosMain/.../StringProvider.kt
// iosMain/kotlin/com/myapp/StringProvider.kt
package com.myapp.i18n

import platform.Foundation.NSLocale
import platform.Foundation.currentLocale
import platform.Foundation.languageCode
import platform.Foundation.countryCode

actual fun currentLocale(): String {
    val locale = NSLocale.currentLocale
    val lang = locale.languageCode
    val country = locale.countryCode
    return if (country != null) "$lang-$country" else lang
}

// In SwiftUI (via KMP exported framework):
// let strings = StringProviderKt.stringsFor(locale: "ja")
// Text(strings.greeting)
Eksportuodami KMP sistemą į Xcode įsitikinkite, kad eksportuojate eilučių teikėjo modulį. Podspec arba XCFramework konfigūracijoje įtraukite i18n paketą, kad Swift kodas galėtų jį importuoti.
5

Prijungti JS / naršyklę

jsMain aplinkoje nuskaitykite naršyklės lokalę iš window.navigator.language. Tai apima ir Kotlin/JS interneto programas, ir Compose for Web tikslines platformas. Tos pačios bendros eilutės naršyklėje atvaizduojamos be dubliavimo.

jsMain/.../StringProvider.kt
// jsMain/kotlin/com/myapp/StringProvider.kt
package com.myapp.i18n

import kotlinx.browser.window

actual fun currentLocale(): String =
    window.navigator.language  // e.g. "en-US", "pt-BR"

// In a Kotlin/JS or Compose for Web app:
fun main() {
    val locale = currentLocale()
    val strings = stringsFor(locale)
    document.getElementById("greeting")?.textContent = strings.greeting
}
Serverio pusės Kotlin/JS (Node.js) atveju lokalę nuskaitykite iš antraštės Accept-Language arba konfigūracijos kintamojo, o ne window.navigator.language.
6

Lyricist, skirtas Compose Multiplatform

Lyricist suteikia Compose savąjį i18n metodą. Pažymėkite eilučių objektus @LyricistStrings, o Lyricist sugeneruos CompositionLocal teikėją. Keiskite kalbas vykdymo metu pakeisdami languageTag – UI automatiškai perkomponuojamas.

Lyricist integration
// Using Lyricist for Compose Multiplatform
// build.gradle.kts
plugins {
    id("cafe.adriel.lyricist") version "1.7.0"
}

// Define strings with @LyricistStrings annotation
@LyricistStrings(languageTag = Locales.EN, default = true)
val EnStrings = Strings(
    greeting = "Hello!",
    farewell = "Goodbye!",
    itemCount = { count ->
        if (count == 1) "$count item" else "$count items"
    },
)

@LyricistStrings(languageTag = Locales.JA)
val JaStrings = Strings(
    greeting = "こんにちは!",
    farewell = "さようなら!",
    itemCount = { count -> "${count}個のアイテム" },
)

// In your Compose UI
@Composable
fun App() {
    // Lyricist provides the strings via CompositionLocal
    ProvideStrings {
        val lyricist = LocalStrings.current
        Text(text = lyricist.greeting)
    }
}

// Switch language at runtime
val lyricist = rememberLyricist(
    defaultLanguageTag = Locales.EN,
)
lyricist.languageTag = Locales.JA  // UI recomposes automatically
Lyricist palaiko eilučių interpoliavimą, daugiskaitą per lambdas ir įdėtas eilučių grupes. Jis veikia Android, iOS (per Compose for iOS), darbalaukio ir interneto tikslinėse platformose.
7

moko-resources XML eilutėms

moko-resources naudoja Android stiliaus XML eilučių failus kaip vienintelį patikimą šaltinį ir generuoja tipų požiūriu saugias prieigos priemones. Apibrėžkite eilutes commonMain/resources/MR/base/ kataloge (anglų) ir pridėkite kiekvienos kalbos lokalės aplankus. Sugeneruotas MR objektas suteikia kompiliavimo metu tikrinamą prieigą.

moko-resources setup
// Using moko-resources for XML-based string management
// build.gradle.kts
plugins {
    id("dev.icerock.mobile.multiplatform-resources") version "0.24.4"
}

multiplatformResources {
    resourcesPackage.set("com.myapp")
}

// commonMain/resources/MR/base/strings.xml (English - default)
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="greeting">Hello!</string>
    <string name="farewell">Goodbye!</string>
    <plurals name="item_count">
        <item quantity="one">%d item</item>
        <item quantity="other">%d items</item>
    </plurals>
</resources>

// commonMain/resources/MR/ja/strings.xml (Japanese)
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="greeting">こんにちは!</string>
    <string name="farewell">さようなら!</string>
    <plurals name="item_count">
        <item quantity="other">%d個のアイテム</item>
    </plurals>
</resources>

// Usage in shared Kotlin code
val greeting = MR.strings.greeting.desc()
val items = MR.plurals.item_count.format(count)
moko-resources reikia Gradle papildinio kodui generuoti. Jei matote „Unresolved reference: MR“, pirmiausia paleiskite Gradle sinchronizavimą. Kodo generavimo veiksmas turi būti baigtas, kad IDE pamatytų MR prieigos priemones.
8

Išmani atsarginė lokalė su kmp-localechain

KMP i18n bibliotekos neturi konfigūruojamų atsarginių grandinių. Kai nėra pt-BR vertimų, jos visiškai praleidžia pt-PT ir rodo anglų kalbą. kmp-localechain tai ištaiso savarankiška pranešimų sujungimo priemone. Ji priima plokščius kiekvienos lokalės Map&lt;String, String&gt; pranešimus ir grąžina sujungtą žemėlapį su pritaikytu atsarginės grandinės prioritetu.

LocaleChain usage
// Using kmp-localechain for smart locale fallback
import com.i18nagent.localechain.LocaleChain

// 1. Configure once at app startup
LocaleChain.configure()  // uses built-in fallback chains

// 2. Load your messages as flat maps
val messages = mapOf(
    "en" to mapOf("greeting" to "Hello", "farewell" to "Goodbye"),
    "pt" to mapOf("greeting" to "Olá", "farewell" to "Adeus"),
    "pt-PT" to mapOf("greeting" to "Olá (PT)"),
    "pt-BR" to mapOf("greeting" to "Oi"),
)

// 3. Resolve with chain priority
val resolved = LocaleChain.resolve("pt-BR", messages)
// "greeting" -> "Oi"       (from pt-BR, most specific)
// "farewell" -> "Adeus"    (from pt, next in chain)

// Without LocaleChain, pt-BR users would see English "Goodbye"
// because pt-BR has no "farewell" key.
Custom configuration
// Custom fallback configuration
LocaleChain.configure(
    defaultLocale = "en",
    overrides = mapOf(
        "es-MX" to listOf("es-419", "es"),
        "fr-CA" to listOf("fr"),
    )
)

// Inspect any chain
LocaleChain.chainFor("pt-BR")
// Returns: ["pt-BR", "pt-PT", "pt", "en"]

// Async resolve (lazy loading from network/disk)
val resolved = LocaleChain.resolve("pt-BR") { localeTag ->
    api.fetchMessages(localeTag)  // returns Map<String, String>?
}
kmp-localechain veikia su plokščiais Map&lt;String, String&gt; žemėlapiais. Jei pranešimai įdėti, prieš perduodami resolve() juos suplokštinkite. Biblioteka nepalaiko giliojo įdėtų struktūrų sujungimo.
9

Automatizuoti vertimus

Baigę KMP i18n sąranką automatizuokite vertimus naudodami DI. Verskite bendrus eilučių failus – Kotlin duomenų klases, XML išteklius ar JSON – tiesiai iš IDE arba CI/CD konvejerio.

Terminal
# Translate your shared string files with i18n Agent
# Works with JSON, XML (moko-resources), or any i18n format

# From your IDE (Claude Code, Cursor, VS Code):
> Translate commonMain/resources/MR/base/strings.xml to Japanese, German, and Spanish

✓ MR/ja/strings.xml created (1.2s)
✓ MR/de/strings.xml created (1.1s)
✓ MR/es/strings.xml created (1.3s)

# Or use the CLI in CI/CD:
npx i18n-agent translate resources/base/strings.xml --lang ja,de,es
Verskite palaipsniui. Pridėję naujų raktų prie angliško šaltinio išverskite tik skirtumą. Taip išsaugomi žmonių peržiūrėti vertimai ir nereikia generuoti visų failų iš naujo.

Automatizuoti vertimo kokybę

Naudodami i18n-validate prieš išleidimą aptikite trūkstamus raktus ir sugadintus vietos rezervavimo ženklus. Kol dar nėra tikrų vertimų, patikrinkite UI su i18n-pseudo pseudoverstimais.

Dažnos klaidos

expect/actual neatitiktis

Kiekvienai expect deklaracijai commonMain reikia actual įgyvendinimo kiekvienoje tikslinėje platformoje (androidMain, iosMain, jsMain). Jei vėliau pridėsite naują tikslinę platformą, kompiliatorius pateiks klaidą, kol pateiksite actual. Naudokite IDE greituosius pataisymus ruošiniams generuoti.

Tiesiogiai įrašyta daugiskaitos logika

Vienaskaitos formoms aptikti niekada nenaudokite count == 1. Prancūzų kalboje 0 laikomas vienaskaita. Arabų kalboje yra šešios daugiskaitos formos. Rusų kalba naudoja skirtingas formas skaičiams, kurie baigiasi 1, 2-4 ir 5-20. Naudokite CLDR suprantančias bibliotekas (moko-resources) arba aiškias kiekvienos lokalės lambdas.

Įdėti žemėlapiai kmp-localechain

kmp-localechain veikia su plokščiu Map&lt;String, String&gt;. Jei perduosite įdėtus žemėlapius, atsarginis išsprendimas tinkamai nesujungs vidinių raktų. Prieš iškviesdami resolve(), suplokštinkite pranešimus naudodami taškinio žymėjimo raktus (pvz., „nav.home“).

Pridėjus moko-resources trūksta sugeneruoto kodo

MR objektą generuoja Gradle papildinys. Pridėję moko-resources eilučių paleiskite Gradle sinchronizavimą prieš naudodami MR.strings.* kode. Jei IDE vis dar rodo klaidų, išbandykite Build > Rebuild Project.

Rekomenduojama projekto struktūra

Project Structure
my-kmp-app/
├── shared/
│   ├── build.gradle.kts
│   └── src/
│       ├── commonMain/
│       │   ├── kotlin/com/myapp/i18n/
│       │   │   ├── Strings.kt           # Shared string definitions
│       │   │   ├── StringProvider.kt     # expect fun currentLocale()
│       │   │   └── LocaleSetup.kt       # LocaleChain configuration
│       │   └── resources/MR/            # moko-resources XML (optional)
│       │       ├── base/strings.xml     # English (default)
│       │       ├── ja/strings.xml
│       │       ├── de/strings.xml
│       │       └── es/strings.xml
│       ├── androidMain/
│       │   └── kotlin/com/myapp/i18n/
│       │       └── StringProvider.kt     # actual fun currentLocale()
│       ├── iosMain/
│       │   └── kotlin/com/myapp/i18n/
│       │       └── StringProvider.kt     # actual fun currentLocale()
│       └── jsMain/
│           └── kotlin/com/myapp/i18n/
│               └── StringProvider.kt     # actual fun currentLocale()
├── androidApp/
│   └── src/main/res/values/strings.xml   # Android-specific overrides
├── iosApp/
│   └── iosApp/Localizable.strings        # iOS-specific overrides
└── settings.gradle.kts

Išbandykite i18n Agent dabar

Nuvilkite vertimo failą čia

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

arba spustelėkite norėdami pasirinkti

Tikslinės kalbos

Registruotis nereikiaMomentinis įvertis

Dažnai užduodami klausimai