1 package freeguide.build.i18n;
2 
3 import java.io.File;
4 import java.io.FileFilter;
5 import java.io.FileInputStream;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 
9 import java.util.ArrayList;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Properties;
14 import java.util.Set;
15 import java.util.TreeSet;
16 import java.util.regex.Pattern;
17 
18 /**
19  * DOCUMENT ME!
20  *
21  * @author $author$
22  * @version $Revision$
23  */
24 public class CheckLocalization
25 {
26     protected static Pattern PATTERN_MAIN =
27         Pattern.compile( ".+[A-Za-z]{3,}\\.properties" );
28     protected static Map<String, String> translatedGlobal;
29 
30     /**
31      * DOCUMENT_ME!
32      *
33      * @param args DOCUMENT_ME!
34      *
35      * @throws Exception DOCUMENT_ME!
36      */
main( String[] args )37     public static void main( String[] args ) throws Exception
38     {
39         final File mainProps =
40             new File( "src/resources/i18n/MessagesBundle.properties" );
41         final File[] propFiles =
42             new File( "src/resources/i18n/" ).listFiles(
43                 new FileFilter(  )
44                 {
45                     public boolean accept( File pathname )
46                     {
47                         return PATTERN_MAIN.matcher( pathname.getName(  ) )
48                                            .matches(  );
49                     }
50                 } );
51 
52         translatedGlobal = readPropertiesFile( mainProps );
53 
54         System.out.println(
55             "================= Files with untranslated strings =================" );
56 
57         for( final File f : propFiles )
58         {
59             processFileFindUntranslated( f );
60         }
61 
62         System.out.println(
63             "================= Translation with non-exists strings =================" );
64 
65         for( final File f : propFiles )
66         {
67             processFileFindUnused( f );
68         }
69 
70         for( final String lang : "be,de,it,fr".split( "," ) )
71         {
72             System.out.println(
73                 "================= Language info: " + lang
74                 + " =================" );
75 
76             for( final File f : propFiles )
77             {
78                 processLang( lang, f );
79             }
80         }
81     }
82 
processLang( final String lang, final File propFile )83     protected static void processLang( final String lang, final File propFile )
84         throws IOException
85     {
86         final Map<String, String> baseStrings = readPropertiesFile( propFile );
87         final File transFile =
88             new File(
89                 propFile.getPath(  )
90                         .replaceAll(
91                     "\\.properties", '_' + lang + ".properties" ) );
92 
93         if( !transFile.exists(  ) )
94         {
95             System.out.println(
96                 "   There is no file : " + transFile.getPath(  ) );
97 
98             return;
99         }
100 
101         final Map<String, String> translatedStrings =
102             readPropertiesFile( transFile );
103 
104         final Set<String> untranslated =
105             new TreeSet<String>( baseStrings.keySet(  ) );
106         removeFromSet( untranslated, translatedStrings.keySet(  ) );
107 
108         if( untranslated.size(  ) > 0 )
109         {
110             System.out.println(
111                 "   Untranslated strings in " + propFile.getPath(  )
112                 + " for language '" + lang + "':" );
113 
114             for( String str : untranslated )
115             {
116                 System.out.println( "         \"" + str + '"' );
117             }
118         }
119 
120         final Set<String> unused =
121             new TreeSet<String>( translatedStrings.keySet(  ) );
122         removeFromSet( unused, baseStrings.keySet(  ) );
123 
124         if( unused.size(  ) > 0 )
125         {
126             System.out.println(
127                 "   Unused translations in " + propFile.getPath(  )
128                 + " for language '" + lang + "':" );
129 
130             for( String str : unused )
131             {
132                 System.out.println( "         \"" + str + '"' );
133             }
134         }
135     }
136 
getJavaFilesForPropertiesFile( final File propFile )137     protected static File[] getJavaFilesForPropertiesFile(
138         final File propFile )
139     {
140         final List<File> allFiles = new ArrayList<File>(  );
141 
142         String classesDir = propFile.getName(  );
143         classesDir = classesDir.substring(
144                 0, classesDir.length(  ) - ".properties".length(  ) );
145         classesDir = classesDir.replace( '_', '/' );
146 
147         if( classesDir.equals( "MessagesBundle" ) )
148         {
149             classesDir = "src/freeguide/common/";
150             findFile(
151                 new File( classesDir ), allFiles,
152                 new FileFilter(  )
153                 {
154                     public boolean accept( File pathname )
155                     {
156                         return pathname.getName(  ).endsWith( ".java" );
157                     }
158                 } );
159             classesDir = "src/freeguide/plugins/program/";
160             findFile(
161                 new File( classesDir ), allFiles,
162                 new FileFilter(  )
163                 {
164                     public boolean accept( File pathname )
165                     {
166                         return pathname.getName(  ).endsWith( ".java" );
167                     }
168                 } );
169         }
170         else
171         {
172             classesDir = "src/freeguide/plugins/" + classesDir;
173             findFile(
174                 new File( classesDir ), allFiles,
175                 new FileFilter(  )
176                 {
177                     public boolean accept( File pathname )
178                     {
179                         return pathname.getName(  ).endsWith( ".java" );
180                     }
181                 } );
182         }
183 
184         return allFiles.toArray( new File[allFiles.size(  )] );
185     }
186 
readFile( final File f )187     protected static String readFile( final File f ) throws IOException
188     {
189         final StringBuffer buf = new StringBuffer(  );
190         final InputStreamReader in =
191             new InputStreamReader( new FileInputStream( f ), "UTF-8" );
192 
193         try
194         {
195             char[] buffer = new char[65536];
196 
197             while( true )
198             {
199                 int len = in.read( buffer );
200 
201                 if( len < 0 )
202                 {
203                     break;
204                 }
205 
206                 buf.append( buffer, 0, len );
207             }
208         }
209         finally
210         {
211             in.close(  );
212         }
213 
214         return buf.toString(  );
215     }
216 
readPropertiesFile( final File f )217     protected static Map<String, String> readPropertiesFile( final File f )
218         throws IOException
219     {
220         final Properties result = new Properties(  );
221         result.load( new FileInputStream( f ) );
222 
223         return (Map)result;
224     }
225 
getStringsForTranslationFromFile( final File f )226     protected static Set<String> getStringsForTranslationFromFile(
227         final File f ) throws IOException
228     {
229         MODE mode = MODE.DATA;
230         final Set<String> result = new TreeSet<String>(  );
231 
232         StringBuffer str = new StringBuffer(  );
233         StringBuffer command = new StringBuffer(  );
234         char prevC = '\0';
235 
236         final String data = readFile( f );
237 
238         for( int i = 0; i < data.length(  ); i++ )
239         {
240             char c = data.charAt( i );
241 
242             switch( mode )
243             {
244             case DATA:
245 
246                 if( ( prevC == '/' ) && ( c == '*' ) )
247                 {
248                     mode = MODE.COMMENT;
249                 }
250                 else if( ( prevC == '/' ) && ( c == '/' ) )
251                 {
252                     mode = MODE.LINE_COMMENT;
253                 }
254                 else if( ( c == '"' ) && ( prevC != '\'' ) )
255                 {
256                     mode = MODE.STRING;
257                 }
258 
259                 break;
260 
261             case COMMENT:
262 
263                 if( ( prevC == '*' ) && ( c == '/' ) )
264                 {
265                     mode = MODE.DATA;
266                     command.setLength( 0 );
267                 }
268 
269                 break;
270 
271             case LINE_COMMENT:
272 
273                 if( c == '\n' )
274                 {
275                     mode = MODE.DATA;
276                 }
277 
278                 break;
279 
280             case STRING:
281 
282                 if( ( c == '"' ) && ( prevC != '\\' ) )
283                 {
284                     mode = MODE.DATA;
285 
286                     if( isNeedToLocalize( command.toString(  ) ) )
287                     {
288                         result.add( str.toString(  ) );
289                     }
290 
291                     str.setLength( 0 );
292                 }
293                 else
294                 {
295                     str.append( c );
296                 }
297 
298                 break;
299             }
300 
301             if( mode == MODE.DATA )
302             {
303                 if( ( c == ';' ) || ( c == '{' ) || ( c == '}' ) )
304                 {
305                     command.setLength( 0 );
306                 }
307                 else
308                 {
309                     command.append( c );
310                 }
311             }
312 
313             prevC = c;
314         }
315 
316         return result;
317     }
318 
isNeedToLocalize( final String command )319     protected static boolean isNeedToLocalize( final String command )
320     {
321         if( command.contains( "static" ) && command.contains( "final" ) )
322         {
323             return false;
324         }
325 
326         if( command.contains( "Exception(" ) )
327         {
328             return false;
329         }
330 
331         if(
332             command.replaceAll( "\\s+", "" )
333                        .contains( "Application.getInstance().getLogger()." ) )
334         {
335             return false;
336         }
337 
338         return true;
339     }
340 
findFile( final File dir, final List<File> files, final FileFilter filter )341     protected static void findFile(
342         final File dir, final List<File> files, final FileFilter filter )
343     {
344         final File[] fs = dir.listFiles(  );
345 
346         if( fs == null )
347         {
348             return;
349         }
350 
351         for( final File f : fs )
352         {
353             if( f.isFile(  ) )
354             {
355                 if( filter.accept( f ) )
356                 {
357                     files.add( f );
358                 }
359             }
360             else
361             {
362                 if( !f.getName(  ).equals( ".svn" ) )
363                 {
364                     findFile( f, files, filter );
365                 }
366             }
367         }
368     }
369 
processFileFindUntranslated( final File propFile )370     protected static void processFileFindUntranslated( final File propFile )
371         throws Exception
372     {
373         System.out.println( "Translation '" + propFile.getName(  ) + "' :" );
374 
375         final Map<String, String> translated = readPropertiesFile( propFile );
376 
377         for( final File f : getJavaFilesForPropertiesFile( propFile ) )
378         {
379             final Set<String> forTranslation =
380                 getStringsForTranslationFromFile( f );
381             removeFromSet( forTranslation, translated.keySet(  ) );
382             removeFromSet( forTranslation, translatedGlobal.keySet(  ) );
383 
384             if( forTranslation.size(  ) > 0 )
385             {
386                 System.out.println(
387                     "   Untranslated strings in " + f.getPath(  ) + " :" );
388 
389                 for( String str : forTranslation )
390                 {
391                     System.out.println( "         \"" + str + '"' );
392                 }
393             }
394         }
395     }
396 
processFileFindUnused( final File propFile )397     protected static void processFileFindUnused( final File propFile )
398         throws Exception
399     {
400         final Map<String, String> translated = readPropertiesFile( propFile );
401         final Set<String> keys = translated.keySet(  );
402 
403         for( final File f : getJavaFilesForPropertiesFile( propFile ) )
404         {
405             final Set<String> forTranslation =
406                 getStringsForTranslationFromFile( f );
407 
408             removeFromSet( keys, forTranslation );
409         }
410 
411         if( keys.size(  ) > 0 )
412         {
413             System.out.println(
414                 "Unused strings in '" + propFile.getName(  ) + "' :" );
415 
416             for( String str : keys )
417             {
418                 System.out.println( "         \"" + str + '"' );
419             }
420         }
421     }
422 
removeFromSet( final Set<String> sourceSet, final Set<String> removeSet )423     protected static void removeFromSet(
424         final Set<String> sourceSet, final Set<String> removeSet )
425     {
426         for( final Iterator<String> it = sourceSet.iterator(  );
427                 it.hasNext(  ); )
428         {
429             if( removeSet.contains( it.next(  ) ) )
430             {
431                 it.remove(  );
432             }
433         }
434     }
435     protected static enum MODE
436     {DATA,
437         COMMENT,
438         LINE_COMMENT,
439         STRING;
440     }
441 }
442