Statistics
| Revision:

root / trunk / extensions / extI18n / src / org / gvsig / i18n / impl / I18nManagerImpl.java @ 38062

History | View | Annotate | Download (24.7 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Gobernment (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.util.ArrayList;
41
import java.util.HashMap;
42
import java.util.HashSet;
43
import java.util.Iterator;
44
import java.util.LinkedHashSet;
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.i18n.I18nException;
56
import org.gvsig.i18n.I18nManager;
57
import org.gvsig.i18n.Messages;
58

    
59
import sun.security.action.GetPropertyAction;
60

    
61
import com.iver.andami.Launcher;
62
import com.iver.andami.PluginServices;
63
import com.iver.andami.config.generate.AndamiConfig;
64
import com.iver.utiles.StringUtilities;
65
import com.iver.utiles.XMLEntity;
66

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

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

    
91
    private Set registeredLocales;
92

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

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

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

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

    
141
    /**
142
     * Empty constructor.
143
     */
144
    I18nManagerImpl() {
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 LinkedHashSet(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 = new LinkedHashSet(localesEntity
165
                        .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
178
                .toArray(new Locale[registeredLocales.size()]);
179
    }
180

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

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

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

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

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

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

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

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

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

    
220
        return name.toString();
221
    }
222

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

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

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

    
233
        String displayName;
234

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

    
258
        return capitalize(displayName);
259
    }
260

    
261
    public Locale getCurrentLocale() {
262
        return Locale.getDefault();
263
    }
264

    
265
    public Locale getDefaultSystemLocale() {
266
        String language, region, country, variant;
267
        language = (String) AccessController
268
                .doPrivileged(new GetPropertyAction("user.language", "en"));
269
        // for compatibility, check for old user.region property
270
        region = (String) AccessController.doPrivileged(new GetPropertyAction(
271
                "user.region"));
272
        if (region != null) {
273
            // region can be of form country, country_variant, or _variant
274
            int i = region.indexOf('_');
275
            if (i >= 0) {
276
                country = region.substring(0, i);
277
                variant = region.substring(i + 1);
278
            } else {
279
                country = region;
280
                variant = "";
281
            }
282
        } else {
283
            country = (String) AccessController
284
                    .doPrivileged(new GetPropertyAction("user.country", ""));
285
            variant = (String) AccessController
286
                    .doPrivileged(new GetPropertyAction("user.variant", ""));
287
        }
288
        return new Locale(language, country, variant);
289
    }
290

    
291
    public void setCurrentLocale(Locale locale) {
292
        AndamiConfig config = Launcher.getAndamiConfig();
293
        config.setLocaleLanguage(locale.getLanguage());
294
        config.setLocaleCountry(locale.getCountry());
295
        config.setLocaleVariant(locale.getVariant());
296
    }
297

    
298
    public Locale[] installLocales(File importFile) throws I18nException {
299
        List importLocales = new ArrayList();
300

    
301
        try {
302
            ZipFile zipFile = new ZipFile(importFile);
303

    
304
            Map locales = getZipFileNonReferenceLocales(zipFile);
305

    
306
            if (locales == null || locales.size() == 0) {
307
                return null;
308
            }
309

    
310
            for (Iterator iterator = locales.entrySet().iterator(); iterator
311
                    .hasNext();) {
312
                Entry entry = (Entry) iterator.next();
313

    
314
                String fileName = (String) entry.getKey();
315
                Locale locale = (Locale) entry.getValue();
316
                importLocales.add(locale);
317

    
318
                // Add the locale to the list of installed ones, if it
319
                // is new, otherwise, update the texts.
320
                if (!registeredLocales.contains(locale)) {
321
                    registeredLocales.add(locale);
322
                    storeInstalledLocales();
323
                }
324

    
325
                // Replace the old bundle with the new one
326
                ZipEntry zipEntry = zipFile.getEntry(fileName);
327
                InputStream is = zipFile.getInputStream(zipEntry);
328
                BufferedReader reader = new BufferedReader(
329
                        new InputStreamReader(is));
330

    
331
                File bundleFile = getResourceFile(locale);
332
                FileWriter fileWriter = new FileWriter(bundleFile);
333
                BufferedWriter writer = new BufferedWriter(fileWriter);
334

    
335
                String line;
336
                while ((line = reader.readLine()) != null) {
337
                    writer.write(line);
338
                    writer.write('\n');
339
                }
340
                writer.flush();
341
                writer.close();
342
                fileWriter.close();
343
                reader.close();
344
                is.close();
345
            }
346

    
347
        } catch (Exception ex) {
348
            throw new InstallLocalesException(importFile, ex);
349
        }
350

    
351
        return (Locale[]) importLocales
352
                .toArray(new Locale[importLocales.size()]);
353
    }
354

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

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

    
362
    public void exportLocalesForUpdate(Locale[] locales,
363
            Locale referenceLocale, File exportFile) throws I18nException {
364

    
365
        exportLocale(locales, new Locale[] { referenceLocale }, exportFile,
366
                true);
367
    }
368

    
369
    public void exportLocaleForTranslation(Locale locale,
370
            Locale referenceLocale, File exportFile) throws I18nException {
371

    
372
        exportLocaleForTranslation(locale, new Locale[] { referenceLocale },
373
                exportFile);
374
    }
375

    
376
    public void exportLocaleForTranslation(Locale locale,
377
            Locale[] referenceLocales, File exportFile) throws I18nException {
378

    
379
        exportLocale(new Locale[] { locale }, referenceLocales, exportFile,
380
                false);
381
    }
382

    
383
    public Locale[] getReferenceLocales() {
384
        return referenceLocales;
385
    }
386

    
387
    public void setReferenceLocales(Locale[] referenceLocales) {
388
        this.referenceLocales = referenceLocales;
389
    }
390

    
391
    public void setDefaultLocales(Locale[] defaultLocales) {
392
        this.defaultLocales = defaultLocales;
393
    }
394

    
395
    private void exportLocale(Locale[] locales, Locale[] referenceLocales,
396
            File exportFile, boolean update) throws I18nException {
397

    
398
        Locale[] refArray = getReferenceLocalesToExport(locales,
399
                referenceLocales);
400

    
401
        Set allReferenceKeys = new HashSet();
402
        Map referenceTexts = new HashMap(refArray.length);
403
        for (int i = 0; i < refArray.length; i++) {
404
            Map texts = getAllTexts(refArray[i]);
405
            referenceTexts.put(refArray[i], texts);
406
            allReferenceKeys.addAll(texts.keySet());
407
        }
408

    
409
        try {
410
            FileOutputStream fos = new FileOutputStream(exportFile);
411
            ZipOutputStream zipos = new ZipOutputStream(fos);
412

    
413
                // Create the index file
414
            writeZipFileLocales(zipos, locales, refArray);
415

    
416
                PrintStream ps = new PrintStream(zipos);
417
            Map texts = null;
418

    
419
                if (update) {
420
                // First, export the locales to update
421
                if (locales != null) {
422
                    for (int i = 0; i < locales.length; i++) {
423
                        texts = getAllTexts(locales[i]);
424
                        addPendingKeys(texts, allReferenceKeys);
425
                        putResourceInZip(zipos, ps, texts,
426
                                getResourceFileName(locales[i]));
427
                    }
428
                }
429
            } else { // translate
430
                // First, export the locales to translate, taking the keys from
431
                // the reference locales, but without values
432
                if (locales != null) {
433
                    for (int i = 0; i < locales.length; i++) {
434
                        putResourceInZip(zipos, ps, allReferenceKeys,
435
                                getResourceFileName(locales[i]));
436
                    }
437
                }
438
                }
439

    
440
            // Next, export the reference locales
441
            if (refArray != null) {
442
                for (int i = 0; i < refArray.length; i++) {
443
                    texts = getAllTexts(refArray[i]);
444
                    putResourceInZip(zipos, ps, texts,
445
                            getResourceFileName(refArray[i]));
446
                }
447
                }
448

    
449
            ps.flush();
450
            ps.close();
451
            zipos.close();
452
            fos.close();
453
        } catch (IOException ex) {
454
            throw new ExportLocaleException(locales, ex);
455
        }
456
    }
457

    
458
    /**
459
     * Adds the keys of the set to the map that aren't still contained in it
460
     * with a related empty String.
461
     *
462
     * @param texts
463
     *            the map to complete with the keys
464
     * @param allReferenceKeys
465
     *            the complete key set
466
     */
467
    private void addPendingKeys(Map texts, Set allReferenceKeys) {
468
        for (Iterator iterator = allReferenceKeys.iterator(); iterator
469
                .hasNext();) {
470
            Object key = (Object) iterator.next();
471
            if (!texts.containsKey(key)) {
472
                texts.put(key, "");
473
            }
474
        }
475
    }
476

    
477
    /**
478
     * Returns the list of reference locales to export, as the union of the
479
     * default reference locales list and the one selected as reference. The
480
     * locales to translate or update are extracted from the list.
481
     */
482
    private Locale[] getReferenceLocalesToExport(Locale[] locales,
483
            Locale[] referenceLocalesSelected) {
484
        // The reference locales to export are the default ones plus the
485
        // selected by the user.
486
        Set exportRefLocales = new HashSet(referenceLocales.length);
487
        for (int i = 0; i < referenceLocales.length; i++) {
488
            exportRefLocales.add(referenceLocales[i]);
489
        }
490
        if (referenceLocalesSelected != null) {
491
            for (int i = 0; i < referenceLocalesSelected.length; i++) {
492
                exportRefLocales.add(referenceLocalesSelected[i]);
493
            }
494
        }
495
        if (locales != null) {
496
            for (int i = 0; i < locales.length; i++) {
497
                exportRefLocales.remove(locales[i]);
498
            }
499
        }
500
        Locale[] refArray = (Locale[]) exportRefLocales
501
                .toArray(new Locale[exportRefLocales.size()]);
502
        return refArray;
503
    }
504

    
505
    /**
506
     * Returns all the localized texts and its keys for a locale.
507
     */
508
    private Map getAllTexts(Locale locale) {
509
        return Messages.getAllTexts(locale);
510
    }
511

    
512
    private Map getZipFileNonReferenceLocales(ZipFile zipFile)
513
            throws I18nException {
514
        ZipEntry zipEntry = zipFile.getEntry(LOCALES_FILE_NAME);
515

    
516
        if (zipEntry == null) {
517
            return null;
518
        }
519

    
520
        Map locales;
521
        try {
522
            InputStream is = zipFile.getInputStream(zipEntry);
523
            BufferedReader reader = new BufferedReader(
524
                    new InputStreamReader(is));
525

    
526
            locales = new HashMap(2);
527
            String line;
528
            while ((line = reader.readLine()) != null) {
529
                // The excepted format is:
530
                // FILENAME,LANGUAGE,COUNTRY,VARIANT,IS_REFERENCE
531
                StringTokenizer st = new StringTokenizer(line, CSV_SEPARATOR,
532
                        true);
533
                // First: locale file name (required)
534
                String fileName = st.nextToken();
535
                if (CSV_SEPARATOR.equals(fileName)) {
536
                    throw new LocaleFileNameRequiredException(line);
537
                } else {
538
                    // Read the next separator
539
                    st.nextToken();
540
                }
541
                // Second: the locale language (required)
542
                String language = st.nextToken();
543
                if (CSV_SEPARATOR.equals(language)) {
544
                    throw new LocaleLanguageRequiredException(line);
545
                } else {
546
                    // Read the next separator
547
                    st.nextToken();
548
                }
549
                // Third: the country
550
                String country = st.nextToken();
551
                if (CSV_SEPARATOR.equals(country)) {
552
                    country = null;
553
                } else {
554
                    // Read the next separator
555
                    st.nextToken();
556
                }
557
                // Fourth: the variant
558
                String variant = st.nextToken();
559
                if (CSV_SEPARATOR.equals(variant)) {
560
                    variant = null;
561
                } else {
562
                    // Read the next separator
563
                    st.nextToken();
564
                }
565
                // Fifth: is a reference locale?
566
                String refStr = st.nextToken();
567
                if (CSV_SEPARATOR.equals(refStr)) {
568
                    refStr = null;
569
                }
570

    
571
                // Only add non reference locales
572
                if (refStr != null && !"true".equals(refStr.toLowerCase())) {
573
                    // Variant only accepted if country defined
574
                    if (country == null) {
575
                        variant = null;
576
                    }
577
                    country = country == null ? "" : country;
578
                    variant = variant == null ? "" : variant;
579
                    Locale locale = new Locale(language, country, variant);
580

    
581
                    locales.put(fileName, locale);
582
                }
583
            }
584

    
585
            reader.close();
586
            is.close();
587
        } catch (IOException ex) {
588
            throw new ReadCSVLocalesFileException(ex);
589
        }
590

    
591
        return locales;
592
    }
593

    
594
    private void writeZipFileLocales(ZipOutputStream zos, Locale[] locales,
595
            Locale[] referenceLocales) throws IOException {
596
        ZipEntry zipEntry = new ZipEntry(LOCALES_FILE_NAME);
597

    
598
        zos.putNextEntry(zipEntry);
599
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos));
600

    
601
        if (locales != null) {
602
            for (int i = 0; i < locales.length; i++) {
603
                writeLocaleEntry(locales[i], writer, false);
604
            }
605
        }
606
        if (referenceLocales != null) {
607
            for (int i = 0; i < referenceLocales.length; i++) {
608
                writeLocaleEntry(referenceLocales[i], writer, true);
609
            }
610
        }
611

    
612
        writer.flush();
613
        zos.closeEntry();
614
    }
615

    
616
    /**
617
     * Writes the locale entry into a writer.
618
     *
619
     * @param locale
620
     *            the locale to create the entry for
621
     * @param writer
622
     *            to write to
623
     * @param reference
624
     *            is it is a reference locale or not
625
     * @throws IOException
626
     *             if there is an error creating the locale entry
627
     */
628
    private void writeLocaleEntry(Locale locale, BufferedWriter writer,
629
            boolean reference) throws IOException {
630
        String language = locale.getLanguage();
631
        String country = locale.getCountry();
632
        country = country == null ? "" : country;
633
        String variant = locale.getVariant();
634
        variant = variant == null ? "" : variant;
635

    
636
        writer.write(getResourceFileName(locale));
637
        writer.write(',');
638
        writer.write(language);
639
        writer.write(',');
640
        writer.write(country);
641
        writer.write(',');
642
        writer.write(variant);
643
        writer.write(',');
644
        writer.write(Boolean.toString(reference));
645
        writer.write('\n');
646
    }
647

    
648
    /**
649
     * Returns if a locale is one of the default reference ones.
650
     */
651
    private boolean isReferenceLocale(Locale locale) {
652
        for (int i = 0; i < referenceLocales.length; i++) {
653
            if (referenceLocales[i].equals(locale)) {
654
                return true;
655
            }
656
        }
657
        return false;
658
    }
