Skip to main content

Kotlin Multiplatform i18n: yhteinen lokalisointi eri alustoille

Kirjoita käännökset kerran yhteiseen Kotlin-koodiin. Julkaise ne Android:ille, iOS:lle ja verkkoon oikeine varakieliketjuineen.

1

Määritä Gradle KMP i18n:lle

Lisää i18n-riippuvuudet yhteisen moduulisi commonMain-lähdejoukkoon. Voit valita XML-pohjaisille merkkijonoille moko-resourcesin, tyyppiturvallisille Compose-merkkijonoille Lyricistin tai molemmat. kmp-localechain lisää älykkään varakielen kummankin päälle.

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")
            }
        }
    }
}
Kaikki kolme kirjastoa julkaistaan Maven Central:iin. Lisää ne commonMain-riippuvuuksiin, jotta ne ovat käytettävissä kaikissa kohteissa (Android, iOS, JS).
2

Määritä yhteiset merkkijonotyypit

Luo commonMainiin Kotlin-dataluokat, jotka sisältävät kaikki käännettävät merkkijonot. Tämä on ainoa ensisijainen lähde — kaikki alustat lukevat samoja tyyppiturvallisia määritelmiä. Ei päällekkäisiä merkkijonotiedostoja tai alustojen välistä eriytymistä.

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.
Käytä monikkomuotoihin lambda-ominaisuuksia erillisten yksikkö- ja monikkomuotoavainten sijaan. Lambda vastaanottaa määrän ja palauttaa oikean muodon. Näin monikkologiikka pysyy Kotlin:issa kääntäjän tarkistettavana.
3

Kytke Android

Toteuta androidMainissa expect/actual-malli laitteen kieliversion lukemiseen java.util.Localen kautta. Android voi käyttää yhteisiä Kotlin-merkkijonoja liiketoimintalogiikassa ja niiden rinnalla tavallista values/strings.xml-tiedostoa järjestelmän käyttöliittymäelementteihin, kuten ilmoituksiin ja pienoisohjelmiin.

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() palauttaa IETF-tunnisteet, kuten "pt-BR", mutta vanhoissa rajapinnoissa jotkin Android-versiot palauttavat arvon "pt-rBR". Käytä yhdenmukaisiin tuloksiin aina toLanguageTag()-funktiota (API 21+).
4

Kytke iOS

Toteuta iosMainissa currentLocale() Foundationin NSLocale:lla. KMP:n yhteinen ohjelmistokehys vie merkkijonomääritelmäsi Swift:iin, joten SwiftUI-näkymät voivat kutsua niitä suoraan luodun Kotlin-ohjelmistokehyksen kautta.

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)
Kun viet KMP-ohjelmistokehyksen Xcode:iin, varmista merkkijonopalvelumoduulin vienti. Sisällytä Podspec- tai XCFramework-määrityksessä i18n-paketti, jotta Swift-koodi voi tuoda sen.
5

Kytke JS tai selain

Lue jsMainissa selaimen kieliversio window.navigator.language-arvosta. Tämä kattaa Kotlin/JS-verkkosovellukset ja Compose for Web -kohteet. Samat yhteiset merkkijonot hahmontuvat selaimessa ilman päällekkäisyyksiä.

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
}
Lue palvelinpuolen Kotlin/JS:ssä (Node.js) kieliversio window.navigator.languagen sijaan Accept-Language-otsakkeesta tai määritysmuuttujasta.
6

Lyricist Compose Multiplatformille

Lyricist tarjoaa Composen omaa i18n-lähestymistapaa. Merkitse merkkijono-objektisi @LyricistStrings-merkinnällä, niin Lyricist luo CompositionLocal-palveluntarjoajan. Vaihda kieltä suorituksen aikana muuttamalla languageTag-arvoa — käyttöliittymä koostetaan automaattisesti uudelleen.

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 tukee merkkijonojen interpolointia, lambda-funktioilla toteutettuja monikkomuotoja ja sisäkkäisiä merkkijonoryhmiä. Se toimii Android-, iOS- (Compose for iOS:n kautta), Desktop- ja Web-kohteissa.
7

moko-resources XML-merkkijonoille

