Интернационализация (i18n) в Kotlin: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Интернационализация (i18n) в Kotlin: от основ до продвинутых техник

Интернационализация (сокращённо i18n — от английского «internationalization», где 18 — количество букв между первой «i» и последней «n») — это процесс проектирования программного обеспечения таким образом, чтобы его можно было легко адаптировать под различные языки и региональные стандарты без внесения изменений в код. Для Kotlin, как современного мультиплатформенного языка, поддержка i18n особенно важна, ведь приложения на нём работают на Android, бэкенде (JVM), вебе (Kotlin/JS) и нативных платформах (Kotlin/Native).

В этой статье мы рассмотрим, как грамотно организовать интернационализацию в Kotlin-проектах: от базовых подходов с использованием ресурсных файлов до продвинутых решений с библиотеками-помощниками.

1. Базовый подход: ресурсные файлы (ResourceBundle)

Самый простой и распространённый способ — использовать стандартные механизмы Java ResourceBundle и файлы свойств (.properties). Этот метод отлично работает для JVM-проектов (Android, Spring Boot, консольные приложения).

Шаг 1. Создаём файлы локализации

Создайте в ресурсах проекта файлы вида messages.properties (по умолчанию) и messages_ru.properties (для русского языка):

# messages.properties (английский, язык по умолчанию)
greeting=Hello, {0}!
farewell=Goodbye, {0}!
app.title=My Application

# messages_ru.properties (русский)
greeting=Привет, {0}!
farewell=До свидания, {0}!
app.title=Моё приложение

Шаг 2. Загружаем и используем в Kotlin

Создайте вспомогательный объект для работы с локализацией:

import java.text.MessageFormat
import java.util.*

object I18n {
    private var bundle: ResourceBundle = ResourceBundle.getBundle("messages", Locale.getDefault())

    fun setLocale(locale: Locale) {
        bundle = ResourceBundle.getBundle("messages", locale)
    }

    fun get(key: String, vararg args: Any?): String {
        val pattern = bundle.getString(key)
        return MessageFormat.format(pattern, *args)
    }
}

fun main() {
    // Используем английский по умолчанию
    println(I18n.get("greeting", "Alice")) // Hello, Alice!

    // Переключаемся на русский
    I18n.setLocale(Locale("ru"))
    println(I18n.get("greeting", "Алиса")) // Привет, Алиса!
}

2. Библиотека kotlin-i18n (от JetBrains)

Для Kotlin Multiplatform существует официальная библиотека kotlin-i18n, которая позволяет работать с переводами на всех платформах единообразно. Она использует формат Gettext PO (Portable Object), который удобен для переводчиков.

Установка (Gradle)

repositories {
    mavenCentral()
}
dependencies {
    implementation("org.jetbrains.kotlinx:kotlin-i18n:0.1.0")
}

Пример использования

import org.jetbrains.kotlinx.i18n.*

fun main() {
    // Создаём переводчик с загрузкой из PO-файлов
    val translator = Translator(
        bundleName = "messages",
        locale = Locale("ru")
    )

    // Используем с подстановкой параметров
    val message = translator.translate(
        "greeting",
        "Мир"
    )
    println(message) // Привет, Мир!
}

Преимущества: поддержка множественного числа (plural forms), контекстов перевода, работа на всех платформах Kotlin.

3. Продвинутый подход: библиотека ok-i18n

Для проектов, где требуется гибкость и интеграция с DI-контейнерами (например, Koin или Dagger), отлично подходит библиотека ok-i18n. Она поддерживает JSON-формат для переводов, что удобно при работе с REST API или конфигурационными файлами.

Пример JSON-файла перевода (ru.json)

{
  "app": {
    "title": "Моё приложение",
    "description": "Это пример интернационализации"
  },
  "errors": {
    "not_found": "Ресурс не найден",
    "server_error": "Ошибка сервера. Код: {0}"
  }
}

Код на Kotlin

import com.github.ok-i18n.*

data class AppTranslations(
    val app: AppSection,
    val errors: ErrorsSection
)

data class AppSection(val title: String, val description: String)
data class ErrorsSection(val notFound: String, val                    

Рекомендаций