diff --git a/README.MD b/README.MD index 4e2cc88..f166547 100644 --- a/README.MD +++ b/README.MD @@ -18,7 +18,7 @@ _Основан на версии Bukkit API 1.12._ 8. [JdbcTemplate](#jdbctemplate) 9. [ScheduleManager](#schedulemanager) 10. [ScheduleTask](#scheduletask) -11. [I18n](#i18n) +11. [Messages](#messages) 12. [XLog](#xlog) 13. [Подключение](#подключение) 1. [Gradle](#gradle) @@ -532,93 +532,97 @@ ScheduleTask scheduleTask = ...; scheduleTask.cancel(); ``` -## I18n +## Messages -Инструмент для работы с мультиязыковыми сообщениями или просто сообщениями, которые храняться в отдельном файле. +Инструмент для работы с параметизированными сообщениями или просто сообщениями, которые храняться в отдельном файле. -Позволяет использовать шаблонизированные сообщения вида `Привет, {player}!`. +Параметизированные сообщения имеют следующий вид: `Привет, {player}!`. -### loadMessages +### load Загрузка сообщений в инструмент. -Передать можно как "мапу" с перечислением ключ-сообщение, так и `Reader` на файл в формате `key=message` (как у `Properties`). +Есть три варианта: через `Properties` ```java -Map messagesMap = ...; -I18n.loadMessages(messagesMap); +Properties properties = ...; +Messages.load(properties); ``` +через `Map` + ```java -Reader reader = AssetsManager.getAsReader("messages.properties"); -I18n.loadMessages(reader); +Map map = ...; +Messages.load(map); ``` -В первом параметре можно указать код языка, для которого загружаются сообщения. По-умолчанию будет "en". +через `Reader` ```java -Map enMessagesMap = ...; -Map ruMessagesMap = ...; - -I18n.loadMessages("en", enMessagesMap); -I18n.loadMessages("ru", ruMessagesMap); +Reader reader = ...; +Messages.load(reader); ``` -```java -Reader readerEn = AssetsManager.getAsReader("messages.properties"); -Reader readerRu = AssetsManager.getAsReader("messages.ru.properties"); - -I18n.loadMessages("en", readerEn); -I18n.loadMessages("ru", readerRu); -``` +Следует учесть, про при работе через `Reader`, **Messages** ожидает там обнаружить список строк в формате `key=value`. ### get -Получение сообщения по его ключу. +Получение обычноего или параметизированного сообщения. -```java -String msg = I18n.get("player.join.msg"); +Для примера, пусть у нас будут такие сообщения: + +```properties +simple=Простое сообщение +welcome=Приветствуем, {player}! ``` -Если следующим сообщением указать `Map`, то можно будет воспользоваться шаблонизатором. +Для получения простого сообщения, просто указываем его ключ: ```java -Map messagesMap = new HashMap<>(); -messagesMap.put("player.join.msg", "Привет, {player}!"); - -I18n.loadMessages(messagesMap); - -Map params = new HashMap<>(); -params.put("player", event.getPlayer().getName()); - -String msg = I18n.get("player.join.msg", params); +String message = Messages.get("simple"); +// Простое сообщение ``` -Однако можно создавать `Map params` явно, а воспользоваться [paramBuilder()](#parambuilder) +Для получения параметизированного сообщения, нужно помимо ключа передать параметры. +Есть два способа: через `Map` ```java -String msg = I18n.get("player.join.msg", I18n.paramBuilder() - .add("player", event.getPlayer().getName()) - .build()); +Map = map = new HashMap<>(1); +map.put("player", "David"); + +String message = Messages.get("welcome", map); +// Приветствуем, David! ``` -Можно первым параметром указать код языка. +через попарное перечисление параметров ```java -String msg = I18n.get("ru", "player.join.msg"); +String message = Messages.get("welcome", "player", "David"); +// Приветствуем, David! ``` -### paramBuilder - -Инструмент для параметизирования шаблонов сообщений. +Если по указанному ключу сообщение отсутствует, то **Messages** вернёт значение самого ключа ```java -Map params = I18n.paramBuilder() - .add("player", event.getPlayer().getName()) - .build(); -String msg = I18n.get("player.join.msg", params); +String message = Messages.get("not_exists_key"); +// not_exists_key + +message = Messages.get("not_exists_key", "player", "David"); +// not_exists_key ``` +Если параметр, который указан в шаблоне не был указан/определён, то параметр останется как есть + +```java +String message = Messages.get("welcome"); +// Приветствуем, {player}! + +message = Messages.get("welcome", "unknown_param_key", 123); +// Приветствуем, {player}! +``` + + + ## XLog Замена стандартному `getLogger()`, который использует `java.utils.Logger` и не всегда удобен для логирования. diff --git a/src/main/java/ghast/I18n.java b/src/main/java/ghast/I18n.java index 2599dcb..0890a19 100644 --- a/src/main/java/ghast/I18n.java +++ b/src/main/java/ghast/I18n.java @@ -14,6 +14,9 @@ import java.io.Reader; import java.util.HashMap; import java.util.Map; +/** + * @deprecated use {@link Messages} + */ @UtilityClass @SuppressWarnings("unused") @Deprecated diff --git a/src/main/java/ghast/Messages.java b/src/main/java/ghast/Messages.java new file mode 100644 index 0000000..b765a78 --- /dev/null +++ b/src/main/java/ghast/Messages.java @@ -0,0 +1,126 @@ +package ghast; + +import lombok.experimental.UtilityClass; +import org.apache.commons.text.StringSubstitutor; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +@UtilityClass +public class Messages { + + private final Map MESSAGES_MAP = new HashMap<>(); + + //region Load messages + + /** + * Загрузка сообщений из {@link Properties} + * + * @param properties список сообщений и шаблонов + */ + public void load(Properties properties) { + MESSAGES_MAP.clear(); + properties.forEach((key, value) -> MESSAGES_MAP.put(key.toString().trim().toLowerCase(), value.toString().trim())); + } + + /** + * Загрузка сообщений из {@link Reader}. + *

+ * Формат строк: {@code key=value} + *

+ * + * @param reader {@link Reader} со списоком сообщений и шаблонов + */ + public void load(Reader reader) { + MESSAGES_MAP.clear(); + try { + BufferedReader bufferedReader = new BufferedReader(reader); + String line; + while ((line = bufferedReader.readLine()) != null) { + String[] split = line.split("=", 2); + MESSAGES_MAP.put(split[0].trim().toLowerCase(), split[1].trim()); + } + } catch (IOException e) { + //TODO заменить на специализированный Exception + throw new RuntimeException("Error load messages: " + e.getMessage(), e); + } + } + + /** + * Загрузка сообщений из {@link Map}<{@link String}, {@link String}>. + * + * @param messages список сообщений и шаблонов + */ + public void load(Map messages) { + MESSAGES_MAP.clear(); + MESSAGES_MAP.putAll(messages); + } + //endregion + + //region Get messages + /** + * Получить обычное сообщение по ключу/коду. + * + * @param key ключ/код + * @return сообщение, если таковое задано. Иначе - ключ + */ + public String get(String key) { + String keyLc = key.toLowerCase(); + return MESSAGES_MAP.getOrDefault(keyLc, keyLc); + } + + /** + * Получить параметизированное сообщение по ключу/коду. + * + * @param key ключ/код + * @param params список параметров + * @return сообщение, если таковое задано. Иначе - ключ + */ + public String get(String key, Map params) { + String keyLc = key.toLowerCase(); + + if (MESSAGES_MAP.containsKey(keyLc)) { + return formatMessage(MESSAGES_MAP.get(keyLc), params); + } else { + return keyLc; + } + } + + /** + * Получить параметизированное сообщение по ключу/коду. + * + * @param key ключ/код + * @param params чередующийся по парный список параметров: {@link String (str)param_name}, {@link Object (obj)param_value} и т.д. + * @return сообщение, если таковое задано. Иначе - ключ + */ + public String get(String key, Object... params) { + String keyLc = key.toLowerCase(); + + if (MESSAGES_MAP.containsKey(keyLc)) { + int len; + if ((params.length % 2) == 1) { + len = params.length - 1; + } else { + len = params.length; + } + + Map mapParams = new HashMap<>(len / 2); + for (int i = 0; i < len; i = i + 2) { + mapParams.put((String) params[i], params[i + 1]); + } + + return formatMessage(MESSAGES_MAP.get(keyLc), mapParams); + } else { + return keyLc; + } + } + //endregion + + private String formatMessage(String format, Map params) { + return StringSubstitutor.replace(format, params, "{", "}"); + } +}