Victor Kropp

Localizing Kotlin applications

Hey! Hóla! こんにちは Hallo!

Tower of Babylon by Anton Joseph von Prenner. The Met

Tower of Babylon by Anton Joseph von Prenner. The Met

Software nowadays is expected to talk to user in their language of choice. Some people know just a single language, some prefer specific languages, even when they are neither their native nor local at their location. For example, when they are learning a new one.

Among all existing frameworks for internationalization (i18n for short), I prefer GNU Gettext. It is an established solution, well-supported in offline (e.g. Poedit) as well as online tools for translators (e.g. Crowdin). The format itself is simple, though it supports pluralization – the feature missing in many other frameworks.

There are original libraries for many languages, but not for Kotlin. On the JVM, we, of course, can use a Java library. It has some issues, though.

There are Kotlin libraries that parse .po, but they either do not support plurals at all, or do this incorrectly. Many such implementations require to manually specify the plural form to use. Different languages have a different number of plural forms (from no dedicated plural form at all, to up to 5 forms). So, for proper plural support, one cannot just pass a single flag, whether the string is singular or plural.

Welcome kotlinx-gettext

That’s why I ultimately decided to create my own i18n library for Kotlin. It requires no preprocessing and parses .po files directly, which means they can be updated on the fly. It correctly supports pluralization. And it is very lightweight (around 35kb for JVM).

There is also a Kotlin compiler plugin and complementary Gradle plugin to extract strings for translations. All APIs are compatible with original implementations.

The library is available on Maven Central and Gradle plugin portal. Source code is on GitHub, and it is licensed under the terms of Apache 2 license.

We’ve been using this library in JetBrains Toolbox App for several months already, and it just works™. The migration was as easy as replacing several import statements with new namespaces.

The library is also under internal testing in some other JetBrains projects, which is very exciting.

Usage

Sample application is provided alongside the library. Take a look!

In short, to translate strings inside your application, you need to load appropriate translation file and call .tr() method.

val i18n = Gettext.load(Locale.GERMAN, Thread.currentThread().contextClassLoader.getResourceAsStream("de.po")!!)
println(i18n.tr("Hello world!"))

These tr method calls are also work as markers, which strings to extract for translation.

All you need is to set up the plugin.

plugins {
  id("name.kropp.kotlinx-gettext") version "0.5.0"
}

gettext {
  potFile.set(File(projectDir, "src/messages.pot"))
  keywords.set(listOf("tr","trn:1,2"))
}

Then invoke ./gradlew gettext to extract strings for translations into src/messages.pot

Tips and tricks

You may want to enable plugin permanently by adding

gettext {
    enabled.set(true)
}

In this case, the messages.pot will always be up-to-date.

Future plans

The ultimate goal is to make the library fully multiplatform. It only supports Kotlin/JVM and Kotlin/JS as of now, but its core is written in pure Kotlin. Multiplatform I/O is implemented via Okio, which is the de-facto standard in this area for Kotlin as it lacks an official multiplatform I/O library as of now.

I also plan to add support for loading .mo files in addition to .po. These require conversion, but are smaller, which may be crucial for some applications.

Give it a try!

If you are looking for an internationalization library for your Kotlin project, give kotlinx-gettext a try and submit feedback on GitHub.

kotlini18n

Subscribe to all blog posts via RSS and follow me @kropp on Twitter.