New wrapper script for handling translation process

Signed-off-by: Chris Cormack <chrisc@catalyst.net.nz>
Signed-off-by: Galen Charlton <gmcharlt@gmail.com>
This commit is contained in:
Frédéric Demians 2010-02-17 20:16:43 +13:00 committed by Galen Charlton
parent 999e78ac30
commit c05b7747b9
6 changed files with 558 additions and 400 deletions

432
misc/translator/LangInstaller.pm Executable file
View File

@ -0,0 +1,432 @@
package LangInstaller;
use strict;
use warnings;
use C4::Context;
use YAML::Syck qw( Dump LoadFile );
use Locale::PO;
sub set_lang {
my ($self, $lang) = @_;
$self->{lang} = $lang;
$self->{po_path_lang} = $self->{context}->config('intrahtdocs') .
"/prog/$lang/modules/admin/preferences";
}
sub new {
my ($class, $lang, $pref_only) = @_;
my $self = { };
my $context = C4::Context->new();
$self->{context} = $context;
$self->{path_pref_en} = $context->config('intrahtdocs') .
'/prog/en/modules/admin/preferences';
set_lang( $self, $lang ) if $lang;
$self->{pref_only} = $pref_only;
$self->{translator_path} = $context->config('intranetdir') . "/misc/translator";
$self->{path_po} = $self->{translator_path} . "/po";
$self->{po} = {};
# Get all .pref file names
opendir my $fh, $self->{path_pref_en};
my @pref_files = grep { /.pref/ } readdir($fh);
close $fh;
$self->{pref_files} = \@pref_files;
# Get all available language codes
opendir $fh, $self->{path_po};
my @langs = map { ($_) =~ /(.*)-i-opac/ }
grep { $_ =~ /.*-opac-/ } readdir($fh);
closedir $fh;
$self->{langs} = \@langs;
# Map for both interfaces opac/intranet
$self->{interface} = {
opac => {
dir => $context->config('opachtdocs') . '/prog',
suffix => '-i-opac-t-prog-v-3002000.po',
},
intranet => {
dir => $context->config('intrahtdocs') . '/prog',
suffix => '-i-staff-t-prog-v-3002000.po',
}
};
bless $self, $class;
}
sub po_filename {
my $self = shift;
my $context = C4::Context->new;
my $trans_path = $context->config('intranetdir') . '/misc/translator/po';
my $trans_file = "$trans_path/" . $self->{lang} . "-pref.po";
return $trans_file;
}
sub po_append {
my ($self, $id, $comment) = @_;
my $po = $self->{po};
my $p = $po->{$id};
if ( $p ) {
$p->comment( $p->comment . "\n" . $comment );
}
else {
$po->{$id} = Locale::PO->new(
-comment => $comment,
-msgid => $id,
-msgstr => ''
);
}
}
sub add_prefs {
my ($self, $comment, $prefs) = @_;
for my $pref ( @$prefs ) {
my $pref_name = '';
for my $element ( @$pref ) {
if ( ref( $element) eq 'HASH' ) {
$pref_name = $element->{pref};
last;
}
}
for my $element ( @$pref ) {
if ( ref( $element) eq 'HASH' ) {
while ( my ($key, $value) = each(%$element) ) {
next unless $key eq 'choices';
next unless ref($value) eq 'HASH';
for my $ckey ( keys %$value ) {
my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
$self->po_append( $id, $comment );
}
}
}
elsif ( $element ) {
$self->po_append( $self->{file} . "#$pref_name# $element", $comment );
}
}
}
}
sub get_trans_text {
my ($self, $id) = @_;
my $po = $self->{po}->{$id};
return unless $po;
return Locale::PO->dequote($po->msgstr);
}
sub update_tab_prefs {
my ($self, $pref, $prefs) = @_;
for my $p ( @$prefs ) {
my $pref_name = '';
next unless $p;
for my $element ( @$p ) {
if ( ref( $element) eq 'HASH' ) {
$pref_name = $element->{pref};
last;
}
}
for my $i ( 0..@$p-1 ) {
my $element = $p->[$i];
if ( ref( $element) eq 'HASH' ) {
while ( my ($key, $value) = each(%$element) ) {
next unless $key eq 'choices';
next unless ref($value) eq 'HASH';
for my $ckey ( keys %$value ) {
my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
my $text = $self->get_trans_text( $id );
$value->{$ckey} = $text if $text;
}
}
}
elsif ( $element ) {
my $text = $self->get_trans_text( $self->{file} . "#$pref_name# $element" );
$p->[$i] = $text if $text;
}
}
}
}
sub get_po_from_prefs {
my $self = shift;
for my $file ( @{$self->{pref_files}} ) {
my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
$self->{file} = $file;
#print Dump($pref), "\n";
while ( my ($tab, $tab_content) = each %$pref ) {
if ( ref($tab_content) eq 'ARRAY' ) {
$self->add_prefs( $tab, $tab_content );
next;
}
while ( my ($section, $sysprefs) = each %$tab_content ) {
my $comment = "$tab > $section";
$self->po_append( $self->{file} . " " . $section, $comment );
$self->add_prefs( $comment, $sysprefs );
}
}
}
}
sub save_po {
my $self = shift;
# Write .po entries into a file put in Koha standard po directory
Locale::PO->save_file_fromhash( $self->po_filename, $self->{po} );
print "Saved in file: ", $self->po_filename, "\n";
}
sub update_prefs {
my $self = shift;
print "Update '", $self->{lang}, "' preferences .po file from 'en' .pref files\n";
# Get po from current 'en' .pref files
$self->get_po_from_prefs();
my $po_current = $self->{po};
# Get po from previous generation
my $po_previous = Locale::PO->load_file_ashash( $self->po_filename );
for my $id ( keys %$po_current ) {
my $po = $po_previous->{'"'.$id.'"'};
next unless $po;
my $text = Locale::PO->dequote( $po->msgstr );
$po_current->{$id}->msgstr( $text );
}
$self->save_po();
}
sub install_prefs {
my $self = shift;
unless ( -r $self->{po_path_lang} ) {
print "Koha directories hierarchy for ", $self->{lang}, " must be created first\n";
exit;
}
# Update the language .po file with last modified 'en' preferences
# and load it.
$self->update_prefs();
for my $file ( @{$self->{pref_files}} ) {
my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
$self->{file} = $file;
while ( my ($tab, $tab_content) = each %$pref ) {
if ( ref($tab_content) eq 'ARRAY' ) {
$self->update_tab_prefs( $pref, $tab_content );
next;
}
while ( my ($section, $sysprefs) = each %$tab_content ) {
$self->update_tab_prefs( $pref, $sysprefs );
}
my $ntab = {};
for my $section ( keys %$tab_content ) {
my $text = $self->get_trans_text($self->{file} . " $section");
my $nsection = $text ? $text : $section;
$ntab->{$nsection} = $tab_content->{$section};
}
$pref->{$tab} = $ntab;
}
my $file_trans = $self->{po_path_lang} . "/$file";
print "Write $file\n";
open my $fh, ">", $file_trans;
print $fh Dump($pref);
}
}
sub install_tmpl {
my $self = shift;
print
"Install templates\n";
while ( my ($interface, $tmpl) = each %{$self->{interface}} ) {
print
" Install templates '$interface\n",
" From: $tmpl->{dir}/en/\n",
" To : $tmpl->{dir}/$self->{lang}\n",
" With: $self->{path_po}/$self->{lang}$tmpl->{suffix}\n";
my $lang_dir = "$tmpl->{dir}/$self->{lang}";
mkdir $lang_dir unless -d $lang_dir;
system
"$self->{translator_path}/tmpl_process3.pl install " .
"-i $tmpl->{dir}/en/ " .
"-o $tmpl->{dir}/$self->{lang} ".
"-s $self->{path_po}/$self->{lang}$tmpl->{suffix} -r"
}
}
sub update_tmpl {
my $self = shift;
print
"Update templates\n";
while ( my ($interface, $tmpl) = each %{$self->{interface}} ) {
print
" Update templates '$interface'\n",
" From: $tmpl->{dir}/en/\n",
" To : $self->{path_po}/$self->{lang}$tmpl->{suffix}\n";
my $lang_dir = "$tmpl->{dir}/$self->{lang}";
mkdir $lang_dir unless -d $lang_dir;
system
"$self->{translator_path}/tmpl_process3.pl update " .
"-i $tmpl->{dir}/en/ " .
"-s $self->{path_po}/$self->{lang}$tmpl->{suffix} -r"
}
}
sub create_prefs {
my $self = shift;
$self->get_po_from_prefs();
$self->save_po();
}
sub create_tmpl {
my $self = shift;
print
"Create templates\n";
while ( my ($interface, $tmpl) = each %{$self->{interface}} ) {
print
" Create templates .po files for '$interface'\n",
" From: $tmpl->{dir}/en/\n",
" To : $self->{path_po}/$self->{lang}$tmpl->{suffix}\n";
system
"$self->{translator_path}/tmpl_process3.pl create " .
"-i $tmpl->{dir}/en/ " .
"-s $self->{path_po}/$self->{lang}$tmpl->{suffix} -r"
}
}
sub install {
my $self = shift;
return unless $self->{lang};
$self->install_tmpl() unless $self->{pref_only};
$self->install_prefs();
}
sub update {
my $self = shift;
return unless $self->{lang};
$self->update_tmpl() unless $self->{pref_only};
$self->update_prefs();
}
sub create {
my $self = shift;
return unless $self->{lang};
$self->create_tmpl() unless $self->{pref_only};
$self->create_prefs();
}
1;
=head1 NAME
LangInstaller.pm - Handle templates and preferences translation
=head1 SYNOPSYS
my $installer = LangInstaller->new( 'fr-FR' );
$installer->create();
$installer->update();
$installer->install();
for my $lang ( @{$installer->{langs} ) {
$installer->set_lang( $lan );
$installer->install();
}
=head1 METHODS
=head2 new
Create a new instance of the installer object.
=head2 create
For the current language, create .po files for templates and preferences based
of the english ('en') version.
=head2 update
For the current language, update .po files.
=head2 install
For the current langage C<$self->{lang}, use .po files to translate the english
version of templates and preferences files and copy those files in the
appropriate directory.
=over
=item translate create F<lang>
Create 3 .po files in F<po> subdirectory: (1) from opac pages templates, (2)
intranet templates, and (3) from preferences.
=over
=item F<lang>-opac.po
Contains extracted text from english (en) OPAC templates found in
<KOHA_ROOT>/koha-tmpl/opac-tmpl/prog/en/ directory.
=item F<lang>-intranet.po
Contains extracted text from english (en) intranet templates found in
<KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/ directory.
=item F<lang>-pref.po
Contains extracted text from english (en) preferences. They are found in files
located in <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/admin/preferences
directory.
=back
=item pref-trans update F<lang>
Update .po files in F<po> directory, named F<lang>-*.po.
=item pref-trans install F<lang>
=back
=head1 COPYRIGHT AND LICENSE
Copyright 2010 by Tamil, s.a.r.l.
L<http://www.tamil.fr>
This script is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License, version 2.1.
=cut

View File

@ -1,47 +0,0 @@
#!/usr/bin/perl
use strict;
=head1 NAME
install-code.pl
=head1 USAGE
Install templates and preferences for given language codes.
For example:
./install-code fr-FR en-ES
creates templates for languages: fr-FR and en-ES
=cut
sub install_code {
my $code = shift;
opendir(PO_DIR, "po") or die "Unable to open po directory";
my @po_files = grep { /^$code-i-opac|^$code-i-staff/ } readdir PO_DIR;
closedir PO_DIR;
foreach ( @po_files ) {
my ($interface) = /(staff|opac)/;
$interface =~ s/staff/intranet/;
mkdir "../../koha-tmpl/$interface-tmpl/prog/$code";
print $_, " : ", $interface, "\n";
my $cmd = "./tmpl_process3.pl install -r " .
"-i ../../koha-tmpl/$interface-tmpl/prog/en/ " .
"-o ../../koha-tmpl/$interface-tmpl/prog/$code " .
"-s po/$_";
system $cmd;
}
system "./pref-trans install $code";
}
# Main
install_code ( $_ ) foreach ( @ARGV );

View File

@ -1,32 +0,0 @@
#!/usr/bin/perl
# script to update all translations
use strict;
use warnings;
# Doesn't' handle anymore 'themes' since there is no theme .po files at all
# Get all available language codes
opendir my $fh, "po";
my @langs = map { ($_) =~ /(.*)-i-opac/ }
grep { $_ =~ /.*-opac-/ } readdir($fh);
closedir DIR;
# Map interface name to .po file suffix
my %its = (
opac => '-i-opac-t-prog-v-3002000.po',
intranet => '-i-staff-t-prog-v-3002000.po',
);
for my $lang ( @langs ) {
print "Language: $lang\n";
while ( my ($interface, $suffix) = each %its ) {
my $cmd = "mkdir ../../koha-tmpl/$interface-tmpl/prog/$lang";
system $cmd;
$cmd =
"./tmpl_process3.pl install " .
"-i ../../koha-tmpl/$interface-tmpl/prog/en/ " .
"-o ../../koha-tmpl/$interface-tmpl/prog/$lang ".
"-s po/$lang$suffix -r";
system $cmd;
}
system "./pref-trans install $lang";
}

View File

@ -1,312 +0,0 @@
#!/usr/bin/perl
package Tokenizer;
use strict;
use warnings;
use C4::Context;
use YAML::Syck qw( Dump LoadFile );
use Data::Dumper;
use Locale::PO;
sub new {
my ($class, $lang) = @_;
my $self = { lang => $lang };
my $context = C4::Context->new();
$self->{path_en} = $context->config('intrahtdocs') .
'/prog/en/modules/admin/preferences';
$self->{path_lang} = $context->config('intrahtdocs') .
"/prog/$lang/modules/admin/preferences";
$self->{po} = {};
bless $self, $class;
}
sub po_filename {
my $self = shift;
my $context = C4::Context->new;
my $trans_path = $context->config('intranetdir') . '/misc/translator/po';
my $trans_file = "$trans_path/" . $self->{lang} . "-pref.po";
return $trans_file;
}
sub po_append {
my ($self, $id, $comment) = @_;
my $po = $self->{po};
my $p = $po->{$id};
if ( $p ) {
$p->comment( $p->comment . "\n" . $comment );
}
else {
$po->{$id} = Locale::PO->new(
-comment => $comment,
-msgid => $id,
-msgstr => ''
);
}
}
sub add_prefs {
my ($self, $comment, $prefs) = @_;
for my $pref ( @$prefs ) {
my $pref_name = '';
for my $element ( @$pref ) {
if ( ref( $element) eq 'HASH' ) {
$pref_name = $element->{pref};
last;
}
}
for my $element ( @$pref ) {
if ( ref( $element) eq 'HASH' ) {
while ( my ($key, $value) = each(%$element) ) {
next unless $key eq 'choices';
next unless ref($value) eq 'HASH';
for my $ckey ( keys %$value ) {
my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
$self->po_append( $id, $comment );
}
}
}
elsif ( $element ) {
$self->po_append( $self->{file} . "#$pref_name# $element", $comment );
}
}
}
}
sub get_trans_text {
my ($self, $id) = @_;
my $po = $self->{po}->{$id};
return unless $po;
return Locale::PO->dequote($po->msgstr);
}
sub update_prefs {
my ($self, $pref, $prefs) = @_;
for my $p ( @$prefs ) {
my $pref_name = '';
next unless $p;
my @pref_array = @$p;
for my $element ( @pref_array ) {
if ( ref( $element) eq 'HASH' ) {
$pref_name = $element->{pref};
last;
}
}
for my $i ( 0..$#pref_array ) {
my $element = $pref_array[$i];
if ( ref( $element) eq 'HASH' ) {
while ( my ($key, $value) = each(%$element) ) {
next unless $key eq 'choices';
next unless ref($value) eq 'HASH';
for my $ckey ( keys %$value ) {
my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
my $text = $self->get_trans_text( $id );
$value->{$ckey} = $text if $text;
}
}
}
elsif ( $element ) {
my $text = $self->get_trans_text( $self->{file} . "#$pref_name# $element" );
$p->[$i] = $text if $text;
}
}
}
}
sub get_po_from_prefs {
my $self = shift;
chdir( $self->{path_en} );
for my $file ( <*.pref> ) {
my $pref = LoadFile($file);
$self->{file} = $file;
#print Dump($pref), "\n";
while ( my ($tab, $tab_content) = each %$pref ) {
if ( ref($tab_content) eq 'ARRAY' ) {
$self->add_prefs( $tab, $tab_content );
next;
}
while ( my ($section, $sysprefs) = each %$tab_content ) {
my $comment = "$tab > $section";
$self->po_append( $self->{file} . " " . $section, $comment );
$self->add_prefs( $comment, $sysprefs );
}
}
}
}
sub save_po {
my $self = shift;
# Write .po entries into a file put in Koha standard po directory
Locale::PO->save_file_fromhash( $self->po_filename, $self->{po} );
print "Saved in file: ", $self->po_filename, "\n";
}
sub init {
my $self = shift;
$self->get_po_from_prefs();
$self->save_po();
}
sub update {
my $self = shift;
print "Update '", $self->{lang}, "' preferences .po file from 'en' .pref files\n";
# Get po from current 'en' .pref files
$self->get_po_from_prefs();
my $po_current = $self->{po};
# Get po from previous generation
my $po_previous = Locale::PO->load_file_ashash( $self->po_filename );
for my $id ( keys %$po_current ) {
my $po = $po_previous->{'"'.$id.'"'};
next unless $po;
my $text = Locale::PO->dequote( $po->msgstr );
$po_current->{$id}->msgstr( $text );
}
$self->save_po();
}
sub install {
my $self = shift;
unless ( -r $self->{path_lang} ) {
print "Koha directories hierarchy for ", $self->{lang}, " must be created first\n";
exit;
}
# Update the language .po file with last modified 'en' preferences
# and load it.
$self->update();
chdir( $self->{path_en} );
for my $file ( <*.pref> ) {
my $pref = LoadFile($file);
$self->{file} = $file;
while ( my ($tab, $tab_content) = each %$pref ) {
if ( ref($tab_content) eq 'ARRAY' ) {
$self->update_prefs( $pref, $tab_content );
next;
}
while ( my ($section, $sysprefs) = each %$tab_content ) {
$self->update_prefs( $pref, $sysprefs );
}
my $ntab = {};
for my $section ( keys %$tab_content ) {
my $text = $self->get_trans_text($self->{file} . " $section");
my $nsection = $text ? $text : $section;
$ntab->{$nsection} = $tab_content->{$section};
}
$pref->{$tab} = $ntab;
}
my $file_trans = $self->{path_lang} . "/$file";
print "Write $file\n";
open my $fh, ">", $file_trans;
print $fh Dump($pref);
}
}
package Main;
use strict;
use warnings;
use Pod::Usage;
sub usage {
pod2usage( -verbose => 2 );
exit;
}
usage() if $#ARGV != 1;
my ($cmd, $lang) = @ARGV;
if ( $cmd =~ /init|install|update/i ) {
my $tokenizer = Tokenizer->new( $lang );
$tokenizer->init( ) if $cmd =~ /init/i;
$tokenizer->update( ) if $cmd =~ /update/i;
$tokenizer->install( ) if $cmd =~ /install/i;
}
else {
usage();
}
=head1 NAME
pref-trans - Handle preferences translation
=head1 SYNOPSYS
pref-trans init fr-FR
pref-trans update fr-FR
pref-trans install fr-FR
=head1 USAGE
=over
=item pref-trans init F<lang>
Create a .po file in po directory, named F<lang>-pref.po. This file contains
text to translate extracted from .pref files.
=item pref-trans update F<lang>
Update a .po file in po directory, named F<lang>-pref.po. This file contains
new text to translate extracted from .pref files. Previous translated text are
kept. There is a minor bug, which can't be fixed due to preferences data
struture: preferences tab subsection labels are lost when updating .po file.
=item pref-trans install F<lang>
Use F<lang>-pref.po file to translate the english version of preferences files
and copy those files in the appropriate directory.
=back
=head1 DESCRIPTION
Koha preferences are stored in a data structure found in
koha-tmpl/intranet-tmpl/en/module/admin/preferences/ files. Depending of user
language, other files are used. This script extract text from 'en' preference
files, and put them in one .po file. This .po file can be updated. When
completed, a .po file can be applied to create localized versions of
preferences templates.
=head1 COPYRIGHT AND LICENSE
Copyright 2010 by Tamil, s.a.r.l.
L<http://www.tamil.fr>
This script is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License, version 2.1.
=cut

View File

@ -1,9 +0,0 @@
#!/bin/sh
#./tmpl_process3.pl install -r -s po/prog_opac_fr_FR.po -i ../../koha-tmpl/opac-tmpl/prog/en -o ../../koha-tmpl/opac-tmpl/prog/fr
./tmpl_process3.pl install -r -s po/prog_intranet_fr_FR.po -i ../../koha-tmpl/intranet-tmpl/prog/en -o ../../koha-tmpl/intranet-tmpl/prog/fr
#./tmpl_process3.pl install -r -s po/prog_opac_zh_TW.po -i ../../koha-tmpl/opac-tmpl/prog/en -o ../../koha-tmpl/opac-tmpl/prog/zh-TW
#./tmpl_process3.pl install -r -s po/default_intranet_zh_TW.po -i ../../koha-tmpl/intranet-tmpl/default/en -o ../../koha-tmpl/intranet-tmpl/default/zh-TW
# ./tmpl_process3.pl install -r -s po/css_opac_es_AR.po -i ../../koha-tmpl/opac-tmpl/css/zh_TW -o ../../koha-tmpl/opac-tmpl/css/es_AR
#./tmpl_process3.pl install -r -s po/default_intranet_es_AR.po -i ../../koha-tmpl/intranet-tmpl/default/en -o ../../koha-tmpl/intranet-tmpl/default/es

126
misc/translator/translate Executable file
View File

@ -0,0 +1,126 @@
#!/usr/bin/perl
package Main;
use strict;
use warnings;
use LangInstaller;
use Getopt::Long;
use Pod::Usage;
my $verbose = 0;
my $pref = 0;
my $all = 0;
GetOptions(
'v|verbose' => \$verbose,
'p' => \$pref,
'a|all' => \$all,
);
sub usage {
pod2usage( -verbose => 2 );
exit;
}
usage() if $#ARGV != 1 && $#ARGV != 0;
my ($cmd, $lang) = @ARGV;
$cmd = lc $cmd;
if ( $cmd =~ /create|install|update/ ) {
my $installer = LangInstaller->new( $lang, $pref );
if ( $all ) {
usage() if $cmd eq 'create';
for my $lang ( @{$installer->{langs}} ) {
$installer->set_lang( $lang );
$installer->$cmd();
}
}
else {
$installer->$cmd();
}
}
else {
usage();
}
=head1 NAME
translate - Handle templates and preferences translation
=head1 SYNOPSYS
translate create fr-FR
translate update fr-FR
translate install fr-FR
translate -p install fr-FR
translate install
=head1 DESCRIPTION
In Koha, three categories of information are translated based on standard GNU
.po files: opac templates pages, intranet templates and system preferences. The
script is a wrapper. It allows to quickly create/update/install .po files for a
given language or for all available languages.
=head1 USAGE
=over
=item translate create F<lang>
Create 3 .po files in F</misc/translator/po> subdirectory: (1) from opac pages
templates, (2) intranet templates, and (3) from preferences. English 'en'
version of templates and preferences are used as references.
=over
=item F<lang>-opac.po
Contains extracted text from english (en) OPAC templates found in
<KOHA_ROOT>/koha-tmpl/opac-tmpl/prog/en/ directory.
=item F<lang>-intranet.po
Contains extracted text from english (en) intranet templates found in
<KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/ directory.
=item F<lang>-pref.po
Contains extracted text from english (en) preferences. They are found in files
located in <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/admin/preferences
directory.
=back
=item translate [-p] update F<lang>
Update .po files in F<po> directory, named F<lang>-*.po. Without F<lang>, all
available languages are updated. With -p option, only preferences .po file is
updated.
=item translate [-p] install F<lang>
Use .po files to translate the english version of templayes and preferences files
and copy those files in the appropriate directory. Without F<lang>, all
available languages are installed. With -p option, only preferences .po file is
updated.
=back
=head1 COPYRIGHT AND LICENSE
Copyright 2010 by Tamil, s.a.r.l.
L<http://www.tamil.fr>
This script is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License, version 2.1.
=cut