source: main/trunk/greenstone2/bin/script/gti.pl@ 26544

Last change on this file since 26544 was 26544, checked in by ak19, 11 years ago

Changes to get-all-chunks to work for GS3 translation files again.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 84.2 KB
Line 
1#!/usr/bin/perl -w
2
3###########################################################################
4#
5# gti.pl
6#
7# A component of the Greenstone digital library software
8# from the New Zealand Digital Library Project at the
9# University of Waikato, New Zealand.
10#
11# Copyright (C) 2005 New Zealand Digital Library Project
12#
13# This program is free software; you can redistribute it and/or modify
14# it under the terms of the GNU General Public License as published by
15# the Free Software Foundation; either version 2 of the License, or
16# (at your option) any later version.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program; if not, write to the Free Software
25# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26#
27###########################################################################
28
29
30BEGIN {
31 die "GSDLHOME not set\n" unless defined $ENV{'GSDLHOME'};
32 unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
33}
34
35
36use iso639;
37use strict;
38use util;
39
40my $gsdl_root_directory = "$ENV{'GSDLHOME'}";
41my $gti_log_file = &util::filename_cat($gsdl_root_directory, "etc", "gti.log");
42my $source_language_code = "en"; # This is non-negiotable
43
44my $gti_translation_files =
45[ # Greenstone macrofiles
46{ 'key' => "coredm",
47 'file_type' => "macrofile",
48 'source_file' => "macros/english.dm",
49 'target_file' => "macros/{bn:bengali;fa:farsi;gd:gaelic;id:indo;lv:latvian;pt-br:port-br;pt-pt:port-pt;zh-tr:chinese-trad;iso_639_1_target_language_name}.dm" },
50
51{ 'key' => "auxdm",
52 'file_type' => "macrofile",
53 'source_file' => "macros/english2.dm",
54 'target_file' => "macros/{bn:bengali;fa:farsi;gd:gaelic;id:indo;lv:latvian;pt-br:port-br;pt-pt:port-pt;zh-tr:chinese-trad;iso_639_1_target_language_name}2.dm" },
55
56{ 'key' => "paperspastdm",
57 'file_type' => "macrofile",
58 'source_file' => "macros/paperspast-english.dm",
59 'target_file' => "macros/paperspast-{bn:bengali;fa:farsi;gd:gaelic;id:indo;lv:latvian;pt-br:port-br;pt-pt:port-pt;zh-tr:chinese-trad;iso_639_1_target_language_name}.dm" },
60
61# GLI dictionary
62{ 'key' => "glidict",
63 'file_type' => "resource_bundle",
64 'source_file' => "gli/classes/dictionary.properties",
65 'target_file' => "gli/classes/dictionary_{target_language_code}.properties" },
66
67# GLI help
68{ 'key' => "glihelp",
69 'file_type' => "greenstone_xml",
70 'source_file' => "gli/help/en/help.xml",
71 'target_file' => "gli/help/{target_language_code}/help.xml" },
72
73# Greenstone Perl modules
74{ 'key' => "perlmodules",
75 'file_type' => "resource_bundle",
76 'source_file' => "perllib/strings.properties",
77 'target_file' => "perllib/strings_{target_language_code}.properties" },
78
79# Greenstone tutorial exercises
80# { 'key' => "tutorials",
81# 'file_type' => "greenstone_xml",
82# 'source_file' => "gsdl-documentation/tutorials/xml-source/tutorial_en.xml",
83# 'target_file' => "gsdl-documentation/tutorials/xml-source/tutorial_{target_language_code}.xml" },
84
85# new Greenstone.org
86{ 'key' => "greenorg",
87 'file_type' => "resource_bundle",
88 'source_file' => "greenstoneorg/website/classes/Gsc.properties",
89 'target_file' => "greenstoneorg/website/classes/Gsc_{target_language_code}.properties"
90}
91
92# { 'key' => "gs3interface",
93#'file_type' => "resource_bundle",
94#'source_file' => "greenstone3",
95#'target_file' => "greenstone3"
96#}
97];
98
99my @gs3_interface_files = ("AbstractBrowse", "AbstractGS2FieldSearch", "AbstractSearch", "Authentication", "CrossCollectionSearch", "GATEServices", "GS2Construct", "GS2LuceneSearch", "interface_basic", "interface_basic2", "interface_default", "interface_default2", "interface_nzdl", "IViaSearch", "LuceneSearch", "MapRetrieve", "MapSearch", "metadata_names", "PhindPhraseBrowse", "QBRWebServicesHelp", "SharedSoleneGS2FieldSearch", "Visualizer");
100# i18n (,log4j)?
101
102
103sub main
104{
105 # Get the command to process, and any arguments
106 my $gti_command = shift(@_);
107 my @gti_command_arguments = @_;
108 my $module = $_[1];
109
110 # for GS3, set gsdl_root_dir to GSDL3HOME
111 if($module eq "gs3interface"){
112 $gsdl_root_directory = (defined $ENV{'GSDL3HOME'}) ? $ENV{'GSDL3HOME'} : &util::filename_cat($ENV{'GSDL3SRCHOME'}, "web");
113 $gti_log_file = &util::filename_cat($gsdl_root_directory, "logs", "gti.log");
114 }
115
116 # Open the GTI log file for appending, or write to STDERR if that fails
117 if (!open(GTI_LOG, ">>$gti_log_file")) {
118 open(GTI_LOG, ">&STDERR");
119 }
120
121 # Log the command that launched this script
122 &log_message("Command: $0 @ARGV");
123
124 # Check that a command was supplied
125 if (!$gti_command) {
126 &throw_fatal_error("Missing command.");
127 }
128
129 # Process the command
130 if ($gti_command =~ /^get-all-chunks$/i) {
131 # Check that GS3 interface is the target
132 if ($module eq "gs3interface") {
133 print &get_all_chunks_for_gs3(@gti_command_arguments);
134 } else {
135 print &get_all_chunks(@gti_command_arguments);
136 }
137 }
138 elsif ($gti_command =~ /^get-first-n-chunks-requiring-work$/i) {
139 if ($module eq "gs3interface") {
140 print &get_first_n_chunks_requiring_work_for_gs3(@gti_command_arguments);
141 } else {
142 print &get_first_n_chunks_requiring_work(@gti_command_arguments);
143 }
144 }
145 elsif ($gti_command =~ /^get-uptodate-chunks$/i) {
146 if ($module eq "gs3interface") {
147 print &get_uptodate_chunks_for_gs3(@gti_command_arguments);
148 } else {
149 print &get_uptodate_chunks(@gti_command_arguments);
150 }
151 }
152 elsif ($gti_command =~ /^get-language-status$/i) {
153 print &get_language_status(@gti_command_arguments);
154 }
155 elsif ($gti_command =~ /^search-chunks$/i) {
156 print &search_chunks(@gti_command_arguments);
157 }
158 elsif ($gti_command =~ /^submit-translations$/i) {
159 # This command cannot produce any output since it reads input
160 &submit_translations(@gti_command_arguments);
161 }
162 elsif ($gti_command =~ /^create-glihelp-zip-file$/i) {
163 # This command cannot produce any output since it reads input
164 &create_glihelp_zip_file(@gti_command_arguments);
165 }
166 else {
167 # The command was not recognized
168 &throw_fatal_error("Unknown command \"$gti_command\".");
169 }
170}
171
172
173sub throw_fatal_error
174{
175 my $error_message = shift(@_);
176
177 # Write an XML error response
178 print "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
179 print "<GTIResponse>\n";
180 print " <GTIError time=\"" . time() . "\">" . $error_message . "</GTIError>\n";
181 print "</GTIResponse>\n";
182
183 # Log the error message, then die
184 &log_message("Error: $error_message");
185 die "\n";
186}
187
188
189sub log_message
190{
191 my $log_message = shift(@_);
192 print GTI_LOG time() . " -- " . $log_message . "\n";
193}
194
195
196sub get_all_chunks
197{
198 # The code of the target language (ensure it is lowercase)
199 my $target_language_code = lc(shift(@_));
200 # The key of the file to translate (ensure it is lowercase)
201 my $translation_file_key = lc(shift(@_));
202
203 # Check that the necessary arguments were supplied
204 if (!$target_language_code || !$translation_file_key) {
205 &throw_fatal_error("Missing command argument.");
206 }
207
208 # Get (and check) the translation configuration
209 my ($source_file, $target_file, $translation_file_type)
210 = &get_translation_configuration($target_language_code, $translation_file_key);
211
212 # Parse the source language and target language files
213 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
214 my @source_file_lines = &read_file_lines($source_file_path);
215 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
216
217 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
218 my @target_file_lines = &read_file_lines($target_file_path);
219 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
220
221 # Filter out any automatically translated chunks
222 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
223 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
224 delete $source_file_key_to_line_mapping{$chunk_key};
225 delete $target_file_key_to_line_mapping{$chunk_key};
226 }
227 }
228
229 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
230 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
231 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
232 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
233
234 my %source_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($source_file, \@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
235 my %target_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($target_file, \@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
236
237 my $xml_response = &create_xml_response_for_all_chunks($translation_file_key, $target_file, \%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping, \%source_file_key_to_last_update_date_mapping, \%target_file_key_to_last_update_date_mapping);
238
239 return $xml_response;
240}
241
242
243sub get_uptodate_chunks
244{
245 # The code of the target language (ensure it is lowercase)
246 my $target_language_code = lc(shift(@_));
247 # The key of the file to translate (ensure it is lowercase)
248 my $translation_file_key = lc(shift(@_));
249
250 # Check that the necessary arguments were supplied
251 if (!$target_language_code || !$translation_file_key) {
252 &throw_fatal_error("Missing command argument.");
253 }
254
255 # Get (and check) the translation configuration
256 my ($source_file, $target_file, $translation_file_type)
257 = &get_translation_configuration($target_language_code, $translation_file_key);
258
259 # Parse the source language and target language files
260 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
261 my @source_file_lines = &read_file_lines($source_file_path);
262 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
263
264 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
265 my @target_file_lines = &read_file_lines($target_file_path);
266 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
267
268 # Filter out any automatically translated chunks
269 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
270 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
271 delete $source_file_key_to_line_mapping{$chunk_key};
272 delete $target_file_key_to_line_mapping{$chunk_key};
273 }
274 }
275
276 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
277 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
278 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
279 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
280
281 my %source_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($source_file, \@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
282 my %target_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($target_file, \@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
283
284
285 # Chunks needing updating are those in the target file that have been more recently edited in the source file
286 # All others are uptodate (which implies that they have certainly been translated at some point and would not be empty)
287 my @uptodate_target_file_keys = ();
288 foreach my $chunk_key (keys(%source_file_key_to_last_update_date_mapping)) {
289 my $source_chunk_last_update_date = $source_file_key_to_last_update_date_mapping{$chunk_key};
290 my $target_chunk_last_update_date = $target_file_key_to_last_update_date_mapping{$chunk_key};
291
292 # print "key: $chunk_key\nsource date : $source_chunk_last_update_date\ntarget date : $target_chunk_last_update_date\nafter? ". &is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date) . "\n\n";
293
294 if (defined($target_chunk_last_update_date) && !&is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date)) {
295 # &log_message("Chunk with key $chunk_key needs updating.");
296 push(@uptodate_target_file_keys, $chunk_key);
297 }
298 }
299
300 my $xml_response = &create_xml_response_for_uptodate_chunks($translation_file_key, $target_file, \@uptodate_target_file_keys, \%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping, \%source_file_key_to_last_update_date_mapping, \%target_file_key_to_last_update_date_mapping);
301
302 return $xml_response;
303}
304
305
306sub get_first_n_chunks_requiring_work
307{
308 # The code of the target language (ensure it is lowercase)
309 my $target_language_code = lc(shift(@_));
310 # The key of the file to translate (ensure it is lowercase)
311 my $translation_file_key = lc(shift(@_));
312 # The number of chunks to return (defaults to one if not specified)
313 my $num_chunks_to_return = shift(@_) || "1";
314
315 # Check that the necessary arguments were supplied
316 if (!$target_language_code || !$translation_file_key) {
317 &throw_fatal_error("Missing command argument.");
318 }
319
320 # Get (and check) the translation configuration
321 my ($source_file, $target_file, $translation_file_type)
322 = &get_translation_configuration($target_language_code, $translation_file_key);
323
324 # Parse the source language and target language files
325 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
326 my @source_file_lines = &read_file_lines($source_file_path);
327 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
328
329 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
330 my @target_file_lines = &read_file_lines($target_file_path);
331 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
332
333 # Filter out any automatically translated chunks
334 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
335 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
336 delete $source_file_key_to_line_mapping{$chunk_key};
337 delete $target_file_key_to_line_mapping{$chunk_key};
338 }
339 }
340
341 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
342 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
343 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
344 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
345
346 # Determine the target file chunks requiring translation
347 my @target_file_keys_requiring_translation = &determine_chunks_requiring_translation(\%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping);
348 &log_message("Number of target chunks requiring translation: " . scalar(@target_file_keys_requiring_translation));
349
350 # Determine the target file chunks requiring updating
351 my %source_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($source_file, \@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
352 my %target_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($target_file, \@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
353 my @target_file_keys_requiring_updating = &determine_chunks_requiring_updating(\%source_file_key_to_last_update_date_mapping, \%target_file_key_to_last_update_date_mapping);
354 &log_message("Number of target chunks requiring updating: " . scalar(@target_file_keys_requiring_updating));
355
356 my $xml_response = &create_xml_response_for_chunks_requiring_work($translation_file_key, $target_file, scalar(keys(%source_file_key_to_text_mapping)), \@target_file_keys_requiring_translation, \@target_file_keys_requiring_updating, $num_chunks_to_return, \%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping, \%source_file_key_to_last_update_date_mapping, \%target_file_key_to_last_update_date_mapping);
357
358 return $xml_response;
359}
360
361
362sub get_language_status
363{
364 # The code of the target language (ensure it is lowercase)
365 my $target_language_code = lc(shift(@_));
366
367 # Check that the necessary arguments were supplied
368 if (!$target_language_code) {
369 &throw_fatal_error("Missing command argument.");
370 }
371
372 # Form an XML response to the command
373 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
374 $xml_response .= "<GTIResponse>\n";
375 $xml_response .= " <LanguageStatus code=\"$target_language_code\">\n";
376
377 foreach my $translation_file (@$gti_translation_files) {
378 my ($num_source_chunks, $num_target_chunks, $num_chunks_requiring_translation, $num_chunks_requiring_updating) = 0;
379 my $target_file_name = "";
380
381 if ($translation_file->{'key'} eq "gs3interface") {
382 my (%source_file_key_to_text_mapping, %target_file_key_to_text_mapping, %source_file_key_to_last_update_date_mapping, %target_file_key_to_last_update_date_mapping ) = ();
383 &build_gs3_configuration($target_language_code, \%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping, \%source_file_key_to_last_update_date_mapping, \%target_file_key_to_last_update_date_mapping );
384
385 my @target_file_keys_requiring_translation = &determine_chunks_requiring_translation(\%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping);
386 my @target_file_keys_requiring_updating = &determine_chunks_requiring_updating(\%source_file_key_to_last_update_date_mapping, \%target_file_key_to_last_update_date_mapping);
387
388 $num_source_chunks = scalar(keys(%source_file_key_to_text_mapping));
389 $num_target_chunks = scalar(keys(%target_file_key_to_text_mapping));
390 $num_chunks_requiring_translation = scalar(@target_file_keys_requiring_translation);
391 $num_chunks_requiring_updating = scalar(@target_file_keys_requiring_updating);
392 }
393 else {
394 # Get (and check) the translation configuration
395 my ($source_file, $target_file, $translation_file_type) = &get_translation_configuration($target_language_code, $translation_file->{'key'});
396 $target_file_name = $target_file;
397
398 # Parse the source language and target language files
399 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
400 my @source_file_lines = &read_file_lines($source_file_path);
401 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
402
403 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
404 my @target_file_lines = &read_file_lines($target_file_path);
405 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
406
407 # Filter out any automatically translated chunks
408 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
409 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
410 delete $source_file_key_to_line_mapping{$chunk_key};
411 delete $target_file_key_to_line_mapping{$chunk_key};
412 }
413 }
414
415 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
416 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
417
418 # Determine the target file chunks requiring translation
419 my @target_file_keys_requiring_translation = &determine_chunks_requiring_translation(\%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping);
420
421 # Determine the target file chunks requiring updating
422 my @target_file_keys_requiring_updating = ();
423 if (-e $target_file_path) {
424 my %source_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($source_file, \@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
425 my %target_file_key_to_last_update_date_mapping = &build_key_to_last_update_date_mapping($target_file, \@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
426 @target_file_keys_requiring_updating = &determine_chunks_requiring_updating(\%source_file_key_to_last_update_date_mapping, \%target_file_key_to_last_update_date_mapping);
427 }
428
429 $num_source_chunks = scalar(keys(%source_file_key_to_text_mapping));
430 $num_target_chunks = scalar(keys(%target_file_key_to_text_mapping));
431 $num_chunks_requiring_translation = scalar(@target_file_keys_requiring_translation);
432 $num_chunks_requiring_updating = scalar(@target_file_keys_requiring_updating);
433 }
434
435 &log_message("Status of " . $translation_file->{'key'});
436 &log_message("Number of source chunks: " . $num_source_chunks);
437 &log_message("Number of target chunks: " . $num_target_chunks);
438 &log_message("Number of target chunks requiring translation: " . $num_chunks_requiring_translation);
439 &log_message("Number of target chunks requiring updating: " . $num_chunks_requiring_updating);
440
441 $xml_response .= " <TranslationFile"
442 . " key=\"" . $translation_file->{'key'} . "\""
443 . " target_file_path=\"" . $target_file_name . "\""
444 . " num_chunks_translated=\"" . ($num_source_chunks - $num_chunks_requiring_translation) . "\""
445 . " num_chunks_requiring_translation=\"" . $num_chunks_requiring_translation . "\""
446 . " num_chunks_requiring_updating=\"" . $num_chunks_requiring_updating . "\"\/>\n";
447 }
448
449 $xml_response .= " </LanguageStatus>\n";
450
451 $xml_response .= "</GTIResponse>\n";
452 return $xml_response;
453}
454
455
456sub search_chunks
457{
458 # The code of the target language (ensure it is lowercase)
459 my $target_language_code = lc(shift(@_));
460 # The key of the file to translate (ensure it is lowercase)
461 my $translation_file_key = lc(shift(@_));
462 # The query string
463 my $query_string = join(' ', @_);
464
465 # Check that the necessary arguments were supplied
466 if (!$target_language_code || !$translation_file_key || !$query_string) {
467 &throw_fatal_error("Missing command argument.");
468 }
469
470 my ($source_file, $target_file, $translation_file_type) = ();
471 my %source_file_key_to_text_mapping = ();
472 my %target_file_key_to_text_mapping = ();
473
474
475 if ($translation_file_key ne "gs3interface") {
476 # Get (and check) the translation configuration
477 ($source_file, $target_file, $translation_file_type) = &get_translation_configuration($target_language_code, $translation_file_key);
478
479 # Parse the source language and target language files
480 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
481 my @source_file_lines = &read_file_lines($source_file_path);
482 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
483
484 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
485 my @target_file_lines = &read_file_lines($target_file_path);
486 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
487
488 # Filter out any automatically translated chunks
489 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
490 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
491 delete $source_file_key_to_line_mapping{$chunk_key};
492 delete $target_file_key_to_line_mapping{$chunk_key};
493 }
494 }
495
496 %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
497 %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
498 }
499 else {
500 # Not needed in this case
501 my (%source_file_key_to_gti_command_mapping, %target_file_key_to_gti_command_mapping) = ();
502 &build_gs3_configuration($target_language_code, \%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping,
503 \%source_file_key_to_gti_command_mapping, \%target_file_key_to_gti_command_mapping);
504 }
505
506 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
507 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
508
509 # Determine the target file chunks matching the query
510 my @target_file_keys_matching_query = ();
511 foreach my $chunk_key (keys(%target_file_key_to_text_mapping)) {
512 my $target_file_text = $target_file_key_to_text_mapping{$chunk_key};
513 if ($target_file_text =~ /$query_string/i) {
514 # &log_message("Chunk with key $chunk_key matches query.");
515 push(@target_file_keys_matching_query, $chunk_key);
516 }
517 }
518
519 # Form an XML response to the command
520 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
521 $xml_response .= "<GTIResponse>\n";
522
523 $xml_response .= " <ChunksMatchingQuery size=\"" . scalar(@target_file_keys_matching_query) . "\">\n";
524 foreach my $chunk_key (@target_file_keys_matching_query) {
525 my $target_file_chunk_text = &make_text_xml_safe($target_file_key_to_text_mapping{$chunk_key});
526
527 $xml_response .= " <Chunk key=\"$chunk_key\">\n";
528 $xml_response .= " <TargetFileText>$target_file_chunk_text</TargetFileText>\n";
529 $xml_response .= " </Chunk>\n";
530 }
531 $xml_response .= " </ChunksMatchingQuery>\n";
532
533 $xml_response .= "</GTIResponse>\n";
534 return $xml_response;
535}
536
537
538sub submit_translations
539{
540 # The code of the target language (ensure it is lowercase)
541 my $target_language_code = lc(shift(@_));
542 # The key of the file to translate (ensure it is lowercase)
543 my $translation_file_key = lc(shift(@_));
544 # The username of the translation submitter
545 my $submitter_username = shift(@_);
546 # Whether to submit a target chunk even if it hasn't changed
547 my $force_submission_flag = shift(@_);
548
549 # Check that the necessary arguments were supplied
550 if (!$target_language_code || !$translation_file_key || !$submitter_username) {
551 &log_message("Fatal error (but cannot be thrown): Missing command argument.");
552 die "\n";
553 }
554
555 my %source_file_key_to_text_mapping = ();
556 my %source_file_key_to_gti_comment_mapping = ();
557 my %target_file_key_to_text_mapping = ();
558 my %target_file_key_to_gti_comment_mapping = ();
559
560 my (@source_file_lines, @target_file_lines) = ();
561 my ($source_file, $target_file, $translation_file_type);
562
563
564 if ($translation_file_key ne "gs3interface") {
565 # Get (and check) the translation configuration
566 ($source_file, $target_file, $translation_file_type)
567 = &get_translation_configuration($target_language_code, $translation_file_key);
568
569 # Parse the source language and target language files
570 @source_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $source_file));
571 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
572 %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
573 %source_file_key_to_gti_comment_mapping = &build_key_to_gti_comment_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
574
575 @target_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $target_file));
576 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
577 %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
578 %target_file_key_to_gti_comment_mapping = &build_key_to_gti_comment_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
579 }
580 else {
581 &build_gs3_configuration($target_language_code, \%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping,
582 \%source_file_key_to_gti_comment_mapping, \%target_file_key_to_gti_comment_mapping);
583 }
584 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
585 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
586
587 # Submission date
588 my $day = (localtime)[3];
589 my $month = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")[(localtime)[4]];
590 my $year = (localtime)[5] + 1900;
591 my $submission_date = "$day-$month-$year";
592
593 open(SUBMISSION, "-");
594 my @submission_lines = <SUBMISSION>;
595 close(SUBMISSION);
596
597 # Remove any nasty carriage returns
598 # &log_message("Submission:");
599 foreach my $submission_line (@submission_lines) {
600 $submission_line =~ s/\r$//;
601 #&log_message(" $submission_line");
602 }
603
604 my %source_file_key_to_submission_mapping = ();
605 my %target_file_key_to_submission_mapping = ();
606 for (my $i = 0; $i < scalar(@submission_lines); $i++) {
607 # Read source file part of submission
608 if ($submission_lines[$i] =~ /^\<SourceFileText key=\"(.+)\"\>/) {
609 my $chunk_key = $1;
610
611 # Read the source file text
612 my $source_file_chunk_text = "";
613 $i++;
614 while ($i < scalar(@submission_lines) && $submission_lines[$i] !~ /^\<\/SourceFileText\>/) {
615 $source_file_chunk_text .= $submission_lines[$i];
616 $i++;
617 }
618 $source_file_chunk_text =~ s/\n$//; # Strip the extra newline character added
619 $source_file_chunk_text = &unmake_text_xml_safe($source_file_chunk_text);
620
621 #&log_message("Source file key: $chunk_key");
622 #&log_message("Source file text: $source_file_chunk_text");
623 $source_file_key_to_submission_mapping{$chunk_key} = $source_file_chunk_text;
624 }
625
626 # Read target file part of submission
627 if ($submission_lines[$i] =~ /^\<TargetFileText key=\"(.+)\"\>/) {
628 my $chunk_key = $1;
629
630 # Read the target file text
631 my $target_file_chunk_text = "";
632 $i++;
633 while ($i < scalar(@submission_lines) && $submission_lines[$i] !~ /^\<\/TargetFileText\>/) {
634 $target_file_chunk_text .= $submission_lines[$i];
635 $i++;
636 }
637 $target_file_chunk_text =~ s/\n$//; # Strip the extra newline character added
638 $target_file_chunk_text = &unmake_text_xml_safe($target_file_chunk_text);
639
640 #&log_message("Target file key: $chunk_key");
641 #&log_message("Target file text: $target_file_chunk_text");
642 $target_file_key_to_submission_mapping{$chunk_key} = $target_file_chunk_text;
643 }
644 }
645
646 # -----------------------------------------
647 # Validate the translation submissions
648 # -----------------------------------------
649
650 # Check that the translations are valid
651 foreach my $chunk_key (keys(%source_file_key_to_submission_mapping)) {
652 # Make sure the submitted chunk still exists in the source file
653 if (!defined($source_file_key_to_text_mapping{$chunk_key})) {
654 &log_message("Warning: Source chunk $chunk_key no longer exists (ignoring submission).");
655 delete $source_file_key_to_submission_mapping{$chunk_key};
656 delete $target_file_key_to_submission_mapping{$chunk_key};
657 next;
658 }
659
660 # Make sure the submitted source chunk matches the source file chunk
661 if ($source_file_key_to_submission_mapping{$chunk_key} ne &unmake_text_xml_safe($source_file_key_to_text_mapping{$chunk_key})) {
662 &log_message("Warning: Source chunk $chunk_key has changed (ignoring submission).");
663 &log_message("Submission source: $source_file_key_to_submission_mapping{$chunk_key}");
664 &log_message(" Source text: $source_file_key_to_text_mapping{$chunk_key}");
665 delete $source_file_key_to_submission_mapping{$chunk_key};
666 delete $target_file_key_to_submission_mapping{$chunk_key};
667 next;
668 }
669 }
670
671 # Apply the submitted translations
672 foreach my $chunk_key (keys(%target_file_key_to_submission_mapping)) {
673 # Only apply the submission if it is a change, unless -force_submission has been specified
674 if ($force_submission_flag || !defined($target_file_key_to_text_mapping{$chunk_key}) || $target_file_key_to_submission_mapping{$chunk_key} ne $target_file_key_to_text_mapping{$chunk_key}) {
675 $target_file_key_to_text_mapping{$chunk_key} = $target_file_key_to_submission_mapping{$chunk_key};
676 $target_file_key_to_gti_comment_mapping{$chunk_key} = "Updated $submission_date by $submitter_username";
677 }
678 }
679
680 if ($translation_file_key ne "gs3interface") {
681 eval "&write_translated_${translation_file_type}(\$source_file, \\\@source_file_lines, \\\%source_file_key_to_text_mapping, \$target_file, \\\@target_file_lines, \\\%target_file_key_to_text_mapping, \\\%target_file_key_to_gti_comment_mapping, \$target_language_code)";
682 } else {
683 eval "&write_translated_gs3interface(\\\%source_file_key_to_text_mapping, \\\%target_file_key_to_text_mapping, \\\%target_file_key_to_gti_comment_mapping, \$target_language_code)";
684 }
685}
686
687
688sub create_glihelp_zip_file
689{
690 my $target_language_code = shift(@_);
691 my $translation_file_key = "glihelp";
692
693 &log_message("Creating GLI Help zip file for $target_language_code");
694
695 my ($source_file, $target_file, $translation_file_type) = &get_translation_data_for($target_language_code, $translation_file_key);
696
697 my $classpath = &util::filename_cat($gsdl_root_directory, "gti-lib");
698 if ( ! -e $classpath) {
699 &throw_fatal_error("$classpath doesn't exist! Need the files in this directory (ApplyXLST and its related files) to create the zip file for GLI Help");
700 }
701
702 my $gli_help_directory = &util::filename_cat($gsdl_root_directory, "gli");
703 $gli_help_directory = &util::filename_cat($gli_help_directory, "help");
704
705 my $gen_many_html_xsl_filepath = &util::filename_cat($gli_help_directory, "gen-many-html.xsl");
706 if ( ! -e $gen_many_html_xsl_filepath) {
707 &throw_fatal_error("$gen_many_html_xsl_filepath doesn't exist! Need this file to create the zip file for GLI Help");
708 }
709
710 my $gen_index_xml_xsl_filepath = &util::filename_cat($gli_help_directory, "gen-index-xml.xsl");
711 my $split_script_filepath = &util::filename_cat($gli_help_directory, "splithelpdocument.pl");
712
713 my $target_file_directory = &util::filename_cat($gli_help_directory, $target_language_code);
714 $target_file_directory = $target_file_directory."/";
715
716 my $target_filepath = &util::filename_cat($gsdl_root_directory, $target_file);
717
718 my $perl_exec = &util::get_perl_exec();
719 my $java_exec = "java";
720 if(defined($ENV{'JAVA_HOME'}) && $ENV{'JAVA_HOME'} ne ""){
721 $java_exec = &util::filename_cat($ENV{'JAVA_HOME'}, "bin", "java");
722 }
723
724 my $cmd = "$java_exec -cp $classpath:$classpath/xalan.jar ApplyXSLT $target_language_code $gen_many_html_xsl_filepath $target_filepath | \"$perl_exec\" -S $split_script_filepath $target_file_directory";
725 my $response = `$cmd`;
726
727 $cmd = "$java_exec -cp $classpath:$classpath/xalan.jar ApplyXSLT $target_language_code $gen_index_xml_xsl_filepath $target_filepath > " . $target_file_directory . "help_index.xml"; # 2>/dev/null";
728 $response = `$cmd`;
729
730 my $zip_file_path = "/greenstone/custom/gti/" . $target_language_code . "_GLIHelp.zip";
731 $cmd = "zip -rj $zip_file_path $target_file_directory -i \*.htm \*.xml";
732 $response = `$cmd`;
733}
734
735
736sub get_translation_configuration
737{
738 # Get the code of the target language
739 my $target_language_code = shift(@_);
740 # Get the key of the file to translate
741 my $translation_file_key = shift(@_);
742
743 # Read the translation data from the gti.cfg file
744 my ($source_file, $target_file, $translation_file_type) =
745 &get_translation_data_for($target_language_code, $translation_file_key);
746
747 # Check that the file to translate is defined in the gti.cfg file
748 if (!$source_file || !$target_file || !$translation_file_type) {
749 &throw_fatal_error("Missing or incomplete specification for translation file \"$translation_file_key\" in gti.pl.");
750 }
751
752 # Check that the source file exists
753 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
754 if (!-e $source_file_path) {
755 &throw_fatal_error("Source file $source_file_path does not exist.");
756 }
757
758 # Check that the source file is up to date
759 # The "2>/dev/null" is very important! If it is missing this will never return when run from the receptionist
760 # unless ($translation_file_is_not_in_cvs) {
761 #my $source_file_cvs_status = `cd $gsdl_root_directory; cvs -d $anonymous_cvs_root update $source_file 2>/dev/null`;
762 my $source_file_cvs_status = `cd $gsdl_root_directory; svn status $source_file 2>/dev/null`;
763 if ($source_file_cvs_status =~ /^C /) {
764 &throw_fatal_error("Source file $source_file_path conflicts with the repository.");
765 }
766 if ($source_file_cvs_status =~ /^M /) {
767 &throw_fatal_error("Source file $source_file_path contains uncommitted changes.");
768 }
769 # }
770
771 return ($source_file, $target_file, $translation_file_type);
772}
773
774
775sub get_translation_data_for
776{
777 my ($target_language_code, $translation_file_key) = @_;
778
779 foreach my $translation_file (@$gti_translation_files) {
780 # If this isn't the correct translation file, move onto the next one
781 next if ($translation_file_key ne $translation_file->{'key'});
782
783 # Resolve the target language file
784 my $target_language_file = $translation_file->{'target_file'};
785 if ($target_language_file =~ /(\{.+\;.+\})/) {
786 my $unresolved_target_language_file_part = $1;
787
788 # Check for a special case for the target language code
789 if ($unresolved_target_language_file_part =~ /(\{|\;)$target_language_code:([^\;]+)(\;|\})/) {
790 my $resolved_target_language_file_part = $2;
791 $target_language_file =~ s/$unresolved_target_language_file_part/$resolved_target_language_file_part/;
792 }
793 # Otherwise use the last part as the default value
794 else {
795 my ($default_target_language_file_part) = $unresolved_target_language_file_part =~ /([^\;]+)\}/;
796 $target_language_file =~ s/$unresolved_target_language_file_part/\{$default_target_language_file_part\}/;
797 }
798 }
799
800 # Resolve instances of {iso_639_1_target_language_name}
801 my $iso_639_1_target_language_name = $iso639::fromiso639{$target_language_code};
802 $iso_639_1_target_language_name =~ tr/A-Z/a-z/ if $iso_639_1_target_language_name;
803 $target_language_file =~ s/\{iso_639_1_target_language_name\}/$iso_639_1_target_language_name/g;
804
805 # Resolve instances of {target_language_code}
806 $target_language_file =~ s/\{target_language_code\}/$target_language_code/g;
807
808 return ($translation_file->{'source_file'}, $target_language_file, $translation_file->{'file_type'});
809}
810
811return ();
812}
813
814
815sub read_file_lines
816{
817 my ($file_path) = @_;
818
819 if (!open(FILE_IN, "<$file_path")) {
820 &log_message("Note: Could not open file $file_path.");
821 return ();
822 }
823 my @file_lines = <FILE_IN>;
824 close(FILE_IN);
825
826 return @file_lines;
827}
828
829
830sub build_key_to_line_mapping
831{
832 my ($file_lines, $translation_file_type) = @_;
833 eval "return &build_key_to_line_mapping_for_${translation_file_type}(\@\$file_lines)";
834}
835
836
837sub build_key_to_text_mapping
838{
839 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
840
841 my %key_to_text_mapping = ();
842 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
843 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
844 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
845
846 my $chunk_text = @$file_lines[$chunk_starting_line];
847 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
848 $chunk_text .= @$file_lines[$l];
849 }
850
851 # Map from chunk key to text
852 eval "\$key_to_text_mapping{\${chunk_key}} = &import_chunk_from_${translation_file_type}(\$chunk_text)";
853 }
854
855 return %key_to_text_mapping;
856}
857
858
859sub build_key_to_last_update_date_mapping
860{
861 my ($file, $file_lines, $key_to_line_mapping, $translation_file_type) = @_;
862
863 # If the files aren't in CVS then we can't tell anything about what needs updating
864 # return () if ($translation_file_is_not_in_cvs);
865
866 # Build a mapping from key to CVS date
867 # Need to be careful with this mapping because the chunk keys won't necessarily all be valid
868 my %key_to_cvs_date_mapping = &build_key_to_cvs_date_mapping($file, $translation_file_type);
869
870 # Build a mapping from key to comment date
871 my %key_to_gti_comment_mapping = &build_key_to_gti_comment_mapping($file_lines, $key_to_line_mapping, $translation_file_type);
872
873 # Build a mapping from key to last update date (the latter of the CVS date and comment date)
874 my %key_to_last_update_date_mapping = ();
875 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
876 # Use the CVS date as a starting point
877 my $chunk_cvs_date = $key_to_cvs_date_mapping{$chunk_key};
878 $key_to_last_update_date_mapping{$chunk_key} = $chunk_cvs_date;
879
880 # If a comment date exists and it is after the CVS date, use that instead
881 # need to convert the comment date format to SVN format
882 my $chunk_gti_comment = $key_to_gti_comment_mapping{$chunk_key};
883 if (defined($chunk_gti_comment) && $chunk_gti_comment =~ /(\d?\d-\D\D\D-\d\d\d\d)/) {
884 my $chunk_comment_date = $1;
885 if ((!defined($chunk_cvs_date) || &is_date_after($chunk_comment_date, $chunk_cvs_date))) {
886 $key_to_last_update_date_mapping{$chunk_key} = $chunk_comment_date;
887 }
888 }
889 }
890
891 return %key_to_last_update_date_mapping;
892}
893
894
895sub build_key_to_cvs_date_mapping
896{
897 my ($filename, $translation_file_type) = @_;
898
899 # Use SVN to annotate each line of the file with the date it was last edited
900 # The "2>/dev/null" is very important! If it is missing this will never return when run from the receptionist
901 my $cvs_annotated_file = `cd $gsdl_root_directory; svn annotate -v --force $filename 2>/dev/null`;
902
903 my @cvs_annotated_file_lines = split(/\n/, $cvs_annotated_file);
904
905 my @cvs_annotated_file_lines_date = ();
906 foreach my $cvs_annotated_file_line (@cvs_annotated_file_lines) {
907 # Extract the date from the SVN annotation at the front
908 # svn format : 2007-07-16
909 $cvs_annotated_file_line =~ s/^\s+\S+\s+\S+\s(\S+)//;
910
911 push(@cvs_annotated_file_lines_date, $1);
912
913 # trim extra date information in svn annotation format
914 # 15:42:49 +1200 (Wed, 21 Jun 2006)
915 $cvs_annotated_file_line =~ s/^\s+\S+\s\S+\s\((.+?)\)\s//;
916 }
917
918 # Build a key to line mapping for the CVS annotated file, for matching the chunk key to the CVS date
919 my %key_to_line_mapping = &build_key_to_line_mapping(\@cvs_annotated_file_lines, $translation_file_type);
920
921 my %key_to_cvs_date_mapping = ();
922 foreach my $chunk_key (keys(%key_to_line_mapping)) {
923 my $chunk_starting_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[0];
924 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[1];
925
926 # Find the date this chunk was last edited, from the CVS annotation
927 my $chunk_date = $cvs_annotated_file_lines_date[$chunk_starting_line];
928 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
929 if (&is_date_after($cvs_annotated_file_lines_date[$l], $chunk_date)) {
930 # This part of the chunk has been updated more recently
931 $chunk_date = $cvs_annotated_file_lines_date[$l];
932
933 }
934 }
935
936 # Map from chunk key to CVS date
937 $key_to_cvs_date_mapping{$chunk_key} = $chunk_date;
938 }
939
940 return %key_to_cvs_date_mapping;
941}
942
943
944sub build_key_to_gti_comment_mapping
945{
946 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
947
948 my %key_to_gti_comment_mapping = ();
949 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
950 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
951 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
952
953 my $chunk_text = @$file_lines[$chunk_starting_line];
954 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
955 $chunk_text .= @$file_lines[$l];
956 }
957
958 # Map from chunk key to GTI comment
959 my $chunk_gti_comment;
960 eval "\$chunk_gti_comment = &get_${translation_file_type}_chunk_gti_comment(\$chunk_text)";
961 $key_to_gti_comment_mapping{$chunk_key} = $chunk_gti_comment if (defined($chunk_gti_comment));
962 }
963
964 return %key_to_gti_comment_mapping;
965}
966
967
968sub determine_chunks_requiring_translation
969{
970 my $source_file_key_to_text_mapping = shift(@_);
971 my $target_file_key_to_text_mapping = shift(@_);
972
973 # Chunks needing translation are those in the source file with no translation in the target file
974 my @target_file_keys_requiring_translation = ();
975 foreach my $chunk_key (keys(%$source_file_key_to_text_mapping)) {
976 if ($source_file_key_to_text_mapping->{$chunk_key} && !$target_file_key_to_text_mapping->{$chunk_key}) {
977 # &log_message("Chunk with key $chunk_key needs translating.");
978 push(@target_file_keys_requiring_translation, $chunk_key);
979 }
980 }
981
982 return @target_file_keys_requiring_translation;
983}
984
985
986sub determine_chunks_requiring_updating
987{
988 my $source_file_key_to_last_update_date_mapping = shift(@_);
989 my $target_file_key_to_last_update_date_mapping = shift(@_);
990
991 # Chunks needing updating are those in the target file that have been more recently edited in the source file
992 my @target_file_keys_requiring_updating = ();
993 foreach my $chunk_key (keys(%$source_file_key_to_last_update_date_mapping)) {
994 my $source_chunk_last_update_date = $source_file_key_to_last_update_date_mapping->{$chunk_key};
995 my $target_chunk_last_update_date = $target_file_key_to_last_update_date_mapping->{$chunk_key};
996
997 # print "key: $chunk_key\nsource date : $source_chunk_last_update_date\ntarget date : $target_chunk_last_update_date\nafter? ". &is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date) . "\n\n";
998
999 if (defined($target_chunk_last_update_date) && &is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date)) {
1000 # &log_message("Chunk with key $chunk_key needs updating.");
1001 push(@target_file_keys_requiring_updating, $chunk_key);
1002 }
1003 }
1004
1005 return @target_file_keys_requiring_updating;
1006}
1007
1008
1009sub is_chunk_automatically_translated
1010{
1011 my ($chunk_key, $translation_file_type) = @_;
1012 eval "return &is_${translation_file_type}_chunk_automatically_translated(\$chunk_key)";
1013}
1014
1015
1016sub make_text_xml_safe
1017{
1018 my $text = shift(@_);
1019 $text =~ s/\&/\&amp\;/g;
1020 $text =~ s/\&amp\;lt\;/\&amp\;amp\;lt\;/g;
1021 $text =~ s/\&amp\;gt\;/\&amp\;amp\;gt\;/g;
1022 $text =~ s/\&amp\;rarr\;/\&amp\;amp\;rarr\;/g;
1023 $text =~ s/\&amp\;mdash\;/\&amp\;amp\;mdash\;/g;
1024 $text =~ s/</\&lt\;/g;
1025 $text =~ s/>/\&gt\;/g;
1026 return $text;
1027}
1028
1029
1030sub unmake_text_xml_safe
1031{
1032 my $text = shift(@_);
1033 $text =~ s/\&lt\;/</g;
1034 $text =~ s/\&gt\;/>/g;
1035 $text =~ s/\&amp\;/\&/g;
1036 return $text;
1037}
1038
1039
1040# Returns 1 if $date1 is after $date2, 0 otherwise
1041sub is_date_after_cvs
1042{
1043 my ($date1, $date2) = @_;
1044 my %months = ("Jan", 1, "Feb", 2, "Mar", 3, "Apr", 4, "May", 5, "Jun", 6,
1045 "Jul", 7, "Aug", 8, "Sep", 9, "Oct", 10, "Nov", 11, "Dec", 12);
1046
1047 if(!defined $date1) {
1048 return 1;
1049 }
1050
1051 my @date1parts = split(/-/, $date1);
1052 my @date2parts = split(/-/, $date2);
1053
1054 # Compare year - nasty because we have rolled over into a new century
1055 my $year1 = $date1parts[2];
1056 if ($year1 < 80) {
1057 $year1 += 2000;
1058 }
1059 my $year2 = $date2parts[2];
1060 if ($year2 < 80) {
1061 $year2 += 2000;
1062 }
1063
1064 # Compare year
1065 if ($year1 > $year2) {
1066 return 1;
1067 }
1068 elsif ($year1 == $year2) {
1069 # Year is the same, so compare month
1070 if ($months{$date1parts[1]} > $months{$date2parts[1]}) {
1071 return 1;
1072 }
1073 elsif ($months{$date1parts[1]} == $months{$date2parts[1]}) {
1074 # Month is the same, so compare day
1075 if ($date1parts[0] > $date2parts[0]) {
1076 return 1;
1077 }
1078 }
1079 }
1080
1081 return 0;
1082}
1083
1084sub is_date_after
1085{
1086 my ($date1, $date2) = @_;
1087
1088 if(!defined $date1) {
1089 return 1;
1090 }
1091 if(!defined $date2) {
1092 return 0;
1093 }
1094
1095 # 16-Aug-2006
1096 if($date1=~ /(\d+?)-(\S\S\S)-(\d\d\d\d)/){
1097 my %months = ("Jan", "01", "Feb", "02", "Mar", "03", "Apr", "04", "May", "05", "Jun", "06",
1098 "Jul", "07", "Aug", "08", "Sep", "09", "Oct", "10", "Nov", "11", "Dec", "12");
1099 $date1=$3 . "-" . $months{$2} . "-" . $1;
1100 # print "** converted date1: $date1\n";
1101 }
1102 if($date2=~ /(\d+?)-(\S\S\S)-(\d\d\d\d)/){
1103 my %months = ("Jan", "01", "Feb", "02", "Mar", "03", "Apr", "04", "May", "05", "Jun", "06",
1104 "Jul", "07", "Aug", "08", "Sep", "09", "Oct", "10", "Nov", "11", "Dec", "12");
1105 $date2=$3 . "-" . $months{$2} . "-" . $1;
1106 # print "** converted date2: $date2\n";
1107 }
1108
1109
1110 # 2006-08-16
1111 my @date1parts = split(/-/, $date1);
1112 my @date2parts = split(/-/, $date2);
1113
1114 # Compare year
1115 if ($date1parts[0] > $date2parts[0]) {
1116 return 1;
1117 }
1118 elsif ($date1parts[0] == $date2parts[0]) {
1119 # Year is the same, so compare month
1120 if ($date1parts[1] > $date2parts[1]) {
1121 return 1;
1122 }
1123 elsif ($date1parts[1] == $date2parts[1]) {
1124 # Month is the same, so compare day
1125 if ($date1parts[2] > $date2parts[2]) {
1126 return 1;
1127 }
1128 }
1129 }
1130
1131 return 0;
1132}
1133
1134
1135sub create_xml_response_for_chunks_requiring_work
1136{
1137 my ($translation_file_key, $target_file, $total_num_chunks, $target_files_keys_requiring_translation, $target_files_keys_requiring_updating, $num_chunks_to_return, $source_files_key_to_text_mapping, $target_files_key_to_text_mapping, $source_files_key_to_last_update_date_mapping, $target_files_key_to_last_update_date_mapping) = @_;
1138
1139 # Form an XML response to the command
1140 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
1141 $xml_response .= "<GTIResponse>\n";
1142 $xml_response .= " <TranslationFile"
1143 . " key=\"" . $translation_file_key . "\""
1144 . " target_file_path=\"" . $target_file . "\""
1145 . " num_chunks_translated=\"" . ($total_num_chunks - scalar(@$target_files_keys_requiring_translation)) . "\""
1146 . " num_chunks_requiring_translation=\"" . scalar(@$target_files_keys_requiring_translation) . "\""
1147 . " num_chunks_requiring_updating=\"" . scalar(@$target_files_keys_requiring_updating) . "\"\/>\n";
1148
1149 # Do chunks requiring translation first
1150 if ($num_chunks_to_return > scalar(@$target_files_keys_requiring_translation)) {
1151 $xml_response .= " <ChunksRequiringTranslation size=\"" . scalar(@$target_files_keys_requiring_translation) . "\">\n";
1152 }
1153 else {
1154 $xml_response .= " <ChunksRequiringTranslation size=\"" . $num_chunks_to_return . "\">\n";
1155 }
1156
1157 my @sorted_chunk_keys = sort (@$target_files_keys_requiring_translation);
1158 foreach my $chunk_key (@sorted_chunk_keys) {
1159 last if ($num_chunks_to_return == 0);
1160
1161 my $source_file_chunk_date = $source_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1162 my $source_file_chunk_text = &make_text_xml_safe($source_files_key_to_text_mapping->{$chunk_key});
1163
1164 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1165 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1166 $xml_response .= " <TargetFileText></TargetFileText>\n";
1167 $xml_response .= " </Chunk>\n";
1168
1169 $num_chunks_to_return--;
1170 }
1171
1172 $xml_response .= " </ChunksRequiringTranslation>\n";
1173
1174 # Then do chunks requiring updating
1175 if ($num_chunks_to_return > scalar(@$target_files_keys_requiring_updating)) {
1176 $xml_response .= " <ChunksRequiringUpdating size=\"" . scalar(@$target_files_keys_requiring_updating) . "\">\n";
1177 }
1178 else {
1179 $xml_response .= " <ChunksRequiringUpdating size=\"" . $num_chunks_to_return . "\">\n";
1180 }
1181
1182 # foreach my $chunk_key (@target_file_keys_requiring_updating) {
1183 @sorted_chunk_keys = sort (@$target_files_keys_requiring_updating);
1184 foreach my $chunk_key (@sorted_chunk_keys) {
1185 last if ($num_chunks_to_return == 0);
1186
1187 my $source_file_chunk_date = $source_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1188 my $source_file_chunk_text = &make_text_xml_safe($source_files_key_to_text_mapping->{$chunk_key});
1189 my $target_file_chunk_date = $target_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1190 my $target_file_chunk_text = &make_text_xml_safe($target_files_key_to_text_mapping->{$chunk_key});
1191
1192 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1193 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1194 $xml_response .= " <TargetFileText date=\"$target_file_chunk_date\">$target_file_chunk_text</TargetFileText>\n";
1195 $xml_response .= " </Chunk>\n";
1196
1197 $num_chunks_to_return--;
1198 }
1199
1200 $xml_response .= " </ChunksRequiringUpdating>\n";
1201
1202 $xml_response .= "</GTIResponse>\n";
1203
1204 return $xml_response;
1205}
1206
1207sub create_xml_response_for_uptodate_chunks
1208{
1209 my ($translation_file_key, $target_file, $uptodate_target_files_keys, $source_files_key_to_text_mapping, $target_files_key_to_text_mapping, $source_files_key_to_last_update_date_mapping, $target_files_key_to_last_update_date_mapping) = @_;
1210
1211 # Form an XML response to the command
1212 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
1213 $xml_response .= "<GTIResponse>\n";
1214 $xml_response .= " <TranslationFile"
1215 . " key=\"" . $translation_file_key . "\""
1216 . " target_file_path=\"" . $target_file . "\""
1217 . " num_chunks_uptodate=\"" . scalar(@$uptodate_target_files_keys) . "\"\/>\n";
1218
1219
1220 # Then do chunks requiring updating
1221 $xml_response .= " <UptodateChunks size=\"" . scalar(@$uptodate_target_files_keys) . "\">\n";
1222
1223
1224 # foreach my $chunk_key (@uptodate_target_file_keys) {
1225 my @sorted_chunk_keys = sort (@$uptodate_target_files_keys);
1226 foreach my $chunk_key (@sorted_chunk_keys) {
1227
1228 my $source_file_chunk_date = $source_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1229 my $source_file_chunk_text = &make_text_xml_safe($source_files_key_to_text_mapping->{$chunk_key});
1230 my $target_file_chunk_date = $target_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1231 my $target_file_chunk_text = &make_text_xml_safe($target_files_key_to_text_mapping->{$chunk_key});
1232
1233 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1234 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1235 $xml_response .= " <TargetFileText date=\"$target_file_chunk_date\">$target_file_chunk_text</TargetFileText>\n";
1236 $xml_response .= " </Chunk>\n";
1237
1238 }
1239
1240 $xml_response .= " </UptodateChunks>\n";
1241
1242 $xml_response .= "</GTIResponse>\n";
1243
1244 return $xml_response;
1245}
1246
1247sub create_xml_response_for_all_chunks
1248{
1249 my ($translation_file_key, $target_file, $source_file_key_to_text_mapping, $target_file_key_to_text_mapping, $source_file_key_to_last_update_date_mapping, $target_file_key_to_last_update_date_mapping) = @_;
1250
1251 # Form an XML response to the command
1252 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
1253 $xml_response .= "<GTIResponse>\n";
1254 $xml_response .= " <TranslationFile"
1255 . " key=\"" . $translation_file_key . "\""
1256 . " target_file_path=\"" . $target_file . "\"\/>\n";
1257
1258 # Do all the chunks
1259 $xml_response .= " <Chunks size=\"" . scalar(keys(%$source_file_key_to_text_mapping)) . "\">\n";
1260
1261 my @sorted_chunk_keys = sort (keys(%$source_file_key_to_text_mapping));
1262 foreach my $chunk_key (@sorted_chunk_keys) {
1263 my $source_file_chunk_date = $source_file_key_to_last_update_date_mapping->{$chunk_key} || "";
1264 my $source_file_chunk_text = &make_text_xml_safe($source_file_key_to_text_mapping->{$chunk_key});
1265
1266 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1267 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1268 if (defined($target_file_key_to_text_mapping->{$chunk_key})) {
1269 my $target_file_chunk_date = $target_file_key_to_last_update_date_mapping->{$chunk_key} || "";
1270 my $target_file_chunk_text = &make_text_xml_safe($target_file_key_to_text_mapping->{$chunk_key});
1271 $xml_response .= " <TargetFileText date=\"$target_file_chunk_date\">$target_file_chunk_text</TargetFileText>\n";
1272 }
1273 else {
1274 $xml_response .= " <TargetFileText></TargetFileText>\n";
1275 }
1276
1277 $xml_response .= " </Chunk>\n";
1278 }
1279 $xml_response .= " </Chunks>\n";
1280
1281 $xml_response .= "</GTIResponse>\n";
1282 return $xml_response;
1283}
1284
1285
1286
1287# ==========================================================================================
1288# MACROFILE FUNCTIONS
1289
1290sub build_key_to_line_mapping_for_macrofile
1291{
1292 my (@file_lines) = @_;
1293
1294 my $macro_package;
1295 my %chunk_key_to_line_mapping = ();
1296 # Process the contents of the file, line by line
1297 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1298 my $line = $file_lines[$i];
1299 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1300
1301 # Check if a new package is being defined
1302 if ($line =~ m/^package\s+(.+)/) {
1303 $macro_package = $1;
1304 }
1305
1306 # Line contains a macro name
1307 elsif ($line =~ m/^(_\w+_)/) {
1308 my $macro_key = $1;
1309 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
1310
1311 # While there is still text of the macro to go...
1312 my $startline = $i;
1313 while ($line !~ /\}$/) {
1314 $i++;
1315 if ($i == scalar(@file_lines)) {
1316 &throw_fatal_error("Could not find end of macro $macro_key.");
1317 }
1318 $line = $file_lines[$i];
1319 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
1320 }
1321
1322 # The chunk key consists of the package name and the macro key
1323 my $chunk_key = $macro_package . "." . $macro_key;
1324 # Map from chunk key to line
1325 $chunk_key_to_line_mapping{$chunk_key} = $startline . "-" . $i;
1326 }
1327
1328 # Icon: line in format ## "image text" ## image_type ## macro_name ##
1329 elsif ($line =~ m/^\#\# .* \#\# .* \#\# (.*) \#\#/) {
1330 # The chunk key consists of package name and macro key
1331 my $chunk_key = $macro_package . "." . $1;
1332 # Map from chunk key to line
1333 $chunk_key_to_line_mapping{$chunk_key} = $i . "-" . $i;
1334}
1335}
1336
1337return %chunk_key_to_line_mapping;
1338}
1339
1340
1341sub import_chunk_from_macrofile
1342{
1343 my ($chunk_text) = @_;
1344
1345 # Is this an icon macro??
1346 if ($chunk_text =~ /^\#\# (.*)/) {
1347 # Extract image macro text
1348 $chunk_text =~ /^\#\#\s+([^\#]+)\s+\#\#/;
1349 $chunk_text = $1;
1350
1351 # Remove enclosing quotes
1352 $chunk_text =~ s/^\"//;
1353 $chunk_text =~ s/\"$//;
1354}
1355
1356# No, so it must be a text macro
1357else {
1358 # Remove macro key
1359 $chunk_text =~ s/^_([^_]+)_(\s*)//;
1360
1361 # Remove language specifier
1362 $chunk_text =~ s/^\[l=.*\](\s*)//;
1363
1364 # Remove braces enclosing text
1365 $chunk_text =~ s/^{(\s*)((.|\n)*)}(\s*)(\#.+\s*)?/$2/;
1366}
1367
1368return $chunk_text;
1369}
1370
1371
1372sub get_macrofile_chunk_gti_comment
1373{
1374 my ($chunk_text) = @_;
1375
1376 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1377 if ($chunk_text =~ /\#\s+(Updated\s+\d?\d-\D\D\D-\d\d\d\d.*)\s*$/i) {
1378 return $1;
1379}
1380
1381return undef;
1382}
1383
1384
1385sub is_macrofile_chunk_automatically_translated
1386{
1387 my ($chunk_key) = @_;
1388
1389 # The _httpiconX_, _widthX_ and _heightX_ image macros are automatically translated
1390 if ($chunk_key =~ /\._(httpicon|width|height)/) {
1391 return 1;
1392 }
1393
1394 return 0;
1395}
1396
1397
1398# Use the source file to generate a target file that is formatted the same
1399sub write_translated_macrofile
1400{
1401 my $source_file = shift(@_); # Not used
1402 my @source_file_lines = @{shift(@_)};
1403 my $source_file_key_to_text_mapping = shift(@_);
1404 my $target_file = shift(@_);
1405 my @target_file_lines = @{shift(@_)};
1406 my $target_file_key_to_text_mapping = shift(@_);
1407 my $target_file_key_to_gti_comment_mapping = shift(@_);
1408 my $target_language_code = shift(@_);
1409
1410 # Build a mapping from source file line to chunk key
1411 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@source_file_lines);
1412 my %source_file_line_to_key_mapping = ();
1413 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1414 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1415 }
1416 my @source_file_line_keys = (sort sort_by_line (keys(%source_file_line_to_key_mapping)));
1417 my $source_file_line_number = 0;
1418
1419 # Build a mapping from target file line to chunk key
1420 my %target_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@target_file_lines);
1421 my %target_file_line_to_key_mapping = ();
1422 foreach my $chunk_key (keys(%target_file_key_to_line_mapping)) {
1423 $target_file_line_to_key_mapping{$target_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1424 }
1425 my @target_file_line_keys = (sort sort_by_line (keys(%target_file_line_to_key_mapping)));
1426
1427 # Write the new target file
1428 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1429 if (!open(TARGET_FILE, ">$target_file_path")) {
1430 &throw_fatal_error("Could not write target file $target_file_path.");
1431 }
1432
1433 # Use the header from the target file, to keep language and author information
1434 if (scalar(@target_file_line_keys) > 0) {
1435 my $target_file_line_number = 0;
1436 my $target_file_chunk_starting_line_number = (split(/-/, $target_file_line_keys[0]))[0];
1437 while ($target_file_line_number < $target_file_chunk_starting_line_number) {
1438 my $target_file_line = $target_file_lines[$target_file_line_number];
1439 last if ($target_file_line =~ /^\# -- Missing translation: /); # We don't want to get into the macros
1440 print TARGET_FILE $target_file_line;
1441 $target_file_line_number++;
1442 }
1443
1444 $source_file_line_number = (split(/-/, $source_file_line_keys[0]))[0];
1445 }
1446
1447 # Model the new target file on the source file, with the target file translations
1448 foreach my $line_key (@source_file_line_keys) {
1449 # Fill in the gaps before this chunk starts
1450 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1451 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1452 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1453 print TARGET_FILE $source_file_lines[$source_file_line_number];
1454 $source_file_line_number++;
1455 }
1456 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1457
1458 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1459 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1460 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1461
1462 my $macrofile_key = $chunk_key;
1463 $macrofile_key =~ s/^(.+?)\.//;
1464
1465 # If no translation exists for this chunk, show this, and move on
1466 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1467 print TARGET_FILE "# -- Missing translation: $macrofile_key\n";
1468 next;
1469 }
1470
1471 # Grab the source chunk text
1472 my $source_file_chunk = $source_file_lines[$source_file_chunk_starting_line_number];
1473 for (my $l = ($source_file_chunk_starting_line_number + 1); $l <= $source_file_chunk_finishing_line_number; $l++) {
1474 $source_file_chunk .= $source_file_lines[$l];
1475 }
1476
1477 # Is this an icon macro??
1478 if ($source_file_chunk =~ /^\#\# (.*)/) {
1479 # Escape any newline and question mark characters so the source text is replaced correctly
1480 $source_file_chunk_text =~ s/\\/\\\\/g;
1481 $source_file_chunk_text =~ s/\?/\\\?/g;
1482
1483 # Build the new target chunk from the source chunk
1484 my $target_file_chunk = $source_file_chunk;
1485 $target_file_chunk =~ s/$source_file_chunk_text/$target_file_chunk_text/;
1486 $target_file_chunk =~ s/(\s)*$//;
1487 print TARGET_FILE "$target_file_chunk";
1488 }
1489
1490 # No, it is just a normal text macro
1491 else {
1492 print TARGET_FILE "$macrofile_key [l=$target_language_code] {$target_file_chunk_text}";
1493 }
1494
1495 # Add the "updated" comment, if one exists
1496 if ($target_file_key_to_gti_comment_mapping->{$chunk_key}) {
1497 print TARGET_FILE " # " . $target_file_key_to_gti_comment_mapping->{$chunk_key};
1498 }
1499 print TARGET_FILE "\n";
1500}
1501
1502close(TARGET_FILE);
1503}
1504
1505
1506sub sort_by_line
1507{
1508 return ((split(/-/, $a))[0] <=> (split(/-/, $b))[0]);
1509}
1510
1511
1512# ==========================================================================================
1513# RESOURCE BUNDLE FUNCTIONS
1514
1515sub build_key_to_line_mapping_for_resource_bundle
1516{
1517 my (@file_lines) = @_;
1518
1519 my %chunk_key_to_line_mapping = ();
1520 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1521 my $line = $file_lines[$i];
1522 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1523
1524 # Line contains a dictionary string
1525 if ($line =~ /^(\S+?)[:|=](.*)$/) {
1526 my $chunk_key = $1;
1527
1528 # Map from chunk key to line
1529 $chunk_key_to_line_mapping{$chunk_key} = $i . "-" . $i;
1530 }
1531 }
1532
1533 return %chunk_key_to_line_mapping;
1534}
1535
1536
1537sub import_chunk_from_resource_bundle
1538{
1539 my ($chunk_text) = @_;
1540
1541 # Simple: just remove string key
1542 $chunk_text =~ s/^(\S+?)[:|=](\s*)//;
1543 $chunk_text =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1544 $chunk_text =~ s/(\s*)\#\s+Updated\s+(\d?\d-\D\D\D-\d\d\d\d.*)\s*$//i;
1545
1546 return $chunk_text;
1547}
1548
1549
1550sub get_resource_bundle_chunk_gti_comment
1551{
1552 my ($chunk_text) = @_;
1553
1554 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1555 if ($chunk_text =~ /\#\s+(Updated\s+\d?\d-\D\D\D-\d\d\d\d.*)\s*$/i) {
1556 return $1;
1557}
1558
1559return undef;
1560}
1561
1562
1563sub is_resource_bundle_chunk_automatically_translated
1564{
1565 # No resource bundle chunks are automatically translated
1566 return 0;
1567}
1568
1569
1570sub write_translated_resource_bundle
1571{
1572 my $source_file = shift(@_); # Not used
1573 my @source_file_lines = @{shift(@_)};
1574 my $source_file_key_to_text_mapping = shift(@_);
1575 my $target_file = shift(@_);
1576 my @target_file_lines = @{shift(@_)}; # Not used
1577 my $target_file_key_to_text_mapping = shift(@_);
1578 my $target_file_key_to_gti_comment_mapping = shift(@_);
1579 my $target_language_code = shift(@_); # Not used
1580
1581 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1582 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_resource_bundle(@source_file_lines);
1583 my %source_file_line_to_key_mapping = ();
1584 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1585 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1586 }
1587
1588 # Write the new target file
1589 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1590 if (!open(TARGET_FILE, ">$target_file_path")) {
1591 &throw_fatal_error("Could not write target file $target_file_path.");
1592 }
1593
1594 # Model the new target file on the source file, with the target file translations
1595 my $source_file_line_number = 0;
1596 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1597 # Fill in the gaps before this chunk starts
1598 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1599 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1600 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1601 print TARGET_FILE $source_file_lines[$source_file_line_number];
1602 $source_file_line_number++;
1603 }
1604 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1605
1606 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1607 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1608 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1609
1610 # If no translation exists for this chunk, show this, and move on
1611 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1612 print TARGET_FILE "# -- Missing translation: $chunk_key\n";
1613 next;
1614 }
1615
1616 print TARGET_FILE "$chunk_key:$target_file_chunk_text";
1617 if ($target_file_key_to_gti_comment_mapping->{$chunk_key}) {
1618 print TARGET_FILE " # " . $target_file_key_to_gti_comment_mapping->{$chunk_key};
1619 }
1620 print TARGET_FILE "\n";
1621 }
1622
1623 close(TARGET_FILE);
1624}
1625
1626
1627# ==========================================================================================
1628# GREENSTONE XML FUNCTIONS
1629
1630sub build_key_to_line_mapping_for_greenstone_xml
1631{
1632 my (@file_lines) = @_;
1633
1634 my %chunk_key_to_line_mapping = ();
1635 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1636 my $line = $file_lines[$i];
1637 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1638
1639 # Line contains a string to translate
1640 if ($line =~ /^\s*<Text id=\"(.*?)\">/) {
1641 my $chunk_key = $1;
1642 $line =~ s/\s*$//; # Remove any nasty whitespace
1643 $line =~ s/<Updated date=\"\d?\d-\D\D\D-\d\d\d\d.*\"\/>$//;
1644
1645 # While there is still text of the string to go...
1646 my $startline = $i;
1647 while ($line !~ /<\/Text>$/) {
1648 $i++;
1649 if ($i == scalar(@file_lines)) {
1650 &throw_fatal_error("Could not find end of string $chunk_key.");
1651 }
1652 $line = $file_lines[$i];
1653 $line =~ s/\s*$//; # Remove any nasty whitespace
1654 $line =~ s/<Updated date=\"\d?\d-\D\D\D-\d\d\d\d.*\"\/>$//;
1655 }
1656
1657 # Map from chunk key to line
1658 if (!defined($chunk_key_to_line_mapping{$chunk_key})) {
1659 $chunk_key_to_line_mapping{$chunk_key} = $startline . "-" . $i;
1660 }
1661 else {
1662 &throw_fatal_error("Duplicate key $chunk_key.");
1663 }
1664 }
1665 }
1666
1667 return %chunk_key_to_line_mapping;
1668}
1669
1670
1671sub import_chunk_from_greenstone_xml
1672{
1673 my ($chunk_text) = @_;
1674
1675 # Simple: just remove the Text tags
1676 $chunk_text =~ s/^\s*<Text id=\"(.*?)\">(\s*)//;
1677 $chunk_text =~ s/<Updated date=\"\d?\d-\D\D\D-\d\d\d\d.*\"\/>$//;
1678 $chunk_text =~ s/<\/Text>$//;
1679
1680 return $chunk_text;
1681}
1682
1683
1684sub get_greenstone_xml_chunk_gti_comment
1685{
1686 my ($chunk_text) = @_;
1687
1688 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1689 if ($chunk_text =~ /<Updated date=\"(\d?\d-\D\D\D-\d\d\d\d.*)\"\/>$/i) {
1690 return $1;
1691 }
1692
1693 return undef;
1694}
1695
1696
1697sub is_greenstone_xml_chunk_automatically_translated
1698{
1699 # No greenstone XML chunks are automatically translated
1700 return 0;
1701}
1702
1703
1704sub write_translated_greenstone_xml
1705{
1706 my $source_file = shift(@_); # Not used
1707 my @source_file_lines = @{shift(@_)};
1708 my $source_file_key_to_text_mapping = shift(@_);
1709 my $target_file = shift(@_);
1710 my @target_file_lines = @{shift(@_)}; # Not used
1711 my $target_file_key_to_text_mapping = shift(@_);
1712 my $target_file_key_to_gti_comment_mapping = shift(@_);
1713 my $target_language_code = shift(@_); # Not used
1714
1715 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1716 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_greenstone_xml(@source_file_lines);
1717 my %source_file_line_to_key_mapping = ();
1718 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1719 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1720 }
1721
1722 # Write the new target file
1723 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1724 if (!open(TARGET_FILE, ">$target_file_path")) {
1725 &throw_fatal_error("Could not write target file $target_file_path.");
1726 }
1727
1728 # Model the new target file on the source file, with the target file translations
1729 my $source_file_line_number = 0;
1730 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1731 # Fill in the gaps before this chunk starts
1732 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1733 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1734 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1735 print TARGET_FILE $source_file_lines[$source_file_line_number];
1736 $source_file_line_number++;
1737 }
1738 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1739
1740 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1741 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1742 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1743 $target_file_chunk_text =~ s/(\n)*$//g;
1744
1745 # If no translation exists for this chunk, show this, and move on
1746 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1747 print TARGET_FILE "<!-- Missing translation: $chunk_key -->\n";
1748 next;
1749 }
1750
1751 print TARGET_FILE "<Text id=\"$chunk_key\">$target_file_chunk_text</Text>";
1752 if ($target_file_key_to_gti_comment_mapping->{$chunk_key}) {
1753 my $chunk_gti_comment = $target_file_key_to_gti_comment_mapping->{$chunk_key};
1754 $chunk_gti_comment =~ s/^Updated //;
1755 print TARGET_FILE "<Updated date=\"" . $chunk_gti_comment . "\"\/>";
1756 }
1757 print TARGET_FILE "\n";
1758 }
1759
1760 # Fill in the end of the file
1761 while ($source_file_line_number < scalar(@source_file_lines)) {
1762 print TARGET_FILE $source_file_lines[$source_file_line_number];
1763 $source_file_line_number++;
1764 }
1765
1766 close(TARGET_FILE);
1767}
1768
1769
1770# ==========================================================================================
1771# GREENSTONE3 FUNCTIONS
1772
1773sub get_all_chunks_for_gs3
1774{
1775 # The code of the target language (ensure it is lowercase)
1776 my $target_language_code = lc(shift(@_));
1777 my $translation_file_key = lc(shift(@_));
1778
1779 # Check that the necessary arguments were supplied
1780 if (!$target_language_code) {
1781 &throw_fatal_error("Missing command argument.");
1782 }
1783
1784 # Get (and check) the translation configuration
1785 # my ($source_file_dir, $target_file, $translation_file_type) = &get_translation_configuration($target_language_code, $translation_file_key);
1786
1787 my %source_files_key_to_text_mapping = ();
1788 my %target_files_key_to_text_mapping = ();
1789 my %source_files_key_to_last_update_date_mapping = ();
1790 my %target_files_key_to_last_update_date_mapping = ();
1791
1792 &build_gs3_configuration($target_language_code, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping, \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1793
1794 &log_message("Total number of source chunks: " . scalar(keys(%source_files_key_to_text_mapping)));
1795 &log_message("Total number of target chunks: " . scalar(keys(%target_files_key_to_text_mapping)));
1796
1797 my $xml_response = &create_xml_response_for_all_chunks($translation_file_key, "", \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping, \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1798 return $xml_response;
1799}
1800
1801
1802sub get_first_n_chunks_requiring_work_for_gs3
1803{
1804 # The code of the target language (ensure it is lowercase)
1805 my $target_language_code = lc(shift(@_));
1806 # The key of the file to translate (ensure it is lowercase)
1807 my $translation_file_key = lc(shift(@_));
1808 # The number of chunks to return (defaults to one if not specified)
1809 my $num_chunks_to_return = shift(@_) || "1";
1810
1811 # Check that the necessary arguments were supplied
1812 if (!$target_language_code || !$translation_file_key) {
1813 &throw_fatal_error("Missing command argument.");
1814 }
1815
1816 my %source_files_key_to_text_mapping = ();
1817 my %target_files_key_to_text_mapping = ();
1818 my %source_files_key_to_last_update_date_mapping = ();
1819 my %target_files_key_to_last_update_date_mapping = ();
1820
1821 &build_gs3_configuration($target_language_code, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping,
1822 \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1823
1824 # Determine the target file chunks requiring translation
1825 my @target_files_keys_requiring_translation = &determine_chunks_requiring_translation(\%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping);
1826 # Determine the target file chunks requiring updating
1827 my @target_files_keys_requiring_updating = &determine_chunks_requiring_updating(\%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1828 &log_message("Total number of target chunks requiring translation: " . scalar(@target_files_keys_requiring_translation));
1829 &log_message("Total number of target chunks requiring updating: " . scalar(@target_files_keys_requiring_updating));
1830
1831 my $xml_response = &create_xml_response_for_chunks_requiring_work($translation_file_key, "", scalar(keys(%source_files_key_to_text_mapping)),
1832 \@target_files_keys_requiring_translation, \@target_files_keys_requiring_updating,
1833 $num_chunks_to_return, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping,
1834 \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1835
1836 return $xml_response;
1837}
1838
1839sub get_uptodate_chunks_for_gs3
1840{
1841 # The code of the target language (ensure it is lowercase)
1842 my $target_language_code = lc(shift(@_));
1843 # The key of the file to translate (ensure it is lowercase)
1844 my $translation_file_key = lc(shift(@_));
1845 # The number of chunks to return (defaults to one if not specified)
1846 my $num_chunks_to_return = shift(@_) || "1";
1847
1848 # Check that the necessary arguments were supplied
1849 if (!$target_language_code || !$translation_file_key) {
1850 &throw_fatal_error("Missing command argument.");
1851 }
1852
1853 my %source_files_key_to_text_mapping = ();
1854 my %target_files_key_to_text_mapping = ();
1855 my %source_files_key_to_last_update_date_mapping = ();
1856 my %target_files_key_to_last_update_date_mapping = ();
1857
1858 &build_gs3_configuration($target_language_code, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping,
1859 \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1860
1861
1862 # Chunks needing updating are those in the target file that have been more recently edited in the source file
1863 # All others are uptodate (which implies that they have certainly been translated at some point and would not be empty)
1864 my @uptodate_target_file_keys = ();
1865 foreach my $chunk_key (keys(%source_files_key_to_last_update_date_mapping)) {
1866 my $source_chunk_last_update_date = $source_files_key_to_last_update_date_mapping{$chunk_key};
1867 my $target_chunk_last_update_date = $target_files_key_to_last_update_date_mapping{$chunk_key};
1868
1869 # print "key: $chunk_key\nsource date : $source_chunk_last_update_date\ntarget date : $target_chunk_last_update_date\nafter? ". &is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date) . "\n\n";
1870
1871 if (defined($target_chunk_last_update_date) && !&is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date)) {
1872 # &log_message("Chunk with key $chunk_key needs updating.");
1873 push(@uptodate_target_file_keys, $chunk_key);
1874 }
1875 }
1876
1877 my $xml_response = &create_xml_response_for_uptodate_chunks($translation_file_key, "", \@uptodate_target_file_keys, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping, \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1878
1879 return $xml_response;
1880}
1881
1882
1883sub build_gs3_configuration
1884{
1885 my ($target_language_code, $source_files_key_to_text_mapping, $target_files_key_to_text_mapping,
1886 $source_files_key_to_gti_comment_mapping, $target_files_key_to_gti_comment_mapping) = @_;
1887
1888 my $source_file_directory = &util::filename_cat("WEB-INF","classes"); # my $source_file_directory = "greenstone3";
1889 my $translation_file_type = "resource_bundle";
1890
1891 foreach my $interface_file_key (@gs3_interface_files) {
1892
1893 &log_message("Greenstone 3 interface file: " . $interface_file_key);
1894
1895 # Parse the source language and target language files
1896 my $source_file = &util::filename_cat($source_file_directory, $interface_file_key.".properties");
1897 my @source_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $source_file));
1898 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
1899 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
1900 my %source_file_key_to_gti_comment_mapping = &build_key_to_gti_comment_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
1901
1902 my $target_file = &util::filename_cat($source_file_directory, $interface_file_key."_".$target_language_code.".properties");
1903 my @target_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $target_file));
1904 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
1905 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
1906 my %target_file_key_to_gti_comment_mapping = &build_key_to_gti_comment_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
1907
1908
1909 # Filter out any automatically translated chunks
1910 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1911 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
1912 delete $source_file_key_to_line_mapping{$chunk_key};
1913 delete $target_file_key_to_line_mapping{$chunk_key};
1914 }
1915 }
1916
1917 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
1918 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
1919
1920 foreach my $chunk_key (keys(%source_file_key_to_text_mapping)) {
1921 my $global_chunk_key = "$interface_file_key.$chunk_key";
1922 $source_files_key_to_text_mapping->{$global_chunk_key} = $source_file_key_to_text_mapping{$chunk_key};
1923 $source_files_key_to_gti_comment_mapping->{$global_chunk_key} = $source_file_key_to_gti_comment_mapping{$chunk_key};
1924
1925 if (defined $target_file_key_to_text_mapping{$chunk_key}) {
1926 $target_files_key_to_text_mapping->{$global_chunk_key} = $target_file_key_to_text_mapping{$chunk_key};
1927 $target_files_key_to_gti_comment_mapping->{$global_chunk_key} = $target_file_key_to_gti_comment_mapping{$chunk_key};
1928 }
1929 }
1930 }
1931}
1932
1933
1934sub write_translated_gs3interface
1935{
1936 my $source_file_key_to_text_mapping = shift(@_);
1937 my $target_file_key_to_text_mapping = shift(@_);
1938 my $target_file_key_to_gti_comment_mapping = shift(@_);
1939 my $target_language_code = shift(@_);
1940
1941 my @sorted_chunk_keys = sort (keys(%$source_file_key_to_text_mapping));
1942
1943 my %translated_interface_file_keys = ();
1944 foreach my $chunk_key (keys(%$target_file_key_to_text_mapping)) {
1945 $chunk_key =~ /^([^\.]+)?\.(.*)$/;
1946 if (!defined $translated_interface_file_keys{$1}) {
1947 &log_message("Updated interface file: " . $1);
1948 $translated_interface_file_keys{$1}="";
1949 }
1950 }
1951 &log_message("Updated interface files: " . scalar(keys(%translated_interface_file_keys)));
1952
1953 my $source_file_directory = "greenstone3";
1954
1955 foreach my $interface_file_key (keys(%translated_interface_file_keys)) {
1956
1957 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1958 my $source_file = &util::filename_cat($source_file_directory, "$interface_file_key.properties");
1959 my @source_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $source_file));
1960 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_resource_bundle(@source_file_lines);
1961 my %source_file_line_to_key_mapping = ();
1962 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1963 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1964 }
1965
1966 # Write the new target file
1967 my $target_file = &util::filename_cat($source_file_directory, $interface_file_key . "_" . $target_language_code . ".properties");
1968 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1969 if (!open(TARGET_FILE, ">$target_file_path")) {
1970 &throw_fatal_error("Could not write target file $target_file_path.");
1971 }
1972
1973 # Model the new target file on the source file, with the target file translations
1974 my $source_file_line_number = 0;
1975 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1976 # Fill in the gaps before this chunk starts
1977 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1978 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1979 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1980 print TARGET_FILE $source_file_lines[$source_file_line_number];
1981 $source_file_line_number++;
1982 }
1983 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1984
1985 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1986 my $global_chunk_key = "$interface_file_key.$chunk_key";
1987 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$global_chunk_key};
1988 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$global_chunk_key} || "";
1989
1990 # If no translation exists for this chunk, show this, and move on
1991 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1992 print TARGET_FILE "# -- Missing translation: $chunk_key\n";
1993 next;
1994 }
1995
1996 print TARGET_FILE "$chunk_key:$target_file_chunk_text";
1997 if ($target_file_key_to_gti_comment_mapping->{$global_chunk_key}) {
1998 print TARGET_FILE " # " . $target_file_key_to_gti_comment_mapping->{$global_chunk_key};
1999 }
2000 print TARGET_FILE "\n";
2001 }
2002
2003 close(TARGET_FILE);
2004 }
2005}
2006
2007&main(@ARGV);
Note: See TracBrowser for help on using the repository browser.