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