summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgerv%gerv.net <>2003-01-24 08:34:00 +0100
committergerv%gerv.net <>2003-01-24 08:34:00 +0100
commitb996f2f722e069d479b6f948c4a7e271beab166a (patch)
tree16e45ac189b7cc7b1982aa6e30ac986ae40a278a
parent551081042333edc563ebccc1a10e0b5805b51858 (diff)
downloadbugzilla-b996f2f722e069d479b6f948c4a7e271beab166a.tar.gz
bugzilla-b996f2f722e069d479b6f948c4a7e271beab166a.tar.xz
Bug 126955 - Bugzilla should support translated/localized templates. Patch by burnus; r=gerv, a=justdave.
-rw-r--r--Bugzilla/Template.pm63
-rwxr-xr-xchecksetup.pl110
-rw-r--r--defparams.pl20
-rw-r--r--t/004template.t118
-rw-r--r--t/005no_tabs.t9
-rw-r--r--t/Support/Templates.pm71
6 files changed, 269 insertions, 122 deletions
diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm
index abcfd3ff6..434785332 100644
--- a/Bugzilla/Template.pm
+++ b/Bugzilla/Template.pm
@@ -22,6 +22,8 @@
# Jacob Steenhagen <jake@bugzilla.org>
# Bradley Baetz <bbaetz@student.usyd.edu.au>
# Christopher Aillon <christopher@aillon.com>
+# Tobias Burnus <burnus@net-b.de>
+
package Bugzilla::Template;
@@ -35,6 +37,65 @@ use Date::Format ();
use base qw(Template);
+my $template_include_path;
+
+# Make an ordered list out of a HTTP Accept-Language header see RFC 2616, 14.4
+# We ignore '*' and <language-range>;q=0
+# For languages with the same priority q the order remains unchanged.
+sub sortAcceptLanguage {
+ sub sortQvalue { $b->{'qvalue'} <=> $a->{'qvalue'} }
+ my $accept_language = $_[0];
+
+ # clean up string.
+ $accept_language =~ s/[^A-Za-z;q=0-9\.\-,]//g;
+ my @qlanguages;
+ my @languages;
+ foreach(split /,/, $accept_language) {
+ if (m/([A-Za-z\-]+)(?:;q=(\d(?:\.\d+)))?/) {
+ my $lang = $1;
+ my $qvalue = $2;
+ $qvalue = 1 if not defined $qvalue;
+ next if $qvalue == 0;
+ $qvalue = 1 if $qvalue > 1;
+ push(@qlanguages, {'qvalue' => $qvalue, 'language' => $lang});
+ }
+ }
+
+ return map($_->{'language'}, (sort sortQvalue @qlanguages));
+}
+
+# Returns the path to the templates based on the Accept-Language
+# settings of the user and of the available languages
+# If no Accept-Language is present it uses the defined default
+sub getTemplateIncludePath () {
+ # Return cached value if available
+ if ($template_include_path) {
+ return $template_include_path;
+ }
+ my $languages = trim(Param('languages'));
+ if (not ($languages =~ /,/)) {
+ return $template_include_path =
+ ["template/$languages/custom", "template/$languages/default"];
+ }
+ my @languages = sortAcceptLanguage($languages);
+ my @accept_language = sortAcceptLanguage($ENV{'HTTP_ACCEPT_LANGUAGE'} || "" );
+ my @usedlanguages;
+ foreach my $lang (@accept_language) {
+ # Per RFC 1766 and RFC 2616 any language tag matches also its
+ # primary tag. That is 'en' (accept lanuage) matches 'en-us',
+ # 'en-uk' etc. but not the otherway round. (This is unfortunally
+ # not very clearly stated in those RFC; see comment just over 14.5
+ # in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
+ if(my @found = grep /^$lang(-.+)?$/i, @languages) {
+ push (@usedlanguages, @found);
+ }
+ }
+ push(@usedlanguages, Param('defaultlanguage'));
+ return $template_include_path =
+ [map(("template/$_/custom", "template/$_/default"), @usedlanguages)];
+}
+
+
###############################################################################
# Templatization Code
@@ -100,7 +161,7 @@ sub create {
return $class->new({
# Colon-separated list of directories containing templates.
- INCLUDE_PATH => "template/en/custom:template/en/default",
+ INCLUDE_PATH => [\&getTemplateIncludePath],
# Remove white-space before template directives (PRE_CHOMP) and at the
# beginning and end of templates and template blocks (TRIM) for better
diff --git a/checksetup.pl b/checksetup.pl
index 59ebb1955..f2c1ef761 100755
--- a/checksetup.pl
+++ b/checksetup.pl
@@ -25,6 +25,7 @@
# Zach Lipton <zach@zachlipton.com>
# Jacob Steenhagen <jake@bugzilla.org>
# Bradley Baetz <bbaetz@student.usyd.edu.au>
+# Tobias Burnus <burnus@net-b.de>
#
#
# Direct any questions on this source code to
@@ -958,82 +959,81 @@ END
}
}
+ # Search for template directories
+ # We include the default and custom directories separately to make
+ # sure we compile all templates
+ my @templatepaths = ();
+ {
+ use File::Spec;
+ opendir(DIR, "template") || die "Can't open 'template': $!";
+ my @files = grep { /^[a-z-]+$/i } readdir(DIR);
+ closedir DIR;
+
+ foreach my $dir (@files) {
+ next if($dir =~ /^CVS$/i);
+ my $path = File::Spec->catdir('template', $dir, 'custom');
+ push(@templatepaths, $path) if(-d $path);
+ $path = File::Spec->catdir('template', $dir, 'default');
+ push(@templatepaths, $path) if(-d $path);
+ }
+ }
+
# Precompile stuff. This speeds up initial access (so the template isn't
# compiled multiple times simulataneously by different servers), and helps
# to get the permissions right.
- eval("use Template");
- my $redir = ($^O =~ /MSWin32/i) ? "NUL" : "/dev/null";
- my $provider = Template::Provider->new(
- {
- # Colon-separated list of directories containing templates.
- INCLUDE_PATH => "template/en/custom:template/en/default",
-
- PRE_CHOMP => 1 ,
- TRIM => 1 ,
-
- COMPILE_DIR => 'data/', # becomes data/template/en/{custom,default}
-
- # These don't actually need to do anything here, just exist
- FILTERS =>
- {
- strike => sub { return $_; } ,
- js => sub { return $_; },
- html_linebreak => sub { return $_; },
- url_quote => sub { return $_; },
- xml => sub { return $_; },
- quoteUrls => sub { return $_; },
- bug_link => [ sub { return sub { return $_; } }, 1],
- csv => sub { return $_; },
- time => sub { return $_; },
- },
- }) || die ("Could not create Template Provider: "
- . Template::Provider->error() . "\n");
-
sub compile {
- # no_chdir doesn't work on perl 5.005
-
- my $origDir = $File::Find::dir;
my $name = $File::Find::name;
return if (-d $name);
return if ($name =~ /\/CVS\//);
return if ($name !~ /\.tmpl$/);
- $name =~ s!template/en/default/!!; # trim the bit we don't pass to TT
-
- chdir($::baseDir);
+ $name =~ s/\Q$::templatepath\E\///; # trim the bit we don't pass to TT
# Do this to avoid actually processing the templates
- my ($data, $err) = $provider->fetch($name);
+ my ($data, $err) = $::provider->fetch($name);
die "Could not compile $name: " . $data . "\n" if $err;
-
- chdir($origDir);
}
+
+ eval("use Template");
{
print "Precompiling templates ...\n" unless $silent;
use File::Find;
- use Cwd;
-
- $::baseDir = cwd();
-
# Don't hang on templates which use the CGI library
eval("use CGI qw(-no_debug)");
-
- # Disable warnings which come from running the compiled templates
- # This way is OK, because they're all runtime warnings.
- # The reason we get these warnings here is that none of the required
- # vars will be present.
- local ($^W) = 0;
-
- # Traverse the default hierachy. Custom templates will be picked up
- # via the INCLUDE_PATH, but we know that bugzilla will only be
- # calling stuff which exists in en/default
- # FIXME - if we start doing dynamic INCLUDE_PATH we may have to
- # recurse all of template/, changing the INCLUDE_PATH each time
-
- find(\&compile, "template/en/default");
+ foreach $::templatepath (@templatepaths) {
+ $::provider = Template::Provider->new(
+ {
+ # Directories containing templates.
+ INCLUDE_PATH => $::templatepath,
+
+ PRE_CHOMP => 1 ,
+ TRIM => 1 ,
+
+ # becomes data/template/{en, ...}/{custom,default}
+ COMPILE_DIR => 'data/',
+
+ # These don't actually need to do anything here, just exist
+ FILTERS =>
+ {
+ strike => sub { return $_; } ,
+ js => sub { return $_; },
+ html_linebreak => sub { return $_; },
+ url_quote => sub { return $_; },
+ xml => sub { return $_; },
+ quoteUrls => sub { return $_; },
+ bug_link => [ sub { return sub { return $_; } }, 1],
+ csv => sub { return $_; },
+ time => sub { return $_; },
+ },
+ }) || die ("Could not create Template Provider: "
+ . Template::Provider->error() . "\n");
+
+ # Traverse the template hierachy.
+ find({ wanted => \&compile, no_chdir => 1 }, $::templatepath);
+ }
}
}
diff --git a/defparams.pl b/defparams.pl
index 922a9dfe2..f75ead4b2 100644
--- a/defparams.pl
+++ b/defparams.pl
@@ -202,6 +202,26 @@ sub check_netmask {
},
{
+ name => 'languages' ,
+ desc => 'A comma-separated list of RFC 1766 language tags. These ' .
+ 'identify the languages in which you wish Bugzilla output ' .
+ 'to be displayed. Note that you must install the appropriate ' .
+ 'language pack before adding a language to this Param. The ' .
+ 'language used is the one in this list with the highest ' .
+ 'q-value in the user\'s Accept-Language header.' ,
+ type => 't' ,
+ default => 'en'
+ },
+
+ {
+ name => 'defaultlanguage',
+ desc => 'The UI language Bugzilla falls back on if no suitable ' .
+ 'language is found in the user\'s Accept-Language header.' ,
+ type => 't' ,
+ default => 'en'
+ },
+
+ {
name => 'cookiepath',
desc => 'Path, relative to your web document root, to which to restrict ' .
'Bugzilla cookies. Normally this is the URI portion of your URL ' .
diff --git a/t/004template.t b/t/004template.t
index be0dd04ec..8429b774f 100644
--- a/t/004template.t
+++ b/t/004template.t
@@ -20,6 +20,7 @@
# Contributor(s): Jacob Steenhagen <jake@bugzilla.org>
# Zach Lipton <zach@zachlipton.com>
# David D. Kilzer <ddkilzer@kilzer.net>
+# Tobias Burnus <burnus@net-b.de>
#
#################
@@ -37,8 +38,7 @@ use CGI qw(-no_debug);
use File::Spec 0.82;
use Template;
-use Test::More tests => ( scalar(@Support::Templates::referenced_files)
- + scalar(@Support::Templates::actual_files) * 2);
+use Test::More tests => ( scalar(@referenced_files) + $num_actual_files * 2 );
# Capture the TESTOUT from Test::More or Test::Builder for printing errors.
# This will handle verbosity for us automatically.
@@ -54,72 +54,84 @@ my $fh;
}
}
-my $include_path = $Support::Templates::include_path;
+# Checks whether one of the passed files exists
+sub existOnce {
+ foreach my $file (@_) {
+ return $file if -e $file;
+ }
+ return 0;
+}
# Check to make sure all templates that are referenced in
# Bugzilla exist in the proper place.
-foreach my $file(@Support::Templates::referenced_files) {
- my $path = File::Spec->catfile($include_path, $file);
- if (-e $path) {
- ok(1, "$path exists");
- } else {
- ok(0, "$path does not exist --ERROR");
+foreach my $lang (@languages) {
+ foreach my $file (@referenced_files) {
+ my @path = map(File::Spec->catfile($_, $file),
+ split(':', $include_path{$lang}));
+ if (my $path = existOnce(@path)) {
+ ok(1, "$path exists");
+ } else {
+ ok(0, "$file cannot be located --ERROR");
+ print $fh "Looked in:\n " . join("\n ", @path);
+ }
}
}
-# Processes all the templates to make sure they have good syntax
-my $provider = Template::Provider->new(
-{
- INCLUDE_PATH => $include_path ,
- # Need to define filters used in the codebase, they don't
- # actually have to function in this test, just be defined.
- # See globals.pl for the actual codebase definitions.
- FILTERS =>
+foreach my $include_path (@include_paths) {
+ # Processes all the templates to make sure they have good syntax
+ my $provider = Template::Provider->new(
{
- html_linebreak => sub { return $_; },
- js => sub { return $_ } ,
- strike => sub { return $_ } ,
- url_quote => sub { return $_ } ,
- xml => sub { return $_ } ,
- quoteUrls => sub { return $_ } ,
- bug_link => [ sub { return sub { return $_; } }, 1] ,
- csv => sub { return $_ } ,
- time => sub { return $_ } ,
- },
-}
-);
-
-foreach my $file(@Support::Templates::actual_files) {
- my $path = File::Spec->catfile($include_path, $file);
- if (-e $path) {
- my ($data, $err) = $provider->fetch($file);
-
- if (!$err) {
- ok(1, "$file syntax ok");
+ INCLUDE_PATH => $include_path ,
+ # Need to define filters used in the codebase, they don't
+ # actually have to function in this test, just be defined.
+ # See globals.pl for the actual codebase definitions.
+ FILTERS =>
+ {
+ html_linebreak => sub { return $_; },
+ js => sub { return $_ } ,
+ strike => sub { return $_ } ,
+ url_quote => sub { return $_ } ,
+ xml => sub { return $_ } ,
+ quoteUrls => sub { return $_ } ,
+ bug_link => [ sub { return sub { return $_; } }, 1] ,
+ csv => sub { return $_ } ,
+ time => sub { return $_ } ,
+ },
+ }
+ );
+
+ foreach my $file (@{$actual_files{$include_path}}) {
+ my $path = File::Spec->catfile($include_path, $file);
+ if (-e $path) {
+ my ($data, $err) = $provider->fetch($file);
+
+ if (!$err) {
+ ok(1, "$file syntax ok");
+ }
+ else {
+ ok(0, "$file has bad syntax --ERROR");
+ print $fh $data . "\n";
+ }
}
else {
- ok(0, "$file has bad syntax --ERROR");
- print $fh $data . "\n";
+ ok(1, "$path doesn't exist, skipping test");
}
}
- else {
- ok(1, "$path doesn't exist, skipping test");
- }
-}
-# check to see that all templates have a version string:
+ # check to see that all templates have a version string:
-foreach my $file(@Support::Templates::actual_files) {
- my $path = File::Spec->catfile($include_path, $file);
- open(TMPL, $path);
- my $firstline = <TMPL>;
- if ($firstline =~ /\d+\.\d+\@[\w\.-]+/) {
- ok(1,"$file has a version string");
- } else {
- ok(0,"$file does not have a version string --ERROR");
+ foreach my $file (@{$actual_files{$include_path}}) {
+ my $path = File::Spec->catfile($include_path, $file);
+ open(TMPL, $path);
+ my $firstline = <TMPL>;
+ if ($firstline =~ /\d+\.\d+\@[\w\.-]+/) {
+ ok(1,"$file has a version string");
+ } else {
+ ok(0,"$file does not have a version string --ERROR");
+ }
+ close(TMPL);
}
- close(TMPL);
}
exit 0;
diff --git a/t/005no_tabs.t b/t/005no_tabs.t
index f1d5f9be5..51433fe13 100644
--- a/t/005no_tabs.t
+++ b/t/005no_tabs.t
@@ -34,12 +34,13 @@ use Support::Templates;
use File::Spec 0.82;
use Test::More tests => ( scalar(@Support::Files::testitems)
- + scalar(@Support::Templates::actual_files));
+ + $Support::Templates::num_actual_files);
my @testitems = @Support::Files::testitems;
-my @templates = map(File::Spec->catfile($Support::Templates::include_path, $_),
- @Support::Templates::actual_files);
-push(@testitems, @templates);
+for my $path (@Support::Templates::include_paths) {
+ push(@testitems, map(File::Spec->catfile($path, $_),
+ Support::Templates::find_actual_files($path)));
+}
foreach my $file (@testitems) {
open (FILE, "$file");
diff --git a/t/Support/Templates.pm b/t/Support/Templates.pm
index 4ef582de4..e90565392 100644
--- a/t/Support/Templates.pm
+++ b/t/Support/Templates.pm
@@ -19,6 +19,7 @@
#
# Contributor(s): Jacob Steenhagen <jake@bugzilla.org>
# David D. Kilzer <ddkilzer@kilzer.net>
+# Tobias Burnus <burnus@net-b.de>
#
package Support::Templates;
@@ -26,18 +27,60 @@ package Support::Templates;
use strict;
use lib 't';
-use vars qw($include_path @referenced_files @actual_files);
+use base qw(Exporter);
+@Support::Templates::EXPORT =
+ qw(@languages @include_paths %include_path @referenced_files
+ %actual_files $num_actual_files);
+use vars qw(@languages @include_paths %include_path @referenced_files
+ %actual_files $num_actual_files);
use Support::Files;
use File::Find;
use File::Spec 0.82;
-# Note that $include_path is assumed to only contain ONE path, not
-# a list of colon-separated paths.
-$include_path = File::Spec->catdir('template', 'en', 'default');
+# The available template languages
+@languages = ();
+
+# The colon separated includepath per language
+%include_path = ();
+
+# All include paths
+@include_paths = ();
+
+# Files which are referenced in the cgi files
@referenced_files = ();
-@actual_files = ();
+
+# All files sorted by include_path
+%actual_files = ();
+
+# total number of actual_files
+$num_actual_files = 0;
+
+# Scan for the template available languages and include paths
+{
+ opendir(DIR, "template") || die "Can't open 'template': $!";
+ my @files = grep { /^[a-z-]+$/i } readdir(DIR);
+ closedir DIR;
+
+ foreach my $langdir (@files) {
+ next if($langdir =~ /^CVS$/i);
+
+ my $path = File::Spec->catdir('template', $langdir, 'custom');
+ my @dirs = ();
+ push(@dirs, $path) if(-d $path);
+ $path = File::Spec->catdir('template', $langdir, 'default');
+ push(@dirs, $path) if(-d $path);
+
+ next if(scalar(@dirs) == 0);
+ push(@languages, $langdir);
+ push(@include_paths, @dirs);
+ $include_path{$langdir} = join(":",@dirs);
+ }
+}
+
+
+my @files;
# Local subroutine used with File::Find
sub find_templates {
@@ -59,13 +102,23 @@ sub find_templates {
$filename = $_;
}
- push(@actual_files, $filename);
+ push(@files, $filename);
}
}
-# Scan the template include path for templates then put them in
-# in the @actual_files array to be used by various tests.
-map(find(\&find_templates, $_), split(':', $include_path));
+# Scan the given template include path for templates
+sub find_actual_files {
+ my $include_path = $_[0];
+ @files = ();
+ find(\&find_templates, $include_path);
+ return @files;
+}
+
+
+foreach my $include_path (@include_paths) {
+ $actual_files{$include_path} = [ find_actual_files($include_path) ];
+ $num_actual_files += scalar(@{$actual_files{$include_path}});
+}
# Scan Bugzilla's perl code looking for templates used and put them
# in the @referenced_files array to be used by the 004template.t test.