Statistics
| Revision:

root / branches / v2_0_0_prep / extensions / extI18n / src / main / java / org / gvsig / i18n / impl / I18nManagerImpl.java @ 37895

History | View | Annotate | Download (24.2 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 * 
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 * 
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
19
 * MA  02110-1301, USA.
20
 * 
21
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {DiSiD Technologies}  {New extension for installation and update of text translations}
26
 */
27
package org.gvsig.i18n.impl;
28

    
29
import java.io.BufferedReader;
30
import java.io.BufferedWriter;
31
import java.io.File;
32
import java.io.FileOutputStream;
33
import java.io.FileWriter;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.InputStreamReader;
37
import java.io.OutputStreamWriter;
38
import java.io.PrintStream;
39
import java.security.AccessController;
40
import java.security.PrivilegedAction;
41
import java.util.ArrayList;
42
import java.util.HashMap;
43
import java.util.HashSet;
44
import java.util.Iterator;
45
import java.util.List;
46
import java.util.Locale;
47
import java.util.Map;
48
import java.util.Map.Entry;
49
import java.util.Set;
50
import java.util.StringTokenizer;
51
import java.util.zip.ZipEntry;
52
import java.util.zip.ZipFile;
53
import java.util.zip.ZipOutputStream;
54

    
55
import org.gvsig.andami.Launcher;
56
import org.gvsig.andami.PluginServices;
57
import org.gvsig.andami.config.generate.AndamiConfig;
58
import org.gvsig.i18n.I18nException;
59
import org.gvsig.i18n.I18nManager;
60
import org.gvsig.i18n.Messages;
61
import org.gvsig.utils.StringUtilities;
62
import org.gvsig.utils.XMLEntity;
63

    
64
/**
65
 * Implementation of the I18nManager interface.
66
 * 
67
 * @author <a href="mailto:dcervera@disid.com">David Cervera</a>
68
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
69
 */
70
public class I18nManagerImpl implements I18nManager {
71

    
72
        private static final String LOCALES_FILE_NAME = "locales.csv";
73

    
74
        private static final String CSV_SEPARATOR = ",";
75

    
76
        private static final String I18N_EXTENSION = "org.gvsig.i18n.extension";
77

    
78
        private static final String VARIANT = "variant";
79

    
80
        private static final String COUNTRY = "country";
81

    
82
        private static final String LANGUAGE = "language";
83

    
84
        private static final String REGISTERED_LOCALES_PERSISTENCE =
85
                        "RegisteredLocales";
86

    
87
        private static final I18nManager DEFAULT = new I18nManagerImpl();
88

    
89
        private Set registeredLocales;
90

    
91
        /**
92
         * The list of default reference locales. The last one will be used to get
93
         * all the keys when translating to a new locale.
94
         */
95
        private Locale[] referenceLocales = new Locale[] { ENGLISH, SPANISH };
96

    
97
        private Locale[] defaultLocales = new Locale[] {
98
        // Default supported locales
99
                        SPANISH, // Spanish
100
                        ENGLISH, // English
101
                        new Locale("en", "US"), // English US
102
                        new Locale("ca"), // Catalan
103
                        new Locale("gl"), // Galician
104
                        new Locale("eu"), // Basque
105
                        new Locale("de"), // German
106
                        new Locale("cs"), // Czech
107
                        new Locale("fr"), // French
108
                        new Locale("it"), // Italian
109
                        new Locale("pl"), // Polish
110
                        new Locale("pt"), // Portuguese
111
                        new Locale("pt", "BR"), // Portuguese Brazil
112
                        new Locale("ro"), // Romanian
113
                        new Locale("zh"), // Chinese
114
                        new Locale("ru"), // Russian
115
                        new Locale("el"), // Greek
116
                        new Locale("ro"), // Romanian
117
                        new Locale("pl"), // Polish
118
                        new Locale("tr"), // Turkish
119
                        new Locale("sr"), // Serbio
120
                        new Locale("sw") // Swahili
121
                        };
122

    
123
        /**
124
         * Returns the unique instance of the I18nManager.
125
         * 
126
         * @return the unique instance
127
         */
128
        public static I18nManager getInstance() {
129
                return DEFAULT;
130
        }
131

    
132
        public static String capitalize(String text) {
133
                // Convert the first letter to uppercase
134
                String capitalLetter =
135
                                new String(new char[] { Character.toUpperCase(text.charAt(0)) });
136
                return capitalLetter.concat(text.substring(1));
137
        }
138

    
139
        public Locale[] getInstalledLocales() {
140
                if (registeredLocales == null) {
141

    
142
                        XMLEntity child = getRegisteredLocalesPersistence();
143

    
144
                        // If the list of registered locales is not already persisted,
145
                        // this should be the first time gvSIG is run with the I18nPlugin
146
                        // so we will take the list of default locales
147
                        if (child == null) {
148
                                Locale[] defaultLocales = getDefaultLocales();
149
                                registeredLocales = new HashSet(defaultLocales.length);
150
                                for (int i = 0; i < defaultLocales.length; i++) {
151
                                        registeredLocales.add(defaultLocales[i]);
152
                                }
153
                                storeInstalledLocales();
154
                        } else {
155
                                XMLEntity localesEntity = getRegisteredLocalesPersistence();
156
                                registeredLocales =
157
                                                new HashSet(localesEntity.getChildrenCount());
158
                                for (int i = 0; i < localesEntity.getChildrenCount(); i++) {
159
                                        XMLEntity localeEntity = localesEntity.getChild(i);
160
                                        String language = localeEntity.getStringProperty(LANGUAGE);
161
                                        String country = localeEntity.getStringProperty(COUNTRY);
162
                                        String variant = localeEntity.getStringProperty(VARIANT);
163
                                        Locale locale = new Locale(language, country, variant);
164
                                        registeredLocales.add(locale);
165
                                }
166
                        }
167
                }
168

    
169
                return (Locale[]) registeredLocales.toArray(new Locale[registeredLocales.size()]);
170
        }
171

    
172
        public void uninstallLocale(Locale locale) throws I18nException {
173
                if (getCurrentLocale().equals(locale) || isReferenceLocale(locale)) {
174
                        throw new UninstallLocaleException(locale);
175
                }
176

    
177
                if (registeredLocales.remove(locale)) {
178
                        // Remove from the configured locale list
179
                        storeInstalledLocales();
180

    
181
                        // Remove the resource bundle file
182
                        File bundleFile =
183
                                        new File(getResourcesFolder(), getResourceFileName(locale));
184

    
185
                        if (bundleFile.exists()) {
186
                                bundleFile.delete();
187
                        }
188
                }
189
        }
190

    
191
        public String getDisplayName(Locale locale) {
192
                return getDisplayName(locale, locale);
193
        }
194

    
195
        public String getDisplayName(Locale locale, Locale displayLocale) {
196
                StringBuffer name =
197
                                new StringBuffer(getLanguageDisplayName(locale, displayLocale));
198

    
199
                if (!isEmpty(locale.getCountry())) {
200
                        name.append(" - ");
201
                        name.append(locale.getDisplayCountry(displayLocale));
202
                }
203

    
204
                if (!isEmpty(locale.getVariant())) {
205
                        name.append(" - ");
206
                        name.append(locale.getDisplayVariant(displayLocale));
207
                }
208

    
209
                name.append(" (").append(locale.toString()).append(")");
210

    
211
                return name.toString();
212
        }
213

    
214
        private boolean isEmpty(String text) {
215
                return text == null || text.trim().length() == 0;
216
        }
217

    
218
        public String getLanguageDisplayName(Locale locale) {
219
                return getLanguageDisplayName(locale, locale);
220
        }
221

    
222
        public String getLanguageDisplayName(Locale locale, Locale displayLocale) {
223

    
224
                String displayName;
225

    
226
                // Correction for the Basque language display name,
227
                // show it in Basque, not in Spanish
228
                if ("eu".equals(locale.getLanguage())
229
                                && "vascuence".equals(locale.getDisplayLanguage())) {
230
                        displayName = "Euskera";
231
                }
232
                // Patch for Valencian/Catalan
233
                else if ("ca".equals(locale.getLanguage())) {
234
                        // displayName = Messages.getText("__valenciano");
235
                        // if ("__valenciano".equals(displayName)) {
236
                        // displayName = Messages.getText("__catalan");
237
                        // }
238
                        displayName = "Valenci?";
239
                } else {
240
                        displayName = locale.getDisplayLanguage(displayLocale);
241
                }
242

    
243
                return capitalize(displayName);
244
        }
245

    
246
        public Locale getCurrentLocale() {
247
                return Locale.getDefault();
248
        }
249

    
250
        public Locale getDefaultSystemLocale() {
251
                String language, region, country, variant;
252
                language = getSystemProperty("user.language", "en");
253
                // for compatibility, check for old user.region property
254
                region = getSystemProperty("user.region", null);
255

    
256
                if (region != null) {
257
                        // region can be of form country, country_variant, or _variant
258
                        int i = region.indexOf('_');
259
                        if (i >= 0) {
260
                                country = region.substring(0, i);
261
                                variant = region.substring(i + 1);
262
                        } else {
263
                                country = region;
264
                                variant = "";
265
                        }
266
                } else {
267
                        country = getSystemProperty("user.country", "");
268
                        variant = getSystemProperty("user.variant", "");
269
                }
270
                return new Locale(language, country, variant);
271
        }
272

    
273
        public void setCurrentLocale(Locale locale) {
274
                AndamiConfig config = Launcher.getAndamiConfig();
275
                config.setLocaleLanguage(locale.getLanguage());
276
                config.setLocaleCountry(locale.getCountry());
277
                config.setLocaleVariant(locale.getVariant());
278
        }
279

    
280
        public Locale[] installLocales(File importFile) throws I18nException {
281
                List importLocales = new ArrayList();
282

    
283
                try {
284
                        ZipFile zipFile = new ZipFile(importFile);
285

    
286
                        Map locales = getZipFileNonReferenceLocales(zipFile);
287

    
288
                        if (locales == null || locales.size() == 0) {
289
                                return null;
290
                        }
291

    
292
                        for (Iterator iterator = locales.entrySet().iterator(); iterator.hasNext();) {
293
                                Entry entry = (Entry) iterator.next();
294

    
295
                                String fileName = (String) entry.getKey();
296
                                Locale locale = (Locale) entry.getValue();
297
                                importLocales.add(locale);
298

    
299
                                // Add the locale to the list of installed ones, if it
300
                                // is new, otherwise, update the texts.
301
                                if (!registeredLocales.contains(locale)) {
302
                                        registeredLocales.add(locale);
303
                                        storeInstalledLocales();
304
                                }
305

    
306
                                // Replace the old bundle with the new one
307
                                ZipEntry zipEntry = zipFile.getEntry(fileName);
308
                                InputStream is = zipFile.getInputStream(zipEntry);
309
                                BufferedReader reader =
310
                                                new BufferedReader(new InputStreamReader(is));
311

    
312
                                File bundleFile = getResourceFile(locale);
313
                                FileWriter fileWriter = new FileWriter(bundleFile);
314
                                BufferedWriter writer = new BufferedWriter(fileWriter);
315

    
316
                                String line;
317
                                while ((line = reader.readLine()) != null) {
318
                                        writer.write(line);
319
                                        writer.write('\n');
320
                                }
321
                                writer.flush();
322
                                writer.close();
323
                                fileWriter.close();
324
                                reader.close();
325
                                is.close();
326
                        }
327

    
328
                } catch (Exception ex) {
329
                        throw new InstallLocalesException(importFile, ex);
330
                }
331

    
332
                return (Locale[]) importLocales.toArray(new Locale[importLocales.size()]);
333
        }
334

    
335
        public void exportLocaleForUpdate(Locale locale, Locale referenceLocale,
336
                        File exportFile) throws I18nException {
337

    
338
                exportLocalesForUpdate(new Locale[] { locale }, referenceLocale,
339
                                exportFile);
340
        }
341

    
342
        public void exportLocalesForUpdate(Locale[] locales,
343
                        Locale referenceLocale, File exportFile) throws I18nException {
344

    
345
                exportLocale(locales, new Locale[] { referenceLocale }, exportFile,
346
                                true);
347
        }
348

    
349
        public void exportLocaleForTranslation(Locale locale,
350
                        Locale referenceLocale, File exportFile) throws I18nException {
351

    
352
                exportLocaleForTranslation(locale, new Locale[] { referenceLocale },
353
                                exportFile);
354
        }
355

    
356
        public void exportLocaleForTranslation(Locale locale,
357
                        Locale[] referenceLocales, File exportFile) throws I18nException {
358

    
359
                exportLocale(new Locale[] { locale }, referenceLocales, exportFile,
360
                                false);
361
        }
362

    
363
        public Locale[] getReferenceLocales() {
364
                return referenceLocales;
365
        }
366

    
367
        public void setReferenceLocales(Locale[] referenceLocales) {
368
                this.referenceLocales = referenceLocales;
369
        }
370

    
371
        public void setDefaultLocales(Locale[] defaultLocales) {
372
                this.defaultLocales = defaultLocales;
373
        }
374

    
375
        private void exportLocale(Locale[] locales, Locale[] referenceLocales,
376
                        File exportFile, boolean update) throws I18nException {
377

    
378
                Locale[] refArray =
379
                                getReferenceLocalesToExport(locales, referenceLocales);
380

    
381
                Set allReferenceKeys = new HashSet();
382
                Map referenceTexts = new HashMap(refArray.length);
383
                for (int i = 0; i < refArray.length; i++) {
384
                        Map texts = getAllTexts(refArray[i]);
385
                        referenceTexts.put(refArray[i], texts);
386
                        allReferenceKeys.addAll(texts.keySet());
387
                }
388

    
389
                try {
390
                        FileOutputStream fos = new FileOutputStream(exportFile);
391
                        ZipOutputStream zipos = new ZipOutputStream(fos);
392

    
393
                        // Create the index file
394
                        writeZipFileLocales(zipos, locales, refArray);
395

    
396
                        PrintStream ps = new PrintStream(zipos);
397
                        Map texts = null;
398

    
399
                        if (update) {
400
                                // First, export the locales to update
401
                                if (locales != null) {
402
                                        for (int i = 0; i < locales.length; i++) {
403
                                                texts = getAllTexts(locales[i]);
404
                                                addPendingKeys(texts, allReferenceKeys);
405
                                                putResourceInZip(zipos, ps, texts,
406
                                                                getResourceFileName(locales[i]));
407
                                        }
408
                                }
409
                        } else { // translate
410
                                // First, export the locales to translate, taking the keys from
411
                                // the reference locales, but without values
412
                                if (locales != null) {
413
                                        for (int i = 0; i < locales.length; i++) {
414
                                                putResourceInZip(zipos, ps, allReferenceKeys,
415
                                                                getResourceFileName(locales[i]));
416
                                        }
417
                                }
418
                        }
419

    
420
                        // Next, export the reference locales
421
                        if (refArray != null) {
422
                                for (int i = 0; i < refArray.length; i++) {
423
                                        texts = getAllTexts(refArray[i]);
424
                                        putResourceInZip(zipos, ps, texts,
425
                                                        getResourceFileName(refArray[i]));
426
                                }
427
                        }
428

    
429
                        ps.flush();
430
                        ps.close();
431
                        zipos.close();
432
                        fos.close();
433
                } catch (IOException ex) {
434
                        throw new ExportLocaleException(locales, ex);
435
                }
436
        }
437

    
438
        /**
439
         * Adds the keys of the set to the map that aren't still contained in it
440
         * with a related empty String.
441
         * 
442
         * @param texts
443
         *            the map to complete with the keys
444
         * @param allReferenceKeys
445
         *            the complete key set
446
         */
447
        private void addPendingKeys(Map texts, Set allReferenceKeys) {
448
                for (Iterator iterator = allReferenceKeys.iterator(); iterator.hasNext();) {
449
                        Object key = (Object) iterator.next();
450
                        if (!texts.containsKey(key)) {
451
                                texts.put(key, "");
452
                        }
453
                }
454
        }
455

    
456
        /**
457
         * Returns the list of reference locales to export, as the union of the
458
         * default reference locales list and the one selected as reference. The
459
         * locales to translate or update are extracted from the list.
460
         */
461
        private Locale[] getReferenceLocalesToExport(Locale[] locales,
462
                        Locale[] referenceLocalesSelected) {
463
                // The reference locales to export are the default ones plus the
464
                // selected by the user.
465
                Set exportRefLocales = new HashSet(referenceLocales.length);
466
                for (int i = 0; i < referenceLocales.length; i++) {
467
                        exportRefLocales.add(referenceLocales[i]);
468
                }
469
                if (referenceLocalesSelected != null) {
470
                        for (int i = 0; i < referenceLocalesSelected.length; i++) {
471
                                exportRefLocales.add(referenceLocalesSelected[i]);
472
                        }
473
                }
474
                if (locales != null) {
475
                        for (int i = 0; i < locales.length; i++) {
476
                                exportRefLocales.remove(locales[i]);
477
                        }
478
                }
479
                Locale[] refArray =
480
                                (Locale[]) exportRefLocales.toArray(new Locale[exportRefLocales.size()]);
481
                return refArray;
482
        }
483

    
484
        /**
485
         * Returns all the localized texts and its keys for a locale.
486
         */
487
        private Map getAllTexts(Locale locale) {
488
                return Messages.getAllTexts(locale);
489
        }
490

    
491
        private Map getZipFileNonReferenceLocales(ZipFile zipFile)
492
                        throws I18nException {
493
                ZipEntry zipEntry = zipFile.getEntry(LOCALES_FILE_NAME);
494

    
495
                if (zipEntry == null) {
496
                        return null;
497
                }
498

    
499
                Map locales;
500
                try {
501
                        InputStream is = zipFile.getInputStream(zipEntry);
502
                        BufferedReader reader =
503
                                        new BufferedReader(new InputStreamReader(is));
504

    
505
                        locales = new HashMap(2);
506
                        String line;
507
                        while ((line = reader.readLine()) != null) {
508
                                // The excepted format is:
509
                                // FILENAME,LANGUAGE,COUNTRY,VARIANT,IS_REFERENCE
510
                                StringTokenizer st =
511
                                                new StringTokenizer(line, CSV_SEPARATOR, true);
512
                                // First: locale file name (required)
513
                                String fileName = st.nextToken();
514
                                if (CSV_SEPARATOR.equals(fileName)) {
515
                                        throw new LocaleFileNameRequiredException(line);
516
                                } else {
517
                                        // Read the next separator
518
                                        st.nextToken();
519
                                }
520
                                // Second: the locale language (required)
521
                                String language = st.nextToken();
522
                                if (CSV_SEPARATOR.equals(language)) {
523
                                        throw new LocaleLanguageRequiredException(line);
524
                                } else {
525
                                        // Read the next separator
526
                                        st.nextToken();
527
                                }
528
                                // Third: the country
529
                                String country = st.nextToken();
530
                                if (CSV_SEPARATOR.equals(country)) {
531
                                        country = null;
532
                                } else {
533
                                        // Read the next separator
534
                                        st.nextToken();
535
                                }
536
                                // Fourth: the variant
537
                                String variant = st.nextToken();
538
                                if (CSV_SEPARATOR.equals(variant)) {
539
                                        variant = null;
540
                                } else {
541
                                        // Read the next separator
542
                                        st.nextToken();
543
                                }
544
                                // Fifth: is a reference locale?
545
                                String refStr = st.nextToken();
546
                                if (CSV_SEPARATOR.equals(refStr)) {
547
                                        refStr = null;
548
                                }
549

    
550
                                // Only add non reference locales
551
                                if (refStr != null && !"true".equals(refStr.toLowerCase())) {
552
                                        // Variant only accepted if country defined
553
                                        if (country == null) {
554
                                                variant = null;
555
                                        }
556
                                        country = country == null ? "" : country;
557
                                        variant = variant == null ? "" : variant;
558
                                        Locale locale = new Locale(language, country, variant);
559

    
560
                                        locales.put(fileName, locale);
561
                                }
562
                        }
563

    
564
                        reader.close();
565
                        is.close();
566
                } catch (IOException ex) {
567
                        throw new ReadCSVLocalesFileException(ex);
568
                }
569

    
570
                return locales;
571
        }
572

    
573
        private void writeZipFileLocales(ZipOutputStream zos, Locale[] locales,
574
                        Locale[] referenceLocales) throws IOException {
575
                ZipEntry zipEntry = new ZipEntry(LOCALES_FILE_NAME);
576

    
577
                zos.putNextEntry(zipEntry);
578
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos));
579

    
580
                if (locales != null) {
581
                        for (int i = 0; i < locales.length; i++) {
582
                                writeLocaleEntry(locales[i], writer, false);
583
                        }
584
                }
585
                if (referenceLocales != null) {
586
                        for (int i = 0; i < referenceLocales.length; i++) {
587
                                writeLocaleEntry(referenceLocales[i], writer, true);
588
                        }
589
                }
590

    
591
                writer.flush();
592
                zos.closeEntry();
593
        }
