Commit d364ff49 authored by sikeda's avatar sikeda
Browse files

[change] Sympa::Language, rewriting of Language module to handle i18n.

The new module is committed for purpose of unit test.

Coming features:
- Language/locale contexts are identified by IETF language tags defined by RFC 5646 (BCP 47).  Older style "locale" by earlier releases would be canonicalized.
- System locale is not absolutely nessessary to add a new language: gettext catalog is required.
- If appropriate POSIX locale is not installed, gettext_strftime() emulates POSIX::strftime(): names of days, months etc. will be taken from catalog.
- Content negotiation used by HTTP 1.1 was implemented.

Internal changes:
- "Language:" field in PO header became mandatory to determine actually selected catalog.  Modified po/Makefile.in.in to run msgcat, msgen or msgmerge with --lang option.
- Dependent libintl-perl (Locale::Messages) requires 1.22 (virtually 1.23) or later to use locale-independent gettext_dumb feature.
- Default language context is fixed to "en" and no longer retrieved implicitly from site config (charset.conf).  $Language::default_lang was deprecated.

Miscelaneous:
- Added unittest t/Language.t and several test data.
- Added test stub module for Sympa::Constants.


git-svn-id: https://subversion.renater.fr/sympa/branches/sympa-6.2-branch@10529 05aa8bb8-cd2b-0410-b1d7-8918dfa770ce
parent ff833ba3
......@@ -23,18 +23,26 @@
SUBDIRS = src wwsympa soap web_tt2 mail_tt2 doc po po-wwsympa
check_SCRIPTS = t/LockedFile.t \
check_SCRIPTS = t/Language.t \
t/LockedFile.t \
t/compile_modules.t \
t/compile_executables.t
check_DATA = t/locale/cs/LC_MESSAGES/sympa.mo \
t/locale/cs/LC_MESSAGES/web_help.mo \
t/locale/zh_TW/LC_MESSAGES/sympa.mo
EXTRA_DIST = important_changes.pl \
sympa.spec.in \
sympa.spec \
README.charset \
etc_README \
$(check_SCRIPTS)
$(check_SCRIPTS) $(check_DATA)
CLEANFILES = previous_sympa_version
MSGFMT=@MSGFMT@
.po.mo:
$(MSGFMT) -o $@ $<
check-local:
[ -z "$(TEST_FILES)" ] && TEST_FILES="$(check_SCRIPTS)"; \
PERL5LIB=src/lib:wwsympa:soap; export PERL5LIB; \
......
......@@ -160,7 +160,7 @@ $(POFILES): $(srcdir)/$(DOMAIN).pot
if test -f "$(srcdir)/$${lang}.po"; then \
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \
cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \
cd $(srcdir) && $(MSGMERGE_UPDATE) --lang=$${lang} $${lang}.po $(DOMAIN).pot; \
else \
$(MAKE) $${lang}.po-create; \
fi
......@@ -363,7 +363,7 @@ update-po: Makefile
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \
cd $(srcdir); \
if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \
if $(MSGMERGE) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot; then \
if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
rm -f $$tmpdir/$$lang.new.po; \
else \
......
......@@ -135,7 +135,7 @@ $(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
$(XGETTEXT) -u \
-o $(DOMAIN).pot \
../src/*.pl.in \
../src/lib/*.pm ../src/lib/HTML/*.pm \
../src/lib/*.pm ../src/lib/HTML/*.pm ../src/lib/Sympa/*.pm \
../soap/*.pl.in ../soap/*.pm ../soap/*.fcgi.in \
../wwsympa/wwsympa.fcgi.in ../wwsympa/*.pl.in ../wwsympa/*.pm \
../web_tt2/*.tt2 ../mail_tt2/*.tt2 \
......@@ -173,7 +173,7 @@ $(POFILES): $(srcdir)/$(DOMAIN).pot
if test -f "$(srcdir)/$${lang}.po"; then \
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \
cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \
cd $(srcdir) && $(MSGMERGE_UPDATE) --lang=$${lang} $${lang}.po $(DOMAIN).pot; \
else \
$(MAKE) $${lang}.po-create; \
fi
......@@ -377,9 +377,9 @@ update-po: Makefile
echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \
cd $(srcdir); \
if test "$$lang" = "en"; then\
$(MSGEN) -o $$tmpdir/$$lang.new.po $(DOMAIN).pot; \
$(MSGEN) --lang=$$lang -o $$tmpdir/$$lang.new.po $(DOMAIN).pot; \
else \
if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \
if $(MSGMERGE) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot; then \
:; \
else \
echo "msgmerge for $$lang.po failed!" 1>&2; \
......@@ -389,7 +389,7 @@ update-po: Makefile
if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
rm -f $$tmpdir/$$lang.new.po; \
else \
$(MSGCAT) --use-first -o $$tmpdir/$$lang.po $$tmpdir/$$lang.new.po $$lang.po; \
$(MSGCAT) --use-first --lang=$$lang -o $$tmpdir/$$lang.po $$tmpdir/$$lang.new.po $$lang.po; \
fi; \
rm -f $$tmpdir/$$lang.new.po;
......@@ -438,13 +438,13 @@ clean-po:
rm -f $$tmpdir/$$lang.new.po; \
else \
$(MSGEN) -o $$tmpdir/$$lang.new.po $(DOMAIN).pot; \
$(MSGCAT) --use-first -o $$tmpdir/$$lang.new.po $$lang.po $$tmpdir/$$lang.new.po; \
$(MSGCAT) --use-first --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $$tmpdir/$$lang.new.po; \
fi; \
else \
echo "Could not gather not fuzzy translated strings in en.po" 1>&2; \
fi;\
else \
if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \
if $(MSGMERGE) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot; then \
if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
rm -f $$tmpdir/$$lang.new.po; \
else \
......
......@@ -21,7 +21,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
nobase_modules_DATA = admin.pm Archive.pm Bulk.pm Commands.pm confdef.pm \
Conf.pm Config_XML.pm Family.pm Language.pm \
Conf.pm Config_XML.pm Family.pm Sympa/Language.pm \
List.pm Sympa/LockedFile.pm Log.pm mail.pm Ldap.pm \
Upgrade.pm Sympa/User.pm \
Fetch.pm Message.pm Task.pm Datasource.pm SQLSource.pm \
......
This diff is collapsed.
......@@ -524,10 +524,8 @@ sub check_cpan {
package_name => 'JSON-XS',
'gettext_id' => 'required when using the VOOT protocol',
},
# Locale::Messages 1.16 or earlier does not have its
# own $VERSION variable. Check the version of
# Locale::TextDomain instead.
'Locale::TextDomain' => {
'Locale::Messages' => {
required_version => '1.22',
package_name => 'libintl-perl',
mandatory => 1,
'gettext_id' => 'internationalization functions',
......
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$
# Unit test for Sympa::Language.
#
# This will run in following environment:
# - Available catalogs: cs, zh_TW.
use strict;
use warnings;
use Test::More;
use lib 't/stub', @INC;
use Sympa::Language;
my %tests = (
## Lang 2 gettext locale
Lang2Locale => [
## not a language tag or available locale.
[undef() => undef],
['C' => undef],
['POSIX' => undef],
['ca' => 'ca'],
['cs' => 'cs'],
['en' => 'en'],
['en-US' => 'en_US'],
['ja-JP' => 'ja_JP'],
['nb' => 'nb'],
['nb-NO' => 'nb_NO', 'not recommended but possible'],
['pt' => 'pt'],
['pt-BR' => 'pt_BR'],
['zh' => 'zh'],
['zh-CN' => 'zh_CN'],
## non-POSIX locales
['cz' => 'cs'],
['us' => 'en_US'],
['cn' => 'zh_CN'],
## OLd style locales
['en_US' => 'en_US'],
['ja_JP' => 'ja'],
['nb_NO' => 'nb'],
['pt_BR' => 'pt_BR'],
['zh_CN' => 'zh_CN'],
## Complex tags
['ca-ES-valencia' => 'ca_ES@valencia'],
['be-Latn' => 'be@latin'],
['tyv-Latn-MN' => 'tyv_MN@latin'],
],
## Lang to old style locale
Lang2Locale_old => [
['ca' => 'ca_ES'],
['cs' => 'cs_CZ'],
['en' => undef, 'special'],
['en-US' => 'en_US'],
['ja-JP' => 'ja_JP'],
['nb' => 'nb_NO'],
['nb-NO' => 'nb_NO'],
['pt' => 'pt_PT'],
['pt-BR' => 'pt_BR'],
['zh' => undef, 'region not determined'],
['zh-CN' => 'zh_CN'],
## zh
['zh-Hant' => 'zh_TW'],
['zh-Hans-HK' => 'zh_HK'],
## non-POSIX locales
['cz' => 'cs_CZ'],
['us' => 'en_US'],
['cn' => 'zh_CN'],
## Old style locales
['en_US' => 'en_US'],
['ja_JP' => 'ja_JP'],
['nb_NO' => 'nb_NO'],
['pt_BR' => 'pt_BR'],
['zh_CN' => 'zh_CN'],
],
## Canonical names
CanonicLang => [
## not a language tag
[undef() => undef],
['C' => undef],
['POSIX' => undef],
['en_Dsrt_US' => undef, 'illegal format'],
['zh-min-nan' => undef, 'unsupported tag'],
['ca' => 'ca'],
['cs' => 'cs'],
['en' => 'en'],
['en-US' => 'en-US'],
['ja-JP' => 'ja-JP'],
['nb' => 'nb'],
['nb-NO' => 'nb-NO', 'not recommended but possible'],
['pt' => 'pt'],
['pt-BR' => 'pt-BR'],
['zh' => 'zh'],
['zh-CN' => 'zh-CN'],
## non-POSIX locales
['cz' => 'cs'],
['us' => 'en-US'],
['cn' => 'zh-CN'],
## Old-style locales
['en_US' => 'en-US'],
['ja_JP' => 'ja'],
['nb_NO' => 'nb'],
['pt_BR' => 'pt-BR'],
['zh_CN' => 'zh-CN'],
],
## Implicated langs
ImplicatedLangs => [
['ca' => ['ca']],
['en-US' => [qw(en-US en)]],
['ca-ES-valencia' => [qw(ca-ES-valencia ca-ES ca)]],
['be-Latn' => [qw(be-Latn be)]],
['tyv-Latn-MN' => [qw(tyv-Latn-MN tyv-Latn tyv)]],
## zh-Hans-*/zh-Hant-* workaround
['zh-Hans-CN' => [qw(zh-Hans-CN zh-CN zh-Hans zh)]],
[ 'zh-Hant-HK-xxxxx' => [
qw(zh-Hant-HK-xxxxx zh-HK-xxxxx zh-Hant-HK zh-HK zh-Hant zh)]
],
## non-POSIX locales
['cn' => [qw(zh-CN zh)]],
## Old style locales
['en_US' => [qw(en-US en)]],
['nb_NO' => ['nb']],
],
## Content negotiation
NegotiateLang => [
[['de', 'en'] => undef],
[['DE,en,fr;Q=0.5,es;q=0.1', 'es,fr,de,en'] => 'de'],
[['en', 'EN-CA,en'] => 'en-CA'],
[['en-US', 'en,en-CA,en-US'] => 'en-US'],
],
SetLang => [
## Unknown language
[undef() => undef],
['C', => undef],
['POSIX' => undef],
['ja' => undef, 'no catalog - error'],
## Fallback
['cs-CZ-lasstina' => 'cs'],
['cs-lasstina' => 'cs', 'locale-independent case'],
['cs-CZ' => 'cs'],
['cs' => 'cs'],
['en-CA' => 'en', 'no catalog (en) - fallback to en'],
['en' => 'en', 'no catalog (en) - fallback to en'],
['zh' => 'zh-TW', 'macrolanguage zh'],
['zh-guoyu' => 'zh-TW', 'macrolanguage zh'],
['zh-TW' => 'zh-TW'],
['zh-Hant' => 'zh-TW', 'semi-macrolanguage zh-Hant'],
['zh-Hant-TW' => 'zh-TW'],
['zh-Hant-HK' => 'zh-TW', 'semi-macrolanguage zh-Hant'],
['zh-Hant-guoyu' => 'zh-TW', 'semi-macrolanguage zh-Hant'],
['zh-Hans-CN' => 'zh-TW', 'macrolanguage zh'],
],
GetLangName => [
[undef() => "\xC4\x8Cesky", 'current lang'],
['cs-CZ' => "\xC4\x8Cesky"],
['en' => 'English'],
['en-CA' => 'English', 'fallback to en'],
['zh-TW' => "\xE7\xB9\x81\xE9\xAB\x94\xE4\xB8\xAD\xE6\x96\x87"],
],
## Translation
gettext => [
[undef() => undef, 'undefined msgid'],
['' => '', 'empty msgid'],
['lorem ipsum' => 'lorem ipsum', 'unknown msgid'],
['_language_' => "\xC4\x8Cesky"],
[ 'Sun:Mon:Tue:Wed:Thu:Fri:Sat' =>
"Ne:Po:\xC3\x9At:St:\xC4\x8Ct:P\xC3\xA1:So"
],
],
dgettext => [
[['web_help', undef] => undef, 'undefined msgid'],
[['web_help', ''] => '', 'empty msgid'],
[['web_help', 'lorem ipsum'] => 'lorem ipsum', 'unknown msgid'],
[['web_help', '_language_'] => "\xC4\x8Cesky"],
[['web_help', 'What is a mailing list?'] => "Co je mail list?"],
],
## POSIX::strftime()
strftime => [['%a, %d %b %Y' => 'Thu, 01 Jan 1970', 'POSIX strftime'],],
## Emulated strftime()
gettext_strftime =>
[['%a, %d %b %Y' => "\xC4\x8Ct 01. Led 1970", 'emulated strftime'],],
);
plan tests => scalar map {@$_} values %tests;
foreach my $test (@{$tests{Lang2Locale}}) {
is( Sympa::Language::Lang2Locale($test->[0]),
$test->[1],
( defined $test->[0]
? "Lang2Locale($test->[0])"
: 'Lang2Locale(undef)'
)
. ($test->[2] ? ": $test->[2]" : '')
);
}
foreach my $test (@{$tests{Lang2Locale_old}}) {
is(Sympa::Language::Lang2Locale_old($test->[0]),
$test->[1],
"Lang2Locale_old($test->[0])" . ($test->[2] ? ": $test->[2]" : ''));
}
foreach my $test (@{$tests{CanonicLang}}) {
is( Sympa::Language::CanonicLang($test->[0]),
$test->[1],
( defined $test->[0]
? "CanonicLang($test->[0])"
: 'CanonicLang(undef)'
)
. ($test->[2] ? ": $test->[2]" : '')
);
}
foreach my $test (@{$tests{ImplicatedLangs}}) {
is_deeply([Sympa::Language::ImplicatedLangs($test->[0])],
$test->[1],
"ImplicatedLangs($test->[0])" . ($test->[2] ? ": $test->[2]" : ''));
}
foreach my $test (@{$tests{NegotiateLang}}) {
is(Sympa::Language::NegotiateLang(@{$test->[0]}), $test->[1],
"NegotiateLang("
. join(' ', @{$test->[0]}) . ')'
. ($test->[2] ? ": $test->[2]" : ''));
}
foreach my $test (@{$tests{SetLang}}) {
is(Sympa::Language::SetLang($test->[0]), $test->[1],
(defined $test->[0] ? "SetLang($test->[0])" : 'SetLang(undef)')
. ($test->[2] ? ": $test->[2]" : ''));
}
Sympa::Language::SetLang('cs');
foreach my $test (@{$tests{GetLangName}}) {
is( Sympa::Language::GetLangName($test->[0]),
$test->[1],
( defined $test->[0]
? "GetLangName($test->[0])"
: 'GetLangName(undef)'
)
. ($test->[2] ? ": $test->[2]" : '')
);
}
Sympa::Language::SetLang('cs');
foreach my $test (@{$tests{gettext}}) {
is(Sympa::Language::gettext($test->[0]), $test->[1],
(defined $test->[0] ? "gettext($test->[0])" : 'gettext(undef)')
. ($test->[2] ? ": $test->[2]" : ''));
}
Sympa::Language::SetLang('cs');
foreach my $test (@{$tests{dgettext}}) {
is( Sympa::Language::dgettext(@{$test->[0]}),
$test->[1],
( defined $test->[0]->[1]
? "dgettext(" . join(' ', @{$test->[0]}) . ")"
: 'dgettext(' . $test->[0]->[0] . ' undef)'
)
. ($test->[2] ? ": $test->[2]" : '')
);
}
Sympa::Language::SetLang('en');
foreach my $test (@{$tests{strftime}}) {
is( Sympa::Language::gettext_strftime($test->[0], gmtime 0),
$test->[1],
"gettext_strftime($test->[0])" . ($test->[2] ? ": $test->[2]" : '')
);
}
Sympa::Language::SetLang('cs');
POSIX::setlocale(POSIX::LC_TIME(), 'C');
foreach my $test (@{$tests{gettext_strftime}}) {
is( Sympa::Language::gettext_strftime($test->[0], gmtime 0),
$test->[1],
"gettext_strftime($test->[0])" . ($test->[2] ? ": $test->[2]" : '')
);
}
msgid ""
msgstr ""
"Language-Team: Česky\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Sun:Mon:Tue:Wed:Thu:Fri:Sat"
msgstr "Ne:Po:Út:St:Čt:Pá:So"
msgid "%a, %d %b %Y"
msgstr "%a %d. %b %Y"
msgid "Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec"
msgstr "Led:Úno:Bře:Dub:Kvě:Čern:Červ:Srp:Zář:Říj:List:Pro"
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../web_tt2/help_introduction.tt2:5
msgid "What is a mailing list?"
msgstr "Co je mail list?"
msgid ""
msgstr ""
"Language-Team: 繁體中文 <zh_TW@li.org>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$
# Test stub for Sympa::Constants;
package Sympa::Constants;
use constant LOCALEDIR => 't/locale';
1;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment