1 package be
.nikiroo
.jvcard
.i18n
;
3 import java
.io
.BufferedWriter
;
5 import java
.io
.FileOutputStream
;
6 import java
.io
.IOException
;
7 import java
.io
.OutputStreamWriter
;
8 import java
.lang
.reflect
.Field
;
9 import java
.util
.Locale
;
10 import java
.util
.ResourceBundle
;
12 import be
.nikiroo
.jvcard
.resources
.Bundles
;
13 import be
.nikiroo
.jvcard
.tui
.UiColors
;
15 import com
.googlecode
.lanterna
.input
.KeyStroke
;
18 * This class manages the translation of {@link Trans.StringId}s into
19 * user-understandable text.
25 private ResourceBundle map
;
28 * Create a translation service with the default language.
35 * Create a translation service for the given language. (Will fall back to
36 * the default one i not found.)
41 public Trans(String language
) {
42 setLanguage(language
);
46 * Translate the given {@link StringId} into user text.
51 * @return the translated text
53 public String
trans(StringId stringId
) {
54 StringId id
= stringId
;
55 if (!UiColors
.getInstance().isUnicode()) {
57 id
= StringId
.valueOf(stringId
.name() + "_NOUTF");
58 } catch (IllegalArgumentException iae
) {
59 // no special _NOUTF version found
63 if (id
== StringId
.NULL
) {
67 if (id
== StringId
.DUMMY
) {
71 if (map
.containsKey(id
.name())) {
72 return map
.getString(id
.name());
79 * Translate the given {@link KeyStroke} into a user text {@link String} of
83 * the key to translate
85 * @return the translated text
87 public String
trans(KeyStroke key
) {
90 switch (key
.getKeyType()) {
92 if (UiColors
.getInstance().isUnicode())
95 keyTrans
= trans(StringId
.KEY_ENTER
);
98 if (UiColors
.getInstance().isUnicode())
101 keyTrans
= trans(StringId
.KEY_TAB
);
105 keyTrans
= " " + key
.getCharacter() + " ";
108 keyTrans
= "" + key
.getKeyType();
110 if (keyTrans
.length() > width
) {
111 keyTrans
= keyTrans
.substring(0, width
);
112 } else if (keyTrans
.length() < width
) {
114 + new String(new char[width
- keyTrans
.length()])
124 * Initialise the translation mappings for the given language.
127 * the language to initialise, in the form "en-GB" or "fr" for
130 private void setLanguage(String language
) {
131 map
= Bundles
.getBundle("resources", getLocaleFor(language
));
135 * Create/update the translation .properties files. Will use the most likely
136 * candidate as base if the file does not already exists (for instance,
137 * "en_US" will use "en" as a base).
140 * the path where the .properties files are, then the languages
143 * @throws IOException
144 * in case of IO errors
146 static public void main(String
[] args
) throws IOException
{
147 String path
= args
[0];
148 for (int i
= 1; i
< args
.length
; i
++) {
149 Locale locale
= getLocaleFor(args
[i
]);
150 String code
= locale
.toString();
151 Trans trans
= new Trans(code
);
154 if (code
.length() > 0) {
155 file
= new File(path
+ "resources_" + code
+ ".properties");
157 // Default properties file:
158 file
= new File(path
+ "resources.properties");
161 BufferedWriter writer
= new BufferedWriter(new OutputStreamWriter(
162 new FileOutputStream(file
), "UTF-8"));
164 String name
= locale
.getDisplayCountry(locale
);
165 if (name
.length() == 0)
166 name
= locale
.getDisplayLanguage(locale
);
167 if (name
.length() == 0)
170 if (code
.length() > 0) {
171 name
= name
+ " (" + code
+ ")";
174 writer
.append("# " + name
+ " translation file (UTF-8)\n");
175 writer
.append("# \n");
176 writer
.append("# Note that any key can be doubled with a _NOUTF suffix\n");
177 writer
.append("# to use when the flag --noutf is passed\n");
178 writer
.append("# \n");
179 writer
.append("# Also, the comments always refer to the key below them.\n");
180 writer
.append("# \n");
183 for (Field field
: StringId
.class.getDeclaredFields()) {
184 Meta meta
= field
.getAnnotation(Meta
.class);
186 StringId id
= StringId
.valueOf(field
.getName());
187 String info
= getMetaInfo(meta
);
193 writer
.append(id
.name());
194 writer
.append(" = ");
195 if (!trans
.trans(id
).equals(id
.name()))
196 writer
.append(trans
.trans(id
));
206 * Return the {@link Locale} representing the given language.
209 * the language to initialise, in the form "en-GB" or "fr" for
212 * @return the corresponding {@link Locale} or the default {@link Locale} if
215 static private Locale
getLocaleFor(String language
) {
218 if (language
== null) {
219 locale
= Locale
.getDefault();
221 language
= language
.replaceAll("_", "-");
222 String lang
= language
;
223 String country
= null;
224 if (language
.contains("-")) {
225 lang
= language
.split("-")[0];
226 country
= language
.split("-")[1];
230 locale
= new Locale(lang
, country
);
232 locale
= new Locale(lang
);
239 * Return formated, display-able information from the {@link Meta} field
240 * given. Each line will always starts with a "#" character.
243 * the {@link Meta} field
245 * @return the information to display or NULL if none
247 static private String
getMetaInfo(Meta meta
) {
248 String what
= meta
.what();
249 String where
= meta
.where();
250 String format
= meta
.format();
251 String info
= meta
.info();
253 int opt
= what
.length() + where
.length() + format
.length();
254 if (opt
+ info
.length() == 0)
257 StringBuilder builder
= new StringBuilder();
258 builder
.append("# ");
262 if (what
.length() > 0) {
263 builder
.append("WHAT: " + what
);
264 if (where
.length() + format
.length() > 0)
265 builder
.append(", ");
268 if (where
.length() > 0) {
269 builder
.append("WHERE: " + where
);
270 if (format
.length() > 0)
271 builder
.append(", ");
274 if (format
.length() > 0) {
275 builder
.append("FORMAT: " + format
);
278 builder
.append(")\n# ");
281 builder
.append(info
);
283 return builder
.toString();
287 * The enum representing textual information to be translated to the user as
290 * Note that each key that should be translated MUST be annotated with a
291 * {@link Meta} annotation.
296 public enum StringId
{
297 DUMMY
, // <-- TODO : remove
298 NULL
, // Special usage, no annotations so it is not visible in
300 @Meta(what
= "a key to press", where
= "action keys", format
= "MUST BE 3 chars long", info
= "Tab key")
302 @Meta(what
= "a key to press", where
= "action keys", format
= "MUST BE 3 chars long", info
= "Enter key")
304 @Meta(what
= "", where
= "", format
= "", info
= "")
305 KEY_ACTION_BACK
, // MainWindow
306 @Meta(what
= "", where
= "", format
= "", info
= "")
308 @Meta(what
= "", where
= "", format
= "", info
= "")
309 KEY_ACTION_VIEW_CARD
, // FileList
310 @Meta(what
= "", where
= "", format
= "", info
= "")
311 KEY_ACTION_VIEW_CONTACT
, // ContactList
312 @Meta(what
= "", where
= "", format
= "", info
= "")
313 KEY_ACTION_EDIT_CONTACT
, //
314 @Meta(what
= "", where
= "", format
= "", info
= "")
315 KEY_ACTION_SAVE_CARD
, //
316 @Meta(what
= "", where
= "", format
= "", info
= "")
317 KEY_ACTION_DELETE_CONTACT
, //
318 @Meta(what
= "", where
= "", format
= "", info
= "")
319 KEY_ACTION_SEARCH
, //
320 @Meta(what
= "", where
= "", format
= "", info
= "we could use: ' ', ┃, │...")
321 DEAULT_FIELD_SEPARATOR
, // MainContentList
322 @Meta(what
= "", where
= "", format
= "", info
= "")
323 DEAULT_FIELD_SEPARATOR_NOUTF
, //
324 @Meta(what
= "", where
= "", format
= "", info
= "")
325 KEY_ACTION_INVERT
, // ContactDetails
326 @Meta(what
= "", where
= "", format
= "", info
= "")
327 KEY_ACTION_FULLSCREEN
, //
328 @Meta(what
= "", where
= "", format
= "", info
= "")
329 KEY_ACTION_SWITCH_FORMAT
, // multi-usage