594

    
595
        /**
596
         * Writes the locale entry into a writer.
597
         * 
598
         * @param locale
599
         *            the locale to create the entry for
600
         * @param writer
601
         *            to write to
602
         * @param reference
603
         *            is it is a reference locale or not
604
         * @throws IOException
605
         *             if there is an error creating the locale entry
606
         */
607
        private void writeLocaleEntry(Locale locale, BufferedWriter writer,
608
                        boolean reference) throws IOException {
609
                String language = locale.getLanguage();
610
                String country = locale.getCountry();
611
                country = country == null ? "" : country;
612
                String variant = locale.getVariant();
613
                variant = variant == null ? "" : variant;
614

    
615
                writer.write(getResourceFileName(locale));
616
                writer.write(',');
617
                writer.write(language);
618
                writer.write(',');
619
                writer.write(country);
620
                writer.write(',');
621
                writer.write(variant);
622
                writer.write(',');
623
                writer.write(Boolean.toString(reference));
624
                writer.write('\n');
625
        }
626

    
627
        /**
628
         * Returns if a locale is one of the default reference ones.
629
         */
630
        private boolean isReferenceLocale(Locale locale) {
631
                for (int i = 0; i < referenceLocales.length; i++) {
632
                        if (referenceLocales[i].equals(locale)) {
633
                                return true;
634
                        }
635
                }
636
                return false;
637
        }
