/i18n4k

Internationalization for Kotlin

Primary LanguageKotlinApache License 2.0Apache-2.0

i18n4k

Version Kotlin Kotlin Kotlin License

Kotlin/Multiplatform Kotlin/JVN Kotlin/JS Kotlin/Native

i18n4k

Internationalization for Kotlin

Home: github.com/comahe-de/i18n4k

Lastest release version: 0.9.0

Table of contents

About

i18n4k is a multiplatform (JVM, JS, native) library and code generator for Kotlin to handle internationalisation (i18n) in your program.

It provides

  • Locale class to store the selected language
  • LocalizedString class that stores the reference to the translatable message. The toString() will return the message string in the currently selected language/locale.
  • Parameter support via LocalizedStringFatory* that creates a LocalizedString with the supplied parameters using a parameter format like "Hello, {0}!"
  • Code generators that creates...
    • ... access objects with a constant per message key
    • ... objects for inline storing of messages of specified locales (no need of loading resources at runtime)
    • ... optimized message files for loading message resources at runtime
  • The code generators currently can load Java Properties files (but can be extended for additional formats).
  • Gradle plugin to start the code generator

Supported platforms

  • Java (JVM) & Android
  • JS (IR backend)
  • Native:
    • iosArm64
    • iosX64
    • iosSimulatorArm64
    • macosX64
    • macosArm64
    • mingwX64
    • linuxX64
    • tvosArm64
    • tvosX64
    • tvosSimulatorArm64
    • watchosArm64
    • watchosX86
    • watchosX64
    • watchosSimulatorArm64

Artefact repository

Ensure that you have Maven-Central (mavenCentral()) in your repository list

repositories {
    mavenCentral()
}

Gradle dependencies

For multiplatform projects: add de.comahe.i18n4k:i18n4k-core:<VERSION> to commonMain source set.

val commonMain by getting {
    dependencies {
        implementation("de.comahe.i18n4k:i18n4k-core:0.9.0")
    }
}

For Kotlin/JS: add de.comahe.i18n4k:i18n4k-core-js:<VERSION> to the dependencies

dependencies {
    implementation("de.comahe.i18n4k:i18n4k-core-js:0.9.0")
}

For Kotlin/Jvm: add de.comahe.i18n4k:i18n4k-core-jvm:<VERSION> to the dependencies

dependencies {
    implementation("de.comahe.i18n4k:i18n4k-core-jvm:0.9.0")
}

Gradle plugin

Apply the plugin "de.comahe.i18n4k", e.g:

plugins {
    id("de.comahe.i18n4k") version "0.9.0"
}

The configuration is done via the i18n4k object, e.g.:

i18n4k {
    sourceCodeLocales = listOf("en", "de")
}

For the full list of parameters, see I18n4kExtension

Code generation

The Gradle plugin can generate code to access the message via constants instead of string keys or similar.

Adding message bundles

By default, the language files of the message bundles are searched in the path src/main/i18n (normal projects) respectively src/commonMain/i18 (for multiplatform projects). See configuration for other folder locations.

Any sub folder in this directory will be converted to a package in the Kotlin code.

Inline storing of translations

To avoid loading of translations at runtime, there is also the possibility to include the messages in the source code. So the translations are always available and no need for platform-specific loading of resources is needed. Drawback is that the messages are hold in memory all the time. So only the most important translations should be stored in the source code.

Optimized message files

i18n4k has an internal format to store message files to be loaded at runtime. It is index-based instead of key-bases as e.g. Java-Property files. This has the advantage that no memory is wasted for storing the key in each language.

More details are described here MessagesProviderViaLoadingText

Runtime configuration

The current locale and other parameters are stored in the global variable i184k of type I18n4kConfig

Predefined implementations of I18n4kConfig are:

The configuration can be changed by assigning (and editing) one of these implementation to the i184k variable.

Hints for Android

