Skip to main content

Internacionalizacija Kotlin Multiplatform: skupna lokalizacija za različne platforme

Prevode enkrat zapišite v skupno kodo Kotlin. Objavite jih za Android, iOS in splet z ustreznimi verigami nadomestnih področnih nastavitev.

1

Konfigurirajte Gradle za internacionalizacijo KMP

Odvisnosti za internacionalizacijo dodajte v nabor izvorne kode commonMain modula v skupni rabi. Izberete lahko moko-resources za nize na osnovi XML, Lyricist za tipsko varne nize Compose ali oboje. kmp-localechain obema možnostma doda pametno uporabo nadomestnih področnih nastavitev.

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")
            }
        }
    }
}
Vse tri knjižnice so objavljene v repozitoriju Maven Central. Dodajte jih med odvisnosti commonMain, da bodo na voljo za vse ciljne platforme (Android, iOS, JS).
2

Določite skupne tipe nizov

V commonMain ustvarite podatkovne razrede Kotlin, ki vsebujejo vse prevedljive nize. To je edini zanesljivi vir — vsaka platforma bere iz istih tipsko varnih definicij. Brez podvojenih datotek nizov in brez razhajanj med platformami.

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.
Za množinske oblike namesto ločenih ključev za ednino in množino uporabite lastnosti lambda. Lambda prejme število in vrne pravilno obliko. Tako množinska logika ostane v jeziku Kotlin, kjer jo lahko preveri prevajalnik.
3

Povežite Android

V androidMain uporabite vzorec expect/actual za branje področne nastavitve naprave prek java.util.Locale. Android lahko skupne nize Kotlin uporablja za poslovno logiko, standardne values/strings.xml pa za elemente sistemskega uporabniškega vmesnika, kot so obvestila in pripomočki.

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() vrne oznake IETF, kot je "pt-BR", vendar nekatere različice Androida iz starejših API-jev vrnejo "pt-rBR". Za dosledne rezultate vedno uporabite toLanguageTag() (API 21+).
4

Povežite iOS

V iosMain implementirajte currentLocale() z uporabo NSLocale iz ogrodja Foundation. Skupno ogrodje KMP izvozi definicije nizov v Swift, zato jih lahko pogledi SwiftUI neposredno kličejo prek ustvarjenega ogrodja Kotlin.

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)
Pri izvozu ogrodja KMP v Xcode poskrbite, da izvozite modul ponudnika nizov. V konfiguracijo Podspec ali XCFramework vključite paket za internacionalizacijo, da ga bo lahko koda Swift uvozila.
5

Povežite JS/brskalnik

V jsMain preberite področno nastavitev brskalnika iz window.navigator.language. To zajema tako spletne aplikacije Kotlin/JS kot ciljne platforme Compose for Web. Isti skupni nizi se v brskalniku izrišejo brez podvajanja.

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
}
Za strežniški Kotlin/JS (Node.js) področno nastavitev preberite iz glave Accept-Language ali konfiguracijske spremenljivke namesto iz window.navigator.language.
6

Lyricist za Compose Multiplatform

Lyricist ponuja pristop k internacionalizaciji, prilagojen okolju Compose. Objekte z nizi označite z @LyricistStrings in Lyricist ustvari ponudnika CompositionLocal. Jezike med izvajanjem zamenjate tako, da spremenite languageTag — uporabniški vmesnik se samodejno znova sestavi.

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 podpira interpolacijo nizov, množinske oblike prek funkcij lambda in ugnezdene skupine nizov. Deluje na ciljnih platformah Android, iOS (prek Compose for iOS), Desktop in Web.
7

moko-resources za nize XML