638

    
639
        /**
640
         * Puts a new resource file into a Jar file.
641
         */
642
        private void putResourceInZip(ZipOutputStream zipos, PrintStream ps,
643
                        Map texts, String resourceFileName) throws IOException {
644
                // Add ZIP entry for the resource bundle file
645
                zipos.putNextEntry(new ZipEntry(resourceFileName));
646

    
647
                for (Iterator iterator = texts.entrySet().iterator(); iterator.hasNext();) {
648
                        Entry entry = (Entry) iterator.next();
649
                        String keyEncoded = escape((String) entry.getKey(), true);
650
                        ps.print(keyEncoded);
651
                        ps.print("=");
652
                        String valueEncoded = escape((String) entry.getValue(), false);
653
                        ps.println(valueEncoded);
654
                }
655

    
656
                ps.flush();
657

    
658
                // Close the ZIP entry, the file is complete
659
                zipos.closeEntry();
660
        }
661

    
662
        /**
663
         * Puts a new resource file into a Jar file.
664
         */
665
        private void putResourceInZip(ZipOutputStream zipos, PrintStream ps,
666
                        Set keys, String resourceFileName) throws IOException {
667
                // Add ZIP entry for the resource bundle file
668
                zipos.putNextEntry(new ZipEntry(resourceFileName));
669

    
670
                for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
671
                        String value = (String) iterator.next();
672
                        String keyEncoded = escape(value, true);
673
                        ps.print(keyEncoded);
674
                        ps.print("=");
675
                        ps.println();
676
                }
