From 668268fc236425a7be575417cd0d3810c29127a1 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Thu, 3 Mar 2016 10:42:16 +0100 Subject: [PATCH] i18n: rework of the system + French translation --- README.md | 2 +- .../i18n/FixedResourceBundleControl.java | 48 ++++++++++ src/be/nikiroo/jvcard/i18n/Trans.java | 92 ++++++++++--------- .../nikiroo/jvcard/i18n/resources.properties | 18 ++++ .../jvcard/i18n/resources_en.properties | 18 ++++ .../jvcard/i18n/resources_fr.properties | 18 ++++ src/be/nikiroo/jvcard/tui/Main.java | 73 +++++++++++---- src/be/nikiroo/jvcard/tui/MainWindow.java | 5 +- src/be/nikiroo/jvcard/tui/TuiLauncher.java | 16 ++-- .../jvcard/tui/panes/MainContentList.java | 3 +- 10 files changed, 216 insertions(+), 77 deletions(-) create mode 100644 src/be/nikiroo/jvcard/i18n/FixedResourceBundleControl.java create mode 100644 src/be/nikiroo/jvcard/i18n/resources.properties create mode 100644 src/be/nikiroo/jvcard/i18n/resources_en.properties create mode 100644 src/be/nikiroo/jvcard/i18n/resources_fr.properties diff --git a/README.md b/README.md index 04850f8..1d888ec 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ Small TUI (text mode) VCard manager (also supports abook files) - it can delete a contact - it can be used to edit your data (currently in RAW format, field by field) - it can save back to file +- English and French versions available (will look for the host language, can be forced with "--lang en") ## TODO - ".properties" files to easily change the colours - correct EDIT support - customisation of VIEW_CONTACT -- French translation to check if everything is translatable - lot of other things diff --git a/src/be/nikiroo/jvcard/i18n/FixedResourceBundleControl.java b/src/be/nikiroo/jvcard/i18n/FixedResourceBundleControl.java new file mode 100644 index 0000000..d29f597 --- /dev/null +++ b/src/be/nikiroo/jvcard/i18n/FixedResourceBundleControl.java @@ -0,0 +1,48 @@ +package be.nikiroo.jvcard.i18n; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.ResourceBundle.Control; + +public class FixedResourceBundleControl extends Control { + public ResourceBundle newBundle(String baseName, Locale locale, + String format, ClassLoader loader, boolean reload) + throws IllegalAccessException, InstantiationException, IOException { + // The below is a copy of the default implementation. + String bundleName = toBundleName(baseName, locale); + String resourceName = toResourceName(bundleName, "properties"); + ResourceBundle bundle = null; + InputStream stream = null; + if (reload) { + URL url = loader.getResource(resourceName); + if (url != null) { + URLConnection connection = url.openConnection(); + if (connection != null) { + connection.setUseCaches(false); + stream = connection.getInputStream(); + } + } + } else { + stream = loader.getResourceAsStream(resourceName); + } + if (stream != null) { + try { + // Only this line is changed to make it to read properties files + // as UTF-8. + // How can someone use an archaic encoding such as ISO 8859-1 by + // *DEFAULT* is beyond me... + bundle = new PropertyResourceBundle(new InputStreamReader( + stream, "UTF-8")); + } finally { + stream.close(); + } + } + return bundle; + } +} \ No newline at end of file diff --git a/src/be/nikiroo/jvcard/i18n/Trans.java b/src/be/nikiroo/jvcard/i18n/Trans.java index 0a06b57..1046d4c 100644 --- a/src/be/nikiroo/jvcard/i18n/Trans.java +++ b/src/be/nikiroo/jvcard/i18n/Trans.java @@ -1,12 +1,12 @@ package be.nikiroo.jvcard.i18n; -import java.util.HashMap; -import java.util.Map; - -import com.googlecode.lanterna.input.KeyStroke; +import java.util.Locale; +import java.util.ResourceBundle; import be.nikiroo.jvcard.tui.UiColors; +import com.googlecode.lanterna.input.KeyStroke; + /** * This class manages the translation of {@link Trans#StringId}s into * user-understandable text. @@ -15,10 +15,7 @@ import be.nikiroo.jvcard.tui.UiColors; * */ public class Trans { - static private Object lock = new Object(); - static private Trans instance = null; - - private Map map = null; + ResourceBundle map; /** * An enum representing information to be translated to the user. @@ -28,6 +25,7 @@ public class Trans { */ public enum StringId { DUMMY, // <-- TODO : remove + KEY_TAB, KEY_ENTER, // keys KEY_ACTION_BACK, KEY_ACTION_HELP, // MainWindow KEY_ACTION_VIEW_CARD, // FileList KEY_ACTION_VIEW_CONTACT, KEY_ACTION_EDIT_CONTACT, KEY_ACTION_SAVE_CARD, KEY_ACTION_DELETE_CONTACT, KEY_ACTION_SEARCH, // ContactList @@ -35,24 +33,24 @@ public class Trans { KEY_ACTION_INVERT, KEY_ACTION_FULLSCREEN, // ContactDetails KEY_ACTION_SWITCH_FORMAT, // multi-usage NULL; // Special usage - - public String trans() { - return Trans.getInstance().trans(this); - } }; /** - * Get the (unique) instance of this class. - * - * @return the (unique) instance + * Create a translation service with the default language. */ - static public Trans getInstance() { - synchronized (lock) { - if (instance == null) - instance = new Trans(); - } + public Trans() { + init(null); + } - return instance; + /** + * Create a translation service for the given language. (Will fall back to + * the default one i not found.) + * + * @param language + * the language to use + */ + public Trans(String language) { + init(language); } /** @@ -73,8 +71,16 @@ public class Trans { } } - if (map.containsKey(id)) { - return map.get(id); + if (id == StringId.NULL) { + return ""; + } + + if (id == StringId.DUMMY) { + return "[dummy]"; + } + + if (map.containsKey(id.toString())) { + return map.getString(id.toString()); } return id.toString(); @@ -97,13 +103,13 @@ public class Trans { if (UiColors.getInstance().isUnicode()) keyTrans = " ⤶ "; else - keyTrans = "ENT"; + keyTrans = trans(StringId.KEY_ENTER); break; case Tab: if (UiColors.getInstance().isUnicode()) keyTrans = " ↹ "; else - keyTrans = "TAB"; + keyTrans = trans(StringId.KEY_TAB); break; case Character: @@ -125,24 +131,22 @@ public class Trans { return keyTrans; } - private Trans() { - map = new HashMap(); - - // TODO: get from a file instead? - map.put(StringId.NULL, ""); - map.put(StringId.DUMMY, "[dummy]"); - // we could use: " ", "┃", "│"... - map.put(StringId.DEAULT_FIELD_SEPARATOR, "┃"); - map.put(StringId.DEAULT_FIELD_SEPARATOR_NOUTF, "|"); - map.put(StringId.KEY_ACTION_BACK, "Back"); - map.put(StringId.KEY_ACTION_HELP, "Help"); - map.put(StringId.KEY_ACTION_VIEW_CONTACT, "Open"); - map.put(StringId.KEY_ACTION_VIEW_CARD, "Open"); - map.put(StringId.KEY_ACTION_EDIT_CONTACT, "Edit"); - map.put(StringId.KEY_ACTION_DELETE_CONTACT, "Delete"); - map.put(StringId.KEY_ACTION_SWITCH_FORMAT, "Change view"); - map.put(StringId.KEY_ACTION_INVERT, "Invert colours"); - map.put(StringId.KEY_ACTION_FULLSCREEN, "Fullscreen"); - map.put(StringId.KEY_ACTION_SEARCH, "Search"); + /** + * Initialise the translation mappings for the given language. + * + * @param lang + * the language to initialise + */ + private void init(String lang) { + Locale locale = null; + + if (lang == null) { + locale = Locale.getDefault(); + } else { + locale = Locale.forLanguageTag(lang); + } + + map = ResourceBundle.getBundle(Trans.class.getPackage().getName() + + ".resources", locale, new FixedResourceBundleControl()); } } diff --git a/src/be/nikiroo/jvcard/i18n/resources.properties b/src/be/nikiroo/jvcard/i18n/resources.properties new file mode 100644 index 0000000..dce79cc --- /dev/null +++ b/src/be/nikiroo/jvcard/i18n/resources.properties @@ -0,0 +1,18 @@ +# English translation file (UTF-8) + +# those 2 keys MUST be 3-characters long +KEY_TAB = TAB +KEY_ENTER = ENT +# we could use: " ", ┃, │... +DEAULT_FIELD_SEPARATOR = ┃ +DEAULT_FIELD_SEPARATOR_NOUTF = | +KEY_ACTION_BACK = Back +KEY_ACTION_HELP = Help +KEY_ACTION_VIEW_CONTACT = Open +KEY_ACTION_VIEW_CARD = Open +KEY_ACTION_EDIT_CONTACT = Edit +KEY_ACTION_DELETE_CONTACT = Delete +KEY_ACTION_SWITCH_FORMAT = Change view +KEY_ACTION_INVERT = Invert colours +KEY_ACTION_FULLSCREEN = Fullscreen +KEY_ACTION_SEARCH = Search diff --git a/src/be/nikiroo/jvcard/i18n/resources_en.properties b/src/be/nikiroo/jvcard/i18n/resources_en.properties new file mode 100644 index 0000000..7195460 --- /dev/null +++ b/src/be/nikiroo/jvcard/i18n/resources_en.properties @@ -0,0 +1,18 @@ +# default translation file (UTF-8) + +# those 2 keys MUST be 3-characters long +KEY_TAB = TAB +KEY_ENTER = ENT +# we could use: " ", ┃, │... +DEAULT_FIELD_SEPARATOR = ┃ +DEAULT_FIELD_SEPARATOR_NOUTF = | +KEY_ACTION_BACK = Back +KEY_ACTION_HELP = Help +KEY_ACTION_VIEW_CONTACT = Open +KEY_ACTION_VIEW_CARD = Open +KEY_ACTION_EDIT_CONTACT = Edit +KEY_ACTION_DELETE_CONTACT = Delete +KEY_ACTION_SWITCH_FORMAT = Change view +KEY_ACTION_INVERT = Invert colours +KEY_ACTION_FULLSCREEN = Fullscreen +KEY_ACTION_SEARCH = Search diff --git a/src/be/nikiroo/jvcard/i18n/resources_fr.properties b/src/be/nikiroo/jvcard/i18n/resources_fr.properties new file mode 100644 index 0000000..260a56b --- /dev/null +++ b/src/be/nikiroo/jvcard/i18n/resources_fr.properties @@ -0,0 +1,18 @@ +# French translation file (UTF-8) + +# les deux touches qui suivent DOIVENT faire 3 caractères de long +KEY_TAB = TAB +KEY_ENTER = ENT +# autres options: " ", ┃, │... +DEAULT_FIELD_SEPARATOR = ┃ +DEAULT_FIELD_SEPARATOR_NOUTF = | +KEY_ACTION_BACK = Retour +KEY_ACTION_HELP = Aide +KEY_ACTION_VIEW_CONTACT = Afficher +KEY_ACTION_VIEW_CARD = Ouvrir +KEY_ACTION_EDIT_CONTACT = Éditer +KEY_ACTION_DELETE_CONTACT = Supprimer +KEY_ACTION_SWITCH_FORMAT = Autre affichage +KEY_ACTION_INVERT = Couleurs inversées +KEY_ACTION_FULLSCREEN = Plein écran +KEY_ACTION_SEARCH = Rechercher diff --git a/src/be/nikiroo/jvcard/tui/Main.java b/src/be/nikiroo/jvcard/tui/Main.java index 8e34e00..a0357be 100644 --- a/src/be/nikiroo/jvcard/tui/Main.java +++ b/src/be/nikiroo/jvcard/tui/Main.java @@ -5,6 +5,8 @@ import java.io.IOException; import java.util.LinkedList; import java.util.List; +import be.nikiroo.jvcard.i18n.Trans; +import be.nikiroo.jvcard.i18n.Trans.StringId; import be.nikiroo.jvcard.tui.panes.FileList; import com.googlecode.lanterna.TextColor; @@ -14,6 +16,7 @@ import com.googlecode.lanterna.gui2.EmptySpace; import com.googlecode.lanterna.gui2.MultiWindowTextGUI; import com.googlecode.lanterna.gui2.Window; import com.googlecode.lanterna.gui2.table.Table; +import com.googlecode.lanterna.input.KeyStroke; import com.googlecode.lanterna.screen.Screen; import com.googlecode.lanterna.screen.TerminalScreen; import com.googlecode.lanterna.terminal.DefaultTerminalFactory; @@ -31,13 +34,58 @@ public class Main { public static final String APPLICATION_TITLE = "jVcard"; public static final String APPLICATION_VERSION = "1.0-beta1-dev"; + static private Trans transService; + + /** + * Translate the given {@link StringId}. + * + * @param id + * the ID to translate + * + * @return the translation + */ + static public String trans(StringId id) { + + if (transService == null) + return ""; + + return transService.trans(id); + } + + /** + * Translate the given {@link KeyStroke}. + * + * @param key + * the key to translate + * + * @return the translation + */ + static public String trans(KeyStroke key) { + if (transService == null) + return ""; + + return transService.trans(key); + } + + /** + * Start the application. + * + * @param args + * the parameters (see --help to know hich are supported) + */ public static void main(String[] args) { Boolean textMode = null; boolean noMoreParams = false; boolean filesTried = false; + // get the "system default" language to help translate the --help + // message if needed + String language = null; + transService = new Trans(null); + List files = new LinkedList(); - for (String arg : args) { + for (int index = 0; index < args.length; index++) { + String arg = args[index]; if (!noMoreParams && arg.equals("--")) { noMoreParams = true; } else if (!noMoreParams && arg.equals("--help")) { @@ -46,6 +94,7 @@ public class Main { + "Usable switches:\n" + "\t--: stop looking for switches\n" + "\t--help: this here thingy\n" + + "\t--lang LANGUAGE: choose the language, for instance en_GB\n" + "\t--tui: force pure text mode even if swing treminal is available\n" + "\t--gui: force swing terminal mode\n" + "\t--noutf: force non-utf8 mode if you need it\n" @@ -59,6 +108,11 @@ public class Main { textMode = false; } else if (!noMoreParams && arg.equals("--noutf")) { UiColors.getInstance().setUnicode(false); + } else if (!noMoreParams && arg.equals("--lang")) { + index++; + if (index < args.length) + language = args[index]; + transService = new Trans(language); } else { filesTried = true; files.addAll(open(arg)); @@ -82,23 +136,6 @@ public class Main { ioe.printStackTrace(); System.exit(2); } - - /* - * String file = args.length > 0 ? args[0] : null; String file2 = - * args.length > 1 ? args[1] : null; - * - * if (file == null) file = - * "/home/niki/workspace/rcard/utils/CVcard/test.vcf"; if (file2 == - * null) file2 = "/home/niki/workspace/rcard/utils/CVcard/test.abook"; - * - * Card card = new Card(new File(file), Format.VCard21); - * System.out.println(card.toString()); - * - * System.out.println("\n -- PINE -- \n"); - * - * card = new Card(new File(file2), Format.Abook); - * System.out.println(card.toString(Format.Abook)); - */ } /** diff --git a/src/be/nikiroo/jvcard/tui/MainWindow.java b/src/be/nikiroo/jvcard/tui/MainWindow.java index 8dc860d..c3277e3 100644 --- a/src/be/nikiroo/jvcard/tui/MainWindow.java +++ b/src/be/nikiroo/jvcard/tui/MainWindow.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import be.nikiroo.jvcard.i18n.Trans; import be.nikiroo.jvcard.i18n.Trans.StringId; import be.nikiroo.jvcard.tui.KeyAction.Mode; import be.nikiroo.jvcard.tui.UiColors.Element; @@ -497,12 +496,12 @@ public class MainWindow extends BasicWindow { actionPanel.removeAllComponents(); for (KeyAction action : this.actions) { - String trans = " " + action.getStringId().trans() + " "; + String trans = " " + Main.trans(action.getStringId()) + " "; if (" ".equals(trans)) continue; - String keyTrans = Trans.getInstance().trans(action.getKey()); + String keyTrans = Main.trans(action.getKey()); Panel kPane = new Panel(); LinearLayout layout = new LinearLayout(Direction.HORIZONTAL); diff --git a/src/be/nikiroo/jvcard/tui/TuiLauncher.java b/src/be/nikiroo/jvcard/tui/TuiLauncher.java index 7bee3e7..2cfcc3e 100644 --- a/src/be/nikiroo/jvcard/tui/TuiLauncher.java +++ b/src/be/nikiroo/jvcard/tui/TuiLauncher.java @@ -14,19 +14,15 @@ import com.googlecode.lanterna.terminal.DefaultTerminalFactory; import com.googlecode.lanterna.terminal.ResizeListener; import com.googlecode.lanterna.terminal.Terminal; -/* - * - * Change in Lanterna 3.0.0-beta2 (issue and fix reported to GitHub): - * - * java.lang.StringIndexOutOfBoundsException: String index out of range: 83 - * at java.lang.String.charAt(String.java:686) - * at com.googlecode.lanterna.TerminalTextUtils.getWordWrappedText(TerminalTextUtils.java:237) - * +/** + * Starting the TUI. * + * @author niki + * */ - public class TuiLauncher { - public static void start(Boolean textMode, Window win) throws IOException { + + static public void start(Boolean textMode, Window win) throws IOException { Terminal terminal = null; DefaultTerminalFactory factory = new DefaultTerminalFactory(); diff --git a/src/be/nikiroo/jvcard/tui/panes/MainContentList.java b/src/be/nikiroo/jvcard/tui/panes/MainContentList.java index cac03e2..23892e5 100644 --- a/src/be/nikiroo/jvcard/tui/panes/MainContentList.java +++ b/src/be/nikiroo/jvcard/tui/panes/MainContentList.java @@ -4,6 +4,7 @@ import java.util.LinkedList; import java.util.List; import be.nikiroo.jvcard.i18n.Trans.StringId; +import be.nikiroo.jvcard.tui.Main; import be.nikiroo.jvcard.tui.StringUtils; import be.nikiroo.jvcard.tui.UiColors; import be.nikiroo.jvcard.tui.UiColors.Element; @@ -157,7 +158,7 @@ abstract public class MainContentList extends MainContent implements Runnable { * @return the separator */ public String getSeparator() { - return StringId.DEAULT_FIELD_SEPARATOR.trans(); + return Main.trans(StringId.DEAULT_FIELD_SEPARATOR); } @Override -- 2.27.0