Generated resource files are not added to the "Java resource", but as "raw Android resources". As Android does not allow sub-folders or uppercase letters, the resource name is adapted: packages names are prefixed to the resource name and concatenated by "_". Also, camel case names are converted to sneak-case, e.g. "/x/y/MyMessages_fr.i18n4k.txt" will be changed to "x_y_my_messages_fr_i18n4k".

Generated raw resources can be loaded in the following way:

 MyMessages.registerTranslation(
    MessagesProviderViaText(
        text = String(
            context.resources.openRawResource(
                R.raw.x_y_my_messages_fr_i18n4k
            ).readBytes()
        )
    )
)

Message format

The default message format is similar to the Java-MessageFormat. E.g. the text "Hello, {0}" has one parameter. Any string can be used as parameter name.

The parameter values can also be formatted, e.g. "{0,number, %.2}" formats parameter 0 as number with max. 2 fraction digits. See Formatters for more information.

For messages with parameters, the generator plugin creates special LocalizedStringFactories where the parameters must be supplied as arguments to get the resulting string.

Other message formatters can be implemented and be configured in I18n4kConfig.messageFormatter

Further dokumentation

For more advanced features, have a look at the documenation files

Example

Source files are the following message bundle as Java-Properties files located in the src/main/i18n/x/y/ folder

MyMessages_en.properties

sayHello=Hello, {0}!
title=Internationalisation example \uD83C\uDDEC\uD83C\uDDE7

MyMessages_de.properties

sayHello=Hallo, {0}!
title=Internationalisierung Beispiel \uD83C\uDDE9\uD83C\uDDEA

In the Gradle script, "en" is defined as source code locale (to be stored in source code)

i18n4k {
    sourceCodeLocaleCodes = listOf("en")
}

This results in the following generate Kotlin code

package x.y

import de.comahe.i18n4k.Locale
import de.comahe.i18n4k.messages.MessageBundle
import de.comahe.i18n4k.messages.providers.MessagesProvider
import de.comahe.i18n4k.strings.LocalizedString
import de.comahe.i18n4k.strings.LocalizedStringFactory1
import kotlin.Array
import kotlin.Int
import kotlin.String

/**
 * Massage constants for bundle 'MyMessages'. Generated by i18n4k.
 */
public object MyMessages : MessageBundle("MyMessages", "x.y") {
    /**
     * Hello, {0}!
     */
    public val sayHello: LocalizedStringFactory1 = getLocalizedStringFactory1("sayHello", 0)

    /**
     * Internationalisation example 🇬🇧
     */
    public val title: LocalizedString = getLocalizedString0("title", 1)

    init {
        registerTranslation(MyMessages_en)
    }
}

/**
 * Translation of message bundle 'MyMessages' for locale 'en'. Generated by i18n4k.
 */
private object MyMessages_en : MessagesProvider {
    private val _data: Array<String?> = arrayOf(
        "Hello, {0}!",
        "Internationalisation example 🇬🇧"
    )

    public override val locale: Locale = Locale("en")

    public override val size: Int = _data.size

    public override fun `get`(index: Int): String? = _data[index]
}

And the following message file:

MyMessages_de.i18n4k.txt

de
^
Hallo, {0}!
^
Internationalisierung Beispiel 🇩🇪
^

The messages can than be accessed like this

println(MyMessages.sayHello("i18n4k"))

which prints

Hello, i18n4k!

The German message file can be loaded at runtime, e.g. like this (for JVM plattform)

MyMessages.registerTranslation(MessagesProviderViaResource(pathToResource = "/x/y/MyMessages_de.i18n4k.txt"))

Changing the locale can be done like this:

val i18n4kConfig = I18n4kConfigDefault()
i18n4k = i18n4kConfig
i18n4kConfig.locale = Locale("de")

Afterwards the expression from above prints

Hallo, i18n4k!

Example projects

Please also check the examples projects in the examples folder!

Contribute

Status

The library is under development. So the API is not stable. Enhancements and feature request are very welcome.