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',
93 dir => $context->config('intrahtdocs') . '/prog',
94 suffix => '-i-staff-t-prog-v-3006000.po',
98 # Alternate opac themes
99 opendir $fh, $context->config('opachtdocs');
100 for ( grep { not /^\.|\.\.|prog|lib$/ } readdir($fh) ) {
101 push @{$self->{interface}}, {
103 dir => "$opachtdocs/$_",
104 suffix => "-opac-$_.po",
115 my $context = C4::Context->new;
116 my $trans_path = $Bin . '/po';
117 my $trans_file = "$trans_path/" . $self->{lang} . "-pref.po";
123 my ($self, $id, $comment) = @_;
124 my $po = $self->{po};
127 $p->comment( $p->comment . "\n" . $comment );
130 $po->{$id} = Locale::PO->new(
131 -comment => $comment,
140 my ($self, $comment, $prefs) = @_;
142 for my $pref ( @$prefs ) {
144 for my $element ( @$pref ) {
145 if ( ref( $element) eq 'HASH' ) {
146 $pref_name = $element->{pref};
150 for my $element ( @$pref ) {
151 if ( ref( $element) eq 'HASH' ) {
152 while ( my ($key, $value) = each(%$element) ) {
153 next unless $key eq 'choices';
154 next unless ref($value) eq 'HASH';
155 for my $ckey ( keys %$value ) {
156 my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
157 $self->po_append( $id, $comment );
161 elsif ( $element && $pref_name ) {
162 $self->po_append( $self->{file} . "#$pref_name# $element", $comment );
170 my ($self, $id) = @_;
172 my $po = $self->{po}->{$id};
174 return Locale::PO->dequote($po->msgstr);
178 sub update_tab_prefs {
179 my ($self, $pref, $prefs) = @_;
181 for my $p ( @$prefs ) {
184 for my $element ( @$p ) {
185 if ( ref( $element) eq 'HASH' ) {
186 $pref_name = $element->{pref};
190 for my $i ( 0..@$p-1 ) {
191 my $element = $p->[$i];
192 if ( ref( $element) eq 'HASH' ) {
193 while ( my ($key, $value) = each(%$element) ) {
194 next unless $key eq 'choices';
195 next unless ref($value) eq 'HASH';
196 for my $ckey ( keys %$value ) {
197 my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
198 my $text = $self->get_trans_text( $id );
199 $value->{$ckey} = $text if $text;
203 elsif ( $element && $pref_name ) {
204 my $id = $self->{file} . "#$pref_name# $element";
205 my $text = $self->get_trans_text( $id );
206 $p->[$i] = $text if $text;
213 sub get_po_from_prefs {
216 for my $file ( @{$self->{pref_files}} ) {
217 my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
218 $self->{file} = $file;
219 # Entries for tab titles
220 $self->po_append( $self->{file}, $_ ) for keys %$pref;
221 while ( my ($tab, $tab_content) = each %$pref ) {
222 if ( ref($tab_content) eq 'ARRAY' ) {
223 $self->add_prefs( $tab, $tab_content );
226 while ( my ($section, $sysprefs) = each %$tab_content ) {
227 my $comment = "$tab > $section";
228 $self->po_append( $self->{file} . " " . $section, $comment );
229 $self->add_prefs( $comment, $sysprefs );
239 # Create file header if it doesn't already exist
240 my $po = $self->{po};
241 $po->{''} ||= $default_pref_po_header;
243 # Write .po entries into a file put in Koha standard po directory
244 Locale::PO->save_file_fromhash( $self->po_filename, $po );
245 say "Saved in file: ", $self->po_filename if $self->{verbose};
249 sub get_po_merged_with_en {
252 # Get po from current 'en' .pref files
253 $self->get_po_from_prefs();
254 my $po_current = $self->{po};
256 # Get po from previous generation
257 my $po_previous = Locale::PO->load_file_ashash( $self->po_filename );
259 for my $id ( keys %$po_current ) {
260 my $po = $po_previous->{Locale::PO->quote($id)};
262 my $text = Locale::PO->dequote( $po->msgstr );
263 $po_current->{$id}->msgstr( $text );
270 print "Update '", $self->{lang},
271 "' preferences .po file from 'en' .pref files\n" if $self->{verbose};
272 $self->get_po_merged_with_en();
280 unless ( -r $self->{po_path_lang} ) {
281 print "Koha directories hierarchy for ", $self->{lang}, " must be created first\n";
285 # Get the language .po file merged with last modified 'en' preferences
286 $self->get_po_merged_with_en();
288 for my $file ( @{$self->{pref_files}} ) {
289 my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
290 $self->{file} = $file;
291 # First, keys are replaced (tab titles)
294 $self->get_trans_text( $self->{file} ) || $_ => $pref->{$_}
298 while ( my ($tab, $tab_content) = each %$pref ) {
299 if ( ref($tab_content) eq 'ARRAY' ) {
300 $self->update_tab_prefs( $pref, $tab_content );
303 while ( my ($section, $sysprefs) = each %$tab_content ) {
304 $self->update_tab_prefs( $pref, $sysprefs );
307 for my $section ( keys %$tab_content ) {
308 my $id = $self->{file} . " $section";
309 my $text = $self->get_trans_text($id);
310 my $nsection = $text ? $text : $section;
311 $ntab->{$nsection} = $tab_content->{$section};
313 $pref->{$tab} = $ntab;
315 my $file_trans = $self->{po_path_lang} . "/$file";
316 print "Write $file\n" if $self->{verbose};
317 open my $fh, ">", $file_trans;
318 print $fh Dump($pref);
325 say "Install templates" if $self->{verbose};
326 for my $trans ( @{$self->{interface}} ) {
328 " Install templates '$trans->{name}'\n",
329 " From: $trans->{dir}/en/\n",
330 " To : $trans->{dir}/$self->{lang}\n",
331 " With: $self->{path_po}/$self->{lang}$trans->{suffix}\n"
333 my $lang_dir = "$trans->{dir}/$self->{lang}";
334 mkdir $lang_dir unless -d $lang_dir;
336 "$self->{process} install " .
337 "-i $trans->{dir}/en/ " .
338 "-o $trans->{dir}/$self->{lang} ".
339 "-s $self->{path_po}/$self->{lang}$trans->{suffix} -r"
347 say "Update templates" if $self->{verbose};
348 for my $trans ( @{$self->{interface}} ) {
350 " Update templates '$trans->{name}'\n",
351 " From: $trans->{dir}/en/\n",
352 " To : $self->{path_po}/$self->{lang}$trans->{suffix}\n"
354 my $lang_dir = "$trans->{dir}/$self->{lang}";
355 mkdir $lang_dir unless -d $lang_dir;
357 "$self->{process} update " .
358 "-i $trans->{dir}/en/ " .
359 "-s $self->{path_po}/$self->{lang}$trans->{suffix} -r"
367 if ( -e $self->po_filename ) {
368 say "Preferences .po file already exists. Delete it if you want to recreate it.";
371 $self->get_po_from_prefs();
379 say "Create templates\n" if $self->{verbose};
380 for my $trans ( @{$self->{interface}} ) {
382 " Create templates .po files for '$trans->{name}'\n",
383 " From: $trans->{dir}/en/\n",
384 " To : $self->{path_po}/$self->{lang}$trans->{suffix}\n"
387 "$self->{process} create " .
388 "-i $trans->{dir}/en/ " .
389 "-s $self->{path_po}/$self->{lang}$trans->{suffix} -r"
396 return unless $self->{lang};
397 $self->install_tmpl() unless $self->{pref_only};
398 $self->install_prefs();
404 opendir( my $dh, $self->{path_po} );
405 my @files = grep { $_ =~ /-i-opac-t-prog-v-3006000.po$/ }
407 @files = map { $_ =~ s/-i-opac-t-prog-v-3006000.po$//; $_ } @files;
413 my @langs = $self->{lang} ? ($self->{lang}) : $self->get_all_langs();
414 for my $lang ( @langs ) {
415 $self->set_lang( $lang );
416 $self->update_tmpl() unless $self->{pref_only};
417 $self->update_prefs();
424 return unless $self->{lang};
425 $self->create_tmpl() unless $self->{pref_only};
426 $self->create_prefs();
436 LangInstaller.pm - Handle templates and preferences translation
440 my $installer = LangInstaller->new( 'fr-FR' );
441 $installer->create();
442 $installer->update();
443 $installer->install();
444 for my $lang ( @{$installer->{langs} ) {
445 $installer->set_lang( $lan );
446 $installer->install();
453 Create a new instance of the installer object.
457 For the current language, create .po files for templates and preferences based
458 of the english ('en') version.
462 For the current language, update .po files.
466 For the current langage C<$self->{lang}, use .po files to translate the english
467 version of templates and preferences files and copy those files in the
468 appropriate directory.
472 =item translate create F<lang>
474 Create 3 .po files in F<po> subdirectory: (1) from opac pages templates, (2)
475 intranet templates, and (3) from preferences.
479 =item F<lang>-opac.po
481 Contains extracted text from english (en) OPAC templates found in
482 <KOHA_ROOT>/koha-tmpl/opac-tmpl/prog/en/ directory.
484 =item F<lang>-intranet.po
486 Contains extracted text from english (en) intranet templates found in
487 <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/ directory.
489 =item F<lang>-pref.po
491 Contains extracted text from english (en) preferences. They are found in files
492 located in <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/admin/preferences
497 =item pref-trans update F<lang>
499 Update .po files in F<po> directory, named F<lang>-*.po.
501 =item pref-trans install F<lang>