Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.i18n.app / org.gvsig.i18n.app.mainplugin / src / main / java / org / gvsig / i18n / impl / I18nManagerImpl.java @ 41314

History | View | Annotate | Download (24.6 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
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 3
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
/*
25
 * AUTHORS (In addition to CIT):
26
 * 2008 {DiSiD Technologies}  {New extension for installation and update of text translations}
27
 */
28
package org.gvsig.i18n.impl;
29

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

    
57
import org.gvsig.andami.Launcher;
58
import org.gvsig.andami.PluginServices;
59
import org.gvsig.andami.PluginsLocator;
60
import org.gvsig.andami.PluginsManager;
61
import org.gvsig.andami.config.generate.AndamiConfig;
62
import org.gvsig.app.ApplicationLocator;
63
import org.gvsig.app.ApplicationManager;
64
import org.gvsig.i18n.I18nException;
65
import org.gvsig.i18n.I18nManager;
66
import org.gvsig.i18n.Messages;
67
import org.gvsig.utils.StringUtilities;
68
import org.gvsig.utils.XMLEntity;
69

    
70
/**
71
 * Implementation of the I18nManager interface.
72
 * 
73
 * @author <a href="mailto:dcervera@disid.com">David Cervera</a>
74
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
75
 */
76
public class I18nManagerImpl implements I18nManager {
77

    
78
    private static final String INSTALLED_TRANSLATIONS_HOME_FOLDER = "i18n";
79

    
80
    private static final String LOCALES_FILE_NAME = "locales.csv";
81

    
82
        private static final String CSV_SEPARATOR = ",";
83

    
84
        private static final String I18N_EXTENSION = "org.gvsig.i18n.extension";
85

    
86
        private static final String VARIANT = "variant";
87

    
88
        private static final String COUNTRY = "country";
89

    
90
        private static final String LANGUAGE = "language";
91

    
92
        private static final String REGISTERED_LOCALES_PERSISTENCE =
93
                        "RegisteredLocales";
94

    
95
        private static final I18nManager DEFAULT = new I18nManagerImpl();
96

    
97
        private Set registeredLocales;
98

    
99
        /**
100
         * The list of default reference locales. The last one will be used to get
101
         * all the keys when translating to a new locale.
102
         */
103
        private Locale[] referenceLocales = new Locale[] { ENGLISH, SPANISH };
104

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

    
131
        /**
132
         * Returns the unique instance of the I18nManager.
133
         * 
134
         * @return the unique instance
135
         */
136
        public static I18nManager getInstance() {
137
                return DEFAULT;
138
        }
139

    
140
        public static String capitalize(String text) {
141
                // Convert the first letter to uppercase
142
                String capitalLetter =
143
                                new String(new char[] { Character.toUpperCase(text.charAt(0)) });
144
                return capitalLetter.concat(text.substring(1));
145
        }
146

    
147
        public Locale[] getInstalledLocales() {
148
                if (registeredLocales == null) {
149

    
150
                        XMLEntity child = getRegisteredLocalesPersistence();
151

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

    
177
                return (Locale[]) registeredLocales.toArray(new Locale[registeredLocales.size()]);
178
        }
179

    
180
        public void uninstallLocale(Locale locale) throws I18nException {
181
                if (getCurrentLocale().equals(locale) || isReferenceLocale(locale)) {
182
                        throw new UninstallLocaleException(locale);
183
                }
184

    
185
                if (registeredLocales.remove(locale)) {
186
                        // Remove from the configured locale list
187
                        storeInstalledLocales();
188

    
189
                        // Remove the resource bundle file
190
                        File bundleFile =
191
                                        new File(getResourcesFolder(), getResourceFileName(locale));
192

    
193
                        if (bundleFile.exists()) {
194
                                bundleFile.delete();
195
                        }
196
                }
197
        }
198

    
199
        public String getDisplayName(Locale locale) {
200
                return getDisplayName(locale, locale);
201
        }
202

    
203
        public String getDisplayName(Locale locale, Locale displayLocale) {
204
                StringBuffer name =
205
                                new StringBuffer(getLanguageDisplayName(locale, displayLocale));
206

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

    
212
                if (!isEmpty(locale.getVariant())) {
213
                        name.append(" - ");
214
                        name.append(locale.getDisplayVariant(displayLocale));
215
                }
216

    
217
                name.append(" (").append(locale.toString()).append(")");
218

    
219
                return name.toString();
220
        }
221

    
222
        private boolean isEmpty(String text) {
223
                return text == null || text.trim().length() == 0;
224
        }
225

    
226
        public String getLanguageDisplayName(Locale locale) {
227
                return getLanguageDisplayName(locale, locale);
228
        }
229

    
230
        public String getLanguageDisplayName(Locale locale, Locale displayLocale) {
231

    
232
                String displayName;
233

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

    
251
                return capitalize(displayName);
252
        }
253

    
254
        public Locale getCurrentLocale() {
255
                return Locale.getDefault();
256
        }
257

    
258
        public Locale getDefaultSystemLocale() {
259
                String language, region, country, variant;
260
                language = getSystemProperty("user.language", "en");
261
                // for compatibility, check for old user.region property
262
                region = getSystemProperty("user.region", null);
263

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

    
281
        public void setCurrentLocale(Locale locale) {
282
            PluginsManager pluginsManager = PluginsLocator.getManager();
283
            pluginsManager.setCurrentLocale(locale);
284
        }
285

    
286
        public Locale[] installLocales(File importFile) throws I18nException {
287
                List importLocales = new ArrayList();
288

    
289
                try {
290
                        ZipFile zipFile = new ZipFile(importFile);
291

    
292
                        Map locales = getZipFileNonReferenceLocales(zipFile);
293

    
294
                        if (locales == null || locales.size() == 0) {
295
                                return null;
296
                        }
297

    
298
                        for (Iterator iterator = locales.entrySet().iterator(); iterator.hasNext();) {
299
                                Entry entry = (Entry) iterator.next();
300

    
301
                                String fileName = (String) entry.getKey();
302
                                Locale locale = (Locale) entry.getValue();
303
                                importLocales.add(locale);
304

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

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

    
318
                                File bundleFile = getResourceFile(locale);
319
                                FileWriter fileWriter = new FileWriter(bundleFile);
320
                                BufferedWriter writer = new BufferedWriter(fileWriter);
321

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

    
334
                } catch (Exception ex) {
335
                        throw new InstallLocalesException(importFile, ex);
336
                }
337

    
338
                return (Locale[]) importLocales.toArray(new Locale[importLocales.size()]);
339
        }
340

    
341
        public void exportLocaleForUpdate(Locale locale, Locale referenceLocale,
342
                        File exportFile) throws I18nException {
343

    
344
                exportLocalesForUpdate(new Locale[] { locale }, referenceLocale,
345
                                exportFile);
346
        }
347

    
348
        public void exportLocalesForUpdate(Locale[] locales,
349
                        Locale referenceLocale, File exportFile) throws I18nException {
350

    
351
                exportLocale(locales, new Locale[] { referenceLocale }, exportFile,
352
                                true);
353
        }
354

    
355
        public void exportLocaleForTranslation(Locale locale,
356
                        Locale referenceLocale, File exportFile) throws I18nException {
357

    
358
                exportLocaleForTranslation(locale, new Locale[] { referenceLocale },
359
                                exportFile);
360
        }
361

    
362
        public void exportLocaleForTranslation(Locale locale,
363
                        Locale[] referenceLocales, File exportFile) throws I18nException {
364

    
365
                exportLocale(new Locale[] { locale }, referenceLocales, exportFile,
366
                                false);
367
        }
368

    
369
        public Locale[] getReferenceLocales() {
370
                return referenceLocales;
371
        }
372

    
373
        public void setReferenceLocales(Locale[] referenceLocales) {
374
                this.referenceLocales = referenceLocales;
375
        }
376

    
377
        public void setDefaultLocales(Locale[] defaultLocales) {
378
                this.defaultLocales = defaultLocales;
379
        }
380

    
381
        private void exportLocale(Locale[] locales, Locale[] referenceLocales,
382
                        File exportFile, boolean update) throws I18nException {
383

    
384
                Locale[] refArray =
385
                                getReferenceLocalesToExport(locales, referenceLocales);
386

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

    
395
                try {
396
                        FileOutputStream fos = new FileOutputStream(exportFile);
397
                        ZipOutputStream zipos = new ZipOutputStream(fos);
398

    
399
                        // Create the index file
400
                        writeZipFileLocales(zipos, locales, refArray);
401

    
402
                        PrintStream ps = new PrintStream(zipos);
403
                        Map texts = null;
404

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

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

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

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

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

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

    
497
        private Map getZipFileNonReferenceLocales(ZipFile zipFile)
498
                        throws I18nException {
499
                ZipEntry zipEntry = zipFile.getEntry(LOCALES_FILE_NAME);
500

    
501
                if (zipEntry == null) {
502
                        return null;
503
                }
504

    
505
                Map locales;
506
                try {
507
                        InputStream is = zipFile.getInputStream(zipEntry);
508
                        BufferedReader reader =
509
                                        new BufferedReader(new InputStreamReader(is));
510

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

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

    
566
                                        locales.put(fileName, locale);
567
                                }
568
                        }
569

    
570
                        reader.close();
571
                        is.close();
572
                } catch (IOException ex) {
573
                        throw new ReadCSVLocalesFileException(ex);
574
                }
575

    
576
                return locales;
577
        }
578

    
579
        private void writeZipFileLocales(ZipOutputStream zos, Locale[] locales,
580
                        Locale[] referenceLocales) throws IOException {
581
                ZipEntry zipEntry = new ZipEntry(LOCALES_FILE_NAME);
582

    
583
                zos.putNextEntry(zipEntry);
584
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos));
585

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

    
597
                writer.flush();
598
                zos.closeEntry();
599
        }
600

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

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

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

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

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

    
662
                ps.flush();
663

    
664
                // Close the ZIP entry, the file is complete
665
                zipos.closeEntry();
666
        }
667

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

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

    
684
                ps.flush();
685

    
686
                // Close the ZIP entry, the file is complete
687
                zipos.closeEntry();
688
        }
689

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

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

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

    
709
                // Add the locale country
710
                if (!isEmpty(locale.getCountry())) {
711
                        fileName.append('_').append(locale.getCountry());
712
                }
713

    
714
                // Add the locale variant
715
                if (!isEmpty(locale.getVariant())) {
716
                        fileName.append('_').append(locale.getVariant());
717
                }
718

    
719
                fileName.append(".properties");
720
                return fileName.toString();
721
        }
722

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

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

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

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

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

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

    
787
                // Create the new persistence for the registered languages
788
                localesEntity = new XMLEntity();
789

    
790
                localesEntity.setName(REGISTERED_LOCALES_PERSISTENCE);
791

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

    
800
                        localesEntity.addChild(localeEntity);
801
                }
802

    
803
                getI18nPersistence().addChild(localesEntity);
804
        }
805

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

    
821
                // Next, encode in raw-unicode-escape
822
                return toRawUnicodeEncoded(value);
823
        }
824

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

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