Minor spelling correction.
[koha_fer] / misc / translator / tmpl_process.pl
1 #!/usr/bin/perl
2
3 use strict;
4 use Getopt::Long;
5
6 my (@in_files, $str_file, $split_char, $recursive, $type, $out_dir, $in_dir, @excludes, $filter);
7 my $help;
8 my $exclude_regex;
9
10 $split_char = ' ';
11
12 GetOptions(
13         'input|i=s'     => \@in_files,
14         'outputdir|o=s' => \$out_dir,
15         'str-file|s=s' => \$str_file,
16         'recursive|r' => \$recursive,
17         'filter=s' => \$filter,
18         'type=s' => \$type,
19         'exclude=s' => \@excludes,
20         'sep=s' => \$split_char,
21         'help'  => \$help,
22 ) || (usage(), exit(-1));
23
24 help() if $help;
25
26 # utiliser glob() pour tous les fichiers d'un repertoire
27
28 my $action = shift or usage();
29 my %strhash = ();
30
31 # Checks for missing input and string list arguments
32
33 if( !defined(@in_files) || !defined($str_file) )
34 {
35         usage("You must at least specify input and string list filenames.");
36 }
37
38 # Type match defaults to *.tmpl if not specified
39 $type = "tmpl|inc" if !defined($type);
40
41 $filter = "./text-extract.pl -f" if !defined($filter);
42 # Input is not a file nor a directory
43 if( !(-d $in_files[0]) && !(-f $in_files[0]))
44 {
45         usage("Unknown input. Input must a file or a directory. (Symbolic links are not supported for the moment.)");
46 }
47 elsif( -d $in_files[0] )
48 {
49         # input is a directory, generates list of files to process
50         $in_dir = $in_files[0];
51         $in_dir =~ s/\/$//; # strips the trailing / if any
52
53         print "Generating list of files to process...\n";
54         
55         @in_files = ();
56         @in_files = &listfiles(\@in_files, $in_dir, $type, $recursive);
57
58         if(scalar(@in_files) == 0)
59         {
60                 warn "Nothing to process in $in_dir matching *.$type.";
61                 exit -1;
62         }
63 }
64
65 # Generates the global exclude regular expression
66 $exclude_regex =  "(".join("|", @excludes).")" if @excludes;
67
68 if( $action eq "create" )
69 {
70         # updates the list. As the list is empty, every entry will be added
71         %strhash = &update_strhash(\%strhash, \@in_files, $exclude_regex, $filter);
72         # saves the list to the file
73         write_strhash(\%strhash, $str_file, "\t");
74 }
75 elsif( $action eq "update" )
76 {
77         # restores the string list from file
78         %strhash = &restore_strhash(\%strhash, $str_file, $split_char);
79         # updates the list, adding new entries if any
80         %strhash = &update_strhash(\%strhash, \@in_files, $exclude_regex, $filter);
81         # saves the list to the file
82         write_strhash(\%strhash, $str_file, $split_char);
83 }
84 elsif( $action eq "install" )
85 {
86         if(!defined($out_dir))
87         {
88                 usage("You must specify an output directory when using the install method.");
89         }
90         
91         if( $in_dir eq $out_dir )
92         {
93                 warn "You must specify a different input and output directory.\n";
94                 exit -1;
95         }
96
97         # restores the string list from file
98         %strhash = &restore_strhash(\%strhash, $str_file, $split_char);
99         # creates the new tmpl file using the new translation
100         &install_strhash(\%strhash, \@in_files, $in_dir, $out_dir);
101 }
102 else
103 {
104         usage("Unknown action specified.");
105 }
106
107 exit 0;
108
109 ##########################################################
110 # Creates the new template files in the output directory #
111 ##########################################################
112
113 sub install_strhash
114 {
115         my($strhash, $in_files, $in_dir, $out_dir) = @_;
116
117         my $fh_in; my $fh_out; # handles for input and output files
118         my $tmp_dir; # temporary directory name (used to create destination dir)
119
120         $out_dir =~ s/\/$//; # chops the trailing / if any.
121
122         # Processes every entry found.
123         foreach my $file (@{$in_files})
124         {
125                 if( !open($fh_in, "< $file") )
126                 {
127                         warn "Can't open $file : $!\n";
128                         next;
129                 }
130
131                 # generates the name of the output file
132                 my $out_file = $file;
133
134                 if(!defined $in_dir)
135                 {
136                         # processing single files not an entire directory
137                         $out_file = "$out_dir/$file";
138                 }
139                 else
140                 {
141                         $out_file =~ s/^$in_dir/$out_dir/;
142                 }
143
144                 my $slash = rindex($out_file, "\/");
145                 $tmp_dir = substr($out_file, 0, $slash); #gets the directory where the file will be saved
146
147                 # the file doesn't exist
148                 if( !(-f $tmp_dir) && !(-l $tmp_dir) && !(-e $tmp_dir) )
149                 {
150                         if(!mkdir($tmp_dir,0775)) # creates with rwxrwxr-x permissions
151                         {
152                                 warn("Make directory $tmp_dir : $!");
153                                 close($fh_in);
154                                 exit(1);
155                         }
156                 }
157                 elsif((-f $tmp_dir) || (-l $tmp_dir))
158                 {
159                         warn("Unable to create directory $tmp_dir.\n A file or symbolic link with the same name already exists.");
160                         close($fh_in);
161                         exit(1);
162                 }
163                 
164                 # opens handle for output
165                 if( !open($fh_out, "> $out_file") )
166                 {
167                         warn "Can't write $out_file : $!\n";
168                         close($fh_in);
169                         next;
170                 }
171
172                 print "Generating $out_file...\n";
173
174                 while(my $line = <$fh_in>)
175                 {
176                         foreach my $text (sort  {length($b) <=> length($a)} keys %{$strhash})
177                         {
178                                 # Test if the key has been translated
179                                 if( %{$strhash}->{$text} != 1 )
180                                 {
181                                         # Does the line contains text that needs to be changed ?
182                                         if( $line =~ /$text/ && %{$strhash}->{$text} ne "IGNORE")
183                                         {
184                                                 # changing text
185                                                 my $subst = %{$strhash}->{$text};
186                                                 $line =~ s/(\W)$text(\W)/$1$subst$2/g;
187                                         }
188                                 }
189                         }
190                         $line =~ s/\<TMPL_(.*?)\>/\<\!-- TMPL_$1 --\>/g;
191                         $line =~ s/\<\/TMPL_(.*?)\>/\<\!-- \/TMPL_$1 --\>/g;
192                         # Writing the modified (or not) line to output
193                         printf($fh_out "%s", $line);
194                 }
195
196                 close($fh_in);
197                 close($fh_out);
198         }
199 }
200
201 ########################################################
202 # Updates the string list hash with the new components #
203 ########################################################
204
205 sub update_strhash
206 {
207         my($strhash, $in_files, $exclude, $filter)= @_;
208
209         my $fh;
210
211         # Processes every file entries
212         foreach my $in (@{$in_files})
213         {
214
215                 print "Processing $in...\n";
216
217                 # Creates a filehandle containing all the strings returned by
218                 # the plain text program extractor
219                 open($fh, "$filter $in |") or print "$filter $in : $!";
220                 next $in if !defined $fh;
221
222                 # Processes every string returned
223                 while(my $str = <$fh>)
224                 {
225                         $str =~ s/[\n\r\f]+$//; # chomps the trailing \n (or <cr><lf> if file was edited with Windows)
226                         $str =~ s/^\s+//; # remove trailing blanks, ':' or '*'
227                         $str =~ s/\s*\**:*\s*$//;
228
229                         # the line begins with letter(s) followed by optional words and/or spaces
230                         if($str =~ /^[ ]*[\w]+[ \w]*/)
231                         {
232                                 # the line is to be excluded ?
233                                 if( !(defined($exclude) && ($str =~ /$exclude/o) && $str>0) )
234                                 {
235                                         if( !defined(%{$strhash}->{$str}) )
236                                         {
237                                                 # the line is not already in the list so add it
238                                                 %{$strhash}->{$str}=1;
239                                         }
240                                 }
241                         }
242                 }
243
244                 close($fh);
245         }
246
247         return %{$strhash};
248 }
249
250 #####################################################
251 # Reads the input file and returns a generated hash #
252 #####################################################
253
254 sub restore_strhash
255 {
256         my($strhash, $str_file, $split_char) = @_;
257         
258         my $fh;
259         
260         open($fh, "< $str_file") or die "$str_file : $!";
261         
262         print "Restoring string list from $str_file...\n";
263         
264         while( my $line = <$fh> )
265         {
266                 chomp $line;
267
268                 # extracts the two fields
269                 my ($original, $translated) = split(/$split_char/, $line, 2);
270
271                 if($translated ne "*****")
272                 {
273                         # the key has been translated
274                         %{$strhash}->{$original} = $translated;
275                 }
276                 else
277                 {
278                         # the key exist but has no translation.
279                         %{$strhash}->{$original} = 1;
280                 }
281
282         }
283
284         close($fh);
285
286         return %{$strhash};
287 }
288
289 #########################################
290 # Writes the string hashtable to a file #
291 #########################################
292
293 sub write_strhash
294 {
295         my($strhash, $str_file, $split_char) = @_;
296
297         my $fh;
298
299         # Opens a handle for saving the list
300         open($fh, "> $str_file") or die "$str_file : $!";
301
302         print "Writing string list to $str_file...\n";
303
304         foreach my $str(sort {uc($a) cmp uc($b) || length($a) <=> length($b)} keys %{$strhash})
305         {
306                 if(%{$strhash}->{$str} != 1)
307                 {
308                         printf($fh "%s%s%s\n", $str, $split_char, %{$strhash}->{$str});
309                 }
310                 else
311                 {
312                         printf($fh "%s%s%s\n", $str, $split_char,"*****") unless ($str >0);
313                 }
314         }
315
316         close($fh);
317 }
318
319 ########################################################
320 # List the contents of dir matching the pattern *.type #
321 ########################################################
322
323 sub listfiles
324 {
325         my($in_files, $dir, $type, $recursive) = @_;
326
327         my $dir_h;
328 #       my @types = split(/ /,$type);
329         opendir($dir_h, $dir) or warn("Can't open $dir : $!\n");
330
331         my @tmp_list = grep(!/^\.\.?$/, readdir($dir_h));
332
333         closedir($dir_h);
334
335         foreach my $tmp_file (@tmp_list)
336         {
337
338                 if( $recursive && (-d "$dir/$tmp_file") ) # entry is a directory
339                 {
340                         @{$in_files} = listfiles($in_files, "$dir/$tmp_file", $type);
341                 }
342                 elsif( $tmp_file =~ /\.$type$/ )
343                 {
344                         push(@{$in_files}, "$dir/$tmp_file");
345                 }
346         }
347         return @{$in_files};
348 }
349
350 ######################################
351 # DEBUG ROUTINE                      #
352 # Prints the contents of a hashtable #
353 ######################################
354
355 sub print_strhash
356 {
357         my($strhash, $split_char) = @_;
358         
359         foreach my $str(sort keys %{$strhash})
360         {
361                 if(%{$strhash}->{$str} != 1)
362                 {
363                         printf("%s%s%s\n", $str, $split_char, %{$strhash}->{$str});
364                 }
365                 else
366                 {
367                         printf("%s%s\n", $str, $split_char);
368                 }
369         }
370 }       
371
372 #########################################
373 # Short help messsage printing function #
374 #########################################
375
376 sub usage
377 {
378         warn join(" ", @_)."\n" if @_;
379         warn <<EOF;
380
381 Usage : $0 method -i input.tmpl|/input/dir -s strlist.file
382         [-o /output/dir] [options]
383
384 where method can be :
385   * create : creates the string list from scratch using the input files.
386   * update : updates an existing string list, adding the new strings to
387              the list, leaving the others alone.
388   * install : creates the new .tmpl files using the string list config file
389               (--outputdir must be used to specify the output directory).
390
391 Use $0 --help for a complete listing of options.
392 EOF
393         exit(1);
394 }
395
396 ##############################################
397 # Long help message describing every options #
398 ##############################################
399
400 sub help
401 {
402         warn <<EOF;
403 Usage : $0 method [options]
404         
405 where method can be :
406   * create : creates the string list from scratch using the input files.
407   * update : updates an existing string list, adding the new strings to
408              the list, leaving the others alone.
409   * install : creates the new .tmpl files using the string list config file
410               (-o must be used to specify the output directory).
411
412 options can be :
413
414   -i or --input=
415      Specify the input to process. Input can be a file or a directory.
416      When input is a directory, files matching the --type option will be
417      processed.
418      When using files, the parameter can be repeated to process a list
419      of files.
420    
421   Example: $0 create -i foo.tmpl --input=bar.tmpl -s foobar.txt
422
423   -s or --str-file=
424      Specify the file where the different strings will be stored.
425
426   -o or --outputdir=
427      Specify the output directory to use when generating the translated
428      input files.
429
430   -r or --recursive=
431      Use the recursive mode to process every entry in subdirectories.
432      Note: Symbolic links used to link directories are not supported.
433
434   --type=
435      Defines the type of files to match when input is a directory.
436      By default --type=tmpl
437
438   --exclude=regex
439      Use this option to exclude some entries extracted by the program.
440      This option can be repeated to exclude many types of strings.
441
442   Example: $0 create -i foo.tmpl -s foo.txt --exclude=^\[0-9\]+\$
443    will create a list from foo.tmpl called foo.txt where lines
444    composed of numbers only are excluded. Special characters need to
445    be escaped.
446
447   --filter=
448      Specify the program to use to extract plain text from files.
449      Default is str-extract which means str-extract must be in the path
450      in order to use it.
451
452   --sep=char
453      Use this option to specify the char to be used to separate entries
454      in the string list file.
455
456   --help
457      This help message.
458 EOF
459         exit(0);
460 }