Projekt

Allgemein

Profil

Herunterladen (22,7 KB) Statistiken
| Zweig: | Markierung: | Revision:
faeeee8a Sven Schöling
#!/usr/bin/perl

15dc9376 Bernd Bleßmann
# this version of locales processes not only all required .pl files
faeeee8a Sven Schöling
# but also all parse_html_templated files.

05c6840d Moritz Bunkus
use utf8;
faeeee8a Sven Schöling
use strict;

7cdfd5a9 Thomas Heck
BEGIN {
cff913a1 Moritz Bunkus
use FindBin;

unshift(@INC, $FindBin::Bin . '/../modules/override'); # Use our own versions of various modules (e.g. YAML).
push (@INC, $FindBin::Bin . '/..');
7cdfd5a9 Thomas Heck
}

880bc0d2 Moritz Bunkus
use Carp;
bc08036a Moritz Bunkus
use Cwd;
faeeee8a Sven Schöling
use Data::Dumper;
use English;
880bc0d2 Moritz Bunkus
use File::Slurp qw(slurp);
faeeee8a Sven Schöling
use FileHandle;
use Getopt::Long;
880bc0d2 Moritz Bunkus
use IO::Dir;
1b415045 Moritz Bunkus
use List::MoreUtils qw(apply);
faeeee8a Sven Schöling
use List::Util qw(first);
use Pod::Usage;
a1ea659f Sven Schöling
use SL::DBUpgrade2;
cff913a1 Moritz Bunkus
use SL::System::Process;
65b2387a Moritz Bunkus
use SL::YAML;
faeeee8a Sven Schöling
$OUTPUT_AUTOFLUSH = 1;

my $opt_v = 0;
my $opt_c = 0;
f16a89c9 Sven Schöling
my $opt_f = 0;
faeeee8a Sven Schöling
my $debug = 0;

parse_args();

