1 package be
.nikiroo
.utils
.resources
;
4 import java
.io
.IOException
;
6 import java
.util
.LinkedList
;
8 import java
.util
.Locale
;
9 import java
.util
.regex
.Pattern
;
12 * This class manages a translation-dedicated Bundle.
14 * Two special cases are handled for the used enum:
16 * <li>NULL will always will return an empty {@link String}</li>
17 * <li>DUMMY will return "[DUMMY]" (maybe with a suffix and/or "NOUTF")</li>
21 * the enum to use to get values out of this class
25 public class TransBundle
<E
extends Enum
<E
>> extends Bundle
<E
> {
26 private boolean utf
= true;
27 private Locale locale
;
28 private boolean defaultLocale
= false;
31 * Create a translation service with the default language.
34 * a runtime instance of the class of E
36 * the name of the {@link Bundles}
38 public TransBundle(Class
<E
> type
, Enum
<?
> name
) {
39 super(type
, name
, null);
44 * Create a translation service for the given language (will fall back to
45 * the default one i not found).
48 * a runtime instance of the class of E
50 * the name of the {@link Bundles}
54 public TransBundle(Class
<E
> type
, Enum
<?
> name
, String language
) {
55 super(type
, name
, null);
56 setLanguage(language
);
60 * Translate the given id into user text.
65 * the values to insert instead of the place holders in the
68 * @return the translated text with the given value where required or NULL
69 * if not found (not present in the resource file)
71 public String
getString(E stringId
, Object
... values
) {
72 return getStringX(stringId
, "", values
);
76 * Translate the given id into user text.
81 * the values to insert instead of the place holders in the
84 * @return the translated text with the given value where required or NULL
85 * if not found (not present in the resource file)
87 public String
getStringNOUTF(E stringId
, Object
... values
) {
88 return getStringX(stringId
, "NOUTF", values
);
92 * Translate the given id suffixed with the runtime value "_suffix" (that
93 * is, "_" and suffix) into user text.
98 * the values to insert instead of the place holders in the
103 * @return the translated text with the given value where required or NULL
104 * if not found (not present in the resource file)
106 public String
getStringX(E stringId
, String suffix
, Object
... values
) {
110 String key
= id
.name()
111 + ((suffix
== null || suffix
.isEmpty()) ?
"" : "_"
112 + suffix
.toUpperCase());
115 if (containsKey(key
+ "_NOUTF")) {
120 if ("NULL".equals(id
.name().toUpperCase())) {
122 } else if ("DUMMY".equals(id
.name().toUpperCase())) {
123 result
= "[" + key
.toLowerCase() + "]";
124 } else if (containsKey(key
)) {
125 result
= getString(key
);
130 if (values
!= null && values
.length
> 0 && result
!= null) {
131 return String
.format(locale
, result
, values
);
138 * Check if unicode characters should be used.
140 * @return TRUE to allow unicode
142 public boolean isUnicode() {
147 * Allow or disallow unicode characters in the program.
150 * TRUE to allow unuciode, FALSE to only allow ASCII characters
152 public void setUnicode(boolean utf
) {
157 * Return all the languages known by the program.
160 * @return the known language codes
162 public List
<String
> getKnownLanguages() {
163 return getKnownLanguages(keyType
);
167 * Initialise the translation mappings for the given language.
170 * the language to initialise, in the form "en-GB" or "fr" for
173 private void setLanguage(String language
) {
174 defaultLocale
= (language
== null || language
.length() == 0);
175 locale
= getLocaleFor(language
);
176 setBundle(keyType
, locale
, false);
180 public void reload(boolean resetToDefault
) {
181 setBundle(keyType
, locale
, resetToDefault
);
185 public String
getString(E id
) {
186 return getString(id
, (Object
[]) null);
190 * Create/update the .properties files for each supported language and for
191 * the default language.
193 * Note: this method is <b>NOT</b> thread-safe.
196 * the path where the .properties files are
198 * @throws IOException
199 * in case of IO errors
202 public void updateFile(String path
) throws IOException
{
203 String prev
= locale
.getLanguage();
204 Object status
= takeSnapshot();
208 if (prev
.equals(getLocaleFor(null).getLanguage())) {
209 // restore snapshot if default locale = current locale
210 restoreSnapshot(status
);
212 super.updateFile(path
);
214 for (String lang
: getKnownLanguages()) {
216 if (lang
.equals(prev
)) {
217 restoreSnapshot(status
);
219 super.updateFile(path
);
223 restoreSnapshot(status
);
227 protected File
getUpdateFile(String path
) {
228 String code
= locale
.toString();
230 if (!defaultLocale
&& code
.length() > 0) {
231 file
= new File(path
, keyType
.name() + "_" + code
+ ".properties");
233 // Default properties file:
234 file
= new File(path
, keyType
.name() + ".properties");
241 protected void writeHeader(Writer writer
) throws IOException
{
242 String code
= locale
.toString();
243 String name
= locale
.getDisplayCountry(locale
);
245 if (name
.length() == 0) {
246 name
= locale
.getDisplayLanguage(locale
);
249 if (name
.length() == 0) {
253 if (code
.length() > 0) {
254 name
= name
+ " (" + code
+ ")";
257 name
= (name
+ " " + getBundleDisplayName()).trim();
259 writer
.write("# " + name
+ " translation file (UTF-8)\n");
260 writer
.write("# \n");
261 writer
.write("# Note that any key can be doubled with a _NOUTF suffix\n");
262 writer
.write("# to use when the NOUTF env variable is set to 1\n");
263 writer
.write("# \n");
264 writer
.write("# Also, the comments always refer to the key below them.\n");
265 writer
.write("# \n");
269 protected void writeValue(Writer writer
, E id
) throws IOException
{
270 super.writeValue(writer
, id
);
272 String name
= id
.name() + "_NOUTF";
273 if (containsKey(name
)) {
274 String value
= getString(name
);
275 writeValue(writer
, name
, value
);
280 * Return the {@link Locale} representing the given language.
283 * the language to initialise, in the form "en-GB" or "fr" for
286 * @return the corresponding {@link Locale} or the default {@link Locale} if
289 static private Locale
getLocaleFor(String language
) {
292 if (language
== null) {
293 locale
= Locale
.getDefault();
295 language
= language
.replaceAll("_", "-");
296 String lang
= language
;
297 String country
= null;
298 if (language
.contains("-")) {
299 lang
= language
.split("-")[0];
300 country
= language
.split("-")[1];
304 locale
= new Locale(lang
, country
);
306 locale
= new Locale(lang
);
313 * Return all the languages known by the program.
316 * the enumeration on which we translate
318 * @return the known language codes
320 static protected List
<String
> getKnownLanguages(Enum
<?
> name
) {
321 List
<String
> resources
= new LinkedList
<String
>();
323 String regex
= ".*" + name
.name() + "[_a-zA-Za]*\\.properties$";
325 for (String res
: TransBundle_ResourceList
.getResources(Pattern
327 String resource
= res
;
328 int index
= resource
.lastIndexOf('/');
329 if (index
>= 0 && index
< (resource
.length() - 1))
330 resource
= resource
.substring(index
+ 1);
331 if (resource
.startsWith(name
.name())) {
332 resource
= resource
.substring(0, resource
.length()
333 - ".properties".length());
334 resource
= resource
.substring(name
.name().length());
335 if (resource
.startsWith("_")) {
336 resource
= resource
.substring(1);
337 resources
.add(resource
);