659

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

    
668
        for (Iterator iterator = texts.entrySet().iterator(); iterator
669
                .hasNext();) {
670
            Entry entry = (Entry) iterator.next();
671
            String keyEncoded = escape((String) entry.getKey(), true);
672
            ps.print(keyEncoded);
673
            ps.print("=");
674
            String valueEncoded = escape((String) entry.getValue(), false);
675
            ps.println(valueEncoded);
676
        }
677

    
678
        ps.flush();
679

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

    
684
    /**
685
     * Puts a new resource file into a Jar file.
686
     */
687
    private void putResourceInZip(ZipOutputStream zipos, PrintStream ps,
688
            Set keys, String resourceFileName) throws IOException {
689
        // Add ZIP entry for the resource bundle file
690
        zipos.putNextEntry(new ZipEntry(resourceFileName));
691

    
692
        for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
693
            String value = (String) iterator.next();
694
            String keyEncoded = escape(value, true);
695
            ps.print(keyEncoded);
696
            ps.print("=");
697
            ps.println();
698
        }
699

    
700
        ps.flush();
701

    
702
        // Close the ZIP entry, the file is complete
703
        zipos.closeEntry();
704
    }
705

    
706
    /**
707
     * Returns the file which contains the translations for a locale.
708
     */
709
    private File getResourceFile(Locale locale) {
710
        return new File(getResourcesFolder(), getResourceFileName(locale));
711
    }