moko-resources käyttää ensisijaisena lähteenä Android-tyyppisiä XML-merkkijonotiedostoja ja luo tyyppiturvalliset käyttömetodit. Määritä englanninkieliset merkkijonot polussa commonMain/resources/MR/base/ ja lisää kielikansiot kullekin kielelle. Luotu MR-objekti tarjoaa käännösaikana tarkistetun käytön.

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 vaatii Gradle-lisäosan koodin luontiin. Jos näet virheen 'Unresolved reference: MR', suorita ensin Gradle-synkronointi. Koodin luontivaiheen on valmistuttava ennen kuin IDE-ympäristösi näkee MR-käyttömetodit.
8

Älykäs varakieli kmp-localechainilla

KMP:n i18n-kirjastoista puuttuvat määritettävät varakieliketjut. Kun pt-BR-käännökset puuttuvat, ne ohittavat pt-PT:n kokonaan ja näyttävät englannin. kmp-localechain korjaa tämän itsenäisellä sanomien yhdistämisapuohjelmalla. Se vastaanottaa kieliversiokohtaiset litteät Map&lt;String, String&gt;-sanomat ja palauttaa yhdistetyn kartan, jossa varakieliketjun prioriteetti on otettu käyttöön.

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 toimii litteiden Map&lt;String, String&gt;-karttojen kanssa. Jos sanomasi ovat sisäkkäisiä, litistä ne ennen resolve()-funktiolle välittämistä. Kirjasto ei tue sisäkkäisten rakenteiden syväyhdistämistä.
9

Automatisoi käännökset

Kun KMP i18n on otettu käyttöön, automatisoi käännökset tekoälyllä. Käännä yhteiset merkkijonotiedostosi — Kotlin-dataluokat, XML-resurssit tai JSON — suoraan IDE-ympäristöstäsi tai CI/CD-putkestasi.

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
Käännä vaiheittain. Kun lisäät englanninkieliseen lähteeseen uusia avaimia, käännä vain diff. Näin ihmisten tarkistamat käännökset säilyvät eikä kokonaisia tiedostoja tarvitse luoda uudelleen.

Automatisoi käännöslaatu

Löydä puuttuvat avaimet ja rikkoutuneet paikkamerkit i18n-validate:lla ennen julkaisua. Testaa käyttöliittymää pseudokäännöksillä i18n-pseudo:n avulla ennen oikeiden käännösten valmistumista.

Tavalliset sudenkuopat

expect/actual-ristiriita

Jokainen commonMainin expect-ilmoitus tarvitsee actual-toteutuksen jokaisessa kohteessa (androidMain, iosMain, jsMain). Jos lisäät myöhemmin uuden alustakohteen, kääntäjä ilmoittaa virheen, kunnes annat actual-toteutuksen. Luo tyhjät toteutukset IDE-ympäristön pikakorjauksilla.

Kovakoodattu monikkologiikka

Älä koskaan tunnista yksikkömuotoa ehdolla count == 1. Ranska käsittelee 0:n yksikkönä. Arabiassa on kuusi monikkomuotoa. Venäjä käyttää eri muotoja lukuihin, jotka päättyvät 1:een, 2-4:ään ja 5-20:een. Käytä CLDR:n huomioivia kirjastoja (moko-resources) tai eksplisiittisiä kieliversiokohtaisia lambda-funktioita.

Sisäkkäiset kartat kmp-localechainissa

kmp-localechain käsittelee litteitä Map&lt;String, String&gt;-karttoja. Jos välität sisäkkäisiä karttoja, varakielen ratkaisu ei yhdistä sisempiä avaimia oikein. Litistä sanomat pistekirjoitusavaimilla (esimerkiksi "nav.home") ennen resolve()-funktion kutsumista.

Luotu koodi puuttuu moko-resourcesin lisäämisen jälkeen

Gradle-lisäosa luo MR-objektin. Kun olet lisännyt moko-resources-merkkijonoja, suorita Gradle-synkronointi ennen MR.strings.*-arvojen käyttämistä koodissa. Jos IDE-ympäristösi näyttää yhä virheitä, kokeile Build > Rebuild Project.

Suositeltu projektirakenne

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

Kokeile i18n Agent:ia nyt

Pudota käännöstiedostosi tähän

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

tai valitse napsauttamalla

Kohdekielet

Rekisteröitymistä ei tarvitaVälitön arvio

Usein kysytyt kysymykset