Localization¶
JDA-Commands provides the I18n
class,
which is used as the main entrypoint for localization. This class can be used by users of the framework to localize their messages.
Localization Messages¶
Localization messages are identified by their corresponding key. A key can be freely chosen but might be limited by
the restrictions of the used Localizer
implementation.
If a certain message for a key isn't found, the key is returned as the messages value.
Implicit Localization¶
Instead of using the localization API manually through the I18n
class, JDA-Commands allows for implicit usage of
localization keys in many common places. These include:
- Component API including the corresponding annotations like
@Button
,@Modal
etc. - Reply API, for example the string content of a message
Reply#reply(String)
- (Slash commands are supported trough JDAs
LocalizationFunction
)
JDA-Commands will first try to find a localization message based on the provided String (as the key) and the users locale
retrieved by GenericInteractionCreateData#getUserLocale()
and if not found, will use the String directly as the content.
Warning
Localization of MessageCreateData
is not supported implicitly.
To localize such messages you have to manually use I18n#localize(...)
.
Example¶
@Bundle("component")
@Interaction
public class ComponentTest {
@Command("say hi")
public void onCommand(CommandEvent event) {
event.reply("command-reply");
}
}
In this example, the bundle component
will be searched for the key command-reply
.
Directly Inserting Localization Messages¶
In some cases, especially while testing it's common to hardcode a messages content. But often times you still want to use dynamic placeholders.
For your luck JDA-Commands treats hardcoded values (where a localization key would normally be used) as the content of
a localization message, thus supporting all functionality of the used Localizer
implementation.
A String value (whether in an annotation, embed, modal or component) will be resolved by following order:
- searched for as a localization key
- treated as localization message content
- used as raw value
An example of this can be found here.
Variables/Placeholders¶
Most localization systems support variables or placeholders to insert dynamic values into a message.
JDA-Commands provides this functionality in many places by using I18n.Entry
.
Often you will find a vararg of this class at the end of a method parameters list. By adding entries
there (preferably by using I18n#entry
as a static import) it's possible for you to define placeholders for a given scope defined by the javadocs of
the used method.
Example (Fluava)¶
import static com.github.kaktushose.jda.commands.i18n.I18n.entry;
@Interaction
public class ComponentTest {
@Command("say hi")
public void onCommand(CommandEvent event) {
event.with()
.components(Component.button("onButton", entry("name", event.getUser().getName())))
.reply();
}
@Button("Hello { $name }")
public void onButton(ComponentEvent event) {
...
}
}
Bundles¶
Localization bundles are a known concept from Javas ResourceBundles. JDA-Commands supports different bundles of
localization files by adding them to the localization key or using the @Bundle("bundle_name")
annotation.
Via Key¶
To state which bundle to use the direct way is to include it in the key following the format bundle#key
.
For example a message with key user#not-found
will be searched for in the bundle user
and the key not-found
.
Via Annotation¶
If no bundle is specified, it will traverse the stack (the called methods) and search for the nearest
@Bundle
annotation with following order:
- Method that called
I18n#localize(...)
- Other called methods in the same class
- This methods class
- The class' packages
package-info.java
file
If no annotation is found, the previous method (in another class) is searched with the same pattern up to the class at the very beginning.
Detailed Example
package my.app;
class A {
void aOne() {
i18n.localize(Locale.GERMAN, "fail", Map.of());
}
void aTwo() {
aOne();
}
}
package my.app.other;
@Bundle("class_bundle")
class B {
A another = new A();
void bOne() {
a.aOne();
}
@Bundle("method_bundle")
void bTwo() {
bOne();
}
}
The order in which the bundle name is searched for is following:
- method
A#aOne()
- method
A#aTwo()
- class
A
package-info.java
of packagemy.app
- method
B#bOne()
- method
B#bTwo()
The found bundle would be package_bundle
. If I18n#localize(Locale, String, I18n.Entry...)
would be called in, for example, B#bTwo
the bundle would be method_bundle
.
Default Bundle¶
If no bundle is found with the above techniques, a bundle called default
will be used.
Default Implementation¶
By default, JDA-Commands supports localization with help of the Fluava library, a Project Fluent implementation for Java.
You can provide an own instance of the Fluava
class by calling
the appropriate builder method.
Fluava myFluava = new Fluava(Locale.GERMAN);
JDACommands.builder(jda, Main.class)
.localizer(new FluavaLocalizer(myFluava))
.start();
Tip
To set a fallback bundle you have to pass it to the constructor of the Fluava
class. In the above example the fallback locale
is German.
Localization Keys¶
Since Project Fluent
doesn't support dots (.
) in localization keys, the Fluava
integration will change all dots to dashes (-
). For example, my.key
will become my-get
. This change also effects
all JDA Slash Command localization keys.
Localization files¶
Fluava supports the loading and discovery of bundles on the classpath
(resource directory) similar to Javas ResourceBundle
but with a slightly more flexible structure.
The classpath will be lazily searched for a fluent file given a specific locale with the following order:
BASE_LANGUAGE_COUNTRY_VARIANT.ftl
BASE/LANGUAGE_COUNTRY_VARIANT.ftl
BASE_LANGUAGE_COUNTRY.ftl
BASE/LANGUAGE_COUNTRY.ftl
BASE_LANGUAGE.ftl
BASE/LANGUAGE.ftl
If a key isn't found in any of the above files, the same procedure will be done for the given "fallback" locale.
A resource folder structure could for example look like this:
src/
├─ main/
│ ├─ resources/
│ │ ├─ component/
│ │ │ ├─ de.ftl
│ │ │ ├─ en.ftl
│ │ ├─ default_de.ftl
│ │ ├─ default_en.ftl
Such a structure has the two bundles component
and default
and a locale specific file for German and English for each bundle.
LocalizationFunction (JDA)¶
JDA uses the LocalizationFunction
for localizing slash commands.
We implement this interface based on our I18n
class as described above.
See the JDA Docs for details.