712

    
713
    /**
714
     * Returns the name of the file which contains the translations for a
715
     * locale.
716
     */
717
    private String getResourceFileName(Locale locale) {
718
        StringBuffer fileName = new StringBuffer("text");
719

    
720
        // Spanish without country is the default locale
721
        if (!(isEmpty(locale.getCountry()) && "es".equals(locale.getLanguage()))) {
722
            fileName.append('_').append(locale.getLanguage());
723
        }
724

    
725
        // Add the locale country
726
        if (!isEmpty(locale.getCountry())) {
727
            fileName.append('_').append(locale.getCountry());
728
        }
729

    
730
        // Add the locale variant
731
        if (!isEmpty(locale.getVariant())) {
732
            fileName.append('_').append(locale.getVariant());
733
        }
734

    
735
        fileName.append(".properties");
736
        return fileName.toString();
737
    }
738

    
739
    /**
740
     * Returns the folder where to store the resource bundle files.
741
     */
742
    private File getResourcesFolder() {
743
        return PluginServices.getPluginServices("com.iver.cit.gvsig")
744
                .getPluginDirectory();
745
    }
746

    
747
    /**
748
     * Returns the child XMLEntity with the RegisteredLocales.
749
     */
750
    private XMLEntity getRegisteredLocalesPersistence() {
751
        XMLEntity entity = getI18nPersistence();
752
        XMLEntity child = null;
753
        for (int i = entity.getChildrenCount() - 1; i >= 0; i--) {
754
            XMLEntity tmpchild = entity.getChild(i);
755
            if (tmpchild.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
756
                child = tmpchild;
757
                break;
758
            }
759
        }
760
        return child;
761
    }
