New wrapper script for handling translation process
[koha.git] / misc / translator / LangInstaller.pm
1 package LangInstaller;
2
3 use strict;
4 use warnings;
5
6 use C4::Context;
7 use YAML::Syck qw( Dump LoadFile );
8 use Locale::PO;
9
10
11 sub set_lang {
12     my ($self, $lang) = @_;
13
14     $self->{lang} = $lang;
15     $self->{po_path_lang} = $self->{context}->config('intrahtdocs') .
16                             "/prog/$lang/modules/admin/preferences";
17 }
18
19
20 sub new {
21     my ($class, $lang, $pref_only) = @_;
22
23     my $self                 = { };
24
25     my $context              = C4::Context->new();
26     $self->{context}         = $context;
27     $self->{path_pref_en}    = $context->config('intrahtdocs') .
28                                '/prog/en/modules/admin/preferences';
29     set_lang( $self, $lang ) if $lang;
30     $self->{pref_only}       = $pref_only;
31     $self->{translator_path} = $context->config('intranetdir') . "/misc/translator";
32     $self->{path_po}         = $self->{translator_path} . "/po";
33     $self->{po}              = {};
34
35     # Get all .pref file names
36     opendir my $fh, $self->{path_pref_en};
37     my @pref_files = grep { /.pref/ } readdir($fh);
38     close $fh;
39     $self->{pref_files} = \@pref_files;
40
41     # Get all available language codes
42     opendir $fh, $self->{path_po};
43     my @langs =  map { ($_) =~ /(.*)-i-opac/ } 
44         grep { $_ =~ /.*-opac-/ } readdir($fh);
45     closedir $fh;
46     $self->{langs} = \@langs;
47
48     # Map for both interfaces opac/intranet
49     $self->{interface} = {
50         opac => {
51             dir    => $context->config('opachtdocs') . '/prog',
52             suffix => '-i-opac-t-prog-v-3002000.po',
53         },
54         intranet => {
55             dir    => $context->config('intrahtdocs') . '/prog',
56             suffix => '-i-staff-t-prog-v-3002000.po',
57         }
58     };
59
60     bless $self, $class;
61 }
62
63
64 sub po_filename {
65     my $self = shift;
66
67     my $context    = C4::Context->new;
68     my $trans_path = $context->config('intranetdir') . '/misc/translator/po';
69     my $trans_file = "$trans_path/" . $self->{lang} . "-pref.po";
70     return $trans_file;
71 }
72
73
74 sub po_append {
75     my ($self, $id, $comment) = @_;
76     my $po = $self->{po};
77     my $p = $po->{$id};
78     if ( $p ) {
79         $p->comment( $p->comment . "\n" . $comment );
80     }
81     else {
82         $po->{$id} = Locale::PO->new(
83             -comment => $comment,
84             -msgid   => $id,
85             -msgstr  => ''
86         );
87     }
88 }
89
90
91 sub add_prefs {
92     my ($self, $comment, $prefs) = @_;
93
94     for my $pref ( @$prefs ) {
95         my $pref_name = '';
96         for my $element ( @$pref ) {
97             if ( ref( $element) eq 'HASH' ) {
98                 $pref_name = $element->{pref};
99                 last;
100             }
101         }
102         for my $element ( @$pref ) {
103             if ( ref( $element) eq 'HASH' ) {
104                 while ( my ($key, $value) = each(%$element) ) {
105                     next unless $key eq 'choices';
106                     next unless ref($value) eq 'HASH';
107                     for my $ckey ( keys %$value ) {
108                         my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
109                         $self->po_append( $id, $comment );
110                     }
111                 }
112             }
113             elsif ( $element ) {
114                 $self->po_append( $self->{file} . "#$pref_name# $element", $comment );
115             }
116         }
117     }
118 }
119
120
121 sub get_trans_text {
122     my ($self, $id) = @_;
123
124     my $po = $self->{po}->{$id};
125     return unless $po;
126     return Locale::PO->dequote($po->msgstr);
127 }
128
129
130 sub update_tab_prefs {
131     my ($self, $pref, $prefs) = @_;
132
133     for my $p ( @$prefs ) {
134         my $pref_name = '';
135         next unless $p;
136         for my $element ( @$p ) {
137             if ( ref( $element) eq 'HASH' ) {
138                 $pref_name = $element->{pref};
139                 last;
140             }
141         }
142         for my $i ( 0..@$p-1 ) {
143             my $element = $p->[$i];
144             if ( ref( $element) eq 'HASH' ) {
145                 while ( my ($key, $value) = each(%$element) ) {
146                     next unless $key eq 'choices';
147                     next unless ref($value) eq 'HASH';
148                     for my $ckey ( keys %$value ) {
149                         my $id = $self->{file} . "#$pref_name# " . $value->{$ckey};
150                         my $text = $self->get_trans_text( $id );
151                         $value->{$ckey} = $text if $text;
152                     }
153                 }
154             }
155             elsif ( $element ) {
156                 my $text = $self->get_trans_text( $self->{file} . "#$pref_name# $element" );
157                 $p->[$i] = $text if $text;
158             }
159         }
160     }
161 }
162
163
164 sub get_po_from_prefs {
165     my $self = shift;
166
167     for my $file ( @{$self->{pref_files}} ) {
168         my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
169         $self->{file} = $file;
170         #print Dump($pref), "\n";
171         while ( my ($tab, $tab_content) = each %$pref ) {
172             if ( ref($tab_content) eq 'ARRAY' ) {
173                 $self->add_prefs( $tab, $tab_content );
174                 next;
175             }
176             while ( my ($section, $sysprefs) = each %$tab_content ) {
177                 my $comment = "$tab > $section";
178                 $self->po_append( $self->{file} . " " . $section, $comment );
179                 $self->add_prefs( $comment, $sysprefs );
180             }
181         }
182     }
183 }
184
185
186 sub save_po {
187     my $self = shift;
188     # Write .po entries into a file put in Koha standard po directory
189     Locale::PO->save_file_fromhash( $self->po_filename, $self->{po} );
190     print "Saved in file: ", $self->po_filename, "\n";
191 }
192
193
194 sub update_prefs {
195     my $self = shift;
196
197     print "Update '", $self->{lang}, "' preferences .po file from 'en' .pref files\n";
198     # Get po from current 'en' .pref files
199     $self->get_po_from_prefs();
200     my $po_current = $self->{po};
201
202     # Get po from previous generation
203     my $po_previous = Locale::PO->load_file_ashash( $self->po_filename );
204
205     for my $id ( keys %$po_current ) {
206         my $po =  $po_previous->{'"'.$id.'"'};
207         next unless $po;
208         my $text = Locale::PO->dequote( $po->msgstr );
209         $po_current->{$id}->msgstr( $text );
210     }
211
212     $self->save_po();
213 }
214
215
216 sub install_prefs {
217     my $self = shift;
218
219     unless ( -r $self->{po_path_lang} ) {
220         print "Koha directories hierarchy for ", $self->{lang}, " must be created first\n";
221         exit;
222     }
223
224     # Update the language .po file with last modified 'en' preferences
225     # and load it.
226     $self->update_prefs();
227
228     for my $file ( @{$self->{pref_files}} ) {
229         my $pref = LoadFile( $self->{path_pref_en} . "/$file" );
230         $self->{file} = $file;
231         while ( my ($tab, $tab_content) = each %$pref ) {
232             if ( ref($tab_content) eq 'ARRAY' ) {
233                 $self->update_tab_prefs( $pref, $tab_content );
234                 next;
235             }
236             while ( my ($section, $sysprefs) = each %$tab_content ) {
237                 $self->update_tab_prefs( $pref, $sysprefs );
238             }
239             my $ntab = {};
240             for my $section ( keys %$tab_content ) {
241                 my $text = $self->get_trans_text($self->{file} . " $section");
242                 my $nsection = $text ? $text : $section;
243                 $ntab->{$nsection} = $tab_content->{$section};
244             }
245             $pref->{$tab} = $ntab;
246         }
247         my $file_trans = $self->{po_path_lang} . "/$file";
248         print "Write $file\n";
249         open my $fh, ">", $file_trans;
250         print $fh Dump($pref);
251     }
252 }
253
254
255 sub install_tmpl {
256     my $self = shift;
257
258     print
259         "Install templates\n";
260     while ( my ($interface, $tmpl) = each %{$self->{interface}} ) {
261         print
262             "  Install templates '$interface\n",
263             "    From: $tmpl->{dir}/en/\n",
264             "    To  : $tmpl->{dir}/$self->{lang}\n",
265             "    With: $self->{path_po}/$self->{lang}$tmpl->{suffix}\n";
266         my $lang_dir = "$tmpl->{dir}/$self->{lang}";
267         mkdir $lang_dir unless -d $lang_dir;
268         system
269             "$self->{translator_path}/tmpl_process3.pl install " .
270             "-i $tmpl->{dir}/en/ " .
271             "-o $tmpl->{dir}/$self->{lang} ".
272             "-s $self->{path_po}/$self->{lang}$tmpl->{suffix} -r"
273     }
274 }
275
276
277 sub update_tmpl {
278     my $self = shift;
279
280     print
281         "Update templates\n";
282     while ( my ($interface, $tmpl) = each %{$self->{interface}} ) {
283         print
284             "  Update templates '$interface'\n",
285             "    From: $tmpl->{dir}/en/\n",
286             "    To  : $self->{path_po}/$self->{lang}$tmpl->{suffix}\n";
287         my $lang_dir = "$tmpl->{dir}/$self->{lang}";
288         mkdir $lang_dir unless -d $lang_dir;
289         system
290             "$self->{translator_path}/tmpl_process3.pl update " .
291             "-i $tmpl->{dir}/en/ " .
292             "-s $self->{path_po}/$self->{lang}$tmpl->{suffix} -r"
293     }
294 }
295
296
297 sub create_prefs {
298     my $self = shift;
299
300     $self->get_po_from_prefs();
301     $self->save_po();
302 }
303
304
305 sub create_tmpl {
306     my $self = shift;
307
308     print
309         "Create templates\n";
310     while ( my ($interface, $tmpl) = each %{$self->{interface}} ) {
311         print
312             "  Create templates .po files for '$interface'\n",
313             "    From: $tmpl->{dir}/en/\n",
314             "    To  : $self->{path_po}/$self->{lang}$tmpl->{suffix}\n";
315         system
316             "$self->{translator_path}/tmpl_process3.pl create " .
317             "-i $tmpl->{dir}/en/ " .
318             "-s $self->{path_po}/$self->{lang}$tmpl->{suffix} -r"
319     }
320 }
321
322
323 sub install {
324     my $self = shift;
325     return unless $self->{lang};
326     $self->install_tmpl() unless $self->{pref_only};
327     $self->install_prefs();
328 }
329
330
331 sub update {
332     my $self = shift;
333     return unless $self->{lang};
334     $self->update_tmpl() unless $self->{pref_only};
335     $self->update_prefs();
336 }
337
338
339 sub create {
340     my $self = shift;
341     return unless $self->{lang};
342     $self->create_tmpl() unless $self->{pref_only};
343     $self->create_prefs();
344 }
345
346
347
348 1;
349
350
351 =head1 NAME
352
353 LangInstaller.pm - Handle templates and preferences translation
354
355 =head1 SYNOPSYS
356
357   my $installer = LangInstaller->new( 'fr-FR' );
358   $installer->create();
359   $installer->update();
360   $installer->install();
361   for my $lang ( @{$installer->{langs} ) {
362     $installer->set_lang( $lan );
363     $installer->install();
364   }
365
366 =head1 METHODS
367
368 =head2 new
369
370 Create a new instance of the installer object. 
371
372 =head2 create
373
374 For the current language, create .po files for templates and preferences based
375 of the english ('en') version.
376
377 =head2 update
378
379 For the current language, update .po files.
380
381 =head2 install
382
383 For the current langage C<$self->{lang}, use .po files to translate the english
384 version of templates and preferences files and copy those files in the
385 appropriate directory.
386
387 =over
388
389 =item translate create F<lang>
390
391 Create 3 .po files in F<po> subdirectory: (1) from opac pages templates, (2)
392 intranet templates, and (3) from preferences.
393
394 =over
395
396 =item F<lang>-opac.po
397
398 Contains extracted text from english (en) OPAC templates found in
399 <KOHA_ROOT>/koha-tmpl/opac-tmpl/prog/en/ directory.
400
401 =item F<lang>-intranet.po
402
403 Contains extracted text from english (en) intranet templates found in
404 <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/ directory.
405
406 =item F<lang>-pref.po
407
408 Contains extracted text from english (en) preferences. They are found in files
409 located in <KOHA_ROOT>/koha-tmpl/intranet-tmpl/prog/en/admin/preferences
410 directory.
411
412 =back
413
414 =item pref-trans update F<lang>
415
416 Update .po files in F<po> directory, named F<lang>-*.po.
417
418 =item pref-trans install F<lang>
419
420 =back
421
422 =head1 COPYRIGHT AND LICENSE
423
424 Copyright 2010 by Tamil, s.a.r.l.
425
426 L<http://www.tamil.fr>
427
428 This script is free software; you can redistribute it and/or modify it under
429 the terms of the GNU Lesser General Public License, version 2.1.
430
431 =cut
432