Statistics
| Revision:

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

History | View | Annotate | Download (24.5 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.PluginsLocator;
58
import org.gvsig.andami.config.generate.AndamiConfig;
59
import org.gvsig.i18n.I18nException;
60
import org.gvsig.i18n.I18nManager;
61
import org.gvsig.i18n.Messages;
62
import org.gvsig.utils.StringUtilities;
63
import org.gvsig.utils.XMLEntity;
64

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

    
73
    private static final String INSTALLED_TRANSLATIONS_HOME_FOLDER = "i18n";
74

    
75
    private static final String LOCALES_FILE_NAME = "locales.csv";
76

    
77
        private static final String CSV_SEPARATOR = ",";
78

    
79
        private static final String I18N_EXTENSION = "org.gvsig.i18n.extension";
80

    
81
        private static final String VARIANT = "variant";
82

    
83
        private static final String COUNTRY = "country";
84

    
85
        private static final String LANGUAGE = "language";
86

    
87
        private static final String REGISTERED_LOCALES_PERSISTENCE =
88
                        "RegisteredLocales";
89

    
90
        private static final I18nManager DEFAULT = new I18nManagerImpl();
91

    
92
        private Set registeredLocales;
93

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

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

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

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

    
142
        public Locale[] getInstalledLocales() {
143
                if (registeredLocales == null) {
144

    
145
                        XMLEntity child = getRegisteredLocalesPersistence();
146

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

    
172
                return (Locale[]) registeredLocales.toArray(new Locale[registeredLocales.size()]);
173
        }
174

    
175
        public void uninstallLocale(Locale locale) throws I18nException {
176
                if (getCurrentLocale().equals(locale) || isReferenceLocale(locale)) {
177
                        throw new UninstallLocaleException(locale);
178
                }
179

    
180
                if (registeredLocales.remove(locale)) {
181
                        // Remove from the configured locale list
182
                        storeInstalledLocales();
183

    
184
                        // Remove the resource bundle file
185
                        File bundleFile =
186
                                        new File(getResourcesFolder(), getResourceFileName(locale));
187

    
188
                        if (bundleFile.exists()) {
189
                                bundleFile.delete();
190
                        }
191
                }
192
        }
193

    
194
        public String getDisplayName(Locale locale) {
195
                return getDisplayName(locale, locale);
196
        }
197

    
198
        public String getDisplayName(Locale locale, Locale displayLocale) {
199
                StringBuffer name =
200
                                new StringBuffer(getLanguageDisplayName(locale, displayLocale));
201

    
202
                if (!isEmpty(locale.getCountry())) {
203
                        name.append(" - ");
204
                        name.append(locale.getDisplayCountry(displayLocale));
205
                }
206

    
207
                if (!isEmpty(locale.getVariant())) {
208
                        name.append(" - ");
209
                        name.append(locale.getDisplayVariant(displayLocale));
210
                }
211

    
212
                name.append(" (").append(locale.toString()).append(")");
213

    
214
                return name.toString();
215
        }
216

    
217
        private boolean isEmpty(String text) {
218
                return text == null || text.trim().length() == 0;
219
        }
220

    
221
        public String getLanguageDisplayName(Locale locale) {
222
                return getLanguageDisplayName(locale, locale);
223
        }
224

    
225
        public String getLanguageDisplayName(Locale locale, Locale displayLocale) {
226

    
227
                String displayName;
228

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

    
246
                return capitalize(displayName);
247
        }
248

    
249
        public Locale getCurrentLocale() {
250
                return Locale.getDefault();
251
        }
252

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

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

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

    
283
        public Locale[] installLocales(File importFile) throws I18nException {
284
                List importLocales = new ArrayList();
285

    
286
                try {
287
                        ZipFile zipFile = new ZipFile(importFile);
288

    
289
                        Map locales = getZipFileNonReferenceLocales(zipFile);
290

    
291
                        if (locales == null || locales.size() == 0) {
292
                                return null;
293
                        }
294

    
295
                        for (Iterator iterator = locales.entrySet().iterator(); iterator.hasNext();) {
296
                                Entry entry = (Entry) iterator.next();
297

    
298
                                String fileName = (String) entry.getKey();
299
                                Locale locale = (Locale) entry.getValue();
300
                                importLocales.add(locale);
301

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

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

    
315
                                File bundleFile = getResourceFile(locale);
316
                                FileWriter fileWriter = new FileWriter(bundleFile);
317
                                BufferedWriter writer = new BufferedWriter(fileWriter);
318

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

    
331
                } catch (Exception ex) {
332
                        throw new InstallLocalesException(importFile, ex);
333
                }
334

    
335
                return (Locale[]) importLocales.toArray(new Locale[importLocales.size()]);
336
        }
337

    
338
        public void exportLocaleForUpdate(Locale locale, Locale referenceLocale,
339
                        File exportFile) throws I18nException {
340

    
341
                exportLocalesForUpdate(new Locale[] { locale }, referenceLocale,
342
                                exportFile);
343
        }
344

    
345
        public void exportLocalesForUpdate(Locale[] locales,
346
                        Locale referenceLocale, File exportFile) throws I18nException {
347

    
348
                exportLocale(locales, new Locale[] { referenceLocale }, exportFile,
349
                                true);
350
        }
351

    
352
        public void exportLocaleForTranslation(Locale locale,
353
                        Locale referenceLocale, File exportFile) throws I18nException {
354

    
355
                exportLocaleForTranslation(locale, new Locale[] { referenceLocale },
356
                                exportFile);
357
        }
358

    
359
        public void exportLocaleForTranslation(Locale locale,
360
                        Locale[] referenceLocales, File exportFile) throws I18nException {
361

    
362
                exportLocale(new Locale[] { locale }, referenceLocales, exportFile,
363
                                false);
364
        }
365

    
366
        public Locale[] getReferenceLocales() {
367
                return referenceLocales;
368
        }
369

    
370
        public void setReferenceLocales(Locale[] referenceLocales) {
371
                this.referenceLocales = referenceLocales;
372
        }
373

    
374
        public void setDefaultLocales(Locale[] defaultLocales) {
375
                this.defaultLocales = defaultLocales;
376
        }
377

    
378
        private void exportLocale(Locale[] locales, Locale[] referenceLocales,
379
                        File exportFile, boolean update) throws I18nException {
380

    
381
                Locale[] refArray =
382
                                getReferenceLocalesToExport(locales, referenceLocales);
383

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

    
392
                try {
393
                        FileOutputStream fos = new FileOutputStream(exportFile);
394
                        ZipOutputStream zipos = new ZipOutputStream(fos);
395

    
396
                        // Create the index file
397
                        writeZipFileLocales(zipos, locales, refArray);
398

    
399
                        PrintStream ps = new PrintStream(zipos);
400
                        Map texts = null;
401

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

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

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

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

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

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

    
494
        private Map getZipFileNonReferenceLocales(ZipFile zipFile)
495
                        throws I18nException {
496
                ZipEntry zipEntry = zipFile.getEntry(LOCALES_FILE_NAME);
497

    
498
                if (zipEntry == null) {
499
                        return null;
500
                }
501

    
502
                Map locales;
503
                try {
504
                        InputStream is = zipFile.getInputStream(zipEntry);
505
                        BufferedReader reader =
506
                                        new BufferedReader(new InputStreamReader(is));
507

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

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

    
563
                                        locales.put(fileName, locale);
564
                                }
565
                        }
566

    
567
                        reader.close();
568
                        is.close();
569
                } catch (IOException ex) {
570
                        throw new ReadCSVLocalesFileException(ex);
571
                }
572

    
573
                return locales;
574
        }
575

    
576
        private void writeZipFileLocales(ZipOutputStream zos, Locale[] locales,
577
                        Locale[] referenceLocales) throws IOException {
578
                ZipEntry zipEntry = new ZipEntry(LOCALES_FILE_NAME);
579

    
580
                zos.putNextEntry(zipEntry);
581
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos));
582

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

    
594
                writer.flush();
595
                zos.closeEntry();
596
        }
597

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

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

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

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

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

    
659
                ps.flush();
660

    
661
                // Close the ZIP entry, the file is complete
662
                zipos.closeEntry();
663
        }