677

    
678
                ps.flush();
679

    
680
                // Close the ZIP entry, the file is complete
681
                zipos.closeEntry();
682
        }
683

    
684
        /**
685
         * Returns the file which contains the translations for a locale.
686
         */
687
        private File getResourceFile(Locale locale) {
688
                return new File(getResourcesFolder(), getResourceFileName(locale));
689
        }
690

    
691
        /**
692
         * Returns the name of the file which contains the translations for a
693
         * locale.
694
         */
695
        private String getResourceFileName(Locale locale) {
696
                StringBuffer fileName = new StringBuffer("text");
697

    
698
                // Spanish without country is the default locale
699
                if (!(isEmpty(locale.getCountry()) && "es".equals(locale.getLanguage()))) {
700
                        fileName.append('_').append(locale.getLanguage());
701
                }
702

    
703
                // Add the locale country
704
                if (!isEmpty(locale.getCountry())) {
705
                        fileName.append('_').append(locale.getCountry());
706
                }
707

    
708
                // Add the locale variant
709
                if (!isEmpty(locale.getVariant())) {
710
                        fileName.append('_').append(locale.getVariant());
711
                }
712

    
713
                fileName.append(".properties");
714
                return fileName.toString();
715
        }
716

    
717
        /**
718
         * Returns the folder where to store the resource bundle files.
719
         */
