From 08300243e39c309e64438df71a45f03245aa3614 Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Mon, 18 Mar 2024 15:23:05 +0100 Subject: [PATCH] Bug 36357: Add Template::Toolkit filters for internationalization (I18N) This patch makes it possible to write in templates the following: [% 'simple text' | t %] [% 'text with {var}' | tx(var = 'value') %] [% 'singular' | tn('plural', count) %] [% 'singular with {var}' | tnx('plural with {var}', cnt, var = 'val') %] These filters are an alternative to the macros defined in i18n.inc. Some advantages: * They do not conflict with variables (see bug 36297) * They are always defined (no need to USE a plugin or PROCESS a file) i18n functions that takes a context argument (tp, tpx, tnp, tnpx) are deliberately not defined as filters. Because context is the first argument, this would require writing: [% 'context' | tp('message') %] Whereas we'd prefer to write: [% 'message' | tp('context') %] There are two ways to fix this: 1. Change the order of arguments in Koha::Template::Plugin::I18N and change the -k options in xgettext-tt2 (the msgid must be first), or 2. Use different keywords, and add them as -k options in xgettext-tt2 Test plan: 1. Find a template file that you can edit and where you can easily see the changes (for instance intranet-main.tt) 2. Add the following code: [% 'simple text' | t %] [% 'text with {var}' | tx(var = 'value') %] [% 'singular' | tn('plural', 1) %] [% 'singular with {var}' | tnx('plural with {var}', 2, var = 'val') %] 3. Run misc/translator/translate update fr-FR 4. Open misc/translator/po/fr-FR-messages.po, make sure you find those 4 new strings. Translate them. 5. Run misc/translator/translate install fr-FR 6. Restart Koha 7. Change the language in the interface. Verify that the strings are translated Signed-off-by: Kyle M Hall Signed-off-by: Victor Grousset/tuxayo --- C4/Templates.pm | 5 ++ Koha/Template/Filters/I18N.pm | 84 +++++++++++++++++++ t/db_dependent/misc/translator/sample.tt | 4 + t/db_dependent/misc/translator/xgettext-tt2.t | 11 ++- 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 Koha/Template/Filters/I18N.pm diff --git a/C4/Templates.pm b/C4/Templates.pm index fb7d3e5cb5..fd7170b863 100644 --- a/C4/Templates.pm +++ b/C4/Templates.pm @@ -70,6 +70,7 @@ sub new { # Do not use template cache if script is called from commandline my $use_template_cache = C4::Context->config('template_cache_dir') && defined $ENV{GATEWAY_INTERFACE}; + require Koha::Template::Filters::I18N; my $template = Template->new( { EVAL_PERL => 1, ABSOLUTE => 1, @@ -78,6 +79,10 @@ sub new { COMPILE_DIR => $use_template_cache ? C4::Context->config('template_cache_dir') : '', INCLUDE_PATH => \@includes, FILTERS => {}, + LOAD_FILTERS => [ + Template::Filters->new(), + Koha::Template::Filters::I18N->new(), + ], ENCODING => 'UTF-8', } ) or die Template->error(); diff --git a/Koha/Template/Filters/I18N.pm b/Koha/Template/Filters/I18N.pm new file mode 100644 index 0000000000..6fdee9a1af --- /dev/null +++ b/Koha/Template/Filters/I18N.pm @@ -0,0 +1,84 @@ +package Koha::Template::Filters::I18N; + +use Modern::Perl; +use base 'Template::Base'; +use Template::Constants; +use Koha::I18N; + +our $STATIC_FILTERS = { + 't' => \&t, +}; + +our $DYNAMIC_FILTERS = { + tx => \&tx_factory, + tn => \&tn_factory, + tnx => \&tnx_factory, + + # i18n functions that takes a context argument (tp, tpx, tnp, tnpx) + # are deliberately not defined as filters. + # Because context is the first argument, this would require writing: + # [% 'context' | tp('message') %] + # Whereas we'd prefer to write: + # [% 'message' | tp('context') %] + # There are two ways to fix this: + # 1. Change the order of arguments in Koha::Template::Plugin::I18N and + # change the -k options in xgettext-tt2 (the msgid must be first), or + # 2. Use different keywords, and add them as -k options in xgettext-tt2 +}; + +sub fetch { + my ($self, $name, $args, $context) = @_; + + return $STATIC_FILTERS->{$name} if $STATIC_FILTERS->{$name}; + + if (my $factory = $DYNAMIC_FILTERS->{$name}) { + return $factory->($context, $args ? @$args : ()); + } + + return (undef, Template::Constants::STATUS_DECLINED); +} + +# This sub is never called in theory as Template::Filters::store is called +# first and accept all filters. +# We declare it anyway, just in case the order of filter providers is changed +sub store { + return (undef, Template::Constants::STATUS_DECLINED); +} + +sub t { + my ($msgid) = @_; + + return __($msgid); +} + +sub tx_factory { + my ($context, $vars) = @_; + + return sub { + my ($msgid) = @_; + + return __x($msgid, $vars ? %$vars : ()); + } +} + +sub tn_factory { + my ($context, $msgid_plural, $count) = @_; + + return sub { + my ($msgid) = @_; + + return __n($msgid, $msgid_plural, $count); + } +} + +sub tnx_factory { + my ($context, $msgid_plural, $count, $vars) = @_; + + return sub { + my ($msgid) = @_; + + return __nx($msgid, $msgid_plural, $count, $vars ? %$vars : ()); + } +} + +1; diff --git a/t/db_dependent/misc/translator/sample.tt b/t/db_dependent/misc/translator/sample.tt index 3fd4fca2a5..3655496815 100644 --- a/t/db_dependent/misc/translator/sample.tt +++ b/t/db_dependent/misc/translator/sample.tt @@ -34,3 +34,7 @@ [% BLOCK %] [% t('Inside block') | $raw %] [% END %] + +[% 'with filter' | t %] + +[% 'filter singular' | tn('filter plural', 2) %] diff --git a/t/db_dependent/misc/translator/xgettext-tt2.t b/t/db_dependent/misc/translator/xgettext-tt2.t index 0c18473ebb..3f8e4b0069 100755 --- a/t/db_dependent/misc/translator/xgettext-tt2.t +++ b/t/db_dependent/misc/translator/xgettext-tt2.t @@ -6,7 +6,7 @@ use File::Slurp; use File::Temp qw(tempdir); use FindBin qw($Bin); use Locale::PO; -use Test::More tests => 36; +use Test::More tests => 43; my $tempdir = tempdir(CLEANUP => 1); @@ -63,6 +63,13 @@ my @expected = ( { msgid => '"Inside block"', }, + { + msgid => '"with filter"', + }, + { + msgid => '"filter singular"', + msgid_plural => '"filter plural"', + }, ); for (my $i = 0; $i < @expected; $i++) { @@ -72,3 +79,5 @@ for (my $i = 0; $i < @expected; $i++) { is($pot->[$i + 1]->$key, $expected, "$i: $key is $expected_str"); } } + +is(@$pot, 1 + @expected, "No more strings than expected"); -- 2.45.2