001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.tools;
016    
017    import com.liferay.portal.kernel.io.OutputStreamWriter;
018    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
019    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedWriter;
020    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
021    import com.liferay.portal.kernel.microsofttranslator.MicrosoftTranslatorException;
022    import com.liferay.portal.kernel.util.FileUtil;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.NumericalStringComparator;
025    import com.liferay.portal.kernel.util.PropertiesUtil;
026    import com.liferay.portal.kernel.util.StringPool;
027    import com.liferay.portal.kernel.util.StringUtil;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.webcache.WebCacheItem;
030    import com.liferay.portal.util.InitUtil;
031    import com.liferay.portlet.translator.model.Translation;
032    import com.liferay.portlet.translator.util.TranslationWebCacheItem;
033    
034    import java.io.File;
035    import java.io.FileInputStream;
036    import java.io.FileOutputStream;
037    import java.io.FileWriter;
038    import java.io.IOException;
039    import java.io.InputStream;
040    
041    import java.util.Map;
042    import java.util.Properties;
043    import java.util.Set;
044    import java.util.TreeSet;
045    
046    /**
047     * @author Brian Wing Shun Chan
048     */
049    public class LangBuilder {
050    
051            public static final String AUTOMATIC_COPY = " (Automatic Copy)";
052    
053            public static final String AUTOMATIC_TRANSLATION =
054                    " (Automatic Translation)";
055    
056            public static void main(String[] args) {
057                    Map<String, String> arguments = ArgumentsUtil.parseArguments(args);
058    
059                    System.setProperty("line.separator", StringPool.NEW_LINE);
060    
061                    InitUtil.initWithSpring();
062    
063                    String langDir = arguments.get("lang.dir");
064                    String langFile = arguments.get("lang.file");
065                    boolean langPlugin = GetterUtil.getBoolean(
066                            arguments.get("lang.plugin"));
067                    boolean langTranslate = GetterUtil.getBoolean(
068                            arguments.get("lang.translate"), true);
069    
070                    try {
071                            new LangBuilder(langDir, langFile, langPlugin, langTranslate);
072                    }
073                    catch (Exception e) {
074                            e.printStackTrace();
075                    }
076            }
077    
078            public LangBuilder(
079                            String langDir, String langFile, boolean langPlugin,
080                            boolean langTranslate)
081                    throws Exception {
082    
083                    _langDir = langDir;
084                    _langFile = langFile;
085                    _langTranslate = langTranslate;
086    
087                    if (langPlugin) {
088                            _portalLanguageProperties = new Properties();
089    
090                            Class<?> clazz = getClass();
091    
092                            ClassLoader classLoader = clazz.getClassLoader();
093    
094                            InputStream inputStream = classLoader.getResourceAsStream(
095                                    "content/Language.properties");
096    
097                            _portalLanguageProperties.load(inputStream);
098                    }
099    
100                    File renameKeysFile = new File(_langDir + "/rename.properties");
101    
102                    if (renameKeysFile.exists()) {
103                            _renameKeys = PropertiesUtil.load(FileUtil.read(renameKeysFile));
104                    }
105    
106                    String content = _orderProperties(
107                            new File(_langDir + "/" + _langFile + ".properties"));
108    
109                    // Locales that are not invoked by _createProperties should still be
110                    // rewritten to use the rignt line separator
111    
112                    _orderProperties(
113                            new File(_langDir + "/" + _langFile + "_en_GB.properties"));
114    
115                    _createProperties(content, "ar"); // Arabic
116                    _createProperties(content, "eu"); // Basque
117                    _createProperties(content, "bg"); // Bulgarian
118                    _createProperties(content, "ca"); // Catalan
119                    _createProperties(content, "zh_CN"); // Chinese (China)
120                    _createProperties(content, "zh_TW"); // Chinese (Taiwan)
121                    _createProperties(content, "hr"); // Croatian
122                    _createProperties(content, "cs"); // Czech
123                    _createProperties(content, "da"); // Danish
124                    _createProperties(content, "nl"); // Dutch (Netherlands)
125                    _createProperties(content, "nl_BE", "nl"); // Dutch (Belgium)
126                    _createProperties(content, "et"); // Estonian
127                    _createProperties(content, "fi"); // Finnish
128                    _createProperties(content, "fr"); // French
129                    _createProperties(content, "gl"); // Galician
130                    _createProperties(content, "de"); // German
131                    _createProperties(content, "el"); // Greek
132                    _createProperties(content, "iw"); // Hebrew
133                    _createProperties(content, "hi_IN"); // Hindi (India)
134                    _createProperties(content, "hu"); // Hungarian
135                    _createProperties(content, "in"); // Indonesian
136                    _createProperties(content, "it"); // Italian
137                    _createProperties(content, "ja"); // Japanese
138                    _createProperties(content, "ko"); // Korean
139                    _createProperties(content, "lo"); // Lao
140                    _createProperties(content, "nb"); // Norwegian Bokmål
141                    _createProperties(content, "fa"); // Persian
142                    _createProperties(content, "pl"); // Polish
143                    _createProperties(content, "pt_BR"); // Portuguese (Brazil)
144                    _createProperties(content, "pt_PT", "pt_BR"); // Portuguese (Portugal)
145                    _createProperties(content, "ro"); // Romanian
146                    _createProperties(content, "ru"); // Russian
147                    _createProperties(content, "sr_RS"); // Serbian (Cyrillic)
148                    _createProperties(content, "sr_RS_latin"); // Serbian (Latin)
149                    _createProperties(content, "sk"); // Slovak
150                    _createProperties(content, "sl"); // Slovene
151                    _createProperties(content, "es"); // Spanish
152                    _createProperties(content, "sv"); // Swedish
153                    _createProperties(content, "tr"); // Turkish
154                    _createProperties(content, "uk"); // Ukrainian
155                    _createProperties(content, "vi"); // Vietnamese
156            }
157    
158            private void _createProperties(String content, String languageId)
159                    throws IOException {
160    
161                    _createProperties(content, languageId, null);
162            }
163    
164            private void _createProperties(
165                            String content, String languageId, String parentLanguageId)
166                    throws IOException {
167    
168                    File propertiesFile = new File(
169                            _langDir + "/" + _langFile + "_" + languageId + ".properties");
170    
171                    Properties properties = new Properties();
172    
173                    if (propertiesFile.exists()) {
174                            properties = PropertiesUtil.load(
175                                    new FileInputStream(propertiesFile), StringPool.UTF8);
176                    }
177    
178                    Properties parentProperties = null;
179    
180                    if (parentLanguageId != null) {
181                            File parentPropertiesFile = new File(
182                                    _langDir + "/" + _langFile + "_" + parentLanguageId +
183                                            ".properties");
184    
185                            if (parentPropertiesFile.exists()) {
186                                    parentProperties = new Properties();
187    
188                                    parentProperties = PropertiesUtil.load(
189                                            new FileInputStream(parentPropertiesFile), StringPool.UTF8);
190                            }
191                    }
192    
193                    String translationId = "en_" + languageId;
194    
195                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
196                            new UnsyncStringReader(content));
197                    UnsyncBufferedWriter unsyncBufferedWriter = new UnsyncBufferedWriter(
198                            new OutputStreamWriter(
199                                    new FileOutputStream(propertiesFile), StringPool.UTF8));
200    
201                    boolean firstLine = true;
202                    int state = 0;
203    
204                    String line = null;
205    
206                    while ((line = unsyncBufferedReader.readLine()) != null) {
207                            line = line.trim();
208    
209                            int pos = line.indexOf("=");
210    
211                            if (pos != -1) {
212                                    String key = line.substring(0, pos);
213                                    String value = line.substring(pos + 1);
214    
215                                    if (((state == 1) && !key.startsWith("lang.")) ||
216                                            ((state == 2) && !key.startsWith("javax.portlet.")) ||
217                                            ((state == 3) && !key.startsWith("category.")) ||
218                                            ((state == 4) && !key.startsWith("model.resource.")) ||
219                                            ((state == 5) && !key.startsWith("action.")) ||
220                                            ((state == 7) && !key.startsWith("currency.")) ||
221                                            ((state != 7) && key.startsWith("currency."))) {
222    
223                                            throw new RuntimeException(
224                                                    "File " + languageId + " with state " + state +
225                                                            " has key " + key);
226                                    }
227    
228                                    String translatedText = properties.getProperty(key);
229    
230                                    if ((translatedText == null) && (parentProperties != null)) {
231                                            translatedText = parentProperties.getProperty(key);
232                                    }
233    
234                                    if ((translatedText == null) && (_renameKeys != null)) {
235                                            String renameKey = _renameKeys.getProperty(key);
236    
237                                            if (renameKey != null) {
238                                                    translatedText = properties.getProperty(key);
239    
240                                                    if ((translatedText == null) &&
241                                                            (parentProperties != null)) {
242    
243                                                            translatedText = parentProperties.getProperty(key);
244                                                    }
245                                            }
246                                    }
247    
248                                    if (translatedText != null) {
249                                            if (translatedText.contains("Babel Fish") ||
250                                                    translatedText.contains("Yahoo! - 999")) {
251    
252                                                    translatedText = "";
253                                            }
254                                            else if (translatedText.endsWith(AUTOMATIC_COPY)) {
255                                                    translatedText = value + AUTOMATIC_COPY;
256                                            }
257                                    }
258    
259                                    if ((translatedText == null) || translatedText.equals("")) {
260                                            if (line.contains("{") || line.contains("<")) {
261                                                    translatedText = value + AUTOMATIC_COPY;
262                                            }
263                                            else if (line.contains("[")) {
264                                                    pos = line.indexOf("[");
265    
266                                                    String baseKey = line.substring(0, pos);
267    
268                                                    translatedText =
269                                                            properties.getProperty(baseKey) + AUTOMATIC_COPY;
270                                            }
271                                            else if (key.equals("lang.dir")) {
272                                                    translatedText = "ltr";
273                                            }
274                                            else if (key.equals("lang.line.begin")) {
275                                                    translatedText = "left";
276                                            }
277                                            else if (key.equals("lang.line.end")) {
278                                                    translatedText = "right";
279                                            }
280                                            else if (translationId.equals("en_el") &&
281                                                             (key.equals("enabled") || key.equals("on") ||
282                                                              key.equals("on-date"))) {
283    
284                                                    translatedText = "";
285                                            }
286                                            else if (translationId.equals("en_es") &&
287                                                             key.equals("am")) {
288    
289                                                    translatedText = "";
290                                            }
291                                            else if (translationId.equals("en_it") &&
292                                                             key.equals("am")) {
293    
294                                                    translatedText = "";
295                                            }
296                                            else if (translationId.equals("en_ja") &&
297                                                             (key.equals("any") || key.equals("anytime") ||
298                                                              key.equals("down") || key.equals("on") ||
299                                                              key.equals("on-date") || key.equals("the"))) {
300    
301                                                    translatedText = "";
302                                            }
303                                            else if (translationId.equals("en_ko") &&
304                                                             key.equals("the")) {
305    
306                                                    translatedText = "";
307                                            }
308                                            else {
309                                                    translatedText = _translate(
310                                                            translationId, key, value, 0);
311    
312                                                    if (Validator.isNull(translatedText)) {
313                                                            translatedText = value + AUTOMATIC_COPY;
314                                                    }
315                                                    else {
316                                                            translatedText =
317                                                                    translatedText + AUTOMATIC_TRANSLATION;
318                                                    }
319                                            }
320                                    }
321    
322                                    if (Validator.isNotNull(translatedText)) {
323                                            if (translatedText.contains("Babel Fish") ||
324                                                    translatedText.contains("Yahoo! - 999")) {
325    
326                                                    throw new IOException(
327                                                            "IP was blocked because of over usage. Please " +
328                                                                    "use another IP.");
329                                            }
330    
331                                            translatedText = _fixTranslation(translatedText);
332    
333                                            if (firstLine) {
334                                                    firstLine = false;
335                                            }
336                                            else {
337                                                    unsyncBufferedWriter.newLine();
338                                            }
339    
340                                            unsyncBufferedWriter.write(key + "=" + translatedText);
341    
342                                            unsyncBufferedWriter.flush();
343                                    }
344                            }
345                            else {
346                                    if (line.startsWith("## Language settings")) {
347                                            if (state == 1) {
348                                                    throw new RuntimeException(languageId);
349                                            }
350    
351                                            state = 1;
352                                    }
353                                    else if (line.startsWith(
354                                                            "## Portlet descriptions and titles")) {
355    
356                                            if (state == 2) {
357                                                    throw new RuntimeException(languageId);
358                                            }
359    
360                                            state = 2;
361                                    }
362                                    else if (line.startsWith("## Category titles")) {
363                                            if (state == 3) {
364                                                    throw new RuntimeException(languageId);
365                                            }
366    
367                                            state = 3;
368                                    }
369                                    else if (line.startsWith("## Model resources")) {
370                                            if (state == 4) {
371                                                    throw new RuntimeException(languageId);
372                                            }
373    
374                                            state = 4;
375                                    }
376                                    else if (line.startsWith("## Action names")) {
377                                            if (state == 5) {
378                                                    throw new RuntimeException(languageId);
379                                            }
380    
381                                            state = 5;
382                                    }
383                                    else if (line.startsWith("## Messages")) {
384                                            if (state == 6) {
385                                                    throw new RuntimeException(languageId);
386                                            }
387    
388                                            state = 6;
389                                    }
390                                    else if (line.startsWith("## Currency")) {
391                                            if (state == 7) {
392                                                    throw new RuntimeException(languageId);
393                                            }
394    
395                                            state = 7;
396                                    }
397    
398                                    if (firstLine) {
399                                            firstLine = false;
400                                    }
401                                    else {
402                                            unsyncBufferedWriter.newLine();
403                                    }
404    
405                                    unsyncBufferedWriter.write(line);
406    
407                                    unsyncBufferedWriter.flush();
408                            }
409                    }
410    
411                    unsyncBufferedReader.close();
412                    unsyncBufferedWriter.close();
413            }
414    
415            private String _fixEnglishTranslation(String key, String value) {
416                    if (value.contains(" this ")) {
417                            if (value.contains(".") || value.contains("?") ||
418                                    value.contains(":") ||
419                                    key.equals("the-url-of-the-page-comparing-this-page-content-with-the-previous-version")) {
420                            }
421                            else {
422                                    value = StringUtil.replace(value, " this ", " This ");
423                            }
424                    }
425                    else {
426                            value = StringUtil.replace(value, " From ", " from ");
427                    }
428    
429                    return value;
430            }
431    
432            private String _fixTranslation(String value) {
433                    value = StringUtil.replace(
434                            value.trim(),
435                            new String[] {
436                                    "  ", "<b>", "</b>", "<i>", "</i>", " url ", "&#39;", "&#39 ;",
437                                    "&quot;", "&quot ;"
438                            },
439                            new String[] {
440                                    " ", "<strong>", "</strong>", "<em>", "</em>", " URL ", "\'",
441                                    "\'", "\"", "\""
442                            });
443    
444                    return value;
445            }
446    
447            private String _orderProperties(File propertiesFile) throws IOException {
448                    if (!propertiesFile.exists()) {
449                            return null;
450                    }
451    
452                    String content = FileUtil.read(propertiesFile);
453    
454                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
455                            new UnsyncStringReader(content));
456                    UnsyncBufferedWriter unsyncBufferedWriter = new UnsyncBufferedWriter(
457                            new FileWriter(propertiesFile));
458    
459                    Set<String> messages = new TreeSet<String>(
460                            new NumericalStringComparator(true, true));
461    
462                    boolean begin = false;
463                    boolean firstLine = true;
464    
465                    String line = null;
466    
467                    while ((line = unsyncBufferedReader.readLine()) != null) {
468                            int pos = line.indexOf("=");
469    
470                            if (pos != -1) {
471                                    String key = line.substring(0, pos);
472    
473                                    String value = _fixTranslation(line.substring(pos + 1));
474    
475                                    value = _fixEnglishTranslation(key, value);
476    
477                                    if (_portalLanguageProperties != null) {
478                                            String portalValue = String.valueOf(
479                                                    _portalLanguageProperties.get(key));
480    
481                                            if (value.equals(portalValue)) {
482                                                    System.out.println("Duplicate key " + key);
483                                            }
484                                    }
485    
486                                    messages.add(key + "=" + value);
487                            }
488                            else {
489                                    if (begin && line.equals("")) {
490                                            _sortAndWrite(unsyncBufferedWriter, messages, firstLine);
491                                    }
492    
493                                    if (line.equals("")) {
494                                            begin = !begin;
495                                    }
496    
497                                    if (firstLine) {
498                                            firstLine = false;
499                                    }
500                                    else {
501                                            unsyncBufferedWriter.newLine();
502                                    }
503    
504                                    unsyncBufferedWriter.write(line);
505                            }
506    
507                            unsyncBufferedWriter.flush();
508                    }
509    
510                    if (!messages.isEmpty()) {
511                            _sortAndWrite(unsyncBufferedWriter, messages, firstLine);
512                    }
513    
514                    unsyncBufferedReader.close();
515                    unsyncBufferedWriter.close();
516    
517                    return FileUtil.read(propertiesFile);
518            }
519    
520            private void _sortAndWrite(
521                            UnsyncBufferedWriter unsyncBufferedWriter, Set<String> messages,
522                            boolean firstLine)
523                    throws IOException {
524    
525                    String[] messagesArray = messages.toArray(new String[messages.size()]);
526    
527                    for (int i = 0; i < messagesArray.length; i++) {
528                            if (!firstLine || (i != 0)) {
529                                    unsyncBufferedWriter.newLine();
530                            }
531    
532                            unsyncBufferedWriter.write(messagesArray[i]);
533                    }
534    
535                    messages.clear();
536            }
537    
538            private String _translate(
539                    String translationId, String key, String fromText, int limit) {
540    
541                    if (translationId.equals("en_ar") ||
542                            translationId.equals("en_eu") ||
543                            translationId.equals("en_bg") ||
544                            translationId.equals("en_ca") ||
545                            translationId.equals("en_hr") ||
546                            translationId.equals("en_cs") ||
547                            translationId.equals("en_da") ||
548                            translationId.equals("en_fi") ||
549                            translationId.equals("en_gl") ||
550                            translationId.equals("en_iw") ||
551                            translationId.equals("en_hi") ||
552                            translationId.equals("en_hu") ||
553                            translationId.equals("en_in") ||
554                            translationId.equals("en_lo") ||
555                            translationId.equals("en_nb") ||
556                            translationId.equals("en_fa") ||
557                            translationId.equals("en_pl") ||
558                            translationId.equals("en_ro") ||
559                            translationId.equals("en_ru") ||
560                            translationId.equals("en_sr_RS") ||
561                            translationId.equals("en_sr_RS_latin") ||
562                            translationId.equals("en_sk") ||
563                            translationId.equals("en_sl") ||
564                            translationId.equals("en_sv") ||
565                            translationId.equals("en_tr") ||
566                            translationId.equals("en_uk") ||
567                            translationId.equals("en_vi") ||
568                            translationId.equals("en_et")) {
569    
570                            // Automatic translator does not support Arabic, Basque, Bulgarian,
571                            // Catalan, Czech, Croatian, Danish, Finnish, Galician, Hebrew,
572                            // Hindi, Hungarian, Indonesian, Lao, Norwegian Bokmål, Persian,
573                            // Polish, Romanian, Russian, Serbian, Slovak, Slovene, Swedish,
574                            // Turkish, Ukrainian, or Vietnamese
575    
576                            return null;
577                    }
578    
579                    if (!_langTranslate) {
580                            return null;
581                    }
582    
583                    // Limit the number of retries to 3
584    
585                    if (limit == 3) {
586                            return null;
587                    }
588    
589                    String toText = null;
590    
591                    try {
592                            System.out.println(
593                                    "Translating " + translationId + " " + key + " " + fromText);
594    
595                            WebCacheItem wci = new TranslationWebCacheItem(
596                                    translationId, fromText);
597    
598                            Translation translation = (Translation)wci.convert("");
599    
600                            toText = translation.getToText();
601                    }
602                    catch (Exception e) {
603                            Throwable cause = e.getCause();
604    
605                            if (cause instanceof MicrosoftTranslatorException) {
606                                    System.out.println(
607                                            cause.getClass().getName() + ": " + cause.getMessage());
608                            }
609                            else {
610                                    e.printStackTrace();
611                            }
612                    }
613    
614                    // Keep trying
615    
616                    if (toText == null) {
617                            return _translate(translationId, key, fromText, ++limit);
618                    }
619    
620                    return toText;
621            }
622    
623            private String _langDir;
624            private String _langFile;
625            private boolean _langTranslate;
626            private Properties _portalLanguageProperties;
627            private Properties _renameKeys;
628    
629    }