Skip to main content

Kotlin Multiplatform i18n: विभिन्न प्लेटफ़ॉर्म पर साझा स्थानीयकरण

साझा Kotlin code में अपने अनुवाद एक बार लिखें। सही locale fallback chains के साथ उन्हें Android, iOS और web पर रिलीज़ करें।

1

KMP i18n के लिए Gradle कॉन्फ़िगर करें

साझा module के commonMain source set में अपनी i18n dependencies जोड़ें। XML-based strings के लिए moko-resources, type-safe Compose strings के लिए Lyricist या दोनों चुन सकते हैं। kmp-localechain इनमें से किसी के ऊपर स्मार्ट locale fallback जोड़ता है।

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")
            }
        }
    }
}
तीनों libraries Maven Central पर उपलब्ध हैं। उन्हें commonMain dependencies में जोड़ें, ताकि वे हर target (Android, iOS, JS) पर उपलब्ध हों।
2

साझा String Types तय करें

commonMain में ऐसी Kotlin data classes बनाएँ जिनमें अनुवाद योग्य सभी strings हों। यही एकमात्र प्रामाणिक स्रोत है—हर प्लेटफ़ॉर्म इन्हीं type-safe definitions से पढ़ता है। इससे न string फ़ाइलों की प्रतिलिपियाँ बनती हैं, न प्लेटफ़ॉर्म के बीच अंतर पैदा होता है।

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.
अलग singular/plural keys की जगह बहुवचन के लिए lambda properties इस्तेमाल करें। Lambda को count मिलता है और वह सही रूप लौटाता है। इससे plural logic Kotlin में रहता है, जहाँ compiler इसकी जाँच कर सकता है।
3

Android से जोड़ें

androidMain में java.util.Locale के माध्यम से device locale पढ़ने के लिए expect/actual pattern implement करें। Android में notifications और widgets जैसे system UI elements के लिए मानक values/strings.xml के साथ business logic में साझा Kotlin strings इस्तेमाल की जा सकती हैं।

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" जैसे IETF tags लौटाता है, लेकिन Android के कुछ versions पुराने APIs से "pt-rBR" लौटाते हैं। एक समान परिणाम पाने के लिए हमेशा toLanguageTag() (API 21+) इस्तेमाल करें।
4

iOS से जोड़ें

iosMain में Foundation के NSLocale से currentLocale() implement करें। KMP shared framework आपकी string definitions को Swift में export करता है, इसलिए SwiftUI views generated Kotlin framework के माध्यम से उन्हें सीधे call कर सकते हैं।

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 framework को Xcode में export करते समय string provider module को export करना सुनिश्चित करें। अपने Podspec या XCFramework config में i18n package शामिल करें, ताकि Swift code उसे import कर सके।
5

JS/Browser से जोड़ें

jsMain में browser locale को window.navigator.language से पढ़ें। इसमें Kotlin/JS web apps और Compose for Web targets दोनों शामिल हैं। वही साझा strings बिना किसी duplication के browser में render होती हैं।

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
}
Server-side Kotlin/JS (Node.js) के लिए locale को window.navigator.language की जगह Accept-Language header या किसी configuration variable से पढ़ें।
6

Compose Multiplatform के लिए Lyricist

Lyricist, i18n के लिए Compose-native तरीका देता है। अपने string objects को @LyricistStrings से annotate करें और Lyricist एक CompositionLocal provider बनाता है। languageTag बदलकर runtime पर भाषा बदलें—UI अपने-आप recompose हो जाता है।

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 string interpolation, lambdas से बहुवचन और nested string groups का समर्थन करता है। यह Android, iOS (Compose for iOS के माध्यम से), Desktop और Web targets पर काम करता है।
7

XML Strings के लिए moko-resources

