3 # Copyright (C) 2010 Tamil s.a.r.l.
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 # WARNING: Any other tested YAML library fails to work properly in this
25 use YAML::Syck qw( Dump LoadFile );
27 use FindBin qw( $Bin );
29 $YAML::Syck::ImplicitTyping = 1;
32 # Default file header for .po syspref files
33 my $default_pref_po_header = Locale::PO->new(-msgid => '', -msgstr =>
34 "Project-Id-Version: PACKAGE VERSION\\n" .
35 "PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\\n" .
36 "Last-Translator: FULL NAME <EMAIL\@ADDRESS>\\n" .
37 "Language-Team: Koha Translate List <koha-translate\@lists.koha-community.org>\\n" .
38 "MIME-Version: 1.0\\n" .
39 "Content-Type: text/plain; charset=UTF-8\\n" .
40 "Content-Transfer-Encoding: 8bit\\n" .
41 "Plural-Forms: nplurals=2; plural=(n > 1);\\n"
46 my ($self, $lang) = @_;
48 $self->{lang} = $lang;
49 $self->{po_path_lang} = $self->{context}->config('intrahtdocs') .
50 "/prog/$lang/modules/admin/preferences";
55 my ($class, $lang, $pref_only, $verbose) = @_;
59 my $context = C4::Context->new();
60 $self->{context} = $context;
61 $self->{path_pref_en} = $context->config('intrahtdocs') .
62 '/prog/en/modules/admin/preferences';
63 set_lang( $self, $lang ) if $lang;
64 $self->{pref_only} = $pref_only;
65 $self->{verbose} = $verbose;
66 $self->{process} = "$Bin/tmpl_process3.pl " . ($verbose ? '' : '-q');
67 $self->{path_po} = "$Bin/po";
68 $self->{po} = { '' => $default_pref_po_header };
70 # Get all .pref file names
71 opendir my $fh, $self->{path_pref_en};
72 my @pref_files = grep { /.pref/ } readdir($fh);
74 $self->{pref_files} = \@pref_files;
76 # Get all available language codes
77 opendir $fh, $self->{path_po};
78 my @langs = map { ($_) =~ /(.*)-i-opac/ }
79 grep { $_ =~ /.*-opac-t-prog/ } readdir($fh);
81 $self->{langs} = \@langs;
83 # Map for both interfaces opac/intranet
84 my $opachtdocs = $context->config('opachtdocs');
85 $self->{interface} = [
88 dir => "$opachtdocs/prog",
89 suffix => '-i-opac-t-prog-v-3006000.po',
92 name => 'Intranet prog UI',
93 dir => $context->config('intrahtdocs') . '/prog',
94 suffix => '-i-staff-t-prog-v-3006000.po',
97 name => 'Intranet prog help',
98 dir => $context->config('intrahtdocs') . '/prog/en/modules/help',
99 suffix => '-staff-help.po',
103 # Alternate opac themes
104 opendir $fh, $context->config('opachtdocs');
105 for ( grep { not /^\.|\.\.|prog|lib$/ } readdir($fh) ) {
106 push @{$self->{interface}}, {
108 dir => "$opachtdocs/$_",
109 suffix => "-opac-$_.po",
120 my $context = C4::Context->new;
121 my $trans_path = $Bin . '/po';
122 my $trans_file = "$trans_path/" . $self->{lang} . "-pref.po";
128 my ($self, $id, $comment) = @_;
129 my $po = $self->{po};
132 $p->comment( $p->comment . "\n" . $comment );
135 $po->{$id} = Locale::PO->new(
136 -comment => $comment,
145 my ($self, $comment, $prefs) = @_;
147 for my $pref ( @$prefs ) {
149 for my $element ( @$pref ) {
150 if ( ref( $element) eq 'HASH' ) {
151 $pref_name = $element->{pref};
155 for my $element ( @$pref ) {
156 if ( ref( $element) eq 'HASH' ) {
157 while ( my ($key, $value) = each(%$element) ) {
158 next unless $key eq 'choices';
159 next unless ref($value) eq 'HASH';
160 for my $ckey ( keys %$value ) {
161 my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
162 $self->po_append( $id, $comment );
166 elsif ( $element && $pref_name ) {
167 $self->po_append( $self->{file} . "#$pref_name# $element", $comment );
175 my ($self, $id) = @_;
177 my $po = $self->{po}->{$id};
179 return Locale::PO->dequote($po->msgstr);
183 sub update_tab_prefs {
184 my ($self, $pref, $prefs) = @_;
186 for my $p ( @$prefs ) {
189 for my $element ( @$p ) {
190 if ( ref( $element) eq 'HASH' ) {
191 $pref_name = $element->{pref};
195 for my $i ( 0..@$p-1 ) {
196 my $element = $p->[$i];
197 if ( ref( $element) eq 'HASH' ) {
198 while ( my ($key, $value) = each(%$element) ) {
199 next unless $key eq 'choices';
200 next unless ref($value) eq 'HASH';
201 for my $ckey ( keys %$value ) {
202 my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
203 my $text = $self->get_trans_text( $id );
204 $value->{$ckey} = $text if $text;
208 elsif ( $element && $pref_name ) {
209 my $id = $self->{file} . "#$pref_name# $element";
210 my $text = $self->get_trans_text( $id );
211 $p->[$i] = $text if $text;
218 sub get_po_from_prefs {
221 for my $file ( @{$self->{pref_files}} ) {
222 my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
223 $self->{file} = $file;
224 # Entries for tab titles
225 $self->po_append( $self->{file}, $_ ) for keys %$pref;
226 while ( my ($tab, $tab_content) = each %$pref ) {
227 if ( ref($tab_content) eq 'ARRAY' ) {
228 $self->add_prefs( $tab, $tab_content );
231 while ( my ($section, $sysprefs) = each %$tab_content ) {
232 my $comment = "$tab > $section";
233 $self->po_append( $self->{file} . " " . $section, $comment );
234 $self->add_prefs( $comment, $sysprefs );
244 # Create file header if it doesn't already exist
245 my $po = $self->{po};
246 $po->{''} ||= $default_pref_po_header;
248 # Write .po entries into a file put in Koha standard po directory
249 Locale::PO->save_file_fromhash( $self->po_filename, $po );
250 say "Saved in file: ", $self->po_filename if $self->{verbose};
254 sub get_po_merged_with_en {
257 # Get po from current 'en' .pref files
258 $self->get_po_from_prefs();
259 my $po_current = $self->{po};
261 # Get po from previous generation
262 my $po_previous = Locale::PO->load_file_ashash( $self->po_filename );
264 for my $id ( keys %$po_current ) {
265 my $po = $po_previous->{Locale::PO->quote($id)};
267 my $text = Locale::PO->dequote( $po->msgstr );
268 $po_current->{$id}->msgstr( $text );
275 print "Update '", $self->{lang},
276 "' preferences .po file from 'en' .pref files\n" if $self->{verbose};
277 $self->get_po_merged_with_en();
285 unless ( -r $self->{po_path_lang} ) {
286 print "Koha directories hierarchy for ", $self->{lang}, " must be created first\n";
290 # Get the language .po file merged with last modified 'en' preferences
291 $self->get_po_merged_with_en();
293 for my $file ( @{$self->{pref_files}} ) {
294 my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
295 $self->{file} = $file;
296 # First, keys are replaced (tab titles)
299 $self->get_trans_text( $self->{file} ) || $_ => $pref->{$_}
303 while ( my ($tab, $tab_content) = each %$pref ) {
304 if ( ref($tab_content) eq 'ARRAY' ) {
305 $self->update_tab_prefs( $pref, $tab_content );
308 while ( my ($section, $sysprefs) = each %$tab_content ) {
309 $self->update_tab_prefs( $pref, $sysprefs );
312 for my $section ( keys %$tab_content ) {
313 my $id = $self->{file} . " $section";
314 my $text = $self->get_trans_text($id);
315 my $nsection = $text ? $text : $section;
316 $ntab->{$nsection} = $tab_content->{$section};
318 $pref->{$tab} = $ntab;
320 my $file_trans = $self->{po_path_lang} . "/$file";
321 print "Write $file\n" if $self->{verbose};
322 open my $fh, ">", $file_trans;
323 print $fh Dump($pref);
329 my ($self, $files) = @_;
330 say "Install templates" if $self->{verbose};
331 for my $trans ( @{$self->{interface}} ) {
333 " Install templates '$trans->{name}'\n",
334 " From: $trans->{dir}/en/\n",
335 " To : $trans->{dir}/$self->{lang}\n",
336 " With: $self->{path_po}/$self->{lang}$trans->{suffix}\n"
339 my $trans_dir = ( $trans->{name} =~ /help/ )?"$trans->{dir}":"$trans->{dir}/en/";
340 my $lang_dir = ( $trans->{name} =~ /help/ )?"$trans->{dir}":"$trans->{dir}/$self->{lang}";
341 $lang_dir =~ s|/en/|/$self->{lang}/|;
342 mkdir $lang_dir unless -d $lang_dir;
343 my $excludes = ( $trans->{name} =~ /UI/ )?"-x 'help'":"";
346 "$self->{process} install " .
349 "-s $self->{path_po}/$self->{lang}$trans->{suffix} -r $excludes" .
352 ? ' -f ' . join ' -f ', @$files
360 my ($self, $files) = @_;
362 say "Update templates" if $self->{verbose};
363 for my $trans ( @{$self->{interface}} ) {
365 " Update templates '$trans->{name}'\n",
366 " From: $trans->{dir}/en/\n",
367 " To : $self->{path_po}/$self->{lang}$trans->{suffix}\n"
369 my $lang_dir = "$trans->{dir}/$self->{lang}";
370 mkdir $lang_dir unless -d $lang_dir;
372 my $trans_dir = ( $trans->{name} =~ /help/ )?"$trans->{dir}":"$trans->{dir}/en/";
373 my $excludes = ( $trans->{name} =~ /UI/ )?"-x 'help'":"";
376 "$self->{process} update " .
378 "-s $self->{path_po}/$self->{lang}$trans->{suffix} -r $excludes" .
381 ? ' -f ' . join ' -f ', @$files
391 if ( -e $self->po_filename ) {
392 say "Preferences .po file already exists. Delete it if you want to recreate it.";
395 $self->get_po_from_prefs();
401 my ($self, $files) = @_;
403 say "Create templates\n" if $self->{verbose};
404 for my $trans ( @{$self->{interface}} ) {
406 " Create templates .po files for '$trans->{name}'\n",
407 " From: $trans->{dir}/en/\n",
408 " To : $self->{path_po}/$self->{lang}$trans->{suffix}\n"
411 my $trans_dir = ( $trans->{name} =~ /help/ )?"$trans->{dir}":"$trans->{dir}/en/";
412 my $excludes = ( $trans->{name} =~ /UI/ )?"-x 'help'":"";
415 "$self->{process} create " .
417 "-s $self->{path_po}/$self->{lang}$trans->{suffix} -r $excludes" .
420 ? ' -f ' . join ' -f ', @$files
428 my ($self, $files) = @_;
429 return unless $self->{lang};
430 $self->install_tmpl($files) unless $self->{pref_only};
431 $self->install_prefs();
437 opendir( my $dh, $self->{path_po} );
438 my @files = grep { $_ =~ /-i-opac-t-prog-v-3006000.po$/ }
440 @files = map { $_ =~ s/-i-opac-t-prog-v-3006000.po$//; $_ } @files;
445 my ($self, $files) = @_;
446 my @langs = $self->{lang} ? ($self->{lang}) : $self->get_all_langs();
447 for my $lang ( @langs ) {
448 $self->set_lang( $lang );
449 $self->update_tmpl($files) unless $self->{pref_only};
450 $self->update_prefs();
456 my ($self, $files) = @_;
457 return unless $self->{lang};
458 $self->create_tmpl($files) unless $self->{pref_only};
459 $self->create_prefs();
469 LangInstaller.pm - Handle templates and preferences translation
473 my $installer = LangInstaller->new( 'fr-FR' );
474 $installer->create();
475 $installer->update();
476 $installer->install();
477 for my $lang ( @{$installer->{langs} ) {
478 $installer->set_lang( $lan );
479 $installer->install();
486 Create a new instance of the installer object.
490 For the current language, create .po files for templates and preferences based
491 of the english ('en') version.
495 For the current language, update .po files.
499 For the current langage C<$self->{lang}, use .po files to translate the english
500 version of templates and preferences files and copy those files in the
501 appropriate directory.
505 =item translate create F<lang>
507 Create 3 .po files in F<po> subdirectory: (1) from opac pages templates, (2)
508 intranet templates, and (3) from preferences.
512 =item F<lang>-opac.po
514 Contains extracted text from english (en) OPAC templates found in
515 <KOHA_ROOT>/koha-tmpl/opac-tmpl/prog/en/ directory.
517 =item F<lang>-intranet.po
519 Contains extracted text from english (en) intranet templates found in
520 <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/ directory.
522 =item F<lang>-pref.po
524 Contains extracted text from english (en) preferences. They are found in files
525 located in <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/admin/preferences
530 =item pref-trans update F<lang>
532 Update .po files in F<po> directory, named F<lang>-*.po.
534 =item pref-trans install F<lang>