Resource A small thread on an easier way to load language files for plugin translations.

Discussion in 'Spigot Plugin Development' started by maxbridgland, Mar 17, 2020.

  1. Recently a user of one of my plugins requested I add support for lang.yml files. I tried to look around for help but couldn't find anything so I decided to implement my own loader for them. I think this snippet would be helpful for anybody who is trying to add multi-language compatibility to their plugins and want to use this method.

    There are two ways to do it, you can go based off player locale and provide your own language yml files for each language and then default to English like most would, or you can provide a single default YML file in any language and allow users to provide others. I don't like the idea of changing the original YML file (so users have a source of truth and version control when things go awry) so I thought it would be better to provide the file name in the config of the plugin and include a default setting if users don't use it or delete it.

    The example in this post will be using the method of loading from configuration.

    First we make a default language file. This can be any format but I personally think it's easiest to use YML so we can take advantage of the YamlConfiguration class Bukkit offers. This file will be stored inside of the resources folder when developing but eventually will get saved inside plugins/PluginName/languages/filename.yml.

    For this example we will just use three strings but it will be just as easy to do this with any number of strings.

    Code (YAML):
    welcome_message: "Using PluginName by Author"
    help_message_title
    : "PluginName Help Menu"
    help_command_description
    : "Run this command to see the help menu"

    These strings will be called by their key in the YAML file. It's important you do not use periods in your YAML or the parser will mess up when iterating over your messages. Use underscores instead.

    Inside of your default configuration you should add "locale: en_US" as a key and variable. This should match whatever your default language file is. I suggest keeping it to locale names.

    Next we make our Language loading class:

    Code (Java):
    public class LanguageLoader {

        HashMap<String, String> translationMap;

        public LanguageLoader(PluginName plugin){
            File languageDirectory = new File(plugin.getDataFolder(), "languages/";
            File defaultLanguageFile = new File(plugin.getDataFolder(), "languages/en_US.yml");
            if (!languageDirectory.isDirectory()){
                languageDirectory.mkdir();
                try {
                    InputStream stream = plugin.getResource("en_US.yml");
                    FileUtils.copyInputStreamToFile(stream, defaultLanguageFile);
                catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (plugin.getConfig().getString("locale") != null && plugin.getConfig().getString("locale").equals("en_US")) {
                FileConfiguration translations = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "languages/" + plugin.getConfig().getString("locale") + ".yml");
                for (String translation : translations.getKeys(false)){
                    translationMap.put(translation, translations.getString(translation));
                }
            } else {
                FileConfiguration translations = YamlConfiguration.loadConfiguration(defaultLanguageFile);
                for (String translation : translations.getKeys(false)){
                    translationMap.put(translation, translations.getString(translation));
                }
            }
        }

    }

    Here we initialize the loader by passing the plugin into it. When we do this we pull from the plugins config and grab the locale key from the config.yml file. This locale key should match up to a file with the name name ending in .yml inside the languages folder. This will create a languages folder if you include the initialization on startup and then don't call it unless you have ran the plugin once.

    Next we have to add a method for grabbing strings from the translation map. This is simple and all it takes is this:

    Code (Java):

    public String get(String path){
        return translationMap.get(path);
    }

    This will return a String or Null when you call it. Make sure to check for null!

    Now whenever we want to use this we can initialize it and substitute our strings with this:

    Code (Java):
    @Override
    public void onEnable(){
        LanguageLoader langLoader = new LanguageLoader(this);
        String welcomeMessage = langLoader.get("welcome_message");
        // Or we can Inline it
        Bukkit.getServer().getConsoleSender().sendMessage(langLoader.get("welcome_message");
    }
    That's all there is to it! Hopefully this helps some of you out. I did come across a couple old threads on Bukkit about it but it never hurts to share a new way of doing something! Let me know if you find any code issues and I will fix them promptly :)
     
    • Like Like x 1
  2. SteelPhoenix

    Moderator

    Or look at what plugins like Essentials do and just use ResourceBundle#getBundle(String baseName, Locale locale)
     
  3. I had no idea... sheesh this is much easier