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 @ 40557

History | View | Annotate | Download (24.5 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

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

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

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

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

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

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

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

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

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

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

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

    
93
        private Set registeredLocales;
94

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

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

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

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

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

    
146
                        XMLEntity child = getRegisteredLocalesPersistence();
147

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

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

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

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

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

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

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

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

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

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

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

    
215
                return name.toString();
216
        }
217

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

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

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

    
228
                String displayName;
229

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

    
247
                return capitalize(displayName);
248
        }
249

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

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

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

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

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

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

    
290
                        Map locales = getZipFileNonReferenceLocales(zipFile);
291

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
574
                return locales;
575
        }
576

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

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

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

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

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

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

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

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

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

    
660
                ps.flush();
661

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

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

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

    
682
                ps.flush();
683

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
788
                localesEntity.setName(REGISTERED_LOCALES_PERSISTENCE);
789

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

    
798
                        localesEntity.addChild(localeEntity);
799
                }
800

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

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

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

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

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