720
        private File getResourcesFolder() {
721
        return PluginServices.getPluginServices("org.gvsig.app")
722
                                .getPluginDirectory();
723
        }
724

    
725
        /**
726
         * Returns the child XMLEntity with the RegisteredLocales.
727
         */
728
        private XMLEntity getRegisteredLocalesPersistence() {
729
                XMLEntity entity = getI18nPersistence();
730
                XMLEntity child = null;
731
                for (int i = entity.getChildrenCount() - 1; i >= 0; i--) {
732
                        XMLEntity tmpchild = entity.getChild(i);
733
                        if (tmpchild.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
734
                                child = tmpchild;
735
                                break;
736
                        }
737
                }
738
                return child;
739
        }
740

    
741
        /**
742
         * Returns the I18n Plugin persistence.
743
         */
744
        private XMLEntity getI18nPersistence() {
745
                XMLEntity entity =
746
                                PluginServices.getPluginServices(I18N_EXTENSION)
747
                                                .getPersistentXML();
748
                return entity;
749
        }
750

    
751
        /**
752
         * Returns the list of default locales bundled with gvSIG.
753
         */
754
        private Locale[] getDefaultLocales() {
755
                return defaultLocales;
756
        }
757

    
758
        /**
759
         * Stores the list of installed locales into the plugin persistence.
760
         */
