|
| 1 | +--- |
| 2 | +title: Localization |
| 3 | +--- |
| 4 | + |
| 5 | +Adventure provides a way to utilize Minecraft's built-in localization system for client-side translations as well as an additional Adventure-specific system for translating text. |
| 6 | + |
| 7 | +## Using Minecraft's localization |
| 8 | + |
| 9 | +To send text to a player that will be translated in the language they have selected in their client settings, use a translatable component. |
| 10 | +For example, `Component.translatable("block.minecraft.diamond_block")` will render as "Block of Diamond" (or translated to another language) when viewed by the client. |
| 11 | +Some translation keys have arguments which are inserted into the translated content. |
| 12 | +For example, `Component.translatable("block.minecraft.player_head.named", Component.text("Mark"))` will render as "Mark's Head". |
| 13 | +Translatable components can have styling, hover/click events and children components just like any other component type. |
| 14 | + |
| 15 | +### Resource pack language files |
| 16 | + |
| 17 | +You can provide translation files in a resource pack in order to change existing translations or add new ones. |
| 18 | +For a guide on how to do that, see the [Minecraft Wiki](https://minecraft.wiki/w/Resource_pack#Language). |
| 19 | + |
| 20 | +### Using Adventure's localization |
| 21 | + |
| 22 | +Adventure also provides a way to handle localization in Adventure itself. |
| 23 | +This can be useful in environments where you do not have access to resource packs, or wish to do translations yourself, without relying on Minecraft's translation system. |
| 24 | + |
| 25 | +Any component that is sent to a client is ran through the `GlobalTranslator` using the locale of the client. |
| 26 | +This means that if you wish to have automatic translation of components using your own translation data, you can add a `Translator` to the `GlobalTranslator`. |
| 27 | +You can either provide your own implementation of `Translator` or use one of the implementations that Adventure provides. |
| 28 | + |
| 29 | +Once you have a `Translator` instance, you can register it to the `GlobalTranslator` using `GlobalTranslator.translator().addSource(myTranslator)`. |
| 30 | +This will then make it available for automatic translation across the platform. |
| 31 | + |
| 32 | +:::caution |
| 33 | + |
| 34 | +Some implementations may not use the `GlobalTranslator` in every area, or at all. |
| 35 | +For example, Paper does not use it for items, and Minestom does not use it unless specifically enabled. |
| 36 | +Please consult the documentation for your platform for any limitations. |
| 37 | + |
| 38 | +::: |
| 39 | + |
| 40 | +## Using a custom `Translator` |
| 41 | + |
| 42 | +A `Translator` is a simple interface that provides two ways of translating content. |
| 43 | + |
| 44 | +The first `translate` method provides the translation key and locale as an argument and expects a nullable `MessageFormat` in return. |
| 45 | +This system is comparable to Minecraft's built-in localization system, using the standard Java [message format](https://docs.oracle.com/javase/8/docs/api/java/text/MessageFormat.html) for arguments. |
| 46 | + |
| 47 | +If the first `translate` method returns `null`, the second method which provides the translatable component and locale as an argument can be used. |
| 48 | +This method allows for much richer customization of the translation process as you can return an entire component. |
| 49 | +This means you can, for example, customize the color and styling of the translated component, rather than relying solely on strings for the message format system. |
| 50 | + |
| 51 | +:::caution |
| 52 | + |
| 53 | +If you are overriding the component `translate` method, you should be careful not to unintentionally lose the children of the translatable component. |
| 54 | +See the Javadocs for the translate method for a code example of how to avoid this common error. |
| 55 | + |
| 56 | +::: |
| 57 | + |
| 58 | +Below is an example of how one might implement a custom `Translator`. |
| 59 | + |
| 60 | +```java title="MyTranslator.java" |
| 61 | +public class MyTranslator implements Translator { |
| 62 | + |
| 63 | + @Override |
| 64 | + public @NotNull Key name() { |
| 65 | + // Every translator has a name which is used to identify this specific translator instance. |
| 66 | + return Key.key("mynamespace:mykey"); |
| 67 | + } |
| 68 | + |
| 69 | + @Override |
| 70 | + public @Nullable MessageFormat translate(final @NotNull String key, final @NotNull Locale locale) { |
| 71 | + // You could retrieve a string from a properties file, a config file, or some other system. |
| 72 | + // An an example, we will hard-code a check for a specific key here. |
| 73 | + if (key.equals("mytranslation.key") && locale == Locale.US) { |
| 74 | + return new MessageFormat("Hello %s!", locale); |
| 75 | + } else { |
| 76 | + // If you only want to use component translation, you can override this method and always return `null`. |
| 77 | + return null; |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + @Override |
| 82 | + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { |
| 83 | + // As above, we will hardcode a check here, but you should be reading this from elsewhere. |
| 84 | + if (key.equals("mytranslation.colorful") && locale == Locale.US) { |
| 85 | + return Component.text("Hello, ", NamedTextColor.GREEN) |
| 86 | + .append(component.arguments().get(0).color(NamedTextColor.RED)) |
| 87 | + .append(component.children()); // Always make sure to copy the children over! |
| 88 | + } else { |
| 89 | + return null; |
| 90 | + } |
| 91 | + } |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +### Using a `TranslationStore` |
| 96 | + |
| 97 | +A `TranslationStore` is a store of translations. |
| 98 | +It provides a simpler way creating a `Translator` without having to implement the logic for determining and storing translations yourself. |
| 99 | +You can create a translation store and then add or remove translations at will, even after registering it to the global translator. |
| 100 | + |
| 101 | +Adventure provides two translation stores, one for message format translating and one for component translating. |
| 102 | +An example of how to use a translation store is below. |
| 103 | + |
| 104 | +```java |
| 105 | +// As above, every translator needs an identifying name! |
| 106 | +// Could also use TranslationStore#component(Key) to work with components instead. |
| 107 | +final TranslationStore myStore = TranslationStore.messageFormat(Key.key("mynamespace:mykey")); |
| 108 | + |
| 109 | +// You can add translations one-by-one, or in bulk. Consult the Javadocs for a full list of methods. |
| 110 | +myStore.register("mytranslation.key", Locale.US, new MessageFormat("Hello %s!", Locale.US)); |
| 111 | + |
| 112 | +// You can then register this to the global translator so the translations are available there! |
| 113 | +GlobalTranslator.translator().addSource(myStore); |
| 114 | +``` |
| 115 | + |
| 116 | +There are additional methods on the message format translation store to bulk register from [resource bundles](https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html). |
| 117 | +You may also want to use Adventure's `UTF8ResourceBundleControl` utility class to create your bundle. |
| 118 | + |
| 119 | +### Using MiniMessage for translations |
| 120 | + |
| 121 | +Adventure also provides a translator that can use MiniMessage strings, with automatic support for placeholders and arguments. |
| 122 | +For more information, see [MiniMessage Translator](adventure/minimessage/translator). |
0 commit comments