Internationalization support for Ktor framework.
add the dependency to your gradle build file:
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
dependencies {
implementation 'com.github.aymanizz:ktor-i18n:VERSION'
}
where "VERSION" is dependent on the ktor version you are using. If you are using ktor 2.0.0 or newer, use version 2.0.0. If you are using older ktor versions, use version 1.0.0.
For this example we will create a messages bundle with English and Arabic localizations.
Start by installing the feature in your application:
install(I18n) {
supportedLocales = listOf("en", "ar").map(Locale::forLanguageTag)
}
Because the english locale is the first in the supported locales list, it will be considered the default/fallback locale.
Next, let's create i18n/
folder inside the resources directory, and create two files: Messages_en.properties
and
Messages_ar.properties
, in this folder with the following content:
- inside Messages_en.properties
greeting=Hello!
- inside Messages_ar.properties
greeting=هلا!
We now have the most basic setup for the i18n feature, we can use it as follows:
routing {
get("/greeting") {
call.respondText(call.t(R("greeting")))
}
}
Now that the route /greeting
is set up, we can send a request and expect a response depending on the request's
Accept-Language
header value.
For example, if the header value is en-US
the response will be Hello
, if it's en;q=0.7,ar;q=0.9
then the response
will be هلا
, and if it's de
then the response will fallback to Hello
because english is the default locale.
Now let's add more keys to our messages. This time we will use a key with a count, and also a placeholder:
- in Messages_en.properties
pieces=You have {0,number} pieces.
pieces.1=You have a single piece.
pieces.2=You have two pieces.
- in Messages_ar.properties
pieces=لديك {0,number} قطع
pieces.1=لديك قطعة واحدة
pieces.2=لديك قطعتان
And use it like so:
routing {
get("/greeting") { /* ... */ }
get("/pieces") {
val count = call.parameters["count"]?.toIntOrNull() ?: 1
call.respondText(call.t(R("pieces", count = count), count))
}
}
Note that we have passed count twice, once to the R
class constructor which generates the keys to lookup the
localization, this class will generate the keys "pieces.$count"
and "pieces"
, the localization provider will try
each key until one of them is present in the messages bundle, then the obtained message is formatted using count
(the
second parameter passed to t
call.) In the case of the pieces.1
and pieces.2
messages, the extra argument is
ignored. But in the case of peices
message, the {}
placeholder is replaced with the value of count
.
Go ahead and try your application now with different Accept-Language
header values and different count
query
parameter values.
So far we have used the default strategy provided by the R
key generator instances. However, one can implement any
other strategy to suit their needs. For example, here is how to add a gender key to our keys:
enum class Gender { Male, Female }
// implement a key generator
class MyKey(
@PropertyKey(resourceBundle = "i18n.Messages")
baseKey: String,
count: Int? = null,
gender: Gender? = null
) : DelimitedKeyGenerator(baseKey, count?.toString(), gender?.name?.toLowerCase())
Then we can use it in our code as before, in messages:
greeting=Hello!
greeting.female=Hi!
And the application code:
call.t(MyKey("greeting", gender=Gender.Female)) // result is Hi!
The key generator could be any iterable that produces strings, so you can customize the generation even further.
To run the tests:
./gradlew test
To run the formatter:
./gradlew spotlessApply
To run all checks at once:
./gradlew check
If you encounter any issues with this feature, let us know by opening a new issue with the issue description. Make sure that there are no similar issues already open.
Before starting on development for a new feature please open a feature request issue for discussion.
The project is licensed under the MIT License. For further details see LICENSE.