10b4930b Thomas Heck
my $locale;
984d50b3 Moritz Bunkus
my $basedir = "../..";
my $locales_dir = ".";
faeeee8a Sven Schöling
my $bindir = "$basedir/bin/mozilla";
b6ae20da Moritz Bunkus
my @progdirs = ( "$basedir/SL" );
b6948612 Geoffrey Richardson
my @menufiles = glob("${basedir}/menus/*/*");
f60d998c Sven Schöling
my @webpages = qw(webpages mobile_webpages design40_webpages);
my @javascript_dirs = ($basedir .'/js', map { $basedir .'/templates/' . $_ } @webpages);
10b4930b Thomas Heck
my $javascript_output_dir = $basedir .'/js';
faeeee8a Sven Schöling
my $submitsearch = qr/type\s*=\s*[\"\']?submit/i;
4d8ad1a5 Moritz Bunkus
our $self = {};
our $missing = {};
our @lost = ();
faeeee8a Sven Schöling
24bff6e4 Moritz Bunkus
my %ignore_unused_templates = (
a9c191de Sven Schöling
map { $_ => 1 } qw(ct/testpage.html oe/periodic_invoices_email.txt part/testpage.html t/render.html t/render.js task_server/failure_notification_email.txt
3a77cbe1 Bernd Bleßmann
failed_background_jobs_report/email.txt presenter/items_list/items_list.txt)
24bff6e4 Moritz Bunkus
);

224291dd Thomas Heck
my (%referenced_html_files, %locale, %htmllocale, %alllocales, %cached, %submit, %jslocale);
faeeee8a Sven Schöling
my ($ALL_HEADER, $MISSING_HEADER, $LOST_HEADER);

init();

b6ae20da Moritz Bunkus
sub find_files {
880bc0d2 Moritz Bunkus
my ($top_dir_name) = @_;
faeeee8a Sven Schöling
880bc0d2 Moritz Bunkus
my (@files, $finder);
b6ae20da Moritz Bunkus
880bc0d2 Moritz Bunkus
$finder = sub {
my ($dir_name) = @_;
b6ae20da Moritz Bunkus
880bc0d2 Moritz Bunkus
tie my %dir_h, 'IO::Dir', $dir_name;
b6ae20da Moritz Bunkus
880bc0d2 Moritz Bunkus
push @files, grep { -f } map { "${dir_name}/${_}" } keys %dir_h;
my @sub_dirs = grep { -d } map { "${dir_name}/${_}" } grep { ! m/^\.\.?$/ } keys %dir_h;
b6ae20da Moritz Bunkus
880bc0d2 Moritz Bunkus
$finder->($_) for @sub_dirs;
};
b6ae20da Moritz Bunkus
880bc0d2 Moritz Bunkus
$finder->($top_dir_name);
b6ae20da Moritz Bunkus
880bc0d2 Moritz Bunkus
return @files;
04b7fa25 Moritz Bunkus
}

4d8ad1a5 Moritz Bunkus
sub merge_texts {
# overwrite existing entries with the ones from 'missing'
$self->{texts}->{$_} = $missing->{$_} for grep { $missing->{$_} } keys %alllocales;

# try to set missing entries from lost ones
my %lost_by_text = map { ($_->{text} => $_->{translation}) } @lost;
$self->{texts}->{$_} = $lost_by_text{$_} for grep { !$self->{texts}{$_} } keys %alllocales;
}

b6ae20da Moritz Bunkus
my @bindir_files = find_files($bindir);
my @progfiles = map { m:^(.+)/([^/]+)$:; [ $2, $1 ] } grep { /\.pl$/ && !/_custom/ } @bindir_files;
my @customfiles = grep /_custom/, @bindir_files;

push @progfiles, map { m:^(.+)/([^/]+)$:; [ $2, $1 ] } grep { /\.pm$/ } map { find_files($_) } @progdirs;

9b36c563 Moritz Bunkus
my %dir_h;
faeeee8a Sven Schöling
8e34e6dc Moritz Bunkus
my @dbplfiles;
0e43d3cf Moritz Bunkus
foreach my $sub_dir ("Pg-upgrade2", "Pg-upgrade2-auth") {
8e34e6dc Moritz Bunkus
my $dir = "$basedir/sql/$sub_dir";
tie %dir_h, 'IO::Dir', $dir;
push @dbplfiles, map { [ $_, $dir ] } grep { /\.pl$/ } keys %dir_h;
}
faeeee8a Sven Schöling
# slurp the translations in
if (-f "$locales_dir/all") {
require "$locales_dir/all";
}
032056b3 Jan Büren
# load custom translation (more_texts)
for my $file (glob("${locales_dir}/more/*")) {
if (open my $in, "<", "$file") {
local $/ = undef;
my $code = <$in>;
eval($code);
close($in);
$self->{more_texts_temp}{$_} = $self->{more_texts}{$_} for keys %{ $self->{more_texts} };
}
}
$self->{more_texts} = delete $self->{more_texts_temp};

faeeee8a Sven Schöling
if (-f "$locales_dir/missing") {
require "$locales_dir/missing" ;
unlink "$locales_dir/missing";
}
if (-f "$locales_dir/lost") {
require "$locales_dir/lost";
unlink "$locales_dir/lost";
}

my %old_texts = %{ $self->{texts} || {} };

880bc0d2 Moritz Bunkus
handle_file(@{ $_ }) for @progfiles;
8e34e6dc Moritz Bunkus
handle_file(@{ $_ }) for @dbplfiles;
502ca2d1 Moritz Bunkus
scanmenu($_) for @menufiles;
a1ea659f Sven Schöling
scandbupgrades();
faeeee8a Sven Schöling
6fd511ee Moritz Bunkus
for my $file_name (grep { /\.(?:js|html)$/i } map({find_files($_)} @javascript_dirs)) {
224291dd Thomas Heck
scan_javascript_file($file_name);
}

4d8ad1a5 Moritz Bunkus
# merge entries to translate with entries from files 'missing' and 'lost'
merge_texts();

7438b9e6 Bernd Bleßmann
# Generate "all" without translations in more_texts.
# But keep the ones which are in both old_texts (texts) and more_texts,
# because this are ones which are overwritten in more_texts for custom usage.
my %to_keep;
$to_keep{$_} = 1 for grep { !!$self->{more_texts}{$_} } keys %old_texts;
my @new_all = grep { $to_keep{$_} || !$self->{more_texts}{$_} } sort keys %alllocales;

faeeee8a Sven Schöling
generate_file(
file => "$locales_dir/all",
header => $ALL_HEADER,
data_name => '$self->{texts}',
7438b9e6 Bernd Bleßmann
data_sub => sub { _print_line($_, $self->{texts}{$_}, @_) for @new_all },
faeeee8a Sven Schöling
);

bc08036a Moritz Bunkus
open(my $js_file, '>:encoding(utf8)', $javascript_output_dir .'/locale/'. $locale .'.js') || die;
440ad8d3 Thomas Heck
print $js_file 'namespace("kivi").setupLocale({';
224291dd Thomas Heck
my $first_entry = 1;
for my $key (sort(keys(%jslocale))) {
dfdb5c36 Bernd Bleßmann
my $trans = $self->{more_texts}{$key} // $self->{texts}{$key};
print $js_file ((!$first_entry ? ',' : '') ."\n". _double_quote($key) .':'. _double_quote($trans));
224291dd Thomas Heck
$first_entry = 0;
}
440ad8d3 Thomas Heck
print $js_file ("\n");
print $js_file ('});'."\n");
224291dd Thomas Heck
close($js_file);

4d8ad1a5 Moritz Bunkus
foreach my $text (keys %$missing) {
if ($locale{$text} || $htmllocale{$text}) {
unless ($self->{texts}{$text}) {
$self->{texts}{$text} = $missing->{$text};
}
}
}


faeeee8a Sven Schöling
# calc and generate missing
032056b3 Jan Büren
# don't add missing ones if we have a translation in more_texts
my @new_missing = grep { !$self->{more_texts}{$_} && !$self->{texts}{$_} } sort keys %alllocales;
faeeee8a Sven Schöling
4d8ad1a5 Moritz Bunkus
if (@new_missing) {
27a422da Moritz Bunkus
if ($opt_c) {
my %existing_lc = map { (lc $_ => $_) } grep { $self->{texts}->{$_} } keys %{ $self->{texts} };
foreach my $entry (@new_missing) {
my $other = $existing_lc{lc $entry};
print "W: No entry for '${entry}' exists, but there is one with different case: '${other}'\n" if $other;
}
}

f16a89c9 Sven Schöling
if ($opt_f) {
for my $string (@new_missing) {
print "new string '$string' in files:\n";
1b415045 Moritz Bunkus
print join "",
map { " $_\n" }
apply { s{^(?:\.\./)+}{} }
grep { $cached{$_}{all}{$string} }
keys %cached;
f16a89c9 Sven Schöling
}
}

faeeee8a Sven Schöling
generate_file(
file => "$locales_dir/missing",
header => $MISSING_HEADER,
data_name => '$missing',
4d8ad1a5 Moritz Bunkus
data_sub => sub { _print_line($_, '', @_) for @new_missing },
faeeee8a Sven Schöling
);
}

# calc and generate lost
while (my ($text, $translation) = each %old_texts) {
next if ($alllocales{$text});
push @lost, { 'text' => $text, 'translation' => $translation };
}

if (scalar @lost) {
splice @lost, 0, (scalar @lost - 50) if (scalar @lost > 50);
generate_file(
file => "$locales_dir/lost",
header => $LOST_HEADER,
delim => '()',
data_name => '@lost',
data_sub => sub {
5f129239 Sven Schöling
_print_line($_->{text}, $_->{translation}, @_, template => " { 'text' => %s, 'translation' => %s },\n") for @lost;
faeeee8a Sven Schöling
},
);
}

my $trlanguage = slurp("$locales_dir/LANGUAGE");
chomp $trlanguage;

search_unused_htmlfiles() if $opt_c;

my $count = scalar keys %alllocales;
4d8ad1a5 Moritz Bunkus
my $notext = scalar @new_missing;
faeeee8a Sven Schöling
my $per = sprintf("%.1f", ($count - $notext) / $count * 100);
print "\n$trlanguage - ${per}%";
print " - $notext/$count missing" if $notext;
print "\n";

exit;

# eom

sub init {
$ALL_HEADER = <<EOL;
# These are all the texts to build the translations files.
# The file has the form of 'english text' => 'foreign text',
# you can add the translation in this file or in the 'missing' file
# run locales.pl from this directory to rebuild the translation files
EOL
$MISSING_HEADER = <<EOL;
# add the missing texts and run locales.pl to rebuild
EOL
$LOST_HEADER = <<EOL;
8317c23b Sven Donath
# The last 50 text strings, that have been removed.
1de53cbf Sven Donath
# This file has been auto-generated by locales.pl. Please don't edit!
faeeee8a Sven Schöling
EOL
}

sub parse_args {
my ($help, $man);

03c6a4f5 Moritz Bunkus
my ($opt_no_c, $ignore_for_compatiblity);

faeeee8a Sven Schöling
GetOptions(
03c6a4f5 Moritz Bunkus
'check-files' => \$ignore_for_compatiblity,
'no-check-files' => \$opt_no_c,
faeeee8a Sven Schöling
'verbose' => \$opt_v,
f16a89c9 Sven Schöling
'filenames' => \$opt_f,
faeeee8a Sven Schöling
'help' => \$help,
'man' => \$man,
'debug' => \$debug,
);

03c6a4f5 Moritz Bunkus
$opt_c = !$opt_no_c;

faeeee8a Sven Schöling
if ($help) {
pod2usage(1);
exit 0;
}

if ($man) {
pod2usage(-exitstatus => 0, -verbose => 2);
exit 0;
}

984d50b3 Moritz Bunkus
if (@ARGV) {
my $arg = shift @ARGV;
my $ok = 0;
foreach my $dir ("../locale/$arg", "locale/$arg", "../$arg", $arg) {
next unless -d $dir && -f "$dir/all" && -f "$dir/LANGUAGE";
10b4930b Thomas Heck
$locale = $arg;

984d50b3 Moritz Bunkus
$ok = chdir $dir;
last;
}

if (!$ok) {
print "The locale directory '$arg' could not be found.\n";
exit 1;
}

} elsif (!-f 'all' || !-f 'LANGUAGE') {
print "locales.pl was not called from a locale/* subdirectory,\n"
. "and no locale directory name was given.\n";
exit 1;
}
bc08036a Moritz Bunkus
$locale ||= (grep { $_ } split m:/:, getcwd())[-1];
$locale =~ s/\.+$//;
faeeee8a Sven Schöling
}

sub handle_file {
my ($file, $dir) = @_;
print "\n$file" if $opt_v;
%locale = ();
%submit = ();

&scanfile("$dir/$file");

# scan custom_{module}.pl or {login}_{module}.pl files
foreach my $customfile (@customfiles) {
if ($customfile =~ /_$file/) {
if (-f "$dir/$customfile") {
&scanfile("$dir/$customfile");
}
}
}

$file =~ s/\.pl//;
}

sub extract_text_between_parenthesis {
my ($fh, $line) = @_;
my ($inside_string, $pos, $text, $quote_next) = (undef, 0, "", 0);

while (1) {
if (length($line) <= $pos) {
$line = <$fh>;
return ($text, "") unless ($line);
$pos = 0;
}

my $cur_char = substr($line, $pos, 1);

if (!$inside_string) {
if ((length($line) >= ($pos + 3)) && (substr($line, $pos, 2)) eq "qq") {
$inside_string = substr($line, $pos + 2, 1);
$pos += 2;

} elsif ((length($line) >= ($pos + 2)) &&
(substr($line, $pos, 1) eq "q")) {
$inside_string = substr($line, $pos + 1, 1);
$pos++;

} elsif (($cur_char eq '"') || ($cur_char eq '\'')) {
$inside_string = $cur_char;

} elsif (($cur_char eq ")") || ($cur_char eq ',')) {
return ($text, substr($line, $pos + 1));
}

} else {
if ($quote_next) {
673c231d Moritz Bunkus
$text .= '\\' unless $cur_char eq "'";
faeeee8a Sven Schöling
$text .= $cur_char;
$quote_next = 0;

} elsif ($cur_char eq '\\') {
$quote_next = 1;

} elsif ($cur_char eq $inside_string) {
undef($inside_string);

} else {
$text .= $cur_char;

}
}
$pos++;
}
}

sub scanfile {
my $file = shift;
my $dont_include_subs = shift;
my $scanned_files = shift;

# sanitize file
$file =~ s=/+=/=g;

$scanned_files = {} unless ($scanned_files);
return if ($scanned_files->{$file});
$scanned_files->{$file} = 1;

if (!defined $cached{$file}) {

return unless (-f "$file");

my $fh = new FileHandle;
71233175 Moritz Bunkus
open $fh, '<:encoding(utf8)', $file or die "$! : $file";
faeeee8a Sven Schöling
my ($is_submit, $line_no, $sub_line_no) = (0, 0, 0);

while (<$fh>) {
bdc7aa84 Moritz Bunkus
last if /^\s*__END__/;

faeeee8a Sven Schöling
$line_no++;

# is this another file
if (/require\s+\W.*\.pl/) {
my $newfile = $&;
$newfile =~ s/require\s+\W//;
$newfile =~ s|bin/mozilla||;
$cached{$file}{scan}{"$bindir/$newfile"} = 1;
} elsif (/use\s+SL::([\w:]*)/) {
my $module = $1;
$module =~ s|::|/|g;
$cached{$file}{scannosubs}{"../../SL/${module}.pm"} = 1;
}

9a18b6cc Moritz Bunkus
# Some calls to render() are split over multiple lines. Deal
# with that.
while (/(?:parse_html_template2?|render)\s*\( *$/) {
$_ .= <$fh>;
chomp;
}

faeeee8a Sven Schöling
# is this a template call?
c0f198fa Moritz Bunkus
if (/(?:parse_html_template2?|render)\s*\(\s*[\"\']([\w\/]+)\s*[\"\']/) {
ed2d834f Sven Schöling
my $new_file_name = $1;
faeeee8a Sven Schöling
if (/parse_html_template2/) {
ed2d834f Sven Schöling
print "E: " . strip_base($file) . " is still using 'parse_html_template2' for $new_file_name.html.\n";
faeeee8a Sven Schöling
}
2d6a42ee Moritz Bunkus
my $found_one = 0;
f60d998c Sven Schöling
for my $space (@webpages) {
ed2d834f Sven Schöling
for my $ext (qw(html js json)) {
my $new_file = "$basedir/templates/$space/$new_file_name.$ext";
if (-f $new_file) {
$cached{$file}{scanh}{$new_file} = 1;
print "." if $opt_v;
$found_one = 1;
}
2d6a42ee Moritz Bunkus
}
}

if ($opt_c && !$found_one) {
ed2d834f Sven Schöling
print "W: missing HTML template: $new_file_name.{html,json,js} (referenced from " . strip_base($file) . ")\n";
faeeee8a Sven Schöling
}
}

my $rc = 1;

while ($rc) {
if (/Locale/) {
unless (/^use /) {
224291dd Thomas Heck
my ($null, $country) = split(/,/);
faeeee8a Sven Schöling
$country =~ s/^ +[\"\']//;
$country =~ s/[\"\'].*//;
}
}

my $postmatch = "";

# is it a submit button before $locale->
if (/$submitsearch/) {
$postmatch = "$'";
if ($` !~ /locale->text/) {
$is_submit = 1;
$sub_line_no = $line_no;
}
}

24ea4061 Sven Schöling
my $found;
if (/ (?: locale->text | \b t8 ) \b .*? \(/x) {
$found = 1;
$postmatch = "$'";
}
faeeee8a Sven Schöling
if ($found) {
my $string;
($string, $_) = extract_text_between_parenthesis($fh, $postmatch);
$postmatch = $_;

# if there is no $ in the string record it
unless (($string =~ /\$\D.*/) || ("" eq $string)) {

# this guarantees one instance of string
$cached{$file}{locale}{$string} = 1;

# this one is for all the locales
$cached{$file}{all}{$string} = 1;

# is it a submit button before $locale->
if ($is_submit) {
$cached{$file}{submit}{$string} = 1;
}
}
} elsif ($postmatch =~ />/) {
$is_submit = 0;
}

# exit loop if there are no more locales on this line
67ca0f93 Moritz Bunkus
($rc) = ($postmatch =~ /locale->text | \b t8/x);
faeeee8a Sven Schöling
if ( ($postmatch =~ />/)
|| (!$found && ($sub_line_no != $line_no) && />/)) {
$is_submit = 0;
}
}
}

close($fh);

}

880bc0d2 Moritz Bunkus
$alllocales{$_} = 1 for keys %{$cached{$file}{all}};
$locale{$_} = 1 for keys %{$cached{$file}{locale}};
$submit{$_} = 1 for keys %{$cached{$file}{submit}};

scanfile($_, 0, $scanned_files) for keys %{$cached{$file}{scan}};
scanfile($_, 1, $scanned_files) for keys %{$cached{$file}{scannosubs}};
scanhtmlfile($_) for keys %{$cached{$file}{scanh}};
faeeee8a Sven Schöling
880bc0d2 Moritz Bunkus
$referenced_html_files{$_} = 1 for keys %{$cached{$file}{scanh}};
faeeee8a Sven Schöling
}

sub scanmenu {
my $file = shift;

65b2387a Moritz Bunkus
my $menu = SL::YAML::LoadFile($file);
faeeee8a Sven Schöling
b251cc22 Sven Schöling
for my $node (@$menu) {
a1ea659f Sven Schöling
# possible for override files
next unless exists $node->{name};

b251cc22 Sven Schöling
$locale{$node->{name}} = 1;
$alllocales{$node->{name}} = 1;
a1ea659f Sven Schöling
$cached{$file}{all}{$node->{name}} = 1;
}
}

sub scandbupgrades {
# we only need to do this for auth atm, because only auth scripts can include new rights, which are translateable
my $auth = 1;

cff913a1 Moritz Bunkus
my $dbu = SL::DBUpgrade2->new(auth => $auth, path => SL::System::Process->exe_dir . '/sql/Pg-upgrade2-auth');
a1ea659f Sven Schöling
for my $upgrade ($dbu->sort_dbupdate_controls) {
for my $string (@{ $upgrade->{locales} || [] }) {
$locale{$string} = 1;
$alllocales{$string} = 1;
$cached{$upgrade->{tag}}{all}{$string} = 1;
}
faeeee8a Sven Schöling
}
}

sub unescape_template_string {
my $in = "$_[0]";
$in =~ s/\\(.)/$1/g;
return $in;
}

sub scanhtmlfile {
ed2d834f Sven Schöling
my ($file) = @_;
faeeee8a Sven Schöling
ea974a5a Moritz Bunkus
return if defined $cached{$file};
faeeee8a Sven Schöling
ed2d834f Sven Schöling
my $template_space = $file =~ m{templates/(\w+)/} ? $1 : 'webpages';

ea974a5a Moritz Bunkus
my %plugins = ( 'loaded' => { }, 'needed' => { } );
faeeee8a Sven Schöling
ed2d834f Sven Schöling
my $fh;
if (!open($fh, '<:encoding(utf8)', $file)) {
ea974a5a Moritz Bunkus
print "E: template file '$file' not found\n";
return;
}
faeeee8a Sven Schöling
ea974a5a Moritz Bunkus
my $copying = 0;
my $issubmit = 0;
my $text = "";
ed2d834f Sven Schöling
while (my $line = <$fh>) {
ea974a5a Moritz Bunkus
chomp($line);
faeeee8a Sven Schöling
ea974a5a Moritz Bunkus
while ($line =~ m/\[\%[^\w]*use[^\w]+(\w+)[^\w]*?\%\]/gi) {
$plugins{loaded}->{$1} = 1;
}
faeeee8a Sven Schöling
ea974a5a Moritz Bunkus
while ($line =~ m/\[\%[^\w]*(\w+)\.\w+\(/g) {
my $plugin = $1;
$plugins{needed}->{$plugin} = 1 if (first { $_ eq $plugin } qw(HTML LxERP JavaScript JSON L P));
faeeee8a Sven Schöling
}

ea974a5a Moritz Bunkus
$plugins{needed}->{T8} = 1 if $line =~ m/\[\%.*\|.*\$T8/;

while ($line =~ m/(?: # Start von Variante 1: LxERP.t8('...'); ohne darumliegende [% ... %]-Tags
(LxERP\.t8)\( # LxERP.t8( ::Parameter $1::
([\'\"]) # Anfang des zu übersetzenden Strings ::Parameter $2::
(.*?) # Der zu übersetzende String ::Parameter $3::
(?<!\\)\2 # Ende des zu übersetzenden Strings
| # Start von Variante 2: [% '...' | $T8 %]
\[\% # Template-Start-Tag
[\-~#]? # Whitespace-Unterdrückung
\s* # Optional beliebig viele Whitespace
([\'\"]) # Anfang des zu übersetzenden Strings ::Parameter $4::
(.*?) # Der zu übersetzende String ::Parameter $5::
(?<!\\)\4 # Ende des zu übersetzenden Strings
\s*\|\s* # Pipe-Zeichen mit optionalen Whitespace davor und danach
(\$T8) # Filteraufruf ::Parameter $6::
.*? # Optionale Argumente für den Filter
\s* # Whitespaces
[\-~#]? # Whitespace-Unterdrückung
\%\] # Template-Ende-Tag
)
/ix) {
my $module = $1 || $6;
my $string = $3 || $5;
print "Found filter >>>$string<<<\n" if $debug;
substr $line, $LAST_MATCH_START[1], $LAST_MATCH_END[0] - $LAST_MATCH_START[0], '';

$string = unescape_template_string($string);
$cached{$file}{all}{$string} = 1;
$cached{$file}{html}{$string} = 1;
$cached{$file}{submit}{$string} = 1 if $PREMATCH =~ /$submitsearch/;
$plugins{needed}->{T8} = 1 if $module eq '$T8';
$plugins{needed}->{LxERP} = 1 if $module eq 'LxERP.t8';
}
faeeee8a Sven Schöling
ea974a5a Moritz Bunkus
while ($line =~ m/\[\% # Template-Start-Tag
40041962 Moritz Bunkus
[\-~#]* # Whitespace-Unterdrückung
ea974a5a Moritz Bunkus
\s* # Optional beliebig viele Whitespace
(?: # Die erkannten Template-Direktiven
PROCESS
|
INCLUDE
)
\s+ # Mindestens ein Whitespace
[\'\"]? # Anfang des Dateinamens
([^\s]+) # Beliebig viele Nicht-Whitespaces -- Dateiname
5b8a7012 Moritz Bunkus
\.(html|js) # Endung ".html" oder ".js", ansonsten kann es der Name eines Blocks sein
ea974a5a Moritz Bunkus
/ix) {
ed2d834f Sven Schöling
my $new_file_name = "$basedir/templates/$template_space/$1.$2";
ea974a5a Moritz Bunkus
$cached{$file}{scanh}{$new_file_name} = 1;
substr $line, $LAST_MATCH_START[1], $LAST_MATCH_END[0] - $LAST_MATCH_START[0], '';
faeeee8a Sven Schöling
}
}

ed2d834f Sven Schöling
close($fh);
ea974a5a Moritz Bunkus
foreach my $plugin (keys %{ $plugins{needed} }) {
next if ($plugins{loaded}->{$plugin});
print "E: " . strip_base($file) . " requires the Template plugin '$plugin', but is not loaded with '[\% USE $plugin \%]'.\n";
}

faeeee8a Sven Schöling
# copy back into global arrays
880bc0d2 Moritz Bunkus
$alllocales{$_} = 1 for keys %{$cached{$file}{all}};
$locale{$_} = 1 for keys %{$cached{$file}{html}};
$submit{$_} = 1 for keys %{$cached{$file}{submit}};
faeeee8a Sven Schöling
880bc0d2 Moritz Bunkus
scanhtmlfile($_) for keys %{$cached{$file}{scanh}};
faeeee8a Sven Schöling
880bc0d2 Moritz Bunkus
$referenced_html_files{$_} = 1 for keys %{$cached{$file}{scanh}};
faeeee8a Sven Schöling
}

224291dd Thomas Heck
sub scan_javascript_file {
my ($file) = @_;

71233175 Moritz Bunkus
open(my $fh, '<:encoding(utf8)', $file) || die('can not open file: '. $file);
224291dd Thomas Heck
while( my $line = readline($fh) ) {
while( $line =~ m/
6dbc83af Moritz Bunkus
\bk(?:ivi)?.t8
224291dd Thomas Heck
\s*
\(
\s*
([\'\"])
(.*?)
(?<!\\)\1
/ixg )
{
my $text = unescape_template_string($2);

$jslocale{$text} = 1;
$alllocales{$text} = 1;
}
}

close($fh);
}
faeeee8a Sven Schöling
sub search_unused_htmlfiles {
f60d998c Sven Schöling
my @unscanned_dirs = map { '../../templates/' . $_ } @webpages;
faeeee8a Sven Schöling
while (scalar @unscanned_dirs) {
my $dir = shift @unscanned_dirs;

foreach my $entry (<$dir/*>) {
if (-d $entry) {
push @unscanned_dirs, $entry;

24bff6e4 Moritz Bunkus
} elsif (!$ignore_unused_templates{strip_base($entry)} && -f $entry && !$referenced_html_files{$entry}) {
faeeee8a Sven Schöling
print "W: unused HTML template: " . strip_base($entry) . "\n";

}
}
}
}

sub strip_base {
my $s = "$_[0]"; # Create a copy of the string.

$s =~ s|^../../||;
ed2d834f Sven Schöling
$s =~ s|templates/\w+/||;
faeeee8a Sven Schöling
return $s;
}

sub _single_quote {
my $val = shift;
ec555868 Moritz Bunkus
$val =~ s/(\'|\\$)/\\$1/g;
faeeee8a Sven Schöling
return "'" . $val . "'";
}

224291dd Thomas Heck
sub _double_quote {
my $val = shift;
$val =~ s/(\"|\\$)/\\$1/g;
return '"'. $val .'"';
}

faeeee8a Sven Schöling
sub _print_line {
my $key = _single_quote(shift);
my $text = _single_quote(shift);
my %params = @_;
my $template = $params{template} || qq| %-29s => %s,\n|;
my $fh = $params{fh} || croak 'need filehandle in _print_line';

print $fh sprintf $template, $key, $text;
}

sub generate_file {
my %params = @_;

my $file = $params{file} || croak 'need filename in generate_file';
my $header = $params{header};
my $lines = $params{data_sub};
my $data_name = $params{data_name};
my @delim = split //, ($params{delim} || '{}');

203ebd4a Moritz Bunkus
open my $fh, '>:encoding(utf8)', $file or die "$! : $file";
faeeee8a Sven Schöling
dbda14c2 Moritz Bunkus
print $fh "#!/usr/bin/perl\n# -*- coding: utf-8; -*-\n# vim: fenc=utf-8\n\nuse utf8;\n\n";
faeeee8a Sven Schöling
print $fh $header, "\n" if $header;
print $fh "$data_name = $delim[0]\n" if $data_name;

$lines->(fh => $fh);

print $fh qq|$delim[1];\n\n1;\n|;
close $fh;
}

__END__

=head1 NAME

008c2e15 Moritz Bunkus
locales.pl - Collect strings for translation in kivitendo
faeeee8a Sven Schöling
=head1 SYNOPSIS

7403f808 Sven Schöling
locales.pl [options] lang_code
faeeee8a Sven Schöling
Options:
66446327 Bernd Bleßmann
-c, --check-files Run extended checks on HTML files (default)
-n, --no-check-files Do not run extended checks on HTML files
f16a89c9 Sven Schöling
-f, --filenames Show the filenames where new strings where found
faeeee8a Sven Schöling
-v, --verbose Be more verbose
-h, --help Show this help

=head1 OPTIONS

=over 8

=item B<-c>, B<--check-files>

Run extended checks on the usage of templates. This can be used to
discover HTML templates that are never used as well as the usage of
66446327 Bernd Bleßmann
non-existing HTML templates. This is enabled by default.

=item B<-n>, B<--no-check-files>

Do not run extended checks on the usage of templates. See
C<--no-check-files>.
faeeee8a Sven Schöling
=item B<-v>, B<--verbose>

Be more verbose.

=back

=head1 DESCRIPTION

b251cc22 Sven Schöling
This script collects strings from Perl files, the menu files and
7403f808 Sven Schöling
HTML templates and puts them into the file "all" for translation.
faeeee8a Sven Schöling
=cut