664

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

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

    
681
                ps.flush();
682

    
683
                // Close the ZIP entry, the file is complete
684
                zipos.closeEntry();
685
        }
686

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

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

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

    
706
                // Add the locale country
707
                if (!isEmpty(locale.getCountry())) {
708
                        fileName.append('_').append(locale.getCountry());
709
                }
710

    
711
                // Add the locale variant
712
                if (!isEmpty(locale.getVariant())) {
713
                        fileName.append('_').append(locale.getVariant());
714
                }
715

    
716
                fileName.append(".properties");
717
                return fileName.toString();
718
        }
719

    
720
        /**
721
         * Returns the folder where to store the resource bundle files.
722
         */
723
    private File getResourcesFolder() {
724
        File i18nFolder =
725
            new File(PluginsLocator.getManager().getApplicationHomeFolder(),
726
                INSTALLED_TRANSLATIONS_HOME_FOLDER);
727
        if (!i18nFolder.exists()) {
728
            i18nFolder.mkdirs();
729
        }
730
        return i18nFolder;
731
    }
732

    
733
        /**
734
         * Returns the child XMLEntity with the RegisteredLocales.
735
         */
736
        private XMLEntity getRegisteredLocalesPersistence() {
737
                XMLEntity entity = getI18nPersistence();
738
                XMLEntity child = null;
739
                for (int i = entity.getChildrenCount() - 1; i >= 0; i--) {
740
                        XMLEntity tmpchild = entity.getChild(i);
741
                        if (tmpchild.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
742
                                child = tmpchild;
743
                                break;
744
                        }
745
                }
746
                return child;
747
        }