762

    
763
    /**
764
     * Returns the I18n Plugin persistence.
765
     */
766
    private XMLEntity getI18nPersistence() {
767
        XMLEntity entity = PluginServices.getPluginServices(I18N_EXTENSION)
768
                .getPersistentXML();
769
        return entity;
770
    }
771

    
772
    /**
773
     * Returns the list of default locales bundled with gvSIG.
774
     */
775
    private Locale[] getDefaultLocales() {
776
        return defaultLocales;
777
    }
778

    
779
    /**
780
     * Stores the list of installed locales into the plugin persistence.
781
     */
782
    private void storeInstalledLocales() {
783
        XMLEntity localesEntity = getRegisteredLocalesPersistence();
784

    
785
        // Remove the previous list of registered languages
786
        if (localesEntity != null) {
787
            XMLEntity i18nPersistence = getI18nPersistence();
788
            for (int i = i18nPersistence.getChildrenCount() - 1; i >= 0; i--) {
789
                XMLEntity child = i18nPersistence.getChild(i);
790
                if (child.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
791
                    i18nPersistence.removeChild(i);
792
                    break;
793
                }
794
            }
795
        }
796

    
797
        // Create the new persistence for the registered languages
798
        localesEntity = new XMLEntity();
799

    
800
        localesEntity.setName(REGISTERED_LOCALES_PERSISTENCE);
801

    
802
        for (Iterator iterator = registeredLocales.iterator(); iterator
803
                .hasNext();) {
804
            Locale locale = (Locale) iterator.next();
805
            XMLEntity localeEntity = new XMLEntity();
806
            localeEntity.setName(locale.getDisplayName());
807
            localeEntity.putProperty(LANGUAGE, locale.getLanguage());
808
            localeEntity.putProperty(COUNTRY, locale.getCountry());
809
            localeEntity.putProperty(VARIANT, locale.getVariant());
810

    
811
            localesEntity.addChild(localeEntity);
812
        }
813

    
814
        getI18nPersistence().addChild(localesEntity);
815
    }
816

    
817
    private String escape(String value, boolean replaceBlanks) {
818
        // First replace non printable characters
819
        if (replaceBlanks) {
820
            value = StringUtilities.replace(value, " ", "\\ ");
821
        }
822
        value = StringUtilities.replace(value, ":", "\\:");
823
        value = StringUtilities.replace(value, "\n", "\\n");
824
        value = StringUtilities.replace(value, "\t", "\\t");
825
        value = StringUtilities.replace(value, "\b", "\\b");
826
        value = StringUtilities.replace(value, "\f", "\\f");
827
        value = StringUtilities.replace(value, "\r", "\\r");
828
        // value = StringUtilities.replace(value, "\\", "\\\\");
829
        // value = StringUtilities.replace(value, "\'", "\\\'");
830
        // value = StringUtilities.replace(value, "\"", "\\\"");
831

    
832
        // Next, encode in raw-unicode-escape
833
        return toRawUnicodeEncoded(value);
834
    }
835

    
836
    private String toRawUnicodeEncoded(String value) {
837
        StringBuffer sb = new StringBuffer();
838
        for (int i = 0; i < value.length(); i++) {
839
            char c = value.charAt(i);
840
            if (c <= 0x80) {
841
                sb.append(c);
842
            } else {
843
                sb.append("\\u");
844
                String hexValue = Integer.toHexString((int) c);
845
                // Append 0 if the hex value has less than 4 digits
846
                for (int j = hexValue.length(); j < 4; j++) {
847
                    sb.append('0');
848
                }
849
                sb.append(hexValue);
850
            }
851
        }
852
        return sb.toString();
853
    }
854
}