Интернационализация Kotlin-приложений: как не запутаться в языках

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

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

Начать курс

Почему вашему приложению нужна интернационализация уже сегодня

Представьте: вы написали крутое Kotlin-приложение — удобное, быстрое, с красивым UI. Но все надписи в нём на русском. Пользователь из Берлина или Токио открывает ваш продукт и... закрывает через 10 секунд. Почему? Потому что он не понимает ни слова. Интернационализация (i18n) — это не просто «перевод кнопок», а мост между вашим кодом и миллионами пользователей по всему миру.

В этой статье мы разберём, как грамотно внедрить интернационализацию Kotlin в проекты любого масштаба: от Android-приложений до серверных сервисов на Ktor. Вы узнаете, как не допустить типичных ошибок, как использовать Kotlin Multiplatform для общей логики и почему «волшебные строки» — зло.

Основы i18n в Kotlin: от простого к сложному

Ресурсные файлы: фундамент локализации

Самый надёжный способ организовать локализацию Kotlin-приложения — вынести все строковые литералы в отдельные ресурсные файлы. В Android Studio это стандартные strings.xml, но для чистого Kotlin (backend, desktop) мы можем использовать .properties или JSON.

Пример для Android:

// res/values/strings.xml (русский — по умолчанию)
<resources>
    <string name="app_name">Моё приложение</string>
    <string name="welcome_message">Добро пожаловать, %s!</string>
    <string name="points">У вас %d баллов</string>
</resources>

// res/values-en/strings.xml (английский)
<resources>
    <string name="app_name">My App</string>
    <string name="welcome_message">Welcome, %s!</string>
    <string name="points">You have %d points</string>
</resources>

В коде обращаемся через context.getString():

val userName = "Алексей"
val message = context.getString(R.string.welcome_message, userName)
// Результат: "Добро пожаловать, Алексей!" (или "Welcome, Alexey!" на английском)

Обратите внимание: мы используем %s для строк и %d для чисел. Никогда не склеивайте строки вручную — в японском или арабском порядок слов может отличаться.

Работа с плюральными формами (множественное число)

Русский язык славится сложными правилами склонения: 1 балл, 2 балла, 5 баллов. В английском всё проще: 1 point, 2 points. Интернационализация Kotlin обязана это учитывать.

В Android для этого есть <plurals>:

// strings.xml
<plurals name="points_count">
    <item quantity="one">%d балл</item>
    <item quantity="few">%d балла</item>
    <item quantity="many">%d баллов</item>
    <item quantity="other">%d баллов</item>
</plurals>

// В коде
val points = 3
val text = resources.getQuantityString(R.plurals.points_count, points, points)
// "3 балла"

Для серверного Kotlin можно использовать библиотеку icu4j или написать свой простой resolver. Главное правило: никогда не хардкодьте тексты с числами.

Интернационализация в Kotlin Multiplatform (KMP)

Если вы пишете кроссплатформенное приложение на Kotlin Multiplatform, задача усложняется. Общий код не имеет доступа к Android-ресурсам. Решение — использовать общий интерфейс и платформенные реализации.

Архитектура для KMP

Создаём expect-класс в common модуле:

// commonMain
expect class StringProvider {
    fun getString(key: String, vararg args: Any): String
}

// Использование в общем коде
class Greeting(private val strings: StringProvider) {
    fun greet(name: String): String {
        return strings.getString("welcome", name)
    }
}

Реализация для Android:

// androidMain
actual class StringProvider(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 (через NSLocalizedString):

// iosMain
actual class StringProvider {
    actual fun getString(key: String, vararg args: Any): String {
        val format = NSLocalizedString(key, comment = "")
        return NSString.stringWithFormat(format, args)
    }
}

Теперь ваш общий код работает с единым API, а платформенная часть отвечает за загрузку переведённых строк. Это идеальный пример локализации Kotlin-приложения без дублирования бизнес-логики.

Современные инструменты: библиотеки для i18n в Kotlin

Ручная работа с ресурсами — это база, но для больших проектов существуют специализированные библиотеки, которые упрощают интернационализацию Kotlin:

  • Kotlin-i18n — лёгкая библиотека для серверных приложений, поддерживает .properties и JSON, автоматическое определение локали из заголовков HTTP.
  • Gettext для Kotlin — порт GNU Gettext, удобен, если вы уже используете .po файлы в других проектах.
  • PluralKit — специализированная библиотека для работы с плюральными формами в Kotlin, поддерживает 200+ языков.

Пример использования Kotlin-i18n:

// build.gradle.kts
implementation("io.github.xxlabaz:kotlin-i18n:1.2.0")

// Код
val i18n = I18n(Locale("ru"))
val message = i18n["welcome", "Мария"]
// "Добро пожаловать, Мария!"

Если вы работаете с Kotlin для backend (Ktor, Spring), обязательно настройте middleware для автоматического определения языка пользователя по заголовку Accept-Language.

Практические советы: как не провалить локализацию

1. Избегайте конкатенации строк

Плохо:

val text = "Привет, " + userName + "! У тебя " + points + " баллов."

Хорошо:

val text = getString(R.string.profile_info, userName, points)

2. Учитывайте направление текста (RTL)

Арабский, иврит и фарси пишутся справа налево. В Android используйте android:layoutDirection="locale" и тестируйте

Блоги

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