From d8e2b91366808f0985b6a1affb53d6a464025ef4 Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Mon, 10 Apr 2017 18:29:12 +0200 Subject: [PATCH] Bug 15395: Make Koha::I18N work with Plack + add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language detection was done in a BEGIN block so it was not working correctly with Plack. This is now done when the first translation is requested. This patch also fixes a bug when set_locale(LC_ALL, ...) fails for some reasons (locale is not installed system-wide for example). And finally, add some unit tests Test plan: 1. prove t/Koha/I18N.t 2. Replay the previous test plans with plack enabled Signed-off-by: Marc Véron --- Koha/I18N.pm | 104 +++++++++++++++++++------------ t/Koha/I18N.t | 58 +++++++++++++++++ t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo | Bin 0 -> 1370 bytes t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po | 81 ++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 41 deletions(-) create mode 100755 t/Koha/I18N.t create mode 100644 t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo create mode 100644 t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po diff --git a/Koha/I18N.pm b/Koha/I18N.pm index 56d48d8766..236454cc0e 100644 --- a/Koha/I18N.pm +++ b/Koha/I18N.pm @@ -26,6 +26,7 @@ use C4::Context; use Encode; use Locale::Util qw(set_locale); use Locale::Messages qw(:locale_h :libintl_h nl_putenv); +use Koha::Cache::Memory::Lite; use parent 'Exporter'; our @EXPORT = qw( @@ -44,55 +45,61 @@ our @EXPORT = qw( N__np ); -my $textdomain; +our $textdomain = 'Koha'; + +sub init { + my $cache = Koha::Cache::Memory::Lite->get_instance(); + my $cache_key = 'i18n:initialized'; + unless ($cache->get_from_cache($cache_key)) { + my $langtag = C4::Languages::getlanguage; + my @subtags = split /-/, $langtag; + my ($language, $region) = @subtags; + if ($region && length $region == 4) { + $region = $subtags[2]; + } + my $locale = set_locale(LC_ALL, $language, $region, 'utf-8'); + unless ($locale) { + set_locale(LC_MESSAGES, ''); + Locale::Messages->select_package('gettext_pp'); + $locale = $language; + if ($region) { + $locale .= '_' . $region; + } + nl_putenv("LANGUAGE=$locale"); + nl_putenv("LANG=$locale"); + nl_putenv('OUTPUT_CHARSET=utf-8'); + } -BEGIN { - $textdomain = 'Koha'; + my $directory = _base_directory(); + textdomain($textdomain); + bindtextdomain($textdomain, $directory); - my $langtag = C4::Languages::getlanguage; - my @subtags = split /-/, $langtag; - my ($language, $region) = @subtags; - if ($region && length $region == 4) { - $region = $subtags[2]; - } - my $locale = set_locale(LC_ALL, $language, $region, 'utf-8'); - unless ($locale) { - set_locale(LC_MESSAGES, 'C'); - Locale::Messages->select_package('gettext_pp'); - $locale = $language; - if ($region) { - $locale .= '_' . $region; - } - nl_putenv("LANGUAGE=$locale"); - nl_putenv("LANG=$locale"); - nl_putenv('OUTPUT_CHARSET=utf-8'); + $cache->set_in_cache($cache_key, 1); } - - my $directory = C4::Context->config('intranetdir') . '/misc/translator/po'; - textdomain($textdomain); - bindtextdomain($textdomain, $directory); } sub __ { my ($msgid) = @_; - my $text = dgettext($textdomain, $msgid); - return __decode($text); + + return _gettext(\&gettext, [ $msgid ]); } sub __x { my ($msgid, %vars) = @_; - return __expand(__($msgid), %vars); + + return _gettext(\&gettext, [ $msgid ], %vars); } sub __n { my ($msgid, $msgid_plural, $count) = @_; - my $text = dngettext($textdomain, $msgid, $msgid_plural, $count); - return __decode($text); + + return _gettext(\&ngettext, [ $msgid, $msgid_plural, $count ]); } sub __nx { my ($msgid, $msgid_plural, $count, %vars) = @_; - return __expand(__n($msgid, $msgid_plural, $count), %vars); + + return _gettext(\&ngettext, [ $msgid, $msgid_plural, $count ], %vars); } sub __xn { @@ -101,24 +108,26 @@ sub __xn { sub __p { my ($msgctxt, $msgid) = @_; - my $text = dpgettext($textdomain, $msgctxt, $msgid); - return __decode($text); + + return _gettext(\&pgettext, [ $msgctxt, $msgid ]); } sub __px { my ($msgctxt, $msgid, %vars) = @_; - return __expand(__p($msgctxt, $msgid), %vars); + + return _gettext(\&pgettext, [ $msgctxt, $msgid ], %vars); } sub __np { my ($msgctxt, $msgid, $msgid_plural, $count) = @_; - my $text = dnpgettext($textdomain, $msgctxt, $msgid, $msgid_plural, $count); - return __decode($text); + + return _gettext(\&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count ]); } sub __npx { my ($msgctxt, $msgid, $msgid_plural, $count, %vars) = @_; - return __expand(__np($msgctxt, $msgid, $msgid_plural, $count), %vars); + + return _gettext(\&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count], %vars); } sub N__ { @@ -137,7 +146,24 @@ sub N__np { return @_; } -sub __expand { +sub _base_directory { + return C4::Context->config('intranetdir') . '/misc/translator/po'; +} + +sub _gettext { + my ($sub, $args, %vars) = @_; + + init(); + + my $text = Encode::decode_utf8($sub->(@$args)); + if (%vars) { + $text = _expand($text, %vars); + } + + return $text; +} + +sub _expand { my ($text, %vars) = @_; my $re = join '|', map { quotemeta $_ } keys %vars; @@ -146,8 +172,4 @@ sub __expand { return $text; } -sub __decode { - return Encode::decode_utf8(shift); -} - 1; diff --git a/t/Koha/I18N.t b/t/Koha/I18N.t new file mode 100755 index 0000000000..a657b1e75c --- /dev/null +++ b/t/Koha/I18N.t @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +use Modern::Perl; +use Test::More tests => 35; +use Test::MockModule; +use FindBin qw($Bin); +use Encode; + +BEGIN { + use_ok('Koha::I18N'); +} + +my $koha_i18n = Test::MockModule->new('Koha::I18N'); +$koha_i18n->mock('_base_directory', sub { "$Bin/I18N/po" }); + +my $c4_languages = Test::MockModule->new('C4::Languages'); +$c4_languages->mock('getlanguage', sub { 'xx-XX' }); + +my @tests = ( + [ __('test') => 'test ツ' ], + [ __x('Hello {name}', name => 'World') => 'Hello World ツ' ], + [ __n('Singular', 'Plural', 0) => 'Zero ツ' ], + [ __n('Singular', 'Plural', 1) => 'Singular ツ' ], + [ __n('Singular', 'Plural', 2) => 'Plural ツ' ], + [ __n('Singular', 'Plural', 3) => 'Plural ツ' ], + [ __nx('one item', '{count} items', 0, count => 0) => 'no item ツ' ], + [ __nx('one item', '{count} items', 1, count => 1) => 'one item ツ' ], + [ __nx('one item', '{count} items', 2, count => 2) => '2 items ツ' ], + [ __nx('one item', '{count} items', 3, count => 3) => '3 items ツ' ], + [ __xn('one item', '{count} items', 0, count => 0) => 'no item ツ' ], + [ __xn('one item', '{count} items', 1, count => 1) => 'one item ツ' ], + [ __xn('one item', '{count} items', 2, count => 2) => '2 items ツ' ], + [ __xn('one item', '{count} items', 3, count => 3) => '3 items ツ' ], + [ __p('biblio', 'title') => 'title (biblio) ツ' ], + [ __p('patron', 'title') => 'title (patron) ツ' ], + [ __px('biblio', 'Remove item {id}', id => 42) => 'Remove item 42 (biblio) ツ' ], + [ __px('list', 'Remove item {id}', id => 42) => 'Remove item 42 (list) ツ' ], + [ __np('ctxt1', 'singular', 'plural', 0) => 'zero (ctxt1) ツ' ], + [ __np('ctxt1', 'singular', 'plural', 1) => 'singular (ctxt1) ツ' ], + [ __np('ctxt1', 'singular', 'plural', 2) => 'plural (ctxt1) ツ' ], + [ __np('ctxt1', 'singular', 'plural', 3) => 'plural (ctxt1) ツ' ], + [ __np('ctxt2', 'singular', 'plural', 0) => 'zero (ctxt2) ツ' ], + [ __np('ctxt2', 'singular', 'plural', 1) => 'singular (ctxt2) ツ' ], + [ __np('ctxt2', 'singular', 'plural', 2) => 'plural (ctxt2) ツ' ], + [ __np('ctxt2', 'singular', 'plural', 3) => 'plural (ctxt2) ツ' ], + [ __npx('biblio', 'one item', '{count} items', 0, count => 0) => 'no item (biblio) ツ' ], + [ __npx('biblio', 'one item', '{count} items', 1, count => 1) => 'one item (biblio) ツ' ], + [ __npx('biblio', 'one item', '{count} items', 2, count => 2) => '2 items (biblio) ツ' ], + [ __npx('biblio', 'one item', '{count} items', 3, count => 3) => '3 items (biblio) ツ' ], + [ __npx('list', 'one item', '{count} items', 0, count => 0) => 'no item (list) ツ' ], + [ __npx('list', 'one item', '{count} items', 1, count => 1) => 'one item (list) ツ' ], + [ __npx('list', 'one item', '{count} items', 2, count => 2) => '2 items (list) ツ' ], + [ __npx('list', 'one item', '{count} items', 3, count => 3) => '3 items (list) ツ' ], +); + +foreach my $test (@tests) { + is($test->[0], decode_utf8($test->[1]), $test->[1]); +} diff --git a/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo b/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo new file mode 100644 index 0000000000000000000000000000000000000000..99be22f268b3e872ac10b2d2e3e538b1fd8293d6 GIT binary patch literal 1370 zcmaKr&rcLF6vwNssG~-O_#;u19ziqf%t9g=SRmpOQC1OGL%f*oZdpoZ+fAoSRta9< zAMoJKyC=_{{1?1%_u`erf5C63yNm-ulb28XeXp;5oj1RR3!e$bW!N3qF6d4cCcKGNRAaI`dX|M``0%la^@ukGieIuc?T7%P(sFyynax z(tpehk7y|fANs+B9|km>h=TF^K`96j=&$fiVKbf>x5N_T*EEw!%#6|zecFu$leEgZ zS~NJF-bmNQwp-7oPhZ!(*ih{mZ;9c(S|6Z!Q)^rCXWTI^M0Wdey~h zB`Y&52Hw1qhD%)FeFv2o-ZKw6F%xo%HrAO=xGBG`E%=jJoW1B8*M3DdR1+UBqLX!D zJl7Zgg3|3IqSAfMq&z-F-LyO^%jJO1(tyHUE{B+gn3l$-yj-i%-#feHE!QfY4Eioz zxHHn};l`t*t|P}%uT-ujbK;;Q@}ao~&AZYj$x9#Xy++f{btetlHz&G^b5gdk^uLXL w@!ySo>1d(0>kpNgNm-GZcBsV6F?c5X;s+b;BEQ2b{kL$xk2~G|J?U%z1(g|z>;M1& literal 0 HcmV?d00001 diff --git a/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po b/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po new file mode 100644 index 0000000000..1b956aa0c8 --- /dev/null +++ b/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po @@ -0,0 +1,81 @@ +# PO file for tests +# Copyright (C) 2017 BibLibre +# This file is distributed under the same license as the Koha package. +# Julian Maurice \n" +"Language-Team: xx \n" +"Language: xx_XX\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2);\n" + +msgid "test" +msgstr "test ツ" + +msgid "Hello {name}" +msgstr "Hello {name} ツ" + +msgid "Singular" +msgid_plural "Plural" +msgstr[0] "Zero ツ" +msgstr[1] "Singular ツ" +msgstr[2] "Plural ツ" + +msgid "one item" +msgid_plural "{count} items" +msgstr[0] "no item ツ" +msgstr[1] "one item ツ" +msgstr[2] "{count} items ツ" + +msgctxt "biblio" +msgid "title" +msgstr "title (biblio) ツ" + +msgctxt "patron" +msgid "title" +msgstr "title (patron) ツ" + +msgctxt "biblio" +msgid "Remove item {id}" +msgstr "Remove item {id} (biblio) ツ" + +msgctxt "list" +msgid "Remove item {id}" +msgstr "Remove item {id} (list) ツ" + +msgctxt "ctxt1" +msgid "singular" +msgid_plural "plural" +msgstr[0] "zero (ctxt1) ツ" +msgstr[1] "singular (ctxt1) ツ" +msgstr[2] "plural (ctxt1) ツ" + +msgctxt "ctxt2" +msgid "singular" +msgid_plural "plural" +msgstr[0] "zero (ctxt2) ツ" +msgstr[1] "singular (ctxt2) ツ" +msgstr[2] "plural (ctxt2) ツ" + +msgctxt "biblio" +msgid "one item" +msgid_plural "{count} items" +msgstr[0] "no item (biblio) ツ" +msgstr[1] "one item (biblio) ツ" +msgstr[2] "{count} items (biblio) ツ" + +msgctxt "list" +msgid "one item" +msgid_plural "{count} items" +msgstr[0] "no item (list) ツ" +msgstr[1] "one item (list) ツ" +msgstr[2] "{count} items (list) ツ" -- 2.14.2