Internationalization
- Automatic language detection (based on the browser language settings).
- Dynamic language change with automatic re-rendering of the whole application GUI.
- Multi-language support for built-in complex components:
- datetime control - 46 languages
- select control - 12 languages
- upload control - 39 languages
- richtext editor - 2 languages
Contributions are welcomed. - Minimal impact of internationalization process on the application source code.
- Automatic extraction of text to be translated from the application code.
- Support for well known translation files format (gettext
*.po
files).
To create a multi-language application you have to add the
kvision-i18n
module as a dependency for your project.dependencies {
// ...
compile "io.kvision:kvision-i18n:${kvisionVersion}"
// ...
}
To mark some text for translation just use one of the four available helper methods from the
io.kvision.i18n.I18n
object instead of plain string literals.Method | Description |
tr | Dynamic translation for singular form. |
ntr | Dynamic translation for plural forms. |
gettext | Static translation for singular form. |
ngettext | Static translation for plural forms. |
Dynamic translations are bound to the components they are part of (tag content, button text etc.). They are dynamically changed when the current language for the application is changed.
// ...
import io.kvision.i18n.tr
// ...
span(tr("Label text"))
button(tr("Button text"))
Static translations are evaluated only when the helper method is called. They can be used to translate text not bound to any KVision component.
// ...
import io.kvision.i18n.gettext
// ...
console.log(gettext("Some info message"))
KVision has support for plural language forms, so you can use
ntr
or ngettext
methods when plural forms are necessary.// ...
import io.kvision.i18n.I18n.ntr
// ...
val count = 5
hPanel {
span("$count")
span(ntr("file", "files", count))
}
The
gettext
and ngettext
methods allow you to use additional runtime parameters, using gettext.js substitution mechanism and the following format of translation files:messages-en.po
msgid "%2 has one car"
msgid_plural "%2 has %1 cars"
msgstr[0] ""
msgstr[1] ""
messages-pl.po
msgid "%2 has one car"
msgid_plural "%2 has %1 cars"
msgstr[0] "%2 ma jeden samochód"
msgstr[1] "%2 ma %1 samochody"
msgstr[2] "%2 ma %1 samochodów"
Use this translation in your application like this:
ngettext("%2 has one car", "%2 has %1 cars", 3, "Robert")
Until you create and initialize translation files for some other language, your application will use string literals used in the source code. It's probably a good practice to use English literals in your code and other languages in the translation files.
To generate basic translation files run the command:
./gradlew generatePotFile (on Linux)
gradlew.bat generatePotFile (on Windows)
This command will search your sources for any usages of internationalization methods (
tr
or others) and generate a messages.pot
file in the src/jsMain/resources/i18n
directory. This file is the base for your translations. For any language you would like to support, copy the messages.pot
file to messages-XX.po
, where XX is a country code (en, de, es, fr etc.). These files should be translated according to the PO format specification. You can use many popular tools for editing PO files to simplify the translation process.You should correctly set
Language
and Plural-Forms
headers of your PO files.After adding some new texts to your sources you can call the
./gradlew generatePotFile
task to refresh the messages.pot
file. You can then use the msgmerge
tool from the GNU gettext package to merge new keys with existing translation files. You can also add new msgid
and msgstr
lines to your translation files by hand.To initialize translations for one or more languages, you need to initialize the
I18n.manager
property during application initialization (preferably in the start
method of your Application
object). You should add all supported languages to the map given as the parameter to the DefaultI18nManager
constructor.class Helloworld : Application() {
override fun start() {
// ...
I18n.manager =
DefaultI18nManager(
mapOf(
"en" to require("i18n/messages-en.json"),
"pl" to require("i18n/messages-pl.json"),
"de" to require("i18n/messages-de.json")
)
)
// ...
}
}
The current language is bound to the
I18n.language
property. This property is initialized with the default country code from the browser language settings. It can be changed at any time from your code.select(listOf("en" to "English", "de" to "Deutsch"), I18n.language) {
onEvent {
change = {
I18n.language = self.value ?: "en"
}
}
}
Last modified 3mo ago