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    
440                    // http://en.wikibooks.org/wiki/Basic_Book_Design/Capitalizing_Words_in_Titles
441                    // http://www.imdb.com
442    
443                    if (value.contains(" this ")) {
444                            if (value.contains(".") || value.contains("?") ||
445                                    value.contains(":") ||
446                                    key.equals("the-url-of-the-page-comparing-this-page-content-with-the-previous-version")) {
447                            }
448                            else {
449                                    value = StringUtil.replace(value, " this ", " This ");
450                            }
451                    }
452                    else {
453                            value = StringUtil.replace(value, " From ", " from ");
454                    }
455    
456                    return value;
457            }
458    
459            private String _fixTranslation(String value) {
460                    value = StringUtil.replace(
461                            value.trim(),
462                            new String[] {
463                                    "  ", "<b>", "</b>", "<i>", "</i>", " url ", "&#39;", "&#39 ;",
464                                    "&quot;", "&quot ;", "ReCaptcha", "Captcha"
465                            },
466                            new String[] {
467                                    " ", "<strong>", "</strong>", "<em>", "</em>", " URL ", "\'",
468                                    "\'", "\"", "\"", "reCAPTCHA", "CAPTCHA"
469                            });
470    
471                    return value;
472            }
473    
474            private String _orderProperties(File propertiesFile) throws IOException {
475                    if (!propertiesFile.exists()) {
476                            return null;
477                    }
478    
479                    String content = FileUtil.read(propertiesFile);
480    
481                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
482                            new UnsyncStringReader(content));
483                    UnsyncBufferedWriter unsyncBufferedWriter = new UnsyncBufferedWriter(
484                            new FileWriter(propertiesFile));
485    
486                    Set<String> messages = new TreeSet<String>(
487                            new NumericalStringComparator(true, true));
488    
489                    boolean begin = false;
490                    boolean firstLine = true;
491    
492                    String line = null;
493    
494                    while ((line = unsyncBufferedReader.readLine()) != null) {
495                            int pos = line.indexOf("=");
496    
497                            if (pos != -1) {
498                                    String key = line.substring(0, pos);
499    
500                                    String value = _fixTranslation(line.substring(pos + 1));
501    
502                                    value = _fixEnglishTranslation(key, value);
503    
504                                    if (_portalLanguageProperties != null) {
505                                            String portalValue = String.valueOf(
506                                                    _portalLanguageProperties.get(key));
507    
508                                            if (value.equals(portalValue)) {
509                                                    System.out.println("Duplicate key " + key);
510                                            }
511                                    }
512    
513                                    messages.add(key + "=" + value);
514                            }
515                            else {
516                                    if (begin && line.equals("")) {
517                                            _sortAndWrite(unsyncBufferedWriter, messages, firstLine);
518                                    }
519    
520                                    if (line.equals("")) {
521                                            begin = !begin;
522                                    }
523    
524                                    if (firstLine) {
525                                            firstLine = false;
526                                    }
527                                    else {
528                                            unsyncBufferedWriter.newLine();
529                                    }
530    
531                                    unsyncBufferedWriter.write(line);
532                            }
533    
534                            unsyncBufferedWriter.flush();
535                    }
536    
537                    if (!messages.isEmpty()) {
538                            _sortAndWrite(unsyncBufferedWriter, messages, firstLine);
539                    }
540    
541                    unsyncBufferedReader.close();
542                    unsyncBufferedWriter.close();
543    
544                    return FileUtil.read(propertiesFile);
545            }
546    
547            private void _sortAndWrite(
548                            UnsyncBufferedWriter unsyncBufferedWriter, Set<String> messages,
549                            boolean firstLine)
550                    throws IOException {
551    
552                    String[] messagesArray = messages.toArray(new String[messages.size()]);
553    
554                    for (int i = 0; i < messagesArray.length; i++) {
555                            if (!firstLine || (i != 0)) {
556                                    unsyncBufferedWriter.newLine();
557                            }
558    
559                            unsyncBufferedWriter.write(messagesArray[i]);
560                    }
561    
562                    messages.clear();
563            }
564    
565            private String _translate(
566                    String fromLanguageId, String toLanguageId, String key, String fromText,
567                    int limit) {
568    
569                    if (toLanguageId.equals("ar") ||
570                            toLanguageId.equals("eu") ||
571                            toLanguageId.equals("bg") ||
572                            toLanguageId.equals("ca") ||
573                            toLanguageId.equals("hr") ||
574                            toLanguageId.equals("cs") ||
575                            toLanguageId.equals("da") ||
576                            toLanguageId.equals("et") ||
577                            toLanguageId.equals("fi") ||
578                            toLanguageId.equals("gl") ||
579    
580                            // LPS-26741
581    
582                            toLanguageId.equals("de") ||
583    
584                            toLanguageId.equals("iw") ||
585                            toLanguageId.equals("hi") ||
586                            toLanguageId.equals("hu") ||
587                            toLanguageId.equals("in") ||
588                            toLanguageId.equals("lo") ||
589                            toLanguageId.equals("lt") ||
590                            toLanguageId.equals("nb") ||
591                            toLanguageId.equals("fa") ||
592                            toLanguageId.equals("pl") ||
593                            toLanguageId.equals("ro") ||
594                            toLanguageId.equals("ru") ||
595                            toLanguageId.equals("sr_RS") ||
596                            toLanguageId.equals("sr_RS_latin") ||
597                            toLanguageId.equals("sk") ||
598                            toLanguageId.equals("sl") ||
599                            toLanguageId.equals("sv") ||
600                            toLanguageId.equals("tr") ||
601                            toLanguageId.equals("uk") ||
602                            toLanguageId.equals("vi")) {
603    
604                            // Automatic translator does not support Arabic, Basque, Bulgarian,
605                            // Catalan, Croatian, Czech, Danish, Estonian, Finnish, Galician,
606                            // German, Hebrew, Hindi, Hungarian, Indonesian, Lao,
607                            // Norwegian Bokmål, Persian, Polish, Romanian, Russian, Serbian,
608                            // Slovak, Slovene, Swedish, Turkish, Ukrainian, or Vietnamese
609    
610                            return null;
611                    }
612    
613                    if (!_langTranslate) {
614                            return null;
615                    }
616    
617                    // Limit the number of retries to 3
618    
619                    if (limit == 3) {
620                            return null;
621                    }
622    
623                    String toText = null;
624    
625                    try {
626                            StringBundler sb = new StringBundler(8);
627    
628                            sb.append("Translating ");
629                            sb.append(fromLanguageId);
630                            sb.append("_");
631                            sb.append(toLanguageId);
632                            sb.append(" ");
633                            sb.append(key);
634                            sb.append(" ");
635                            sb.append(fromText);
636    
637                            System.out.println(sb.toString());
638    
639                            WebCacheItem wci = new TranslationWebCacheItem(
640                                    fromLanguageId, toLanguageId, fromText);
641    
642                            Translation translation = (Translation)wci.convert("");
643    
644                            toText = translation.getToText();
645                    }
646                    catch (Exception e) {
647                            Throwable cause = e.getCause();
648    
649                            if (cause instanceof MicrosoftTranslatorException) {
650                                    System.out.println(
651                                            cause.getClass().getName() + ": " + cause.getMessage());
652                            }
653                            else {
654                                    e.printStackTrace();
655                            }
656                    }
657    
658                    // Keep trying
659    
660                    if (toText == null) {
661                            return _translate(
662                                    fromLanguageId, toLanguageId, key, fromText, ++limit);
663                    }
664    
665                    return toText;
666            }
667    
668            private String _langDir;
669            private String _langFile;
670            private boolean _langTranslate;
671            private Properties _portalLanguageProperties;
672            private Properties _renameKeys;
673    
674    }