Skip to content
Mehmet edited this page Sep 6, 2021 · 9 revisions

Internationalization (I18N) / Locales API

I18N is the concept of making your app translatable. In ACF, we automatically load English language tables (and potentially more), and provide the tools to let you change the messaging.

Autoloading .properties files for your plugin

This method should work as long as you do not need to let your end-users adjust the messaging (if you do, see below for File System based)

For Bukkit, Bungee and Sponge, if you create a file named acf-YourPlugin_<language>.properties in your resources folder, ACF will automatically load this file.

for language, use en for english, or any of the other Locale based codes (de, fr, es, etc)

You may override any predefined key and or load new languages this way.

However, in order to add a new language, you must first, before ever calling manager.getLocales(), call manager.addSupportedLanguage(Locale.XXXX).

You must do this before accessing .getLocales(), as getLocales will load the bundle.

If you need to manually load a bundle that is not a "Supported Language", or just want to use alternate file names, you may call:

// To load for all supported language locales
manager.getLocales().addMessageBundle("bundleName");
// to load for an explicit locale (may or may not be supported)
manager.getLocales().addMessageBundle("bundleName", locale);

Using File System based language files

CommandManager.getLocales provides an API to load messages into the language table.

For the Bukkit platform, YAML based API (and an API for anything that supports the FileConfiguration API) has been provided. If you create a YAML file such as lang_en.yaml, in the following format:

acf-minecraft:
  multiple_players_match: "<language here>"
  # ... more
acf-core:
  permission_denied: "Perm denied message here"
  # ... more

Then you may load it with

manager.getLocales().loadYamlLanguageFile("lang_en.yml", Locale.ENGLISH);

NOTE: This will only get the master keys and their first subkeys. Because the bungee Configuration class cannot get the following subkeys yet.

Repeat this for other languages.

Changing Message Colors

While not directly part of the Locales API, it strongly relates. Messages in ACF route through a platform agnostic Message Format system.

ACF is designed to support multiple colors, but not (currently) other formatting options.

Everything is color 1 by default, unless overridden with <c#>TEXT</c#> tags, for example: Error: Multiple players matched <c2>{search}</c2> <c3>({all})</c3>, please be more specific.

This will use color 2 for {Search}, color 3 for the list of all valid options, and color 1 for everything else.

Colors are registered on a per message type basis, so you may change them like so:

// Sets 1,2,3 (and more, as many colors you provide)
manager.setFormat(MessageType.ERROR, ChatColor.ORANGE, ChatColor.RED, ChatColor.YELLOW);
// Change only a specific color
manager.setFormat(MessageType.ERROR, 3, ChatColor.ORANGE);

If you wish to use other formatting options (such as italic/bold for Minecraft), you may use those characters in your language files, as long as you are not sharing those language files between another platform (such as Sponge) which might not understand it.

But as Minecraft stands, I believe all 3 platforms support the section symbol codes.

Default Locale

ACF defaults to English as the default Locale as that is what we provide language files for. Currently, only the default locale is used to look up messages, as we have not completed the work yet to do per-player contextual Locales.

So if you need to use non English language for ACF, then simply ensure you load messages completely for your language, and then call:

manager.getLocales().setDefaultLocale(locale);

Locale References

Java provides a few constants in the Locale class. ACF has taken that concept and built an even larger constant pool for more languages in Locales

So if you need a reference to a Locale, you may use Locales.SPANISH for example. This should behave the same as doing new Locale("es").

Adding more languages to ACF

Simply PR new files to the languages folder following same pattern as the others. Then in CommandManager.java add the language to the supportedLanguages Set at the top. Please use a reference to the Locales class, adding a Locale if the locale is missing.

I am wanting to keep it to just root languages for now, and not country-specific dialects.

You can load country specific versions using the API. We do not need it in core.

Using Per Issuer Locales

Per Issuer Locales is a system for determining what is the preferred language. This will vary depending on Implementation This is currently only supported on the Bukkit/Paper platforms. PR's welcome to add support to other platforms.

Call manager.usePerIssuerLocale(true); to enable.

Important

While using usePerIssueLocale, please keep in mind that in order to give you full control of the languages and be allowed to change the languages of users properly via commands, use manager.usePerIssuerLocale(true, false);. This will make it so that it won't detect from the client and allow you to override with the setPlayerLocale() method.

Bukkit/Paper - Drawbacks

Minecraft does not send the Locale data as part of the join process, so this means PlayerJoinEvent, the place commonly used to send on join messages, can not be used if your trying to use ACF's Locale API yourself to send translated messages.

You may use the onLocaleChange API to help know when it's ready.

manager.onLocaleChange((issuer, oldLocale, newLocale) -> {
    if (oldLocale == null) {
        // joined
    }
});

But note that this solution isn't perfect and may have issues.

Bukkit/Paper - Suggested Workaround:

Give your players the ability to set their desired language, and in PlayerJoinEvent before you send any messages, Then call manager.setPlayerLocale(player, preferredLocale), and that language will be used for I18N.

Using I18N Keys in Annotations

If the value passed to one of your annotations needs to also be translated, you may use the following syntax:

@Description("{@@my-plugin.cmd-help.mycommand}")

The {@@<key>} portion will be replaced with whatever is in your Locales language tables.

Using for @Description is a great example on how you could translate your help documentation, and then using above mentioned Per Issuer Locales, your players will receive the help descriptions in their own language.