Internationalization (i18n) in Kotlin: A Complete Guide with Examples
Internationalization (i18n) is the process of designing and developing software so that it can be adapted to various languages and regions without requiring changes to the source code. In the Kotlin ecosystem, whether it's Android applications, server-side projects on Ktor, or multiplatform solutions with Kotlin Multiplatform, proper implementation of i18n is critically important for the global success of a product. In this article, we will explore the main approaches, tools, and best practices for internationalizing Kotlin applications.
Why is i18n important in Kotlin?
Kotlin, being a modern language fully compatible with Java, inherits all of Java's localization capabilities but also offers its own unique solutions. Proper internationalization allows you to:
- Significantly expand your application's audience
- Improve the user experience for speakers of different languages
- Comply with the legal requirements of different countries (e.g., regarding currencies or date formats)
- Simplify the process of adding new languages in the future
Main approaches to i18n in Kotlin
1. Using Resource Bundle (Java-compatible approach)
The most common and simple way is to use standard ResourceBundle and property files (.properties). This method is ideal for console applications and server-side projects in Kotlin.
// messages_en.propertiesgreeting=Hello, {0}!farewell=Goodbye
// messages_ru.propertiesgreeting=Привет, {0}!farewell=До свиданияimport java.util.ResourceBundleimport java.text.MessageFormat
fun main() { val bundle = ResourceBundle.getBundle("messages", Locale("ru")) val greeting = MessageFormat.format(bundle.getString("greeting"), "Мир") println(greeting) // Выведет: Привет, Мир! println(bundle.getString("farewell")) // Выведет: До свидания}2. Kotlin-specific approach with extension functions
Kotlin allows you to create a more concise and type-safe API for working with localization using extension functions and delegated properties.
import java.util.*import kotlin.properties.ReadOnlyPropertyimport kotlin.reflect.KProperty
class I18n(private val baseName: String) { private val bundle: ResourceBundle get() = ResourceBundle.getBundle(baseName) operator fun provideDelegate( thisRef: Any?, prop: KProperty<*> ): ReadOnlyProperty { return ReadOnlyProperty { _, _ -> bundle.getString(prop.name) } }}
object Strings { val i18n = I18n("messages") val greeting by i18n val farewell by i18n}
fun main() { // Автоматически использует локаль системы println(Strings.greeting) // Выведет соответствующее значение из файла свойств println(Strings.farewell)} Tools for localization in Kotlin
1. Kotlin Multiplatform (KMP) and expect/actual
For multiplatform projects, Kotlin offers a powerful expect/actual mechanism, allowing you to declare a common API for i18n and implement it separately for each platform.
// В общем коде (commonMain)expect class PlatformStringResources { fun getString(key: String, vararg args: Any?): String}
// В Android (androidMain)actual class PlatformStringResources { private val context: Context = // получаем контекст actual fun getString(key: String, vararg args: Any?): String { val resId = context.resources.getIdentifier(key, "string", context.packageName) return context.getString(resId, *args) }}
// В iOS (iosMain)actual class PlatformStringResources { actual fun getString(key: String, vararg args: Any?): Str