diff --git a/README.md b/README.md index 932df09..9e25228 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,60 @@ [![alt tag](http://img.shields.io/badge/maintainer-hawkinswnaf-blue.svg)](https://github.com/hawkinswnaf) -commotion-translation-generator +# commotion-translation-generator ============================================= -Parses commotion-router source files for user-facing strings and uses them to generate PO files for [https://github.com/opentechinstitute/luci-i18n-commotion](luci-i18n-commotion). +## Summary +Parses commotion-router source files for user-facing strings and uses them to generate PO files for [https://github.com/opentechinstitute/luci-i18n-commotion](luci-i18n-commotion). The PO file generator will attempt to re-use any existing translations in the new PO file. -Translation of Commotion OpenWRT is managed on Transifex: +Translation of Commotion Router is managed on Transifex: https://www.transifex.com/projects/p/commotion-user-interface/ + +## Prerequisites +* Perl 5 +* perl modules ([available through cpan](http://www.cpan.org/modules/INSTALL.html)) + * DateTime + * File::Copy + * File::Next + * File::Path + * Git::Repository + * Text::Balanced + +## Setup and Usage + +1. Install perl. +2. Install prerequisite modules: + a. From a terminal prompt, install cpanm (`cpan App::cpanm`). + b. Use cpanm to install modules (e.g., `cpanm File::Copy`). +3. Change to the scripts directory (`cd scripts`). +4. Run `parse.pl`. Newly generated PO files will be written to `scripts/working/translations/` and must be manually added to luci-i18n-commotion for upload to github. + + +## PO Files +Commotion-Router uses LuCI's i18n API and PO file system to manage system language. Any user-facing text in a LuCI model, view, or controller file may be marked translatable using standard tags (i.e., `translate("some text")`, `translatef("formatted text: %d", var)` for code or `<%:some text%>` for templates). Translatable text will then be checked against the active system language file (PO file). If an exact match is found, the page will be rendered using the PO file's translation for that string. If no match is found, or if the string has not yet been translated, the page will be rendered using the original text. + +Each PO file uses a standard header containing translation project metadata, followed by origin/translation string pairs. + +### PO File Header Format +PO file header structure is as follows (using Commotion Router's French translation as an example). + +``` +"Project-Id-Version: Commotion User Interface\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-08-01 02:43+0500\n" +"PO-Revision-Date: 2013-11-20 10:05+0000\n" +"Last-Translator: st\n" +"Language-Team: French (http://www.transifex.com/projects/p/commotion-user-interface/language/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +``` + +### PO File Translation Format +PO file origin/translation string pairs are designated as follows. + +``` +msgid "Access Point Name" +msgstr "Nom du point d'accès" +``` \ No newline at end of file diff --git a/scripts/parse.pl b/scripts/parse.pl index 5414815..f59155c 100755 --- a/scripts/parse.pl +++ b/scripts/parse.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl #** # @file parse.pl @@ -31,6 +31,7 @@ #* use strict; +use warnings; use File::Path qw(make_path remove_tree); use Git::Repository; use File::Copy; @@ -38,18 +39,22 @@ use Text::Balanced qw(extract_bracketed extract_delimited extract_tagged); use DateTime; +our $VERSION = 0.3.0; + #** # @var verbosity Print debug messages # @param 0|1 0 == off, 1 == on #* my $verbosity = 1; -my @todo = ("\n\nTo Do:"); -push(@todo, "Fix use vs. require"); +my @todo = ("\n\nTo Do:"); +push( @todo, 'Fix use vs. require' ); ## TODO: for next major revision: ## Look for ways to minimize string overlap ## http://www.perlmonks.org/?node_id=816086 -push(@todo, "minimize duplicate strings in po files - http://www.perlmonks.org/?node_id=816086"); +push( @todo, +'minimize duplicate strings in po files - http://www.perlmonks.org/?node_id=816086' +); #** # @var repos Repos to be scanned for translatable strings @@ -58,78 +63,61 @@ # @param branch Named k:v pair listing appropriate repo branch as defined in commotion-feed. Value of name parameter. #* my %repos = ( - 'luci-commotion-apps' => { - 'source' => 'https://github.com/opentechinstitute/luci-commotion-apps.git', - 'branch' => 'master', - }, - - 'luci-theme-commotion' => { - 'source' => 'https://github.com/opentechinstitute/luci-theme-commotion.git', - 'branch' => 'master', - }, - 'luci-commotion' => { - 'source' => 'https://github.com/opentechinstitute/luci-commotion.git', - 'branch' => 'master', - }, - 'commotion-dashboard-helper' => { - 'source' => 'https://github.com/opentechinstitute/commotion-dashboard-helper.git', - 'branch' => 'master', - }, - 'commotiond commotion-service-manager' => { - 'source' => 'https://github.com/opentechinstitute/commotion-service-manager.git', - 'branch' => 'master', - }, - 'commotion-router' => { - 'source' => 'https://github.com/opentechinstitute/commotion-router.git', - 'branch' => 'master', - }, - 'commotion-feed' => { - 'source' => 'https://github.com/opentechinstitute/commotion-feed.git', - 'branch' => 'master', - }, - 'commotion-lua-helpers' => { - 'source' => 'https://github.com/opentechinstitute/commotion-lua-helpers.git', - 'branch' => 'master', - }, - 'serval-dna' => { - 'source' => 'https://github.com/opentechinstitute/commotion-dashboard-helper.git', - 'branch' => 'commotion-wireless', - }, - 'luci-commotion-splash' => { - 'source' => 'https://github.com/opentechinstitute/luci-commotion-splash.git', - 'branch' => 'master', - }, - 'commotion-debug-helper' => { - 'source' => 'https://github.com/opentechinstitute/commotion-debug-helper.git', - 'branch' => 'master', - }, - 'luci-i18n-commotion' => { - 'source' => 'https://github.com/opentechinstitute/luci-i18n-commotion.git', - 'branch' => 'master', - }, - 'olsrd' => { - 'source' => 'https://github.com/opentechinstitute/olsrd.git', - 'branch' => 'master', - }, + 'commotion-router' => { + 'source' => 'https://github.com/opentechinstitute/commotion-router.git', + 'branch' => 'master', + }, + 'commotion-feed' => { + 'source' => 'https://github.com/opentechinstitute/commotion-feed.git', + 'branch' => 'master', + }, + 'commotiond' => { + 'source' => 'https://github.com/opentechinstitute/commotiond.git', + 'branch' => 'master', + }, + 'commotion-service-manager' => { + 'source' => + 'https://github.com/opentechinstitute/commotion-service-manager.git', + 'branch' => 'master', + }, + 'luci-commotion' => { + 'source' => 'https://github.com/opentechinstitute/luci-commotion.git', + 'branch' => 'master', + }, + 'luci-i18n-commotion' => { + 'source' => + 'https://github.com/opentechinstitute/luci-i18n-commotion.git', + 'branch' => 'master', + }, + 'olsrd' => { + 'source' => 'https://github.com/opentechinstitute/olsrd.git', + 'branch' => 'master', + }, + 'serval-dna' => { + 'source' => 'https://github.com/opentechinstitute/serval-dna.git', + 'branch' => 'commotion-wireless', + }, ); #** -# @section Directories +# @section Directories # @brief Define internal directory structure, including translation source and working area. # Include trailing slashes! #* -my $working_dir = 'working/'; -my $working_source_dir = $working_dir . 'source/'; +my $working_dir = 'working/'; +my $working_source_dir = $working_dir . 'source/'; my $working_translations_dir = $working_dir . 'translations/'; -my $stable_translations_dir = $working_source_dir . 'luci-i18n-commotion/translations/'; +my $stable_translations_dir = + $working_source_dir . 'luci-i18n-commotion/translations/'; #** # @brief Prepare working directories #* -if (not -e $working_dir) { - print("Creating working directories\n"); - make_path($working_dir, $working_source_dir, $working_translations_dir, {verbose=>1}) - || die "ERROR: Couldn't create " . $working_dir . "\n"; +if ( not -e $working_dir ) { + print("Creating working directories\n"); + make_path( $working_dir, $working_source_dir, $working_translations_dir, + { verbose => 1 } ) + || die "ERROR: Couldn't create " . $working_dir . "\n"; } #** @@ -137,47 +125,60 @@ # If working copy does not exist, clone it and check out proper branch. # If working copy does exist, check out proper branch and pull updates. #* -foreach my $repo (keys %repos) { - if (not -e $working_source_dir . $repo) { - print("Cloning " . $repo . " into " . $working_source_dir ."\n"); - Git::Repository->run( clone => $repos{$repo}{'source'} => $working_source_dir . $repo) - || warn "Couldn't clone " . $repos{$repo}{'source'} . "\n"; - my $r = Git::Repository->new( work_tree => $working_source_dir . $repo); - print("\tChecking out $repos{$repo}{'branch'} branch\n"); - $r->command(checkout => $repos{$repo}{'branch'}) || die "Couldn't check out proper branch\n"; - } else { - print("Updating " . $repo . "\n"); - my $r = Git::Repository->new( work_tree => $working_source_dir . $repo); - $r->command(checkout => $repos{$repo}{'branch'}) || die "Couldn't check out proper branch\n"; - $r->command(pull => 'origin', $repos{$repo}{'branch'}, { quiet => 1 }) || warn "Couldn't pull remotes\n"; - } +foreach my $repo ( keys %repos ) { + if ( not -e $working_source_dir . $repo ) { + print( "Cloning " . $repo . " into " . $working_source_dir . "\n" ); + Git::Repository->run( + clone => $repos{$repo}{'source'} => $working_source_dir . $repo ) + || warn "Couldn't clone " . $repos{$repo}{'source'} . "\n"; + my $r = + Git::Repository->new( work_tree => $working_source_dir . $repo ); + print("\tChecking out $repos{$repo}{'branch'} branch\n"); + $r->command( checkout => $repos{$repo}{'branch'} ) + || die "Couldn't check out proper branch\n"; + } + else { + print( "Updating " . $repo . "\n" ); + my $r = + Git::Repository->new( work_tree => $working_source_dir . $repo ); + $r->command( checkout => $repos{$repo}{'branch'} ) + || die "Couldn't check out proper branch\n"; + $r->command( + pull => 'origin', + $repos{$repo}{'branch'}, { quiet => 1 } + ) || warn "Couldn't pull remotes\n"; + } } - #** # @section Locate Translation Files # @brief Find current set of translation files (.po) and copy to working area -#* -push(@todo, "add transifex github integration"); +#* +push( @todo, "add transifex github integration" ); -opendir(DIR, $stable_translations_dir) or warn "Couldn't open stable po dir: $!"; -my @po_files = glob "$stable_translations_dir*.po"; +opendir( DIR, $stable_translations_dir ) + or warn "Couldn't open stable po dir: $!"; +my @po_files = glob "$stable_translations_dir*.po"; closedir(DIR); -for (0..$#po_files){ - $po_files[$_] =~ s/^$stable_translations_dir//; +for ( 0 .. $#po_files ) { + $po_files[$_] =~ s/^$stable_translations_dir//; } #** # Copy stable PO file to working area. #* if (@po_files) { - foreach (@po_files) { - if ($verbosity == 1) { print("Copying $_ to $working_translations_dir\n"); } - copy($stable_translations_dir . $_, $working_translations_dir . $_) || die "Couldn't copy PO file $_: $!\n"; - } -} else { - warn "Couldn't find any stable PO files!\n"; + foreach (@po_files) { + if ( $verbosity == 1 ) { + print("Copying $_ to $working_translations_dir\n"); + } + copy( $stable_translations_dir . $_, $working_translations_dir . $_ ) + || die "Couldn't copy PO file $_: $!\n"; + } +} +else { + warn "Couldn't find any stable PO files!\n"; } #** @@ -186,7 +187,6 @@ # and separating strings from their tags. #* - #** # @brief File Scan Options # @param descend_filter define directories to be searched or ignored @@ -195,14 +195,16 @@ my $descend_filter = sub { $_ ne '.git' }; my $file_filter = sub { $_ =~ '.htm' or $_ =~ '.lua' }; my @working_source_files; -my $scan = File::Next::files( { - descend_filter => $descend_filter, - file_filter => $file_filter, - error_handler => sub { my $msg = shift; warn($msg) }, - }, - $working_source_dir); -while (defined(my $file = $scan->())) { - push(@working_source_files, $file); +my $scan = File::Next::files( + { + descend_filter => $descend_filter, + file_filter => $file_filter, + error_handler => sub { my $msg = shift; warn($msg) }, + }, + $working_source_dir +); +while ( defined( my $file = $scan->() ) ) { + push( @working_source_files, $file ); } #** @@ -218,23 +220,24 @@ #* my %stringtable; foreach my $file (@working_source_files) { - chomp $file; - if ($verbosity == 1) { print "Populating string table from $file\n"; } - # read file into $raw - if( open S, "< $file" ) { - local $/ = undef; - my $raw = ; - close S; - - my @res = Extract_Translations($raw); - if (@res) { - push(@{ $stringtable{$file} }, @res); - } - my @code = Extract_Luci_Translations($raw); - if (@code) { - push(@{ $stringtable{$file} }, @code); - } - } + chomp $file; + if ( $verbosity == 1 ) { print "Populating string table from $file\n"; } + + # read file into $raw + if ( open my $S, "<", $file ) { + local $/ = undef; + my $raw = <$S>; + close $S; + + my @res = Extract_Translations($raw); + if (@res) { + push( @{ $stringtable{$file} }, @res ); + } + my @code = Extract_Luci_Translations($raw); + if (@code) { + push( @{ $stringtable{$file} }, @code ); + } + } } #** @@ -245,38 +248,45 @@ # @see _Generate_PO_Header() # @see Write_PO_File() #* -foreach my $po_file (@po_files) { - if ($verbosity == 1) { print "Salvaging translations from $po_file\n"; } - my $translations = (); - # English file can be overwritten each time - - # NOTE: we don't care about anything but msgid changes - # and existence of previous translations - - # Generate id:str pairs for PO files - # NOTE: translations is a hash ref - unless ($po_file =~ m|-en.po$|) { - $translations = Fetch_Translations($working_translations_dir . $po_file); - } - - if ($verbosity == 1) { print "Generating file header for $po_file\n"; } - my @po_header = _Generate_PO_Header($working_translations_dir . $po_file); - - if ($verbosity == 1) { print "Generating file body for $po_file\n"; } - my @po_body = _Generate_PO_Body($working_translations_dir . $po_file, \%stringtable, $translations); - - # Write File - print "Writing to $working_translations_dir$po_file\n"; - Write_PO_File($working_translations_dir . $po_file, \@po_header, \@po_body); +foreach my $po_file (@po_files) { + if ( $verbosity == 1 ) { print "Salvaging translations from $po_file\n"; } + my $translations = (); + + # English file can be overwritten each time + + # NOTE: we don't care about anything but msgid changes + # and existence of previous translations + + # Generate id:str pairs for PO files + # NOTE: translations is a hash ref + unless ( $po_file =~ m|-en.po$| ) { + $translations = + Fetch_Translations( $working_translations_dir . $po_file ); + } + + if ( $verbosity == 1 ) { print "Generating file header for $po_file\n"; } + my @po_header = _Generate_PO_Header( $working_translations_dir . $po_file ); + + if ( $verbosity == 1 ) { print "Generating file body for $po_file\n"; } + my @po_body = _Generate_PO_Body( $working_translations_dir . $po_file, + \%stringtable, $translations ); + + # Write File + print "Writing to $working_translations_dir$po_file\n"; + Write_PO_File( $working_translations_dir . $po_file, + \@po_header, \@po_body ); } -print "\n\n\nFile generation complete.\nNew PO files can be found in $working_translations_dir\n"; +print +"\n\n\nFile generation complete.\nNew PO files can be found in $working_translations_dir\n"; # Upload to Transifex/GitHub # http://support.transifex.com/customer/portal/topics/440186-api/articles # print to do list -if ($verbosity == 1) { foreach (@todo) { print "$_\n"; } } +if ( $verbosity == 1 ) { + foreach (@todo) { print "$_\n"; } +} exit 0; @@ -287,54 +297,62 @@ # @retval res Unique translatable strings without translation tags #* sub Extract_Translations { - my $text = pop(@_); - my @res = (); - my $res = ""; - my $sub = ""; - # search $text for translate flags - while( $text =~ s/ ^ .*? (?:translate|translatef|i18n|_) [\n\s]* \( /(/sgx ) { - # separate usable $code from $text. $code and $text reverse of expected - ( my $code, $text ) = extract_bracketed($text, q{('")}); - # strip newlines and extra whitespace out of $code - $code =~ s/\\\n/ /g; - $code =~ s/^\([\n\s]*//; - $code =~ s/[\n\s]*\)$//; - - - - # Check code for quoted text. Store in $sub - if( $code =~ /^['"]/ ) { - while( defined $sub ) { - ( $sub, $code ) = extract_delimited($code, q{'"}, q{\s*(?:\.\.\s*)?}); - - if( defined $sub && length($sub) > 2 ) { - # use sub to build $res - $res .= substr $sub, 1, length($sub) - 2; - } else { - undef $sub; - } - } - # Check code for tagged text. store in $res - } elsif( $code =~ /^(\[=*\[)/ ) { - my $stag = quotemeta $1; - my $etag = $stag; - $etag =~ s/\[/]/g; - - ( $res ) = extract_tagged($code, $stag, $etag); - - $res =~ s/^$stag//; - $res =~ s/$etag$//; - } - - # Strip superfluous strings out of $res - $res = dec_lua_str($res); - if ($res) { - push(@res, $res); - } - # add $res to %stringtable - } - @res = uniq(@res); - return(@res); + my $text = pop(@_); + my @res = (); + my $res = ""; + my $sub = ""; + + # search $text for translate flags + while ( + $text =~ s/ ^ .*? (?:translate|translatef|i18n|_) [\n\s]* \( /(/sgx ) + { + # separate usable $code from $text. $code and $text reverse of expected + ( my $code, $text ) = extract_bracketed( $text, q{('")} ); + + # strip newlines and extra whitespace out of $code + $code =~ s/\\\n/ /g; + $code =~ s/^\([\n\s]*//; + $code =~ s/[\n\s]*\)$//; + + # Check code for quoted text. Store in $sub + if ( $code =~ /^['"]/ ) { + while ( defined $sub ) { + ( $sub, $code ) = + extract_delimited( $code, q{'"}, q{\s*(?:\.\.\s*)?} ); + + if ( defined $sub && length($sub) > 2 ) { + + # use sub to build $res + $res .= substr $sub, 1, length($sub) - 2; + } + else { + undef $sub; + } + } + + # Check code for tagged text. store in $res + } + elsif ( $code =~ /^(\[=*\[)/ ) { + my $stag = quotemeta $1; + my $etag = $stag; + $etag =~ s/\[/]/g; + + ($res) = extract_tagged( $code, $stag, $etag ); + + $res =~ s/^$stag//; + $res =~ s/$etag$//; + } + + # Strip superfluous strings out of $res + $res = dec_lua_str($res); + if ($res) { + push( @res, $res ); + } + + # add $res to %stringtable + } + @res = uniq(@res); + return (@res); } #** @@ -344,18 +362,18 @@ sub Extract_Translations { # @retval res Unique translatable strings without translation tags #* sub Extract_Luci_Translations { - my $text = pop(@_); - my @code; - while( $text =~ s/ ^ .*? <% -? [:_] /<%/sgx ) { - ( my $code, $text ) = extract_tagged($text, '<%', '%>'); - - if( defined $code ) { - $code = dec_tpl_str(substr $code, 2, length($code) - 4); - } - push(@code, $code); - } - @code = uniq(@code); - return(@code); + my $text = pop(@_); + my @code; + while ( $text =~ s/ ^ .*? <% -? [:_] /<%/sgx ) { + ( my $code, $text ) = extract_tagged( $text, '<%', '%>' ); + + if ( defined $code ) { + $code = dec_tpl_str( substr $code, 2, length($code) - 4 ); + } + push( @code, $code ); + } + @code = uniq(@code); + return (@code); } #** @@ -364,16 +382,15 @@ sub Extract_Luci_Translations { # @brief Strip translation tags # @see Extract_Translations #* -sub dec_lua_str -{ - my $s = shift; - $s =~ s/[\s\n]+/ /g; - $s =~ s/\\n/\n/g; - $s =~ s/\\t/\t/g; - $s =~ s/\\(.)/$1/g; - $s =~ s/^ //; - $s =~ s/ $//; - return $s; +sub dec_lua_str { + my $s = shift; + $s =~ s/[\s\n]+/ /g; + $s =~ s/\\n/\n/g; + $s =~ s/\\t/\t/g; + $s =~ s/\\(.)/$1/g; + $s =~ s/^ //; + $s =~ s/ $//; + return $s; } #** @@ -382,15 +399,14 @@ sub dec_lua_str # @brief Strip translation tags # @see Extract_Luci_Translations #* -sub dec_tpl_str -{ - my $s = shift; - $s =~ s/-$//; - $s =~ s/[\s\n]+/ /g; - $s =~ s/^ //; - $s =~ s/ $//; - $s =~ s/\\/\\\\/g; - return $s; +sub dec_tpl_str { + my $s = shift; + $s =~ s/-$//; + $s =~ s/[\s\n]+/ /g; + $s =~ s/^ //; + $s =~ s/ $//; + $s =~ s/\\/\\\\/g; + return $s; } #** @@ -400,33 +416,37 @@ sub dec_tpl_str # @retval translations Uses string:translation as key:value to be checked against new strings #* sub Fetch_Translations { - my $working_po_file = pop(@_); - if ($verbosity == 1) { print "Getting translations for $working_po_file\n"; } - my %translations; - open(WPO, "< $working_po_file") || die "Couldn't open translation file: $!\n"; - my @wpo = ; - close(WPO); - for (my $i = 0; $i < $#wpo; $i++) { - local $/ = ""; - chomp($wpo[$i]); - if ($wpo[$i] =~ m|^msgid|) { - my $mid = $wpo[$i]; - unless ($wpo[$i+1] =~ m|^msgstr|) { - $mid = $mid . $wpo[$i+1]; - } else { - my $mstr = $wpo[$i+1]; - # quote removal might be better with extract - $mid =~ s|^msgid "?||; - $mid =~ s|"?$||; - $mstr =~ s|^msgstr "?||; - $mstr =~ s|"?$||; - chomp($mstr); - $translations{$mid} = $mstr; - } - } - - } - return \%translations; + my $working_po_file = pop(@_); + if ( $verbosity == 1 ) { + print "Getting translations for $working_po_file\n"; + } + my %translations; + open( my $WPO, "<", $working_po_file ) + || die "Couldn't open translation file: $!\n"; + my @wpo = <$WPO>; + close($WPO); + for ( my $i = 0 ; $i < $#wpo ; $i++ ) { + local $/ = ""; + chomp( $wpo[$i] ); + if ( $wpo[$i] =~ m|^msgid| ) { + my $mid = $wpo[$i]; + unless ( $wpo[ $i + 1 ] =~ m|^msgstr| ) { + $mid = $mid . $wpo[ $i + 1 ]; + } + else { + my $mstr = $wpo[ $i + 1 ]; + + # quote removal might be better with extract + $mid =~ s|^msgid "?||; + $mid =~ s|"?$||; + $mstr =~ s|^msgstr "?||; + $mstr =~ s|"?$||; + chomp($mstr); + $translations{$mid} = $mstr; + } + } + } + return \%translations; } #** @@ -436,24 +456,26 @@ sub Fetch_Translations { # @retval po_header Array containing header lines #* sub _Generate_PO_Header { - my $working_po_file = pop; - my @po_header; - # DateTime is probably overkill - my $dt = DateTime->now(); - $dt = $dt.'\n"'; - #my $date = strftime "%Y-%m-%d %R\n"; - #"PO-Revision-Date: 2013-08-16 20:50+0000\n" - open(WPF, "< $working_po_file"); - while() { - chomp; - if ($_ =~ m|^\"PO-Revision-Date\:\ |) { - $_ = $& . $dt; - } - push(@po_header, $_); - last if ($_ =~ m|^"Plural|); - } - close(WPF); - return(@po_header); + my $working_po_file = pop; + my @po_header; + + # DateTime is probably overkill + my $dt = DateTime->now(); + $dt = $dt . '\n"'; + + #my $date = strftime "%Y-%m-%d %R\n"; + #"PO-Revision-Date: 2013-08-16 20:50+0000\n" + open( my $WPF, "<", $working_po_file ); + while (<$WPF>) { + chomp; + if ( $_ =~ m|^\"PO-Revision-Date\:\ | ) { + $_ = $& . $dt; + } + push( @po_header, $_ ); + last if ( $_ =~ m|^"Plural| ); + } + close($WPF); + return (@po_header); } #** @@ -465,33 +487,35 @@ sub _Generate_PO_Header { # @retval wps Array containing header lines #* sub _Generate_PO_Body { - my ($working_po_file, $stringtable, $translations) = @_; - my @wps = (); - - # Get k:v else write k:msgstr - foreach my $f (keys %{$stringtable}) { - chomp($f); - push(@wps, "#: $f"); - foreach my $id ( @{$stringtable->{$f}} ) { - my $str; - if ($working_po_file =~ m|-en.po$|) { - $str = 'msgstr "' . $id . '"'; - } else { - # need to do better string extraction - if ( exists $translations->{$id} ) { - $str = 'msgstr "' . $translations->{$id} . '"'; - } else { - $str = 'msgstr ""'; - } - } - my $mid = 'msgid "' . $id . '"'; - push(@wps, $mid); - push(@wps, $str); - } - push(@wps, "\n"); - } - @wps = uniq(@wps); - return(@wps); + my ( $working_po_file, $stringtable, $translations ) = @_; + my @wps = (); + + # Get k:v else write k:msgstr + foreach my $f ( keys %{$stringtable} ) { + chomp($f); + push( @wps, "#: $f" ); + foreach my $id ( @{ $stringtable->{$f} } ) { + my $str; + if ( $working_po_file =~ m|-en.po$| ) { + $str = 'msgstr "' . $id . '"'; + } + else { + # need to do better string extraction + if ( exists $translations->{$id} ) { + $str = 'msgstr "' . $translations->{$id} . '"'; + } + else { + $str = 'msgstr ""'; + } + } + my $mid = 'msgid "' . $id . '"'; + push( @wps, $mid ); + push( @wps, $str ); + } + push( @wps, "\n" ); + } + @wps = uniq(@wps); + return (@wps); } #** @@ -502,18 +526,20 @@ sub _Generate_PO_Body { # @param po_body Array containing all new translatable strings and any relevant translations #* sub Write_PO_File { - # NOTE: po_header and po_body are array references - my ($working_po_file, $po_header, $po_body) = @_; - - open(WPO, "> $working_po_file") || die "Couldn't open $working_po_file: $!\n"; - foreach (@{$po_header}) { - print WPO $_,"\n"; - } - foreach (@{$po_body}) { - print WPO $_,"\n"; - } - close(WPO); - return; + + # NOTE: po_header and po_body are array references + my ( $working_po_file, $po_header, $po_body ) = @_; + + open( my $WPO, ">", $working_po_file ) + || die "Couldn't open $working_po_file: $!\n"; + foreach ( @{$po_header} ) { + print $WPO $_, "\n"; + } + foreach ( @{$po_body} ) { + print $WPO $_, "\n"; + } + close($WPO); + return; } #** @@ -523,20 +549,21 @@ sub Write_PO_File { # @retval r List of unique elements #* sub uniq { - my %seen = (); - my @r = (); - #** - # Does not handle multidimensional arrays well - #* - foreach my $item (@_) { - if ($item eq 'msgstr ""' || $item eq '\n') { - push(@r, $item); - next; - } - unless ($seen{$item}) { - push @r, $item; - $seen{$item} = 1; - } - } - return @r; + my %seen = (); + my @r = (); + + #** + # Does not handle multidimensional arrays well + #* + foreach my $item (@_) { + if ( $item eq 'msgstr ""' || $item eq '\n' ) { + push( @r, $item ); + next; + } + unless ( $seen{$item} ) { + push @r, $item; + $seen{$item} = 1; + } + } + return @r; }