Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

[Совет] Делаем возможность самостоятельной локализации в своем плагине

Jampire

Старожил
Пользователь
Баллы
173
Имя в Minecraft
Jampire
После внедрения следующей системы в ваш плагин, пользователи сами смогут локализировать ваш плагин, не вскрывая его сторонними программами.

Особенности:
  • Не создает нагрузки на сервер, даже уменьшает ее.
  • Меньше времени занимает перевод плагина на другой язык.
  • Смена языка в плагине в 2 клика.
  • В памяти хранится только выбранный перед запуском плагина язык.
Установка:
  1. Создаем в своем плагине класс под названием Lang со следующим кодом:
    Java:
    package ru.jampire.bukkit.MyCoolPlugin;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    
    import org.bukkit.configuration.file.YamlConfiguration;
    
    public class Lang {
        private static Map<String, String> language = new HashMap<String, String>();
    
        public static void load(YamlConfiguration langYml) {
            for (Entry<String, Object> entry : langYml.getConfigurationSection(Main.config.getString("lang")).getValues(false).entrySet()) {
                language.put(Main.config.getString("lang") + "." + entry.getKey(), String.valueOf(entry.getValue()));
            }
        }
    
        public static String getMessage(String target) {
            return language.get(Main.config.getString("lang") + "." + target);
        }}
    • Main.config - Путь к переменной, в которую записан ваш config
    • ru.jampire.bukkit.MyCoolPlugin - Пакет вашего плагина
  2. Создаем в корне плагина файл lang.yml и наполняем его подобным содержимым:
    Java:
    ru:
      msg1: Плагин включен.
      msg2: Плагин отключен.
    en:
      msg1: Plugin enabled.
      msg2: Plugin disabled.
    • ru / en - раздел локализации
    • msgX - сообщения
  3. Заставляем плагин выгрузить lang.yml при первой загрузке:
    Java:
    File flang = new File(plug.getDataFolder(), "lang.yml");
    if (!flang.exists()) {
        InputStream lang = Main.class.getResourceAsStream("/lang.yml");
        try {
            FileOutputStream fos = new FileOutputStream(flang);
            byte[] buff = new byte[65536];
            int n;
            while ((n = lang.read(buff)) > 0) {
                fos.write(buff, 0, n);
                fos.flush();
            }
            fos.close();
            buff = null;
        } catch (Exception e) {
            Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Error: " + e);
        }
    }
    Lang.load(YamlConfiguration.loadConfiguration(flang));
    • plug - Ваш плагин. В моем случае код находился в отдельном классе под названием ConfigHandler, что исключало возможность вызова getDataFolder() напрямую.
  4. Добавляем в ваш config.yml опцию lang, равную стандартому языку. Пример: lang: ru
  5. Прописываем все сообщения плагина в файле lang.yml на всех стандартных языках.
  6. В коде плагина меняем все сообщения плагина на подобные строки:
    Java:
    Lang.getMessage("msg1")
Упростим же жизнь переводчиков :D​
 
Если у вас есть какие-то поправки или улучшения к коду - прошу высказывать их в пм, не засоряя тему:)
Думаю данная система пригодится многим)
 
Мб лучше так?
Java:
public static String getMessage(String target) {
    String r = language.get(Main.config.getString("lang") + "." + target);
    if (r == null)
        return ChatColor.DARK_RED + "Ошибка локализации, строка [" + target + "] не обнаружена";
    else
        return r;
}}
 
Мб лучше так?
Java:
public static String getMessage(String target) {
    String r = language.get(Main.config.getString("lang") + "." + target);
    if (r == null)
        return ChatColor.DARK_RED + "Ошибка локализации, строка [" + target + "] не обнаружена";
    else
        return r;
}}
Данная проблема может возникнуть только если автор плагина допустит ошибку (или кривой переводчик). А модифицировать код ради этого - бред)
Велосипеду не нужно 3 колесо, что-бы ехать)
 
Эх, добавлю и свой метод (точнее метод Jampire , но сильно переработанный)

Java:
public class Translator {
 
    Map<String,String> tran = new HashMap<String, String>();
 
    public Translator(Plugin plugin, String locale){
        if(locale.contains(File.separator)) throw new RuntimeException("locale name is not valid");
        if (locale.equals("system")) locale = System.getProperty("user.language");
        InputStream is = null;
        File file = new File(plugin.getLangFolder(),locale+".yml");
        if(!file.exists()){
            is = plugin.getResource(locale+".yml");
            if (is==null){
                is = plugin.getResource("en.yml");
            }
        } else {
            try {
                is = new FileInputStream(file);
            } catch (FileNotFoundException ignored) {}
        }
        if(is!=null){
            try {
                Yaml yaml = new Yaml();
                Map<String, Object> map = (Map<String, Object>) yaml.load(is);
                Map<String,Object> vals = (Map<String, Object>) map.get("locale");
                for(Map.Entry<String,Object> e:vals.entrySet()){
                    tran.put(e.getKey(),e.getValue().toString());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
 
    public String translate(String key, Object... values){
        if(!tran.containsKey(key))return key;
        return String.format(tran.get(key),values);
    }
 
    public String translate(String key){
        if(!tran.containsKey(key))return key;
        return tran.get(key);
    }
}
Преимущества:
  • Можно указать язык "system" - будет взят язык системы. Если нет перевода на системный, то по умолчанию - английский.
  • Можно указывать параметры с форматированием. (например "Игрок %s нашел клад")
  • Поиск файла локализации происходит сначала в папке, затем в ресурсах jar
Использование:
1) Создаем у плагина метод public File getLangFolder(); например так:
Java:
public File getLangFolder(){
        File lang = new File(getDataFolder(),"lang");
        lang.mkdirs();
        return lang
}
2) При загрузке плагина создаем новый Translator, например:
Java:
String lang = this.getConfig().getString("lang");
this.translator = new Translator(this,lang);
3) создаем файл перевода, который потом кладем в jar
YAML:
locale:
  error_offline: "player %s is offline"
  error_noplayer: "player required"
  success_removeentity: "entity with id %d successfully removed"
4) теперь, чтобы что-то перевести, достаточно вызвать метод translate у объекта translator:
Java:
String s1 = translator.translate("error_noplayer");
String s2 = translator.translate("success_removeentity",entity.getEntityId());
String s3 = translator.translate("error_offline",player.getName());
Желательно методы translate продублировать у класса плагина и сделать их статическими. Тогда код будет проще и понятнее.

ПС: можно улучшить этот метод заменой Yaml на YamlConfiguration.
 
Давно хотел форматирование сделать. Но не было времени )
 

Поделиться этой страницей

Назад
Верх