Skip to main content

Kotlin Multiplatform i18n: Platformlar arasında paylaşılan yerelleştirme

Çevirilerinizi paylaşılan Kotlin kodunda bir kez yazın. Doğru yerel ayar geri dönüş zincirleriyle Android, iOS ve web için yayımlayın.

1

Gradle'ı KMP i18n için yapılandırın

i18n bağımlılıklarınızı paylaşılan modülün commonMain kaynak kümesine ekleyin. XML tabanlı dizeler için moko-resources'ı, tür güvenli Compose dizeleri için Lyricist'i veya her ikisini seçebilirsiniz. kmp-localechain bunların üzerine akıllı yerel ayar geri dönüşü ekler.

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")
            }
        }
    }
}
Üç kütüphane de Maven Central'da yayımlanır. Her hedefte (Android, iOS, JS) kullanılabilmeleri için bunları commonMain bağımlılıklarına ekleyin.
2

Paylaşılan dize türlerini tanımlayın

commonMain içinde tüm çevrilebilir dizeleri tutan Kotlin veri sınıfları oluşturun. Bunlar tek doğruluk kaynağıdır; her platform aynı tür güvenli tanımları okur. Yinelenen dize dosyaları ve platformlar arasında uyumsuzluk oluşmaz.

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.
Çoğullar için ayrı tekil/çoğul anahtarları yerine lambda özelliklerini kullanın. Lambda sayımı alır ve doğru biçimi döndürür. Böylece çoğul mantığı, derleyicinin denetleyebildiği Kotlin kodunda kalır.
3

Android bağlantısını kurun

androidMain içinde cihazın yerel ayarını java.util.Locale aracılığıyla okumak için expect/actual kalıbını uygulayın. Android, bildirimler ve parçacıklar gibi sistem kullanıcı arayüzü öğelerinde standart values/strings.xml dosyasını kullanırken iş mantığında paylaşılan Kotlin dizelerini kullanabilir.

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(), "pt-BR" gibi IETF etiketleri döndürür; ancak bazı Android sürümleri eski API'lerden "pt-rBR" döndürür. Tutarlı sonuçlar için her zaman toLanguageTag() (API 21+) kullanın.
4

iOS bağlantısını kurun

iosMain içinde Foundation'daki NSLocale'i kullanarak currentLocale() işlevini uygulayın. Paylaşılan KMP çerçevesi dize tanımlarınızı Swift'e aktarır; böylece SwiftUI görünümleri oluşturulan Kotlin çerçevesi aracılığıyla bunları doğrudan çağırabilir.

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)
KMP çerçevesini Xcode'a aktarırken dize sağlayıcı modülünü de aktardığınızdan emin olun. Podspec veya XCFramework yapılandırmanızda Swift kodunun içe aktarabilmesi için i18n paketini ekleyin.
5

JS/tarayıcı bağlantısını kurun

jsMain içinde tarayıcı yerel ayarını window.navigator.language değerinden okuyun. Bu yöntem hem Kotlin/JS web uygulamalarını hem de Compose for Web hedeflerini kapsar. Aynı paylaşılan dizeler, yinelenmeden tarayıcıda oluşturulur.

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
}
Sunucu tarafındaki Kotlin/JS (Node.js) için yerel ayarı window.navigator.language yerine Accept-Language üstbilgisinden veya bir yapılandırma değişkeninden okuyun.
6

Compose Multiplatform için Lyricist

Lyricist, i18n için Compose'a özgü bir yaklaşım sağlar. Dize nesnelerinizi @LyricistStrings ile işaretleyin; Lyricist bir CompositionLocal sağlayıcısı oluşturur. languageTag değerini değiştirerek çalışma zamanında dil değiştirin; kullanıcı arayüzü otomatik olarak yeniden oluşturulur.

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 dize eklemeyi, lambdalar aracılığıyla çoğulları ve iç içe dize gruplarını destekler. Android, iOS (Compose for iOS aracılığıyla), Desktop ve Web hedeflerinde çalışır.
7

XML dizeleri için moko-resources

