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