1 package be
.nikiroo
.jvcard
.resources
;
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
;
13 * This class manages the translation of {@link Trans.StringId}s into
14 * user-understandable text.
20 private ResourceBundle map
;
21 private boolean utf
= true;
22 private Locale locale
;
25 * Create a translation service with the default language.
32 * Create a translation service for the given language. (Will fall back to
33 * the default one i not found.)
38 public Trans(String language
) {
39 setLanguage(language
);
43 * Translate the given {@link StringId} into user text.
48 * the values to insert instead of the place holders in the
51 * @return the translated text with the given value where required
53 public String
trans(StringId stringId
, String
... values
) {
54 StringId id
= stringId
;
59 id
= StringId
.valueOf(stringId
.name() + "_NOUTF");
60 } catch (IllegalArgumentException iae
) {
61 // no special _NOUTF version found
65 if (id
== StringId
.NULL
) {
67 } else if (id
== StringId
.DUMMY
) {
69 } else if (map
.containsKey(id
.name())) {
70 result
= map
.getString(id
.name());
72 result
= id
.toString();
75 if (values
!= null && values
.length
> 0)
76 return String
.format(locale
, result
, (Object
[]) values
);
82 * Check if unicode characters should be used.
84 * @return TRUE to allow unicode
86 public boolean isUnicode() {
91 * Allow or disallow unicode characters in the program.
94 * TRUE to allow unuciode, FALSE to only allow ASCII characters
96 public void setUnicode(boolean utf
) {
101 * Initialise the translation mappings for the given language.
104 * the language to initialise, in the form "en-GB" or "fr" for
107 private void setLanguage(String language
) {
108 locale
= getLocaleFor(language
);
109 map
= Bundles
.getBundle("resources", locale
);
113 * Create/update the translation .properties files. Will use the most likely
114 * candidate as base if the file does not already exists (for instance,
115 * "en_US" will use "en" as a base).
118 * the path where the .properties files are
121 * the language code to create/update (e.g.: <tt>fr-BE</tt>)
123 * @throws IOException
124 * in case of IO errors
126 static public void generateTranslationFile(String path
, String language
)
129 Locale locale
= getLocaleFor(language
);
130 String code
= locale
.toString();
131 Trans trans
= new Trans(code
);
134 if (code
.length() > 0) {
135 file
= new File(path
+ "resources_" + code
+ ".properties");
137 // Default properties file:
138 file
= new File(path
+ "resources.properties");
141 BufferedWriter writer
= new BufferedWriter(new OutputStreamWriter(
142 new FileOutputStream(file
), "UTF-8"));
144 String name
= locale
.getDisplayCountry(locale
);
145 if (name
.length() == 0)
146 name
= locale
.getDisplayLanguage(locale
);
147 if (name
.length() == 0)
150 if (code
.length() > 0) {
151 name
= name
+ " (" + code
+ ")";
154 writer
.append("# " + name
+ " translation file (UTF-8)\n");
155 writer
.append("# \n");
156 writer
.append("# Note that any key can be doubled with a _NOUTF suffix\n");
157 writer
.append("# to use when the flag --noutf is passed\n");
158 writer
.append("# \n");
159 writer
.append("# Also, the comments always refer to the key below them.\n");
160 writer
.append("# \n");
163 for (Field field
: StringId
.class.getDeclaredFields()) {
164 Meta meta
= field
.getAnnotation(Meta
.class);
166 StringId id
= StringId
.valueOf(field
.getName());
167 String info
= getMetaInfo(meta
);
173 writer
.append(id
.name());
174 writer
.append(" = ");
175 if (!trans
.trans(id
).equals(id
.name()))
176 writer
.append(trans
.trans(id
));
185 * Return the {@link Locale} representing the given language.
188 * the language to initialise, in the form "en-GB" or "fr" for
191 * @return the corresponding {@link Locale} or the default {@link Locale} if
194 static private Locale
getLocaleFor(String language
) {
197 if (language
== null) {
198 locale
= Locale
.getDefault();
200 language
= language
.replaceAll("_", "-");
201 String lang
= language
;
202 String country
= null;
203 if (language
.contains("-")) {
204 lang
= language
.split("-")[0];
205 country
= language
.split("-")[1];
209 locale
= new Locale(lang
, country
);
211 locale
= new Locale(lang
);
218 * Return formated, display-able information from the {@link Meta} field
219 * given. Each line will always starts with a "#" character.
222 * the {@link Meta} field
224 * @return the information to display or NULL if none
226 static private String
getMetaInfo(Meta meta
) {
227 String what
= meta
.what();
228 String where
= meta
.where();
229 String format
= meta
.format();
230 String info
= meta
.info();
232 int opt
= what
.length() + where
.length() + format
.length();
233 if (opt
+ info
.length() == 0)
236 StringBuilder builder
= new StringBuilder();
237 builder
.append("# ");
241 if (what
.length() > 0) {
242 builder
.append("WHAT: " + what
);
243 if (where
.length() + format
.length() > 0)
244 builder
.append(", ");
247 if (where
.length() > 0) {
248 builder
.append("WHERE: " + where
);
249 if (format
.length() > 0)
250 builder
.append(", ");
253 if (format
.length() > 0) {
254 builder
.append("FORMAT: " + format
);
258 if (info
.length() > 0) {
259 builder
.append("\n# ");
263 builder
.append(info
);
265 return builder
.toString();
269 * The enum representing textual information to be translated to the user as
272 * Note that each key that should be translated MUST be annotated with a
273 * {@link Meta} annotation.
278 public enum StringId
{
279 DUMMY
, // <-- TODO : remove
280 NULL
, // Special usage, no annotations so it is not visible in
282 @Meta(what
= "a key to press", where
= "action keys", format
= "MUST BE 3 chars long", info
= "Tab key")
284 @Meta(what
= "a key to press", where
= "action keys", format
= "MUST BE 3 chars long", info
= "Enter key")
286 @Meta(what
= "", where
= "", format
= "", info
= "")
287 KEY_ACTION_BACK
, // MainWindow
288 @Meta(what
= "", where
= "", format
= "", info
= "")
290 @Meta(what
= "", where
= "", format
= "", info
= "")
291 KEY_ACTION_VIEW_CARD
, // FileList
292 @Meta(what
= "", where
= "", format
= "", info
= "")
293 KEY_ACTION_VIEW_CONTACT
, // ContactList
294 @Meta(what
= "", where
= "", format
= "", info
= "")
295 KEY_ACTION_EDIT_CONTACT
, //
296 @Meta(what
= "", where
= "", format
= "", info
= "")
297 KEY_ACTION_SAVE_CARD
, //
298 @Meta(what
= "", where
= "", format
= "", info
= "")
299 KEY_ACTION_DELETE_CONTACT
, //
300 @Meta(what
= "", where
= "", format
= "", info
= "")
301 KEY_ACTION_SEARCH
, //
302 @Meta(what
= "", where
= "", format
= "", info
= "we could use: ' ', ┃, │...")
303 DEAULT_FIELD_SEPARATOR
, // MainContentList
304 @Meta(what
= "", where
= "", format
= "", info
= "")
305 DEAULT_FIELD_SEPARATOR_NOUTF
, //
306 @Meta(what
= "", where
= "", format
= "", info
= "")
307 KEY_ACTION_INVERT
, // ContactDetails
308 @Meta(what
= "", where
= "", format
= "", info
= "")
309 KEY_ACTION_FULLSCREEN
, //
310 @Meta(what
= "", where
= "", format
= "", info
= "")
311 KEY_ACTION_SWITCH_FORMAT
, // multi-usage
312 @Meta(what
= "Action key", where
= "Contact list, Edit Contact", format
= "", info
= "Add a new contact/field")
314 @Meta(what
= "User question: TEXT", where
= "Contact list", format
= "", info
= "New contact")
315 ASK_USER_CONTACT_NAME
, //
316 @Meta(what
= "User question: [Y|N]", where
= "Contact list", format
= "%s = contact name", info
= "Delete contact")
317 CONFIRM_USER_DELETE_CONTACT
, //
318 @Meta(what
= "Error", where
= "Contact list", format
= "%s = contact name", info
= "cannot delete a contact")
319 ERR_CANNOT_DELETE_CONTACT
, //