748

    
749
        /**
750
         * Returns the I18n Plugin persistence.
751
         */
752
        private XMLEntity getI18nPersistence() {
753
                XMLEntity entity =
754
                                PluginServices.getPluginServices(I18N_EXTENSION)
755
                                                .getPersistentXML();
756
                return entity;
757
        }
758

    
759
        /**
760
         * Returns the list of default locales bundled with gvSIG.
761
         */
762
        private Locale[] getDefaultLocales() {
763
                return defaultLocales;
764
        }
765

    
766
        /**
767
         * Stores the list of installed locales into the plugin persistence.
768
         */
769
        private void storeInstalledLocales() {
770
                XMLEntity localesEntity = getRegisteredLocalesPersistence();
771

    
772
                // Remove the previous list of registered languages
773
                if (localesEntity != null) {
774
                        XMLEntity i18nPersistence = getI18nPersistence();
775
                        for (int i = i18nPersistence.getChildrenCount() - 1; i >= 0; i--) {
776
                                XMLEntity child = i18nPersistence.getChild(i);
777
                                if (child.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
778
                                        i18nPersistence.removeChild(i);
779
                                        break;
780
                                }
781
                        }
782
                }
783

    
784
                // Create the new persistence for the registered languages
785
                localesEntity = new XMLEntity();
786

    
787
                localesEntity.setName(REGISTERED_LOCALES_PERSISTENCE);
788

    
789
                for (Iterator iterator = registeredLocales.iterator(); iterator.hasNext();) {
790
                        Locale locale = (Locale) iterator.next();
791
                        XMLEntity localeEntity = new XMLEntity();
792
                        localeEntity.setName(locale.getDisplayName());
793
                        localeEntity.putProperty(LANGUAGE, locale.getLanguage());
794
                        localeEntity.putProperty(COUNTRY, locale.getCountry());
795
                        localeEntity.putProperty(VARIANT, locale.getVariant());
796

    
797
                        localesEntity.addChild(localeEntity);
798
                }
799

    
800
                getI18nPersistence().addChild(localesEntity);
801
        }
802

    
803
        private String escape(String value, boolean replaceBlanks) {
804
                // First replace non printable characters
805
                if (replaceBlanks) {
806
                        value = StringUtilities.replace(value, " ", "\\ ");
807
                }
808
                value = StringUtilities.replace(value, ":", "\\:");
809
                value = StringUtilities.replace(value, "\n", "\\n");
810
                value = StringUtilities.replace(value, "\t", "\\t");
811
                value = StringUtilities.replace(value, "\b", "\\b");
812
                value = StringUtilities.replace(value, "\f", "\\f");
813
                value = StringUtilities.replace(value, "\r", "\\r");
814
                // value = StringUtilities.replace(value, "\\", "\\\\");
815
                // value = StringUtilities.replace(value, "\'", "\\\'");
816
                // value = StringUtilities.replace(value, "\"", "\\\"");
817

    
818
                // Next, encode in raw-unicode-escape
819
                return toRawUnicodeEncoded(value);
820
        }
821

    
822
        private String toRawUnicodeEncoded(String value) {
823
                StringBuffer sb = new StringBuffer();
824
                for (int i = 0; i < value.length(); i++) {
825
                        char c = value.charAt(i);
826
                        if (c <= 0x80) {
827
                                sb.append(c);
828
                        } else {
829
                                sb.append("\\u");
830
                                String hexValue = Integer.toHexString((int) c);
831
                                // Append 0 if the hex value has less than 4 digits
832
                                for (int j = hexValue.length(); j < 4; j++) {
833
                                        sb.append('0');
834
                                }
835
                                sb.append(hexValue);
836
                        }
837
                }
838
                return sb.toString();
839
        }
840

    
841
        @SuppressWarnings("unchecked")
842
        private String getSystemProperty(final String property, String defaultValue) {
843
                String value =
844
                                (String) AccessController.doPrivileged(new PrivilegedAction() {
845
                                        public Object run() {
846
                                                return System.getProperty(property);
847
                                        }
848
                                });
849
                return value == null ? defaultValue : value;
850
        }
851
}