moko-resources uporablja datoteke nizov XML v slogu Androida kot glavni vir in ustvari tipsko varne dostopnike. Angleške nize določite v commonMain/resources/MR/base/, za vsak jezik pa dodajte mape področnih nastavitev. Ustvarjeni objekt MR omogoča dostop, preverjen med prevajanjem.

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 za ustvarjanje kode zahteva vtičnik Gradle. Če vidite 'Unresolved reference: MR', najprej zaženite sinhronizacijo Gradle. Korak ustvarjanja kode se mora dokončati, preden Vaše razvojno okolje zazna dostopnike MR.
8

Pametne nadomestne področne nastavitve s kmp-localechain

Knjižnice za internacionalizacijo KMP nimajo nastavljivih verig nadomestnih področnih nastavitev. Če manjkajo prevodi pt-BR, v celoti preskočijo pt-PT in prikažejo angleščino. kmp-localechain to odpravi s samostojnim pripomočkom za združevanje sporočil. Za vsako področno nastavitev sprejme sporočila v ploščati strukturi Map&lt;String, String&gt; in vrne združen zemljevid z uporabljeno prednostno verigo nadomestnih področnih nastavitev.

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 deluje s ploščatimi zemljevidi Map&lt;String, String&gt;. Če so Vaša sporočila ugnezdena, jih pred posredovanjem funkciji resolve() pretvorite v ploščato strukturo. Knjižnica ne podpira globokega združevanja ugnezdenih struktur.
9

Avtomatizirajte prevajanje

Ko je internacionalizacija KMP nastavljena, prevajanje avtomatizirajte z umetno inteligenco. Skupne datoteke z nizi — bodisi podatkovne razrede Kotlin, vire XML ali JSON — prevedite neposredno iz razvojnega okolja ali cevovoda CI/CD.

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
Prevajajte postopoma. Ko v angleški vir dodate nove ključe, prevedite samo razliko. Tako ohranite prevode, ki jih je pregledal človek, in se izognete ponovnemu ustvarjanju celotnih datotek.

Avtomatizirajte zagotavljanje kakovosti prevodov

Z i18n-validate odkrijte manjkajoče ključe in okvarjene označbe mest, preden pridejo v izdajo. Preden prispejo pravi prevodi, uporabniški vmesnik preizkusite s psevdoprevodi z uporabo i18n-pseudo.

Pogoste pasti

Neujemanje expect/actual

Vsaka deklaracija expect v commonMain potrebuje implementacijo actual na vsaki ciljni platformi (androidMain, iosMain, jsMain). Če pozneje dodate novo ciljno platformo, bo prevajalnik javljal napako, dokler ne zagotovite implementacije actual. Za ustvarjanje ogrodij kode uporabite hitre popravke razvojnega okolja.

Trdo kodirana množinska logika

Za zaznavanje edninskih oblik nikoli ne uporabite count == 1. Francoščina obravnava 0 kot ednino. Arabščina ima šest množinskih oblik. Ruščina uporablja različne oblike za števila, ki se končajo z 1, 2-4 in 5-20. Uporabite knjižnice, ki upoštevajo CLDR (moko-resources), ali izrecne funkcije lambda za vsako področno nastavitev.

Ugnezdeni zemljevidi v kmp-localechain

kmp-localechain deluje s ploščatimi zemljevidi Map&lt;String, String&gt;. Če posredujete ugnezdene zemljevide, razreševanje nadomestnih področnih nastavitev ne bo pravilno združilo notranjih ključev. Pred klicem resolve() sporočila pretvorite v ploščato strukturo s ključi v zapisu s pikami (npr. "nav.home").

Manjkajoča ustvarjena koda po dodajanju moko-resources

Objekt MR ustvari vtičnik Gradle. Ko dodate nize moko-resources, pred uporabo MR.strings.* v kodi zaženite sinhronizacijo Gradle. Če razvojno okolje še vedno prikazuje napake, poskusite Build > Rebuild Project.

Priporočena struktura projekta

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

Preizkusite i18n Agent zdaj

Spustite prevajalsko datoteko sem

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

ali kliknite za izbiro

Ciljni jeziki

Registracija ni potrebnaTakojšnja ocena

Pogosta vprašanja