Translation system improvement (can now generate .properties files
[jvcard.git] / src / be / nikiroo / jvcard / i18n / Trans.java
CommitLineData
a3b510ab
NR
1package be.nikiroo.jvcard.i18n;
2
e7418457
NR
3import java.io.BufferedWriter;
4import java.io.File;
5import java.io.FileOutputStream;
6import java.io.IOException;
7import java.io.OutputStreamWriter;
8import java.lang.reflect.Field;
668268fc
NR
9import java.util.Locale;
10import java.util.ResourceBundle;
296a0b75 11
2a96e7b2 12import be.nikiroo.jvcard.resources.Bundles;
296a0b75
NR
13import be.nikiroo.jvcard.tui.UiColors;
14
668268fc
NR
15import com.googlecode.lanterna.input.KeyStroke;
16
a3b510ab 17/**
e7418457 18 * This class manages the translation of {@link Trans.StringId}s into
a3b510ab
NR
19 * user-understandable text.
20 *
21 * @author niki
22 *
23 */
24public class Trans {
668268fc 25 ResourceBundle map;
a3b510ab 26
a3b510ab 27 /**
668268fc 28 * Create a translation service with the default language.
a3b510ab 29 */
668268fc 30 public Trans() {
e7418457 31 setLanguage(null);
668268fc 32 }
a3b510ab 33
668268fc
NR
34 /**
35 * Create a translation service for the given language. (Will fall back to
36 * the default one i not found.)
37 *
38 * @param language
39 * the language to use
40 */
41 public Trans(String language) {
e7418457 42 setLanguage(language);
a3b510ab
NR
43 }
44
296a0b75
NR
45 /**
46 * Translate the given {@link StringId} into user text.
47 *
48 * @param stringId
49 * the ID to translate
50 *
51 * @return the translated text
52 */
a3b510ab 53 public String trans(StringId stringId) {
296a0b75
NR
54 StringId id = stringId;
55 if (!UiColors.getInstance().isUnicode()) {
56 try {
2a96e7b2 57 id = StringId.valueOf(stringId.name() + "_NOUTF");
296a0b75
NR
58 } catch (IllegalArgumentException iae) {
59 // no special _NOUTF version found
60 }
61 }
62
668268fc
NR
63 if (id == StringId.NULL) {
64 return "";
65 }
66
67 if (id == StringId.DUMMY) {
68 return "[dummy]";
69 }
70
2a96e7b2
NR
71 if (map.containsKey(id.name())) {
72 return map.getString(id.name());
296a0b75
NR
73 }
74
75 return id.toString();
76 }
77
78 /**
79 * Translate the given {@link KeyStroke} into a user text {@link String} of
80 * size 3.
81 *
82 * @param key
83 * the key to translate
84 *
85 * @return the translated text
86 */
87 public String trans(KeyStroke key) {
88 String keyTrans = "";
89
90 switch (key.getKeyType()) {
91 case Enter:
92 if (UiColors.getInstance().isUnicode())
93 keyTrans = " ⤶ ";
94 else
668268fc 95 keyTrans = trans(StringId.KEY_ENTER);
296a0b75
NR
96 break;
97 case Tab:
98 if (UiColors.getInstance().isUnicode())
99 keyTrans = " ↹ ";
100 else
668268fc 101 keyTrans = trans(StringId.KEY_TAB);
296a0b75
NR
102
103 break;
104 case Character:
105 keyTrans = " " + key.getCharacter() + " ";
106 break;
107 default:
108 keyTrans = "" + key.getKeyType();
109 int width = 3;
110 if (keyTrans.length() > width) {
111 keyTrans = keyTrans.substring(0, width);
112 } else if (keyTrans.length() < width) {
113 keyTrans = keyTrans
114 + new String(new char[width - keyTrans.length()])
115 .replace('\0', ' ');
116 }
117 break;
a3b510ab
NR
118 }
119
296a0b75 120 return keyTrans;
a3b510ab
NR
121 }
122
668268fc
NR
123 /**
124 * Initialise the translation mappings for the given language.
125 *
126 * @param lang
127 * the language to initialise
128 */
e7418457 129 private void setLanguage(String lang) {
668268fc
NR
130 Locale locale = null;
131
132 if (lang == null) {
133 locale = Locale.getDefault();
134 } else {
135 locale = Locale.forLanguageTag(lang);
136 }
137
2a96e7b2 138 map = Bundles.getBundle("resources", locale);
a3b510ab 139 }
e7418457
NR
140
141 /**
142 * Create/update the translation .properties files. Will use the most likely
143 * candidate as base if the file does not already exists (for instance,
144 * "en_US" will use "en" as a base).
145 *
146 * @param args
147 * the path where the .properties files are, then the languages
148 * to create/update
149 *
150 * @throws IOException
151 * in case of IO errors
152 */
153 public static void main(String[] args) throws IOException {
154 String path = args[0];
155 for (int i = 1; i < args.length; i++) {
156 Locale locale = Locale.forLanguageTag(args[i].replaceAll("_", "-"));
157 String code = locale.toString();
158 Trans trans = new Trans(code);
159
160 File file = null;
161 if (code.length() > 0) {
162 file = new File(path + "resources_" + code + ".properties");
163 } else {
164 // Default properties file:
165 file = new File(path + "resources.properties");
166 }
167
168 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
169 new FileOutputStream(file), "UTF-8"));
170
171 String name = locale.getDisplayCountry(locale);
172 if (name.length() == 0)
173 name = locale.getDisplayLanguage(locale);
174 if (name.length() == 0)
175 name = "default";
176
177 if (code.length() > 0) {
178 name = name + " (" + code + ")";
179 }
180
181 writer.append("# " + name + " translation file (UTF-8)\n");
182 writer.append("# \n");
183 writer.append("# Note that any key can be doubled with a _NOUTF suffix\n");
184 writer.append("# to use when the flag --noutf is passed\n");
185 writer.append("# \n");
186 writer.append("# Also, the comments always refer to the key below them.\n");
187 writer.append("# \n");
188 writer.append("\n");
189
190 for (Field field : StringId.class.getDeclaredFields()) {
191 Meta meta = field.getAnnotation(Meta.class);
192 if (meta != null) {
193 StringId id = StringId.valueOf(field.getName());
194 String info = getMetaInfo(meta);
195 if (info != null) {
196 writer.append(info);
197 writer.append("\n");
198 }
199
200 writer.append(id.name());
201 writer.append(" = ");
202 if (!trans.trans(id).equals(id.name()))
203 writer.append(trans.trans(id));
204 writer.append("\n");
205 }
206 }
207
208 writer.close();
209 }
210 }
211
212 /**
213 * Return formated, display-able information from the {@link Meta} field
214 * given. Each line will always starts with a "#" character.
215 *
216 * @param meta
217 * the {@link Meta} field
218 *
219 * @return the information to display or NULL if none
220 */
221 private static String getMetaInfo(Meta meta) {
222 String what = meta.what();
223 String where = meta.where();
224 String format = meta.format();
225 String info = meta.info();
226
227 int opt = what.length() + where.length() + format.length();
228 if (opt + info.length() == 0)
229 return null;
230
231 StringBuilder builder = new StringBuilder();
232 builder.append("# ");
233
234 if (opt > 0) {
235 builder.append("(");
236 if (what.length() > 0) {
237 builder.append("WHAT: " + what);
238 if (where.length() + format.length() > 0)
239 builder.append(", ");
240 }
241
242 if (where.length() > 0) {
243 builder.append("WHERE: " + where);
244 if (format.length() > 0)
245 builder.append(", ");
246 }
247
248 if (format.length() > 0) {
249 builder.append("FORMAT: " + format);
250 }
251
252 builder.append(")\n# ");
253 }
254
255 builder.append(info);
256
257 return builder.toString();
258 }
259
260 /**
261 * The enum representing textual information to be translated to the user as
262 * a key.
263 *
264 * Note that each key that should be translated MUST be annotated with a
265 * {@link Meta} annotation.
266 *
267 * @author niki
268 *
269 */
270 public enum StringId {
271 DUMMY, // <-- TODO : remove
272 NULL, // Special usage, no annotations so it is not visible in
273 // .properties files
274 @Meta(what = "a key to press", where = "action keys", format = "MUST BE 3 chars long", info = "Tab key")
275 KEY_TAB, // keys
276 @Meta(what = "a key to press", where = "action keys", format = "MUST BE 3 chars long", info = "Enter key")
277 KEY_ENTER, //
278 @Meta(what = "", where = "", format = "", info = "")
279 KEY_ACTION_BACK, // MainWindow
280 @Meta(what = "", where = "", format = "", info = "")
281 KEY_ACTION_HELP, //
282 @Meta(what = "", where = "", format = "", info = "")
283 KEY_ACTION_VIEW_CARD, // FileList
284 @Meta(what = "", where = "", format = "", info = "")
285 KEY_ACTION_VIEW_CONTACT, // ContactList
286 @Meta(what = "", where = "", format = "", info = "")
287 KEY_ACTION_EDIT_CONTACT, //
288 @Meta(what = "", where = "", format = "", info = "")
289 KEY_ACTION_SAVE_CARD, //
290 @Meta(what = "", where = "", format = "", info = "")
291 KEY_ACTION_DELETE_CONTACT, //
292 @Meta(what = "", where = "", format = "", info = "")
293 KEY_ACTION_SEARCH, //
294 @Meta(what = "", where = "", format = "", info = "we could use: ' ', ┃, │...")
295 DEAULT_FIELD_SEPARATOR, // MainContentList
296 @Meta(what = "", where = "", format = "", info = "")
297 DEAULT_FIELD_SEPARATOR_NOUTF, //
298 @Meta(what = "", where = "", format = "", info = "")
299 KEY_ACTION_INVERT, // ContactDetails
300 @Meta(what = "", where = "", format = "", info = "")
301 KEY_ACTION_FULLSCREEN, //
302 @Meta(what = "", where = "", format = "", info = "")
303 KEY_ACTION_SWITCH_FORMAT, // multi-usage
304 };
a3b510ab 305}