Adding new systempreference allowing for the library to add borrowers to institutions...
[koha_fer] / misc / translator / tmpl_process3.pl
index 345d6fd..5a26be2 100755 (executable)
@@ -6,7 +6,7 @@
 
 =head1 NAME
 
-tmpl_process3.pl - Experimental version of tmpl_process.pl
+tmpl_process3.pl - Alternative version of tmpl_process.pl
 using gettext-compatible translation files
 
 =cut
@@ -20,7 +20,7 @@ use VerboseWarnings qw( :warn :die );
 
 ###############################################################################
 
-use vars qw( @in_files $in_dir $str_file $out_dir );
+use vars qw( @in_files $in_dir $str_file $out_dir $quiet );
 use vars qw( @excludes $exclude_regex );
 use vars qw( $recursive_p );
 use vars qw( $pedantic_p );
@@ -54,7 +54,7 @@ sub text_replace_tag ($$) {
        if ($attr->{$a}) {
            next if $a eq 'content' && $tag ne 'meta';
            next if $a eq 'value' && ($tag ne 'input'
-               || (ref $attr->{'type'} && $attr->{'type'}->[1] =~ /^(?:hidden|radio)$/)); # FIXME
+               || (ref $attr->{'type'} && $attr->{'type'}->[1] =~ /^(?:hidden|radio|text)$/)); # FIXME
            my($key, $val, $val_orig, $order) = @{$attr->{$a}}; #FIXME
            if ($val =~ /\S/s) {
                my $s = find_translation($val);
@@ -90,20 +90,31 @@ sub text_replace (**) {
            print $output find_translation($t);
        } elsif ($kind eq TmplTokenType::TEXT_PARAMETRIZED) {
            my $fmt = find_translation($s->form);
-           print $output TmplTokenizer::parametrize($fmt, [ map {
+           print $output TmplTokenizer::parametrize($fmt, 1, $s, sub {
+               $_ = $_[0];
                my($kind, $t, $attr) = ($_->type, $_->string, $_->attributes);
                $kind == TmplTokenType::TAG && %$attr?
-                   text_replace_tag($t, $attr): $t } $s->parameters ], [ $s->anchors ]);
+                   text_replace_tag($t, $attr): $t });
        } elsif ($kind eq TmplTokenType::TAG && %$attr) {
            print $output text_replace_tag($t, $attr);
+       } elsif ($s->has_js_data) {
+           for my $t (@{$s->js_data}) {
+               # FIXME for this whole block
+               if ($t->[0]) {
+                   printf $output "%s%s%s", $t->[2], find_translation $t->[3],
+                           $t->[2];
+               } else {
+                   print $output $t->[1];
+               }
+           }
        } elsif (defined $t) {
            print $output $t;
        }
     }
 }
 
