X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2Fi18n%2FTrans.java;h=2a4438383378e19a1fa2b8a5daea72842f6ead35;hb=7f82bf682ab5747b2d4ccdccae9ea3fcdb013cee;hp=3ea95625ca177182d75a0ddce0be5fcd68cd1a63;hpb=a3b510ab4bf89a7a2a05f3851ffe0f030b8a78f4;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/i18n/Trans.java b/src/be/nikiroo/jvcard/i18n/Trans.java index 3ea9562..2a44383 100644 --- a/src/be/nikiroo/jvcard/i18n/Trans.java +++ b/src/be/nikiroo/jvcard/i18n/Trans.java @@ -1,66 +1,331 @@ package be.nikiroo.jvcard.i18n; -import java.util.HashMap; -import java.util.Map; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.lang.reflect.Field; +import java.util.Locale; +import java.util.ResourceBundle; + +import be.nikiroo.jvcard.resources.Bundles; +import be.nikiroo.jvcard.tui.UiColors; + +import com.googlecode.lanterna.input.KeyStroke; /** - * This class manages the translation of {@link Trans#StringId}s into + * This class manages the translation of {@link Trans.StringId}s into * user-understandable text. * * @author niki * */ public class Trans { - static private Object lock = new Object(); - static private Trans instance = null; + private ResourceBundle map; - private Map map = null; + /** + * Create a translation service with the default language. + */ + public Trans() { + setLanguage(null); + } /** - * An enum representing information to be translated to the user. + * Create a translation service for the given language. (Will fall back to + * the default one i not found.) * - * @author niki + * @param language + * the language to use + */ + public Trans(String language) { + setLanguage(language); + } + + /** + * Translate the given {@link StringId} into user text. + * + * @param stringId + * the ID to translate * + * @return the translated text */ - public enum StringId { - KEY_ACTION_BACK, KEY_ACTION_HELP, KEY_ACTION_VIEW_CONTACT, KEY_ACTION_EDIT_CONTACT, KEY_ACTION_SWITCH_FORMAT, TITLE, NULL; + public String trans(StringId stringId) { + StringId id = stringId; + if (!UiColors.getInstance().isUnicode()) { + try { + id = StringId.valueOf(stringId.name() + "_NOUTF"); + } catch (IllegalArgumentException iae) { + // no special _NOUTF version found + } + } - public String trans() { - return Trans.getInstance().trans(this); + if (id == StringId.NULL) { + return ""; } - }; + + if (id == StringId.DUMMY) { + return "[dummy]"; + } + + if (map.containsKey(id.name())) { + return map.getString(id.name()); + } + + return id.toString(); + } /** - * Get the (unique) instance of this class. + * Translate the given {@link KeyStroke} into a user text {@link String} of + * size 3. * - * @return the (unique) instance + * @param key + * the key to translate + * + * @return the translated text */ - static public Trans getInstance() { - synchronized (lock) { - if (instance == null) - instance = new Trans(); + public String trans(KeyStroke key) { + String keyTrans = ""; + + switch (key.getKeyType()) { + case Enter: + if (UiColors.getInstance().isUnicode()) + keyTrans = " ⤶ "; + else + keyTrans = trans(StringId.KEY_ENTER); + break; + case Tab: + if (UiColors.getInstance().isUnicode()) + keyTrans = " ↹ "; + else + keyTrans = trans(StringId.KEY_TAB); + + break; + case Character: + keyTrans = " " + key.getCharacter() + " "; + break; + default: + keyTrans = "" + key.getKeyType(); + int width = 3; + if (keyTrans.length() > width) { + keyTrans = keyTrans.substring(0, width); + } else if (keyTrans.length() < width) { + keyTrans = keyTrans + + new String(new char[width - keyTrans.length()]) + .replace('\0', ' '); + } + break; } - return instance; + return keyTrans; } - public String trans(StringId stringId) { - if (map.containsKey(stringId)) { - return map.get(stringId); + /** + * Initialise the translation mappings for the given language. + * + * @param language + * the language to initialise, in the form "en-GB" or "fr" for + * instance + */ + private void setLanguage(String language) { + map = Bundles.getBundle("resources", getLocaleFor(language)); + } + + /** + * Create/update the translation .properties files. Will use the most likely + * candidate as base if the file does not already exists (for instance, + * "en_US" will use "en" as a base). + * + * @param args + * the path where the .properties files are, then the languages + * to create/update + * + * @throws IOException + * in case of IO errors + */ + static public void main(String[] args) throws IOException { + String path = args[0]; + for (int i = 1; i < args.length; i++) { + Locale locale = getLocaleFor(args[i]); + String code = locale.toString(); + Trans trans = new Trans(code); + + File file = null; + if (code.length() > 0) { + file = new File(path + "resources_" + code + ".properties"); + } else { + // Default properties file: + file = new File(path + "resources.properties"); + } + + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(file), "UTF-8")); + + String name = locale.getDisplayCountry(locale); + if (name.length() == 0) + name = locale.getDisplayLanguage(locale); + if (name.length() == 0) + name = "default"; + + if (code.length() > 0) { + name = name + " (" + code + ")"; + } + + writer.append("# " + name + " translation file (UTF-8)\n"); + writer.append("# \n"); + writer.append("# Note that any key can be doubled with a _NOUTF suffix\n"); + writer.append("# to use when the flag --noutf is passed\n"); + writer.append("# \n"); + writer.append("# Also, the comments always refer to the key below them.\n"); + writer.append("# \n"); + writer.append("\n"); + + for (Field field : StringId.class.getDeclaredFields()) { + Meta meta = field.getAnnotation(Meta.class); + if (meta != null) { + StringId id = StringId.valueOf(field.getName()); + String info = getMetaInfo(meta); + if (info != null) { + writer.append(info); + writer.append("\n"); + } + + writer.append(id.name()); + writer.append(" = "); + if (!trans.trans(id).equals(id.name())) + writer.append(trans.trans(id)); + writer.append("\n"); + } + } + + writer.close(); + } + } + + /** + * Return the {@link Locale} representing the given language. + * + * @param language + * the language to initialise, in the form "en-GB" or "fr" for + * instance + * + * @return the corresponding {@link Locale} or the default {@link Locale} if + * it is not known + */ + static private Locale getLocaleFor(String language) { + Locale locale; + + if (language == null) { + locale = Locale.getDefault(); + } else { + language = language.replaceAll("_", "-"); + String lang = language; + String country = null; + if (language.contains("-")) { + lang = language.split("-")[0]; + country = language.split("-")[1]; + } + + if (country != null) + locale = new Locale(lang, country); + else + locale = new Locale(lang); } - return stringId.toString(); + return locale; } - private Trans() { - map = new HashMap(); + /** + * Return formated, display-able information from the {@link Meta} field + * given. Each line will always starts with a "#" character. + * + * @param meta + * the {@link Meta} field + * + * @return the information to display or NULL if none + */ + static private String getMetaInfo(Meta meta) { + String what = meta.what(); + String where = meta.where(); + String format = meta.format(); + String info = meta.info(); + + int opt = what.length() + where.length() + format.length(); + if (opt + info.length() == 0) + return null; + + StringBuilder builder = new StringBuilder(); + builder.append("# "); + + if (opt > 0) { + builder.append("("); + if (what.length() > 0) { + builder.append("WHAT: " + what); + if (where.length() + format.length() > 0) + builder.append(", "); + } + + if (where.length() > 0) { + builder.append("WHERE: " + where); + if (format.length() > 0) + builder.append(", "); + } + + if (format.length() > 0) { + builder.append("FORMAT: " + format); + } + + builder.append(")\n# "); + } + + builder.append(info); - // TODO: get from a file instead? - map.put(StringId.NULL, ""); - map.put(StringId.KEY_ACTION_BACK, "Back"); - map.put(StringId.TITLE, "[ jVcard: version 0.9 ]"); - map.put(StringId.KEY_ACTION_VIEW_CONTACT, "view"); - map.put(StringId.KEY_ACTION_EDIT_CONTACT, "edit"); - map.put(StringId.KEY_ACTION_SWITCH_FORMAT, "Change view"); + return builder.toString(); } + + /** + * The enum representing textual information to be translated to the user as + * a key. + * + * Note that each key that should be translated MUST be annotated with a + * {@link Meta} annotation. + * + * @author niki + * + */ + public enum StringId { + DUMMY, // <-- TODO : remove + NULL, // Special usage, no annotations so it is not visible in + // .properties files + @Meta(what = "a key to press", where = "action keys", format = "MUST BE 3 chars long", info = "Tab key") + KEY_TAB, // keys + @Meta(what = "a key to press", where = "action keys", format = "MUST BE 3 chars long", info = "Enter key") + KEY_ENTER, // + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_BACK, // MainWindow + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_HELP, // + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_VIEW_CARD, // FileList + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_VIEW_CONTACT, // ContactList + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_EDIT_CONTACT, // + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_SAVE_CARD, // + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_DELETE_CONTACT, // + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_SEARCH, // + @Meta(what = "", where = "", format = "", info = "we could use: ' ', ┃, │...") + DEAULT_FIELD_SEPARATOR, // MainContentList + @Meta(what = "", where = "", format = "", info = "") + DEAULT_FIELD_SEPARATOR_NOUTF, // + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_INVERT, // ContactDetails + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_FULLSCREEN, // + @Meta(what = "", where = "", format = "", info = "") + KEY_ACTION_SWITCH_FORMAT, // multi-usage + }; }