i18n: rework of the system + French translation
authorNiki Roo <niki@nikiroo.be>
Thu, 3 Mar 2016 09:42:16 +0000 (10:42 +0100)
committerNiki Roo <niki@nikiroo.be>
Thu, 3 Mar 2016 09:42:16 +0000 (10:42 +0100)
README.md
src/be/nikiroo/jvcard/i18n/FixedResourceBundleControl.java [new file with mode: 0644]
src/be/nikiroo/jvcard/i18n/Trans.java
src/be/nikiroo/jvcard/i18n/resources.properties [new file with mode: 0644]
src/be/nikiroo/jvcard/i18n/resources_en.properties [new file with mode: 0644]
src/be/nikiroo/jvcard/i18n/resources_fr.properties [new file with mode: 0644]
src/be/nikiroo/jvcard/tui/Main.java
src/be/nikiroo/jvcard/tui/MainWindow.java
src/be/nikiroo/jvcard/tui/TuiLauncher.java
src/be/nikiroo/jvcard/tui/panes/MainContentList.java

index 04850f836e3a0f494e4ce4564cb9c17814691af3..1d888ecb5f7c24e26c734a102e0ad1888a773684 100644 (file)
--- 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 (file)
index 0000000..d29f597
--- /dev/null
@@ -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
index 0a06b573f5d188bcd101676584c10a945f649028..1046d4cdbc982c06f061f0e36830a93305de8ff2 100644 (file)
@@ -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<StringId, String> 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<StringId, String>();
-
-               // 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 (file)
index 0000000..dce79cc
--- /dev/null
@@ -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 (file)
index 0000000..7195460
--- /dev/null
@@ -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 (file)
index 0000000..260a56b
--- /dev/null
@@ -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
index 8e34e00b1e51a430ae93e8a29f4da07a232be61c..a0357bec89e4c0a59ee19899d0d8bbb32cd9723d 100644 (file)
@@ -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<File> files = new LinkedList<File>();
-               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));
-                */
        }
 
        /**
index 8dc860dcb3f75212de68187a778cc183e94bc888..c3277e3c0f7dc5c11a6b9f3519b3bbdeb1734870 100644 (file)
@@ -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);
index 7bee3e7bc87eb89163de7aebd677990f2641e867..2cfcc3eb9dfc03a8e2b1c32341101b7a6d5f51d6 100644 (file)
@@ -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();
index cac03e2426c615b9e61b2420d049408ed4d15f67..23892e58cd13a1cf654905b893f574408dce5e64 100644 (file)
@@ -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