2 # BEGIN BPS TAGGED BLOCK {{{
6 # This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
7 # <jesse@bestpractical.com>
9 # (Except where explicitly superseded by other copyright notices)
14 # This work is made available to you under the terms of Version 2 of
15 # the GNU General Public License. A copy of that license should have
16 # been provided with this software, but in any event can be snarfed
19 # This work is distributed in the hope that it will be useful, but
20 # WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 # General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 # 02110-1301 or visit their web page on the internet at
28 # http://www.gnu.org/copyleft/gpl.html.
31 # CONTRIBUTION SUBMISSION POLICY:
33 # (The following paragraph is not intended to limit the rights granted
34 # to you to modify and distribute this software under the terms of
35 # the GNU General Public License and is only of importance to you if
36 # you choose to contribute your changes and enhancements to the
37 # community by submitting them to Best Practical Solutions, LLC.)
39 # By intentionally submitting any modifications, corrections or
40 # derivatives to this work, or any other work intended for use with
41 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
42 # you are the copyright holder for those contributions and you grant
43 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
44 # royalty-free, perpetual, license to use, copy, create derivative
45 # works based on those contributions, and sublicense and distribute
46 # those contributions and any derivatives thereof.
48 # END BPS TAGGED BLOCK }}}
49 # Portions Copyright 2002 Autrijus Tang <autrijus@autrijus.org>
58 use vars qw($DEBUG $FILECAT);
62 @ARGV = <lib/RT/I18N/*.po> unless @ARGV;
66 # extract all strings and stuff them into $FILECAT
67 File::Find::find( { wanted => \&extract_strings_from_code, follow => 1 }, '.' );
69 # ensure proper escaping and [_1] => %1 transformation
70 foreach my $str ( sort keys %{$FILECAT} ) {
71 my $entry = $FILECAT->{$str};
76 $str =~ s/((?<!~)(?:~~)*)\[_(\d+)\]/$1%$2/g;
77 $str =~ s/((?<!~)(?:~~)*)\[([A-Za-z#*]\w*),([^\]]+)\]/"$1%$2(".escape($3).")"/eg;
78 $str =~ s/~([\[\]])/$1/g;
80 delete $FILECAT->{$oldstr};
81 $FILECAT->{$str} = $entry;
84 # update all language dictionaries
85 foreach my $dict (@ARGV) {
86 $dict = "lib/RT/I18N/$dict.po" unless -f $dict or $dict =~ m!/!;
96 # {{{ pull strings out of the code.
98 sub extract_strings_from_code {
103 return if ( $File::Find::dir =~ 'lib/blib|lib/t/autogen|var|m4|local|\.svn' );
104 return if ( /\.po$|\.bak$|~|,D|,B$|extract-message-catalog$/ );
105 return if ( /^[\.#]/ );
106 return if ( -f "$_.in" );
108 print "Looking at $File::Find::name\n";
109 my $filename = $File::Find::name;
110 $filename =~ s'^\./'';
111 $filename =~ s'\.in$'';
113 unless (open _, $file) {
114 print "Cannot open $file for reading ($!), skipping.\n";
120 # Mason filter: <&|/l>...</&>
122 while (m!\G.*?<&\|/l(.*?)&>(.*?)</&>!sg) {
123 my ( $vars, $str ) = ( $1, $2 );
124 $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
126 #print "STR IS $str\n";
127 push @{ $FILECAT->{$str} }, [ $filename, $line, $vars ];
130 # Localization function: loc(...)
133 while (m/\G.*?\bloc$RE{balanced}{-parens=>'()'}{-keep}/sg) {
135 $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
139 /\(\s*($RE{delimited}{-delim=>q{'"}}{-keep})(.*?)\s*\)$/ ) {
141 $str = substr( $1, 1, -1 ); # $str comes before $vars now
148 $vars =~ s/[\n\r]//g;
151 push @{ $FILECAT->{$str} }, [ $filename, $line, $vars ];
154 # Comment-based mark: "..." # loc
157 while (m/\G.*?($RE{delimited}{-delim=>q{'"}}{-keep})[\}\)\],]*\s*\#\s*loc\s*$/smg) {
158 my $str = substr($1, 1, -1);
159 $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
161 push @{ $FILECAT->{$str} }, [ $filename, $line, '' ];
164 # Comment-based pair mark: "..." => "..." # loc_pair
167 while (m/\G.*?(\w+)\s*=>\s*($RE{delimited}{-delim=>q{'"}}{-keep})[\}\)\],]*\s*\#\s*loc_pair\s*$/smg) {
169 my $val = substr($2, 1, -1);
170 $line += ( () = ( $& =~ /\n/g ) ); # cryptocontext!
173 push @{ $FILECAT->{$key} }, [ $filename, $line, '' ];
174 push @{ $FILECAT->{$val} }, [ $filename, $line, '' ];
179 # }}} extract from strings
184 my ( %Lexicon, %Header);
187 unless (!-e $file or -w $file) {
188 warn "Can't write to $lang, skipping...\n";
192 print "Updating $lang...\n";
195 @lines = (<LEXICON>) if open (LEXICON, $file);
196 @lines = grep { !/^(#(:|\.)\s*|$)/ } @lines;
199 $msghdr .= shift @lines while ( $lines[0] && $lines[0] !~ /^msgid/ );
201 my $msgid = shift @lines;
203 $msgstr .= shift @lines while ( $lines[0] && $lines[0] =~ /^(msgstr|")/ );
209 $msgid =~ s/^msgid "(.*)"\s*?$/$1/ms or warn "$msgid in $file";
210 $msgstr =~ s/^msgstr "(.*)"\s*?$/$1/ms or warn "$msgstr in $file";
212 $Lexicon{$msgid} = $msgstr;
213 $Header{$msgid} = $msghdr;
216 my $is_english = ( $lang =~ /^en(?:[^A-Za-z]|$)/ );
218 foreach my $str ( sort keys %{$FILECAT} ) {
219 $Lexicon{$str} ||= '';;
221 foreach ( sort keys %Lexicon ) {
222 my $f = join ( ' ', sort map $_->[0].":".$_->[1], @{ $FILECAT->{$_} } );
226 if ( !$Lexicon{$_} and $Lexicon{$nospace} ) {
228 $Lexicon{$nospace} . ( ' ' x ( length($_) - length($nospace) ) );
231 next if !length( $Lexicon{$_} ) and $is_english;
234 $out .= $Header{$_} if exists $Header{$_};
238 next if (!$f && $_ && !$Lexicon{$_});
239 if ( $f && $f !~ /^\s+$/ ) {
244 $out .= "#: NOT FOUND IN SOURCE\n";
246 foreach my $entry ( grep { $_->[2] } @{ $FILECAT->{$_} } ) {
247 my ( $file, $line, $var ) = @{$entry};
248 $var =~ s/^\s*,\s*//;
250 $out .= "#. ($var)\n" unless $seen{$var}++;
252 $out .= "msgid \"$_\"\nmsgstr \"$Lexicon{$_}\"\n\n";
255 open PO, ">$file" or die $!;
264 $text =~ s/\b_(\d+)/%$1/;
270 # c-indentation-style: bsd
272 # indent-tabs-mode: nil
274 # vim: expandtab shiftwidth=4: