Commit 1ef5a240 authored by sikeda's avatar sikeda
Browse files

[feature] i18n titles in configuration.

- title.<lang> lines in scenario files are available again, though this feature was broken for a while.
- Now topics.conf recognizes "title.gettext" lines.
- scenario, task and topics.conf recognize "title" line for default title.

[-dev] (r10623) Canonicalize incoming language codes to language tags from old-style locale names:
- lang value in user_table
- title.<lang> line in scenario files
- title.<lang> line in task files
- title.<lang> line in topics.conf
- lang names in charset.conf
- lang parameters of list config, robot.conf and sympa.conf
Note that canonicalization will be done internally so that existing config must not be changed.


git-svn-id: https://subversion.renater.fr/sympa/branches/sympa-6.2-branch@10626 05aa8bb8-cd2b-0410-b1d7-8918dfa770ce
parent 90c7ace3
......@@ -139,7 +139,7 @@ $(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
../soap/*.pl.in ../soap/*.pm ../soap/*.fcgi.in \
../wwsympa/wwsympa.fcgi.in ../wwsympa/*.pl.in ../wwsympa/*.pm \
../web_tt2/*.tt2 ../mail_tt2/*.tt2 \
../src/etc/mhonarc-ressources.tt2 \
../src/etc/mhonarc-ressources.tt2 ../src/etc/topics.conf \
../src/etc/scenari/* \
../src/etc/global_task_models/* ../src/etc/list_task_models/* \
../src/etc/create_list_templates/*/* \
......
......@@ -31,14 +31,13 @@ use Exporter;
use Carp;
use Storable;
use confdef;
use Sympa::Constants;
use Sympa::Language;
use List;
use SDM;
use Log;
use Language;
use confdef;
use SDM;
use tools;
use Sympa::Constants;
use Data::Dumper;
our @ISA = qw(Exporter);
our @EXPORT = qw(%params %Conf DAEMON_MESSAGE DAEMON_COMMAND DAEMON_CREATION DAEMON_ALL);
......@@ -1030,16 +1029,16 @@ sub load_charset {
s/\s*#.*//;
s/^\s+//;
next unless /\S/;
my ($locale, $cset) = split(/\s+/, $_);
my ($lang, $cset) = split(/\s+/, $_);
unless ($cset) {
Log::do_log('err', 'Charset name is missing in configuration file %s line %d',$config_file, $.);
Log::do_log('err',
'Charset name is missing in configuration file %s line %d',
$config_file, $.);
next;
}
unless ($locale =~ s/^([a-z]+)_([a-z]+)/lc($1).'_'.uc($2).$'/ei) { #'
Log::do_log('err', 'Illegal locale name in configuration file %s line %d',$config_file, $.);
next;
}
$charset->{$locale} = $cset;
# canonicalize lang if possible.
$lang = Sympa::Language::canonic_lang($lang) || $lang;
$charset->{$lang} = $cset;
}
close CONFIG;
......@@ -1487,7 +1486,7 @@ sub _load_config_file_to_hash {
$result->{'numbered_config'}{$keyword} = [ $value, $line_num ];
}
} else {
printf STDERR "Conf::_load_config_file_to_hash(): ".gettext("Error at line %d: %s\n"), $line_num, $param->{'path_to_config_file'}, $_;
printf STDERR "Conf::_load_config_file_to_hash(): Error at line %d: %s\n", $line_num, $param->{'path_to_config_file'}, $_;
$result->{'errors'}++;
}
}
......@@ -1615,6 +1614,11 @@ sub _load_server_specific_secondary_config_files {
);
}
# canonicalize language, or if failed, apply site-wide default.
$param->{'config_hash'}{'lang'} =
Sympa::Language::canonic_lang($param->{'config_hash'}{'lang'})
|| 'en-US';
## Load charset.conf file if necessary.
if($param->{'config_hash'}{'legacy_character_support_feature'} eq 'on'){
$param->{'config_hash'}{'locale2charset'} = &load_charset ();
......@@ -1625,7 +1629,6 @@ sub _load_server_specific_secondary_config_files {
## Load nrcpt_by_domain.conf
$param->{'config_hash'}{'nrcpt_by_domain'} = &load_nrcpt_by_domain () ;
$param->{'config_hash'}{'crawlers_detection'} = &load_crawlers_detection($param->{'config_hash'}{'robot_name'});
}
sub _infer_robot_parameter_values {
......@@ -1683,6 +1686,11 @@ sub _infer_robot_parameter_values {
$param->{'config_hash'}{'automatic_list_families'} = \%families_description;
}
# canonicalize language
$param->{'config_hash'}{'lang'} =
Sympa::Language::canonic_lang($param->{'config_hash'}{'lang'})
or delete $param->{'config_hash'}{'lang'};
&_parse_custom_robot_parameters({'config_hash' => $param->{'config_hash'}});
}
......
......@@ -25,11 +25,19 @@ package List;
use strict;
use warnings;
use Exporter;
use Encode;
use Fcntl qw(LOCK_SH LOCK_EX LOCK_NB LOCK_UN);
use HTML::Entities qw(encode_entities);
use POSIX qw(strftime);
use IO::Scalar;
use Storable;
use Time::Local qw(timelocal);
use MIME::Entity;
use MIME::EncWords;
use MIME::Parser;
use Datasource;
use LDAPSource;
use SDM;
......@@ -41,20 +49,24 @@ use Task;
use Scenario;
use Fetch;
use WebAgent;
use Exporter;
use Data::Dumper;
use tt2;
use Sympa::Constants;
use Sympa::ListDef;
use Archive;
use Sympa::Language;
use Log;
use Conf;
use mail;
use Ldap;
use Message;
use Family;
use PlainDigest;
our @ISA = qw(Exporter);
our @EXPORT = qw(%list_of_lists);
=encoding utf-8
=cut
my @sources_providing_listmembers = qw/
include_file
include_ldap_2level_query
......@@ -262,26 +274,6 @@ currently selected descriptor.
=cut
use Carp;
use IO::Scalar;
use Storable;
use Mail::Header;
use Archive;
use Sympa::Language;
use Log;
use Conf;
use mail;
use Ldap;
use Time::Local;
use MIME::Entity;
use MIME::EncWords;
use MIME::Parser;
use Message;
use Family;
use PlainDigest;
## Database and SQL statement handlers
my ($sth, @sth_stack);
......@@ -519,9 +511,7 @@ my %edit_list_conf = ();
## Last modification times
my %mtime;
use Fcntl;
use DB_File;
$DB_BTREE->{compare} = \&_compare_addresses;
our %listmaster_messages_stack;
......@@ -5453,15 +5443,12 @@ sub _create_add_error_string {
my $self = shift;
$self->{'add_outcome'}{'errors'}{'error_message'} = '';
if ($self->{'add_outcome'}{'errors'}{'max_list_members_exceeded'}) {
$self->{'add_outcome'}{'errors'}{'error_message'} .=
$language->gettext_sprintf(
'Attempt to exceed the max number of members (%s) for this list.',
$self->{'admin'}{'max_list_members'});
$self->{'add_outcome'}{'errors'}{'error_message'} .= $language->gettext_sprintf('Attempt to exceed the max number of members (%s) for this list.', $self->{'admin'}{'max_list_members'});
}
if ($self->{'add_outcome'}{'errors'}{'unable_to_add_to_database'}) {
$self->{'add_outcome'}{'error_message'} .= ' '. $language->gettext('Attempts to add some users in database failed.');
}
$self->{'add_outcome'}{'errors'}{'error_message'} .= ' '. $language->gettext_sorintf('Added %s users out of %s required.'),$self->{'add_outcome'}{'added_members'},$self->{'add_outcome'}{'expected_number_of_added_users'});
$self->{'add_outcome'}{'errors'}{'error_message'} .= ' '. $language->gettext_sprintf('Added %s users out of %s required.', $self->{'add_outcome'}{'added_members'},$self->{'add_outcome'}{'expected_number_of_added_users'});
}
## Adds a new list admin user, no overwrite.
......@@ -6106,6 +6093,7 @@ sub load_task_list {
) {
next unless (-d $dir);
LOOP_FOREACH_FILE:
foreach my $file (<$dir/$action.*>) {
next unless ($file =~ /$action\.(\w+)\.task$/);
my $name = $1;
......@@ -6114,19 +6102,25 @@ sub load_task_list {
$list_of_task{$name}{'name'} = $name;
my $titles = &List::_load_task_title ($file);
my $titles = List::_load_task_title($file);
## Set the title in the current language
if (defined $titles->{$language->get_lang}) {
$list_of_task{$name}{'title'} = $titles->{$language->get_lang};
}elsif (defined $titles->{'gettext'}) {
$list_of_task{$name}{'title'} = $language->gettext( $titles->{'gettext'});
}elsif (defined $titles->{'us'}) {
$list_of_task{$name}{'title'} = $language->gettext( $titles->{'us'});
}else {
foreach my $lang (
Sympa::Language::implicated_langs($language->get_lang)
) {
if (exists $titles->{$lang}) {
$list_of_task{$name}{'title'} = $titles->{$lang};
next LOOP_FOREACH_FILE;
}
}
if (exists $titles->{'gettext'}) {
$list_of_task{$name}{'title'} =
$language->gettext($titles->{'gettext'});
} elsif (exists $titles->{'default'}) {
$list_of_task{$name}{'title'} = $titles->{'default'};
} else {
$list_of_task{$name}{'title'} = $name;
}
}
}
......@@ -6134,11 +6128,11 @@ sub load_task_list {
}
sub _load_task_title {
Log::do_log('debug3', '(%s)', @_);
my $file = shift;
&Log::do_log('debug3', 'List::_load_task_title(%s)', $file);
my $title = {};
my $titles = {};
unless (open TASK, $file) {
unless (open TASK, '<', $file) {
&Log::do_log('err', 'Unable to open file "%s"' , $file);
return undef;
}
......@@ -6146,14 +6140,21 @@ sub _load_task_title {
while (<TASK>) {
last if /^\s*$/;
if (/^title\.([\w-]+)\s+(.*)\s*$/) {
$title->{$1} = $2;
if (/^title\.gettext\s+(.*)\s*$/i) {
$titles->{'gettext'} = $1;
} elsif (/^title\.(\S+)\s+(.*)\s*$/i) {
my ($lang, $title) = ($1, $2);
# canonicalize lang if possible.
$lang = Sympa::Language::canonic_lang($lang) || $lang;
$titles->{$lang} = $title;
} elsif (/^title\s+(.*)\s*$/i) {
$titles->{'default'} = $1;
}
}
close TASK;
return $title;
return $titles;
}
## Loads all data sources
......@@ -7296,8 +7297,8 @@ sub _load_list_members_from_include {
$result->{'users'} = \%users;
$result->{'errors'} = \@errors;
$result->{'exclusions'} = \@ex_sources;
use Data::Dumper;
if(open OUT, '>/tmp/result') { print OUT Dumper $result; close OUT }
##use Data::Dumper;
##if(open OUT, '>/tmp/result') { print OUT Dumper $result; close OUT }
return $result;
}
## Loads the list of admin users from an external include source
......@@ -8943,7 +8944,6 @@ sub lowercase_field {
## Loads the list of topics if updated
## FIXME: This might be moved to Robot package.
sub load_topics {
my $robot = shift ;
&Log::do_log('debug2', 'List::load_topics(%s)',$robot);
......@@ -8972,9 +8972,9 @@ sub load_topics {
return undef;
}
## Raugh parsing
## Rough parsing
my $index = 0;
my (@raugh_data, $topic);
my (@rough_data, $topic);
while (<FILE>) {
Encode::from_to($_, $Conf::Conf{'filesystem_encoding'}, 'utf8');
if (/^([\-\w\/]+)\s*$/) {
......@@ -8987,29 +8987,29 @@ sub load_topics {
$topic->{$1} = $2;
}elsif (/^\s*$/) {
if (defined $topic->{'name'}) {
push @raugh_data, $topic;
$topic = {};
}
next unless defined $topic->{'name'};
push @rough_data, $topic;
$topic = {};
}
}
close FILE;
## Last topic
if (defined $topic->{'name'}) {
push @raugh_data, $topic;
push @rough_data, $topic;
$topic = {};
}
$mtime{'topics'}{$robot} = (stat($conf_file))[9];
unless ($#raugh_data > -1) {
unless ($#rough_data > -1) {
Log::do_log('notice', 'No topic defined in %s', $conf_file);
return undef;
}
## Analysis
foreach my $topic (@raugh_data) {
foreach my $topic (@rough_data) {
my @tree = split '/', $topic->{'name'};
if ($#tree == 0) {
......@@ -9041,10 +9041,39 @@ sub load_topics {
my $lang = $language->get_lang;
foreach my $top (keys %{$list_of_topics{$robot}}) {
my $topic = $list_of_topics{$robot}{$top};
$topic->{'current_title'} = $topic->{'title'}{$lang} || $topic->{'title'}{'default'} || $top;
foreach my $l (Sympa::Language::implicated_langs($lang)) {
if (exists $topic->{'title'}{$l}) {
$topic->{'current_title'} = $topic->{'title'}{$l};
}
}
unless (exists $topic->{'current_title'}) {
if (exists $topic->{'title'}{'gettext'}) {
$topic->{'current_title'} =
$language->gettext($topic->{'title'}{'gettext'});
} else {
$topic->{'current_title'} = $topic->{'title'}{'default'}
|| $top;
}
}
foreach my $subtop (keys %{$topic->{'sub'}}) {
$topic->{'sub'}{$subtop}{'current_title'} = $topic->{'sub'}{$subtop}{'title'}{$lang} || $topic->{'sub'}{$subtop}{'title'}{'default'} || $subtop;
foreach my $l (Sympa::Language::implicated_langs($lang)) {
if (exists $topic->{'sub'}{$subtop}{'title'}{$l}) {
$topic->{'sub'}{$subtop}{'current_title'} =
$topic->{'sub'}{$subtop}{'title'}{$l};
}
}
unless (exists $topic->{'sub'}{$subtop}{'current_title'}) {
if (exists $topic->{'sub'}{$subtop}{'title'}{'gettext'}) {
$topic->{'sub'}{$subtop}{'current_title'} =
$language->gettext(
$topic->{'sub'}{$subtop}{'title'}{'gettext'});
} else {
$topic->{'sub'}{$subtop}{'current_title'} =
$topic->{'sub'}{$subtop}{'title'}{'default'}
|| $subtop;
}
}
}
}
......@@ -9056,12 +9085,18 @@ sub _get_topic_titles {
my $title;
foreach my $key (%{$topic}) {
if ($key =~ /^title(.(\w+))?$/) {
my $lang = $2 || 'default';
if ($key =~ /^title\.gettext$/i) {
$title->{'gettext'} = $topic->{$key};
} elsif ($key =~ /^title\.(\S+)$/i) {
my $lang = $1;
# canonicalize lang if possible.
$lang = Sympa::Language::canonic_lang($lang) || $lang;
$title->{$lang} = $topic->{$key};
} elsif ($key =~ /^title$/i) {
$title->{'default'} = $topic->{$key};
}
}
return $title;
}
......@@ -9539,6 +9574,12 @@ sub _load_list_config_file {
$admin{'forced_reply_to'} = undef;
}
# lang
# canonicalize language
unless ($admin{'lang'} = Sympa::Language::canonic_lang($admin{'lang'})) {
$admin{'lang'} = Conf::get_robot_conf($robot, 'lang');
}
############################################
## Below are constraints between parameters
############################################
......
......@@ -176,8 +176,9 @@ sub _parse_scenario {
next;
}elsif ($current_rule =~ /^\s*title\.(\S+)\s+(.*)\s*$/i) {
my ($lang, $title) = ($1, $2);
my $locale = Sympa::Language::lang2oldlocale($lang) || $lang;
$structure->{'title'}{$locale} = $title;
# canonicalize lang if possible.
$lang = Sympa::Language::canonic_lang($lang) || $lang;
$structure->{'title'}{$lang} = $title;
next;
} elsif ($current_rule =~ /^\s*title\s+(.*)\s*$/i) {
$structure->{'title'}{'default'} = $1;
......@@ -1464,12 +1465,16 @@ sub get_current_title {
my $language = Sympa::Language->instance;
my $locale = Sympa::Language::lang2oldlocale($language->get_lang);
if (defined $self->{'title'}{$locale}) {
return $self->{'title'}{$locale};
} elsif (defined $self->{'title'}{'gettext'}) {
foreach my $lang (
Sympa::Language::implicated_langs($language->get_lang)
) {
if (exists $self->{'title'}{$lang}) {
return $self->{'title'}{$lang};
}
}
if (exists $self->{'title'}{'gettext'}) {
return $language->gettext($self->{'title'}{'gettext'});
} elsif (defined $self->{'title'}{'default'}) {
} elsif (exists $self->{'title'}{'default'}) {
return $self->{'title'}{'default'};
} else {
return $self->{'name'};
......
......@@ -81,10 +81,10 @@ sub new {
my $self;
return undef unless $who;
# ## Canonicalize lang if possible
# $values{'lang'} =
# Sympa::Language::canonic_lang($values{'lang'}) || $values{'lang'}
# if $values{'lang'};
## Canonicalize lang if possible
$values{'lang'} =
Sympa::Language::canonic_lang($values{'lang'}) || $values{'lang'}
if $values{'lang'};
if (!($self = get_global_user($who))) {
## unauthenticated user would not be added to database.
......@@ -365,11 +365,11 @@ sub get_global_user {
&tools::decrypt_password($user->{'password'});
}
# ## Canonicalize lang if possible
# if ($user->{'lang'}) {
# $user->{'lang'} =
# Sympa::Language::canonic_lang($user->{'lang'}) || $user->{'lang'};
# }
## Canonicalize lang if possible
if ($user->{'lang'}) {
$user->{'lang'} =
Sympa::Language::canonic_lang($user->{'lang'}) || $user->{'lang'};
}
## Turn user_attributes into a hash
my $attributes = $user->{'attributes'};
......@@ -465,10 +465,10 @@ sub update_global_user {
$values->{'password'} = &Auth::password_fingerprint($values->{'password'})
if ($values->{'password'});
# ## Canonicalize lang if possible.
# $values->{'lang'} =
# Sympa::Language::canonic_lang($values->{'lang'}) || $values->{'lang'}
# if $values->{'lang'};
## Canonicalize lang if possible.
$values->{'lang'} =
Sympa::Language::canonic_lang($values->{'lang'}) || $values->{'lang'}
if $values->{'lang'};
my ($field, $value);
......@@ -538,10 +538,10 @@ sub add_global_user {
$values->{'password'} = &Auth::password_fingerprint($values->{'password'})
if ($values->{'password'});
# ## Canonicalize lang if possible
# $values->{'lang'} =
# Sympa::Language::canonic_lang($values->{'lang'}) || $values->{'lang'}
# if $values->{'lang'};
## Canonicalize lang if possible
$values->{'lang'} =
Sympa::Language::canonic_lang($values->{'lang'}) || $values->{'lang'}
if $values->{'lang'};
return undef unless (my $who = &tools::clean_email($values->{'email'}));
return undef if (is_global_user($who));
......
......@@ -45,7 +45,7 @@ use if (5.008 < $] && $] < 5.016), qw(Unicode::CaseFold fc);
use if (5.016 <= $]), qw(feature fc);
use Conf;
use Language qw(gettext_strftime);
use Sympa::Language;
use Sympa::LockedFile;
use Log;
use Sympa::Constants;
......@@ -498,26 +498,38 @@ sub get_list_list_tpl {
unless ($list_conf = &load_create_list_conf($robot)) {
return undef;
}
foreach my $dir (
reverse
@{tools::get_search_path($robot, subdir => 'create_list_templates')}
) {
if (opendir(DIR, $dir)) {
LOOP_FOREACH_TEMPLATE:
foreach my $template ( sort grep (!/^\./,readdir(DIR))) {
my $status = $list_conf->{$template} || $list_conf->{'default'};
next if ($status eq 'hidden') ;
next if $status eq 'hidden';
$list_templates->{$template}{'path'} = $dir;
my $locale = Sympa::Language::lang2oldlocale( $language->get_lang);
## Look for a comment.tt2 in the appropriate locale first
if (-r $dir.'/'.$template.'/'.$locale.'/comment.tt2') {
$list_templates->{$template}{'comment'} = $dir.'/'.$template.'/'.$locale.'/comment.tt2';
}elsif (-r $dir.'/'.$template.'/comment.tt2') {
$list_templates->{$template}{'comment'} = $dir.'/'.$template.'/comment.tt2';
# Look for a comment.tt2.
# Check old style locale first then canonic language and its
# fallbacks.
my $lang = Sympa::Language->instance->get_lang;
my $comment_tt2;
foreach my $l (
Sympa::Language::lang2oldlocale($lang),
Sympa::Language::implicated_langs($lang)
) {
next unless $l;
$comment_tt2 = $dir.'/'.$template.'/'.$l.'/comment.tt2';
if (-r $comment_tt2) {
$list_templates->{$template}{'comment'} = $comment_tt2;
next LOOP_FOREACH_TEMPLATE;
}
}
$comment_tt2 = $dir.'/'.$template.'/comment.tt2';
if (-r $comment_tt2) {
$list_templates->{$template}{'comment'} = $comment_tt2;
}
}
closedir(DIR);
......@@ -714,7 +726,7 @@ sub get_templates_list {
# return the path for a specific template
sub get_template_path {
Log::do_log('debug2', '(%s, %s. %s, %s, %s, %s)', @_);
my $type = shift;
my $robot = shift;
my $scope = shift;
......@@ -722,41 +734,44 @@ sub get_template_path {
my $lang = shift || 'default';
my $list = shift;
&Log::do_log('debug', "get_templates_path ($type,$robot,$scope,$tpl,$lang,%s)", $list->{'name'});
my $listdir;
if (defined $list) {
$listdir = $list->{'dir'};
my $subdir = '';
# canonicalize language name which may be old-style locale name.
unless ($lang eq 'default') {
my $oldlocale = Sympa::Language::lang2oldlocale($lang);
unless ($oldlocale eq $lang) {
$subdir = Sympa::Language::canonic_lang($lang);
unless ($subdir) {
Log::do_log('info', 'internal error incorrect parameter');
return undef;
}
}
}
unless (($type == 'web')||($type == 'mail')) {
&Log::do_log('info', 'get_templates_path () : internal error incorrect parameter');
unless ($type eq 'web' or $type eq 'mail') {
Log::do_log('info', 'internal error incorrect parameter');
return undef;
}
my $distrib_dir = Sympa::Constants::DEFAULTDIR . '/'.$type.'_tt2';
$distrib_dir .= '/' . $lang unless $lang eq 'default';
my $site_dir = $Conf::Conf{'etc'}.'/'.$type.'_tt2';
$site_dir .= '/'.$lang unless ($lang eq 'default');
my $robot_dir = $Conf::Conf{'etc'}.'/'.$robot.'/'.$type.'_tt2';
$robot_dir .= '/'.$lang unless ($lang eq 'default');
my $dir;
if ($scope eq 'list') {
my $dir = $listdir.'/'.$type.'_tt2';
$dir .= '/'.$lang unless ($lang eq 'default');
return $dir.'/'.$tpl ;
}elsif ($scope eq 'robot') {
return $robot_dir.'/'.$tpl;
}elsif ($scope eq 'site') {
return $site_dir.'/'.$tpl;
}elsif ($scope eq 'distrib') {
return $distrib_dir.'/'.$tpl;
unless (ref $list eq 'List') {
Log::do_log('err', 'missing parameter "list"');
return undef;
}
$dir = $list->{'dir'};
} elsif ($scope eq 'robot') {