// dependencies: // https://mvnrepository.com/artifact/tv.cntt/scaposer // compile group: 'tv.cntt', name: 'scaposer_2.12', version: '1.11.0' package internationalization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.collection.Seq; import scala.util.Either; import scaposer.ParseFailure; import scaposer.Parser$; import scaposer.Translation; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; public final class I18n { private static final Logger LOG = LoggerFactory.getLogger(I18n.class); /** * The current language to translate to. */ private static Locale _currentLocale = Locale.ENGLISH; private static Map _dictionaries = new HashMap<>(); /** * Selects the current language as the default language to translate to. * @param locale target language */ public static void select(Locale locale){ _currentLocale = locale; } public static String t(String singular){ if(_dictionaries.containsKey(_currentLocale)){ return _dictionaries.get(_currentLocale).t(singular); } return singular; // fallback } public static String tc(String ctx, String singular){ if(_dictionaries.containsKey(_currentLocale)){ return _dictionaries.get(_currentLocale).tc(ctx, singular); } return singular; // fallback } public static String tn(String singular, String plural, Long n) { if(_dictionaries.containsKey(_currentLocale)){ return _dictionaries.get(_currentLocale).tn(singular, plural, n); } return (n != 1) ? plural : singular; // fallback } public static String tcn(String ctx, String singular, String plural, Long n){ if(_dictionaries.containsKey(_currentLocale)){ return _dictionaries.get(_currentLocale).tcn(ctx, singular, plural, n); } return (n != 1) ? plural : singular; // fallback } public static Locale localeFromFileName(String fileName){ String[] ns = fileName.split("\\."); return Locale.forLanguageTag(ns[0]); } /** * Loads all *.po files from the given path. *

* The files should follow this naming convention: *
* *language_code*.*name*.po * * example: de.myComponent.po & de.myOtherComponent.po & de.po *

* @param path the path to search for language files for */ public static void init(String path){ File dir = new File(path); if(!dir.isDirectory()){ throw new IllegalArgumentException("Path needs to point to a directory."); } String[] fileNames = dir.list((dir1, name) -> name.endsWith(".po")); if(fileNames == null || fileNames.length == 0){ LOG.warn("No translation files (*.po) found."); return; } initPoFiles(path, fileNames); } private static void initPoFiles(String path, String[] fileNames){ _dictionaries.clear(); for(String name: fileNames){ try { Locale loc = localeFromFileName(name); scaposer.I18n i18n = readPoFile(path + File.separator + name); if(_dictionaries.containsKey(loc)){ // if a dictionary already exists, combine them _dictionaries.put(loc, _dictionaries.get(loc).$plus$plus(i18n)); } else { _dictionaries.put(loc, i18n); } } catch (IOException e) { LOG.error("Error while reading translation file: " + path); } catch (PoParseFailure poParseFailure) { LOG.error("Error while parsing translation file: " + path + "; Error: " + poParseFailure.getMessage()); } } } private static scaposer.I18n readPoFile(String pathToFile) throws IOException, PoParseFailure { byte[] encoded = Files.readAllBytes(Paths.get(pathToFile)); String fileContent = new String(encoded, Charset.forName("UTF-8")); return parsePoFile(fileContent); } private static scaposer.I18n parsePoFile(String fileContent) throws PoParseFailure { final Either> parseFailureSeqEither = Parser$.MODULE$.parse(fileContent); if(parseFailureSeqEither.isRight()){ Seq translationSeq = parseFailureSeqEither.right().get(); return scaposer.I18n.apply(translationSeq); }else { throw new PoParseFailure (parseFailureSeqEither.left().toString()); } } private static class PoParseFailure extends Exception { PoParseFailure (String message) { super(message); } } }