761
        private void storeInstalledLocales() {
762
                XMLEntity localesEntity = getRegisteredLocalesPersistence();
763

    
764
                // Remove the previous list of registered languages
765
                if (localesEntity != null) {
766
                        XMLEntity i18nPersistence = getI18nPersistence();
767
                        for (int i = i18nPersistence.getChildrenCount() - 1; i >= 0; i--) {
768
                                XMLEntity child = i18nPersistence.getChild(i);
769
                                if (child.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
770
                                        i18nPersistence.removeChild(i);
771
                                        break;
772
                                }
773
                        }
774
                }
775

    
776
                // Create the new persistence for the registered languages
777
                localesEntity = new XMLEntity();
778

    
779
                localesEntity.setName(REGISTERED_LOCALES_PERSISTENCE);
780

    
781
                for (Iterator iterator = registeredLocales.iterator(); iterator.hasNext();) {
782
                        Locale locale = (Locale) iterator.next();
783
                        XMLEntity localeEntity = new XMLEntity();
784
                        localeEntity.setName(locale.getDisplayName());
785
                        localeEntity.putProperty(LANGUAGE, locale.getLanguage());
786
                        localeEntity.putProperty(COUNTRY, locale.getCountry());
787
                        localeEntity.putProperty(VARIANT, locale.getVariant());
788

    
789
                        localesEntity.addChild(localeEntity);
790
                }
791

    
792
                getI18nPersistence().addChild(localesEntity);
793
        }
