Internationalization
UltiTools provides an easy-to-use API for internationalization, allowing you to easily add multilingual support to your plugin.
Create a language file
Create a lang folder in the resources folder. Put your plugin language files in it according to your needs.
{
"test": "测试",
"test2": "测试2"
}Name the file zh.json, where zh is the language code.
Language codes can be found here.
YAML Language Files
A .yml/.yaml catalogue is also supported
As of v6.3.0, a module's lang folder can also use .yml/.yaml instead of .json -- Language.fromYaml flattens nested keys with dots, and a section node is not itself turned into a string entry.
A module shipping only .yml can still fail to load its own dictionary
Every internal module shares one classloader with the core UltiTools plugin, so a lookup for lang/<code>.json inside your jar can resolve the core's own .json file before .yml is ever tried, leaving i18n(...) returning raw keys (issue #412) -- ship a lang/<code>.json alongside your .yml (even a near-duplicate) as a workaround until this is resolved.
Provenance-based file refresh
As of v6.3.0, an untouched language file is refreshed automatically
saveResources() records the SHA-256 of every lang/<code><ext> file it extracts. On the next start, a file whose recorded hash still matches its on-disk bytes is replaced by the current jar's copy, with one INFO line naming the file.
A file you have edited is never overwritten this way: if its bytes no longer match the recorded hash, or no hash was ever recorded and the bytes differ from the jar's, the framework leaves your file alone. Only the individual keys whose String.format placeholder count (%s, %d, or an explicit %1$s index) has changed between your file and the current jar are resolved from the jar instead, each logged once as a WARN naming the module, file and key -- your other keys keep your wording.
To force a refresh of a customised file, delete it and restart the module: the framework re-extracts it from the jar and records a fresh baseline hash.
Register language
The active language comes from the UltiTools config
The language actually used is the language key of the UltiTools main plugin config, which getLanguageCode() reads. As of v6.3.0, supported() is now consulted before that language is constructed: an unsupported configured code produces a warning and the module falls back to a language that actually exists, instead of silently loading an empty dictionary.
supported()'s default implementation is derived from the module's own lang/*.json files, so name each language file lang/<code>.json and it does not need overriding at all. Overriding it still works and takes priority, for the rare case where a module wants to claim support for a code it has no file for.
UltiTools needs to know which languages your plugin supports, so you need to register them in the class that inherits UltiToolsPlugin.
A simple way is to add @I18n and add language codes in the class that inherits UltiToolsPlugin:
@I18n({"zh", "en"})Sure, you can also override the supported() method and return a List<String> containing the language code:
@Override
public List<String> supported() {
return Arrays.asList("zh", "en");
}How to use
In the class that inherits UltiToolsPlugin, there is an i18n method for getting multilingual strings.
String test = i18n("test");
// Output:测试If the string does not exist in the language file, the string itself will be returned.
String test3 = i18n("test3");
// Output:test3TIP
In the case of only two languages, you can create only one language file, where the key of the key-value pair is the original text and the value is the translation.