moko-resources, Android tarzı XML dize dosyalarını tek doğruluk kaynağı olarak kullanır ve tür güvenli erişimciler üretir. Dizeleri commonMain/resources/MR/base/ (İngilizce) altında tanımlayın ve her dil için yerel ayar klasörleri ekleyin. Oluşturulan MR nesnesi derleme zamanında denetlenen erişim sağlar.

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 kod üretmek için Gradle eklentisini gerektirir. 'Unresolved reference: MR' iletisini görürseniz önce Gradle eşitlemesi çalıştırın. IDE'niz MR erişimcilerini görmeden önce kod üretme adımı tamamlanmalıdır.
8

kmp-localechain ile akıllı yerel ayar geri dönüşü

KMP i18n kütüphanelerinde yapılandırılabilir geri dönüş zincirleri yoktur. pt-BR çevirileri eksik olduğunda pt-PT tamamen atlanır ve İngilizce gösterilir. kmp-localechain, bağımsız bir ileti birleştirme yardımcı programıyla bu sorunu giderir. Her yerel ayar için düz Map&lt;String, String&gt; iletileri alır ve geri dönüş zinciri önceliğinin uygulandığı birleştirilmiş bir eşleme döndürür.

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 düz Map&lt;String, String&gt; eşlemeleriyle çalışır. İletileriniz iç içeyse resolve() işlevine aktarmadan önce bunları düzleştirin. Kütüphane, iç içe yapıların derinlemesine birleştirilmesini desteklemez.
9

Çevirileri otomatikleştirin

KMP i18n kurulumunuzu tamamladıktan sonra çevirileri yapay zeka kullanarak otomatikleştirin. Kotlin veri sınıfları, XML kaynakları veya JSON biçimindeki paylaşılan dize dosyalarınızı doğrudan IDE'nizden ya da CI/CD işlem hattınızdan çevirin.

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
Çevirileri aşamalı yapın. İngilizce kaynağınıza yeni anahtarlar eklediğinizde yalnızca farkı çevirin. Böylece insanlar tarafından incelenmiş çeviriler korunur ve dosyaların tamamı yeniden oluşturulmaz.

Çeviri kalitesini otomatikleştirin

Eksik anahtarları ve bozuk yer tutucuları yayımlanmadan önce i18n-validate ile yakalayın. Gerçek çeviriler gelmeden önce kullanıcı arayüzünüzü i18n-pseudo ile sözde çeviriler kullanarak test edin.

Yaygın sorunlar

expect/actual uyuşmazlığı

commonMain içindeki her expect bildirimi, her hedefte (androidMain, iosMain, jsMain) bir actual uygulaması gerektirir. Daha sonra yeni bir platform hedefi eklerseniz actual uygulamasını sağlayana kadar derleyici hata verir. Taslaklar oluşturmak için IDE hızlı düzeltmelerini kullanın.

Doğrudan kodlanmış çoğul mantığı

Tekil biçimleri algılamak için asla count == 1 kullanmayın. Fransızca 0'ı tekil kabul eder. Arapçada altı çoğul biçimi vardır. Rusça 1, 2-4 ve 5-20 ile biten sayılar için farklı biçimler kullanır. CLDR kurallarını dikkate alan kütüphaneleri (moko-resources) veya her yerel ayar için açık lambdaları kullanın.

kmp-localechain'de iç içe eşlemeler

kmp-localechain düz Map&lt;String, String&gt; üzerinde çalışır. İç içe eşlemeler aktarırsanız geri dönüş çözümlemesi iç anahtarları doğru şekilde birleştirmez. resolve() işlevini çağırmadan önce iletilerinizi nokta gösterimli anahtarlar (ör. "nav.home") kullanarak düzleştirin.

moko-resources eklendikten sonra oluşturulan kodun eksik olması

MR nesnesi bir Gradle eklentisi tarafından oluşturulur. moko-resources dizelerini ekledikten sonra kodunuzda MR.strings.* kullanmadan önce Gradle eşitlemesi çalıştırın. IDE'niz hâlâ hata gösteriyorsa Build > Rebuild Project seçeneğini deneyin.

Önerilen proje yapısı

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

i18n Agent'ı şimdi deneyin

Çeviri dosyanızı buraya bırakın

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

veya göz atmak için tıklayın

Hedef diller

Kayıt gerekmezAnında fiyat tahmini

Sık sorulan sorular