Keys
Translation keys represent individual, named strings in your translation bundle files.
These are represented by Key objects that contain information on their containing bundle and refer to the string's
name, and can be copied along with a host of extra data to be used when those strings are eventually translated.
Generally speaking, you don't need to create your own Key objects, as they're generated by the
Gradle plugin and our code generators.
General Usage
Key objects are immutable data classes.
They can be serialised using kotlinx.serialization, and you can use their API functions to create clones with extra
data and edited settings.
We designed these Key objects to be copied and passed around because this makes them considerably more flexible when
compared to passing around Strings representing translation keys, providing a single container type that does
everything you'd need.
Basic Translations
All translations happen in the context of a specific locale, represented by a Java Locale object.
The following example assumes ICU message format v2, with the following key:
command.slap.name->slap
// Implicitly creating a clone with the given locale,
// and immediately translating it.
Translations.Command.Slap.name
.translateLocale(locale)
// Equivalent call using withLocale directly,
// creating a clone with the given locale explicitly
// before translating it.
Translations.Command.Slap.name
.withLocale(locale)
.translate()
Placeholders
Keys support both ordinal and named placeholder types.
However, they only support one type at a time, and mixing them may result in unpredictable behaviour.
Additionally, please note that some message formats only support named placeholders, and will throw an exception if you try to use ordinal placeholders. For that reason, we recommend you only use named placeholders, unless you know for sure you'll only ever need to use ordinal placeholders.
Depending on the message format you're using, this framework supports two types of placeholders — ordinal and named.
The following examples assume ICU message format v1, with the following keys:
command.mappings.ordinal->Information on the {0} mappings for Minecraft.command.mappings.named->Information on the {name} mappings for Minecraft.
Ordinal
Ordinal placeholders can be thought of as a simple indexed array, where placeholders are represented by the number referring to their index in the array.
// Implicit, without storing placeholders in an
// extra `Key` copy.
Translations.Command.Mappings.ordinal
.translateNamedLocale(locale, "name" to "hashed")
// Implicit, storing placeholders in an
// extra `Key` copy before translating.
Translations.Command.Mappings.ordinal
.withNamedPlaceholders("name" to "hashed")
.translateLocale(locale)
// Explicit, without storing placeholders in an
// extra `Key` copy.
Translations.Command.Mappings.ordinal
.withLocale(locale)
.translateNamed("name" to "hashed")
// Explicit, storing placeholders in an
// extra `Key` copy before translating.
Translations.Command.Mappings.ordinal
.withLocale(locale)
.withNamedPlaceholders("name" to "hashed")
.translate()
Named
Named placeholders can be thought of as a map, where placeholders are represented by the string key referring to their place in the map.
// Implicit, without storing placeholders in an
// extra `Key` copy.
Translations.Command.Mappings.named
.translateNamedLocale(locale, "name" to "hashed")
// Implicit, storing placeholders in an
// extra `Key` copy before translating.
Translations.Command.Mappings.named
.withNamedPlaceholders("name" to "hashed")
.translateLocale(locale)
// Explicit, without storing placeholders in an
// extra `Key` copy.
Translations.Command.Mappings.named
.withLocale(locale)
.translateNamed("name" to "hashed")
// Explicit, storing placeholders in an
// extra `Key` copy before translating.
Translations.Command.Mappings.named
.withLocale(locale)
.withNamedPlaceholders("name" to "hashed")
.translate()
Advanced Concepts
We designed Key objects to support several advanced use-cases, solving problems we ran into when we were designing
our Discord bot framework.
Nesting Keys
When you provide values for placeholders in your translation strings, sometimes you need to replace a placeholder with
another translated string.
By default, Key objects will detect any nested Keys in your placeholders and automatically translate them, which
makes it easier to nest them.
Nested Keys will inherit the parent key's bundle and locale, but only if the nested Key doesn't already contain them.
The following example assumes ICU message format v2, with the following keys:
command.time->Get the current time in {$country}.country.france->France
// Manual approach.
Translations.Command.time
.withLocale(locale)
.translateNamed(
"country" to Translations.Country.France
.withLocale(locale)
.translate()
)
// With nesting.
Translations.Command.time
.withLocale(locale)
.translateNamed("country" to Translations.Country.France)
If you need to disable this feature, you can use the withTranslateNestedKeys() function.
// Disable nested `Key` support.
Translations.Command.time
.withTranslateNestedKeys(false)
// ...
Post-Processors
Post-processors are applied after a Key is translated, to modify the translated value before the translation functions
return it.
These are simply lambdas, of the form: Key.(translation: String) -> String
You can register a single post-processor using the withPostProcessor() function, which you can also use as a builder.
If you need to register multiple post-processors at once, you can pass a collection to the withPostProcessors()
function.
We also provide a handful of convenience functions you can use to add common post-processors to a Key.
These functions use the locale stored in the Key, or the configured default locale
if the Key doesn't have one set.
Capitalise the first letter of the translated string, if that makes sense in the current locale.
Capitalise the first letter of each word in the translated string, if that makes sense in the current locale.
Transform the translated string to lower-case letters, if that makes sense in the current locale.
Transform the translated string to upper-case letters, if that makes sense in the current locale.
Preset Placeholders
Sometimes, it is useful to be able to store placeholders in a copy of a Key object,
to be passed around and translated elsewhere.
Keys can store both named and ordinal placeholders, and you can create clones containing them using the
withNamedPlaceholders() and withOrdinalPlaceholders() functions, respectively.
These placeholders are stored in the namedPlaceholders and ordinalPlaceholders properties, allowing other functions
to retrieve them and act accordingly.
The following example assumes ICU message format v2, with the following key:
command.time.response->The time in {$country} is {$time}.
// Create `Key` clone with a locale and a preset placeholder.
val key = Translations.Command.Time.response
.withLocale(locale)
.withNamedPlaceholders("country" to "France")
// Pass the key to another function, which translates the `Key`,
// providing the correct time.
user.sendTime(key)
You can use withPresetPlaceholderPosition() to change whether the preset placeholders are processed before or after
any placeholders passed to the translation functions, a particularly useful tool for ordinal placeholders.
Provide FIRST (the default) to use the preset placeholders first, or LAST to use the translation function's
arguments first:
- Named Placeholders: Combine them into a single map in the given order, giving priority to any duplicate values in the second map.
- Ordinal Placeholders: Combine them into a single list, placing the first list at the beginning, and the second list at the end.
Removing Settings
Key objects also provide several functions you can use to copy them with filtered preset placeholders, or without
certain settings.
Filtering Functions
Returns a copy of this Key, containing a filtered set of named placeholders based on the provided predicate.
Returns a copy of this Key, containing a filtered set of ordinal placeholders based on the provided predicate.
Returns a copy of this Key, containing a filtered set of post-processors based on the provided predicate.
Removal Functions
Returns a copy of this Key, with the stored bundle and locale removed.
Returns the same Key if both are already null.
Returns a copy of this Key, with the stored bundle removed.
Returns the same Key if the bundle is already null.
Returns a copy of this Key, with the stored locale removed.
Returns the same Key if the locale is already null.
Returns a copy of this Key, with the stored named preset placeholders removed.
Returns the same Key if there are already no named preset placeholders.
Returns a copy of this Key, with the stored ordinal preset placeholders removed.
Returns the same Key if there are already no ordinal preset placeholders.
Replacing Settings
You can replace specific Key object settings using the following functions.
Returns a copy of this Key, potentially with a new bundle and locale set.
The new bundle to store in the Key.
The new locale to store in the Key.
Whether to overwrite the bundle, if this Key already contains one.
Provide false and the existing bundle will not be overwritten.
Whether to overwrite the locale, if this Key already contains one.
Provide false and the existing locale will not be overwritten.
Returns a copy of this Key, potentially with a new bundle set.
The new bundle to store in the Key.
Whether to overwrite the bundle, if this Key already contains one.
Provide false and the existing bundle will not be overwritten.
Returns a copy of this Key, potentially with a new locale set.
The new locale to store in the Key.
Whether to overwrite the locale, if this Key already contains one.
Provide false and the existing locale will not be overwritten.
Other APIs
Returns a string in this form: Key "$name" (Bundle $bundle, Locale $locale).
The bundle and locale will be omitted if they're missing.