moko-resources सत्य के स्रोत के रूप में Android-style XML string फ़ाइलें इस्तेमाल करता है और type-safe accessors बनाता है। commonMain/resources/MR/base/ (English) में strings तय करें और हर भाषा के लिए locale folders जोड़ें। Generated MR object compile-time checked access देता है।

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)
Code बनाने के लिए moko-resources को Gradle plugin की आवश्यकता होती है। अगर आपको 'Unresolved reference: MR' दिखाई दे, तो पहले Gradle sync चलाएँ। आपके IDE को MR accessors दिखाई देने से पहले code generation चरण पूरा होना आवश्यक है।
8

kmp-localechain के साथ स्मार्ट Locale Fallback

KMP i18n libraries में configurable fallback chains नहीं होतीं। pt-BR अनुवाद न मिलने पर वे pt-PT को पूरी तरह छोड़कर English दिखाती हैं। kmp-localechain एक standalone message-merging utility से इसे ठीक करता है। यह हर locale के flat Map&lt;String, String&gt; messages लेता है और fallback chain priority लागू करके merged map लौटाता है।

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 flat Map&lt;String, String&gt; maps के साथ काम करता है। अगर आपके messages nested हैं, तो resolve() को देने से पहले उन्हें flatten करें। यह library nested structures के deep merge का समर्थन नहीं करती।
9

अनुवाद स्वचालित करें

KMP i18n सेटअप पूरा होने के बाद AI से अनुवाद स्वचालित करें। अपनी साझा string फ़ाइलों—चाहे वे Kotlin data classes, XML resources या JSON हों—का सीधे अपने IDE या CI/CD pipeline से अनुवाद करें।

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
अनुवाद चरणबद्ध रूप से करें। English source में नई keys जोड़ने पर केवल diff का अनुवाद करें। इससे मनुष्यों द्वारा जाँचे गए अनुवाद सुरक्षित रहते हैं और पूरी फ़ाइलों को दोबारा बनाने की आवश्यकता नहीं पड़ती।

अनुवाद की गुणवत्ता स्वचालित करें

i18n-validate से missing keys और टूटे हुए placeholders को रिलीज़ होने से पहले पकड़ें। वास्तविक अनुवाद आने से पहले i18n-pseudo की pseudo-translations से अपने UI की जाँच करें।

आम समस्याएँ

expect/actual का मेल न खाना

commonMain में हर expect declaration के लिए प्रत्येक target (androidMain, iosMain, jsMain) में actual implementation आवश्यक है। बाद में नया platform target जोड़ने पर actual उपलब्ध कराने तक compiler error देगा। Stubs बनाने के लिए IDE quick-fixes इस्तेमाल करें।

हार्डकोडेड बहुवचन लॉजिक

Singular forms पहचानने के लिए count == 1 कभी इस्तेमाल न करें। French में 0 को singular माना जाता है। Arabic में छह बहुवचन रूप हैं। Russian में 1, 2-4 और 5-20 पर समाप्त होने वाली संख्याओं के लिए अलग रूप इस्तेमाल होते हैं। CLDR-aware libraries (moko-resources) या हर locale के लिए explicit lambdas इस्तेमाल करें।

kmp-localechain में Nested Maps

kmp-localechain flat Map&lt;String, String&gt; पर काम करता है। अगर आप nested maps देते हैं, तो fallback resolution अंदरूनी keys को सही ढंग से merge नहीं करेगा। resolve() call करने से पहले dot-notation keys (जैसे "nav.home") इस्तेमाल करके अपने messages को flatten करें।

moko-resources जोड़ने के बाद Generated Code का न मिलना

MR object एक Gradle plugin बनाता है। moko-resources strings जोड़ने के बाद अपने code में MR.strings.* इस्तेमाल करने से पहले Gradle sync चलाएँ। अगर आपका IDE अब भी errors दिखाता है, तो Build > Rebuild Project आज़माएँ।

सुझाई गई प्रोजेक्ट संरचना

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 अभी आज़माएँ

अपनी अनुवाद फ़ाइल यहाँ छोड़ें

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

या ब्राउज़ करने के लिए क्लिक करें

लक्षित भाषाएँ

साइन अप की ज़रूरत नहींतुरंत अनुमान

अक्सर पूछे जाने वाले प्रश्न