794

    
795
        private String escape(String value, boolean replaceBlanks) {
796
                // First replace non printable characters
797
                if (replaceBlanks) {
798
                        value = StringUtilities.replace(value, " ", "\\ ");
799
                }
800
                value = StringUtilities.replace(value, ":", "\\:");
801
                value = StringUtilities.replace(value, "\n", "\\n");
802
                value = StringUtilities.replace(value, "\t", "\\t");
803
                value = StringUtilities.replace(value, "\b", "\\b");
804
                value = StringUtilities.replace(value, "\f", "\\f");
805
                value = StringUtilities.replace(value, "\r", "\\r");
806
                // value = StringUtilities.replace(value, "\\", "\\\\");
807
                // value = StringUtilities.replace(value, "\'", "\\\'");
808
                // value = StringUtilities.replace(value, "\"", "\\\"");
809

    
810
                // Next, encode in raw-unicode-escape
811
                return toRawUnicodeEncoded(value);
812
        }
813

    
814
        private String toRawUnicodeEncoded(String value) {
815
                StringBuffer sb = new StringBuffer();
816
                for (int i = 0; i < value.length(); i++) {
817
                        char c = value.charAt(i);
818
                        if (c <= 0x80) {
819
                                sb.append(c);
820
                        } else {
821
                                sb.append("\\u");
822
                                String hexValue = Integer.toHexString((int) c);
823
                                // Append 0 if the hex value has less than 4 digits
824
                                for (int j = hexValue.length(); j < 4; j++) {
825
                                        sb.append('0');
826
                                }
827
                                sb.append(hexValue);
828
                        }
829
                }
830
                return sb.toString();
831
        }
832

    
833
        @SuppressWarnings("unchecked")
834
        private String getSystemProperty(final String property, String defaultValue) {
835
                String value =
836
                                (String) AccessController.doPrivileged(new PrivilegedAction() {
837
                                        public Object run() {
838
                                                return System.getProperty(property);
839
                                        }
840
                                });
841
                return value == null ? defaultValue : value;
842
        }
843
}