-sub listfiles ($$) {
-    my($dir, $type) = @_;
+sub listfiles ($$$) {
+    my($dir, $type, $action) = @_;
     my @it = ();
     if (opendir(DIR, $dir)) {
        my @dirent = readdir DIR;       # because DIR is shared when recursing
@@ -114,9 +125,9 @@ sub listfiles ($$) {
            || (defined $exclude_regex && $dirent =~ /^(?:$exclude_regex)$/)) {
                ;
            } elsif (-f $path) {
-               push @it, $path if !defined $type || $dirent =~ /\.(?:$type)$/;
+               push @it, $path if (!defined $type || $dirent =~ /\.(?:$type)$/) || $action eq 'install';
            } elsif (-d $path && $recursive_p) {
-               push @it, listfiles($path, $type);
+               push @it, listfiles($path, $type, $action);
            }
        }
     } else {
@@ -127,6 +138,21 @@ sub listfiles ($$) {
 
 ###############################################################################
 
+sub mkdir_recursive ($) {
+    my($dir) = @_;
+    local($`, $&, $', $1);
+    $dir = $` if $dir ne /^\/+$/ && $dir =~ /\/+$/;
+    my ($prefix, $basename) = ($dir =~ /\/([^\/]+)$/s)? ($`, $1): ('.', $dir);
+    mkdir_recursive($prefix) if $prefix ne '.' && !-d $prefix;
+    if (!-d $dir) {
+       print STDERR "Making directory $dir..." unless $quiet;
+       # creates with rwxrwxr-x permissions
+       mkdir($dir, 0775) || warn_normal "$dir: $!", undef;
+    }
+}
+
+###############################################################################
+
 sub usage ($) {
     my($exitcode) = @_;
     my $h = $exitcode? *STDERR: *STDOUT;
@@ -147,12 +173,13 @@ Create or update PO files from templates, or install translated templates.
                               for input (install) or output (create, update)
   -x, --exclude=REGEXP        Exclude files matching the given REGEXP
       --help                  Display this help and exit
+  -q, --quiet                 no output to screen (except for errors)
 
 The -o option is ignored for the "create" and "update" actions.
-Try `perldoc $0' for perhaps more information.
+Try `perldoc $0 for perhaps more information.
 EOF
     exit($exitcode);
-}
+}#`
 
 ###############################################################################
 
@@ -160,7 +187,7 @@ sub usage_error (;$) {
     for my $msg (split(/\n/, $_[0])) {
        print STDERR "$msg\n";
     }
-    print STDERR "Try `$0 --help' for more information.\n";
+    print STDERR "Try `$0 --help for more information.\n";
     exit(-1);
 }
 
@@ -172,6 +199,7 @@ GetOptions(
     'recursive|r'                      => \$recursive_p,
     'str-file|s=s'                     => \$str_file,
     'exclude|x=s'                      => \@excludes,
+       'quiet|q'                               => \$quiet,
     'pedantic-warnings|pedantic'       => sub { $pedantic_p = 1 },
     'help'                             => \&usage,
 ) || usage_error;
@@ -210,7 +238,7 @@ if (-d $in_files[0]) {
     # input is a directory, generates list of files to process
     $in_dir = $in_files[0];
     $in_dir =~ s/\/$//; # strips the trailing / if any
-    @in_files = listfiles($in_dir, $type);
+    @in_files = listfiles($in_dir, $type, $action);
 } else {
     for my $input (@in_files) {
        die "You cannot specify input files and directories at the same time.\n"
@@ -251,16 +279,41 @@ if ($action eq 'create')  {
        unlink $str_file || die "$str_file: $!\n";
     }
     die "$str_file: Output file already exists\n" if -f $str_file;
-    my($tmph, $tmpfile) = tmpnam();
+    my($tmph1, $tmpfile1) = tmpnam();
+    my($tmph2, $tmpfile2) = tmpnam();
+    close $tmph2; # We just want a name
     # Generate the temporary file that acts as <MODULE>/POTFILES.in
     for my $input (@in_files) {
-       print $tmph "$input\n";
+       print $tmph1 "$input\n";
     }
-    close $tmph;
+    close $tmph1;
     # Generate the specified po file ($str_file)
-    $st = system ($xgettext, '-s', '-f', $tmpfile, '-o', $str_file);
-    warn_normal "Text extraction failed: $xgettext: $!\n", undef if $st != 0;
-#   unlink $tmpfile || warn_normal "$tmpfile: unlink failed: $!\n", undef;
+    $st = system ($xgettext, '-s', '-f', $tmpfile1, '-o', $tmpfile2);
+    # Run msgmerge so that the pot file looks like a real pot file
+    # We need to help msgmerge a bit by pre-creating a dummy po file that has
+    # the headers and the "" msgid & msgstr. It will fill in the rest.
+    if ($st == 0) {
+       # Merge the temporary "pot file" with the specified po file ($str_file)
+       # FIXME: msgmerge(1) is a Unix dependency
+       # FIXME: need to check the return value
+       unless (-f $str_file) {
+           local(*INPUT, *OUTPUT);
+           open(INPUT, "<$tmpfile2");
+           open(OUTPUT, ">$str_file");
+           while (<INPUT>) {
+               print OUTPUT;
+           last if /^\n/s;
+           }
+           close INPUT;
+           close OUTPUT;
+       }
+       $st = system('msgmerge', '-U', '-s', $str_file, $tmpfile2);
+    } else {
+       error_normal "Text extraction failed: $xgettext: $!\n", undef;
+       error_additional "Will not run msgmerge\n", undef;
+    }
+#   unlink $tmpfile1 || warn_normal "$tmpfile1: unlink failed: $!\n", undef;
+#   unlink $tmpfile2 || warn_normal "$tmpfile2: unlink failed: $!\n", undef;
 
 } elsif ($action eq 'update') {
     my($tmph1, $tmpfile1) = tmpnam();
@@ -308,35 +361,39 @@ if ($action eq 'create')  {
 
     # creates the new tmpl file using the new translation
     for my $input (@in_files) {
-       die "Assertion failed"
-               unless substr($input, 0, length($in_dir) + 1) eq "$in_dir/";
-
-       my $h = TmplTokenizer->new( $input );
-       $h->set_allow_cformat( 1 );
-       VerboseWarnings::set_input_file_name $input;
-
-       my $target = $out_dir . substr($input, length($in_dir));
-       my $targetdir = $` if $target =~ /[^\/]+$/s;
-       if (!-d $targetdir) {
-           print STDERR "Making directory $targetdir...";
-           # creates with rwxrwxr-x permissions
-           mkdir($targetdir, 0775) || warn_normal "$targetdir: $!", undef;
+               die "Assertion failed"
+                       unless substr($input, 0, length($in_dir) + 1) eq "$in_dir/";
+#              print "$input / $type\n";
+               if (!defined $type || $input =~ /\.(?:$type)$/) {
+                       my $h = TmplTokenizer->new( $input );
+                       $h->set_allow_cformat( 1 );
+                       VerboseWarnings::set_input_file_name $input;
+               
+                       my $target = $out_dir . substr($input, length($in_dir));
+                       my $targetdir = $` if $target =~ /[^\/]+$/s;
+                       mkdir_recursive($targetdir) unless -d $targetdir;
+                       print STDERR "Creating $target...\n" unless $quiet;
+                       open( OUTPUT, ">$target" ) || die "$target: $!\n";
+                       text_replace( $h, *OUTPUT );
+                       close OUTPUT;
+               } else {
+               # just copying the file
+                       my $target = $out_dir . substr($input, length($in_dir));
+                       my $targetdir = $` if $target =~ /[^\/]+$/s;
+                       mkdir_recursive($targetdir) unless -d $targetdir;
+                       system("cp -f $input $target");
+                       print STDERR "Copying $input...\n" unless $quiet;
+               }
        }
-       print STDERR "Creating $target...\n";
-       open( OUTPUT, ">$target" ) || die "$target: $!\n";
-       text_replace( $h, *OUTPUT );
-       close OUTPUT;
-    }
 
 } else {
     usage_error('Unknown action specified.');
 }
 
 if ($st == 0) {
-    printf "The %s seems to be successful, with %d warning(s).\n",
-           $action, VerboseWarnings::warned;
+    printf "The %s seems to be successful.\n", $action unless $quiet;
 } else {
-    printf "%s FAILED.\n", "\u$action";
+    printf "%s FAILED.\n", "\u$action" unless $quiet;
 }
 exit 0;
 
@@ -348,27 +405,85 @@ exit 0;
 
 =head1 DESCRIPTION
 
-This is an experimental version of the tmpl_process.pl script,
-using standard gettext-style PO files.  Note that the behaviour
-of this script should still be considered unstable.
+This is an alternative version of the tmpl_process.pl script,
+using standard gettext-style PO files.  While there still might
+be changes made to the way it extracts strings, at this moment
+it should be stable enough for general use; it is already being
+used for the Chinese and Polish translations.
 
 Currently, the create, update, and install actions have all been
 reimplemented and seem to work.
 
+=head2 Features
+
+=over
+
+=item -
+
+Translation files in standard Uniforum PO format.
+All standard tools including all gettext tools,
+plus PO file editors like kbabel(1) etc.
+can be used.
+
+=item -
+
+Minor changes in whitespace in source templates
+do not generally require strings to be re-translated.
+
+=item -
+
+Able to handle <TMPL_VAR> variables in the templates;
+<TMPL_VAR> variables are usually extracted in proper context,
+represented by a short %s placeholder.
+
+=item -
+
+Able to handle text input and radio button INPUT elements
+in the templates; these INPUT elements are also usually
+extracted in proper context,
+represented by a short %S or %p placeholder.
+
+=item -
+
+Automatic comments in the generated PO files to provide
+even more context (line numbers, and the names and types
+of the variables).
+
+=item -
+
+The %I<n>$s (or %I<n>$p, etc.) notation can be used
+for change the ordering of the variables,
+if such a reordering is required for correct translation.
+
+=item -
+
+If a particular <TMPL_VAR> should not appear in the
+translation, it can be suppressed with the %0.0s notation.
+
+=item -
+
+Using the PO format also means translators can add their
+own comments in the translation files, if necessary.
+
+=item -
+
+Create, update, and install actions are all based on the
+same scanner module. This ensures that update and install
+have the same idea of what is a translatable string;
+attribute names in tags, for example, will not be
+accidentally translated.
+
+=back
+
+=head1 NOTES
+
+Anchors are represented by an <AI<n>> notation.
+The meaning of this non-standard notation might not be obvious.
+
 The create action calls xgettext.pl to do the actual work;
 the update action calls xgettext.pl and msgmerge(1) to do the
 actual work.
 
-The script can detect <TMPL_VAR> directives embedded inside what
-appears to be a full sentence (this actual work being done by
-TmplTokenizer(3)); these larger patterns appear in the translation
-file as c-format strings with %s.
-
-Whitespace in extracted strings are folded to single blanks, in
-order to prevent new strings from appearing when minor changes in
-the original templates occur, and to prevent overly difficult to
-read strings in the PO file.
-
 =head1 BUGS
 
 xgettext.pl must be present in the current directory; the
@@ -381,14 +496,19 @@ generate GNU PO files properly; a couple of workarounds have
 been written in TmplTokenizer and more is likely to be needed
 (e.g., to get rid of the "Strange line" warning for #~).
 
+This script may not work in Windows.
+
 There are probably some other bugs too, since this has not been
 tested very much.
 
 =head1 SEE ALSO
 
 xgettext.pl,
+TmplTokenizer.pm,
 msgmerge(1),
 Locale::PO(3),
 translator_doc.txt
 
+http://www.saas.nsw.edu.au/koha_wiki/index.php?page=DifficultTerms
+
 =cut