source: trunk/gsdl/bin/script/gti.pl@ 11056

Last change on this file since 11056 was 11056, checked in by mdewsnip, 18 years ago

Disabled greenstone.org translation by default.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 45.5 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
40
41my $anonymous_cvs_root = ":pserver:cvs_anon\@cvs.scms.waikato.ac.nz:2402/usr/local/global-cvs/gsdl-src";
42my $gsdl_root_directory = "$ENV{'GSDLHOME'}";
43my $gti_log_file = &util::filename_cat($gsdl_root_directory, "etc", "gti.log");
44my $source_language_code = "en"; # This is non-negiotable
45
46my $gti_translation_files =
47 [ # Greenstone macrofiles
48 { 'key' => "coredm",
49 'file_type' => "macrofile",
50 'source_file' => "macros/english.dm",
51 'target_file' => "macros/{bn:bengali;fa:farsi;id:indo;lv:latvian;pt-br:port-br;pt-pt:port-pt;zh-tr:chinese-trad;iso_639_1_target_language_name}.dm" },
52 { 'key' => "auxdm",
53 'file_type' => "macrofile",
54 'source_file' => "macros/english2.dm",
55 'target_file' => "macros/{bn:bengali;fa:farsi;id:indo;lv:latvian;pt-br:port-br;pt-pt:port-pt;zh-tr:chinese-trad;iso_639_1_target_language_name}2.dm" },
56
57 # GLI dictionary
58 { 'key' => "glidict",
59 'file_type' => "resource_bundle",
60 'source_file' => "gli/classes/dictionary.properties",
61 'target_file' => "gli/classes/dictionary_{target_language_code}.properties" },
62
63 # Greenstone Perl modules
64 { 'key' => "perlmodules",
65 'file_type' => "resource_bundle",
66 'source_file' => "perllib/strings.rb",
67 'target_file' => "perllib/strings_{target_language_code}.rb" },
68
69 # Greenstone.org
70 # { 'key' => "greenorg",
71 # 'file_type' => "macrofile",
72 # 'source_file' => "greenorg/macros/english.dm",
73 # 'target_file' => "greenorg/macros/{iso_639_1_target_language_name}.dm" }
74 ];
75
76
77sub main
78{
79 # Get the command to process, and any arguments
80 my $gti_command = shift(@_);
81 my @gti_command_arguments = @_;
82
83 # Open the GTI log file for appending, or write to STDERR if that fails
84 if (!open(GTI_LOG, ">>$gti_log_file")) {
85 open(GTI_LOG, ">&STDERR");
86 }
87
88 # Log the command that launched this script
89 &log_message("Command: $0 @ARGV");
90
91 # Check that a command was supplied
92 if (!$gti_command) {
93 &throw_fatal_error("Missing command.");
94 }
95
96 # Process the command
97 if ($gti_command =~ /^get-first-n-chunks-requiring-work$/i) {
98 print &get_first_n_chunks_requiring_work(@gti_command_arguments);
99 }
100 if ($gti_command =~ /^get-language-status$/i) {
101 print &get_language_status(@gti_command_arguments);
102 }
103 if ($gti_command =~ /^search-chunks$/i) {
104 print &search_chunks(@gti_command_arguments);
105 }
106 if ($gti_command =~ /^submit-translations$/i) {
107 # This command cannot produce any output since it reads input
108 &submit_translations(@gti_command_arguments);
109 }
110
111 # The command was not recognized
112 # &throw_fatal_error("Unknown command \"$gti_command\".");
113}
114
115
116sub throw_fatal_error
117{
118 my $error_message = shift(@_);
119
120 # Write an XML error response
121 print "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
122 print "<GTIResponse>\n";
123 print " <GTIError time=\"" . time() . "\">" . $error_message . "</GTIError>\n";
124 print "</GTIResponse>\n";
125
126 # Log the error message, then die
127 &log_message("Error: $error_message");
128 die "\n";
129}
130
131
132sub log_message
133{
134 my $log_message = shift(@_);
135 print GTI_LOG time() . " -- " . $log_message . "\n";
136}
137
138
139sub get_first_n_chunks_requiring_work
140{
141 # The code of the target language (ensure it is lowercase)
142 my $target_language_code = lc(shift(@_));
143 # The key of the file to translate (ensure it is lowercase)
144 my $translation_file_key = lc(shift(@_));
145 # The number of chunks to return (defaults to one if not specified)
146 my $num_chunks_to_return = shift(@_) || "1";
147
148 # Check that the necessary arguments were supplied
149 if (!$target_language_code || !$translation_file_key) {
150 &throw_fatal_error("Missing command argument.");
151 }
152
153 # Get (and check) the translation configuration
154 my ($source_file, $target_file, $translation_file_type)
155 = &get_translation_configuration($target_language_code, $translation_file_key);
156
157 # Parse the source language and target language files
158 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
159 my @source_file_lines = &read_file_lines($source_file_path);
160 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
161
162 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
163 my @target_file_lines = &read_file_lines($target_file_path);
164 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
165
166 # Filter out any automatically translated chunks
167 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
168 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
169 delete $source_file_key_to_line_mapping{$chunk_key};
170 delete $target_file_key_to_line_mapping{$chunk_key};
171 }
172 }
173
174 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
175 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
176 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
177 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
178
179 # Determine the target file chunks requiring translation
180 my @target_file_keys_requiring_translation = &determine_chunks_requiring_translation(\%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping);
181 &log_message("Number of target chunks requiring translation: " . scalar(@target_file_keys_requiring_translation));
182
183 # Determine the target file chunks requiring updating
184 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);
185 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);
186 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);
187 &log_message("Number of target chunks requiring updating: " . scalar(@target_file_keys_requiring_updating));
188
189 # Form an XML response to the command
190 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
191 $xml_response .= "<GTIResponse>\n";
192 $xml_response .= " <TranslationFile"
193 . " key=\"" . $translation_file_key . "\""
194 . " target_file_path=\"" . $target_file . "\""
195 . " num_chunks_translated=\"" . (scalar(keys(%source_file_key_to_text_mapping)) - scalar(@target_file_keys_requiring_translation)) . "\""
196 . " num_chunks_requiring_translation=\"" . scalar(@target_file_keys_requiring_translation) . "\""
197 . " num_chunks_requiring_updating=\"" . scalar(@target_file_keys_requiring_updating) . "\"\/>\n";
198
199 # Do chunks requiring translation first
200 if ($num_chunks_to_return > scalar(@target_file_keys_requiring_translation)) {
201 $xml_response .= " <ChunksRequiringTranslation size=\"" . scalar(@target_file_keys_requiring_translation) . "\">\n";
202 }
203 else {
204 $xml_response .= " <ChunksRequiringTranslation size=\"" . $num_chunks_to_return . "\">\n";
205 }
206
207 foreach my $chunk_key (@target_file_keys_requiring_translation) {
208 last if ($num_chunks_to_return == 0);
209
210 my $source_file_chunk_date = $source_file_key_to_last_update_date_mapping{$chunk_key};
211 my $source_file_chunk_text = &make_text_xml_safe($source_file_key_to_text_mapping{$chunk_key});
212
213 $xml_response .= " <Chunk key=\"$chunk_key\">\n";
214 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
215 $xml_response .= " <TargetFileText></TargetFileText>\n";
216 $xml_response .= " </Chunk>\n";
217
218 $num_chunks_to_return--;
219 }
220
221 $xml_response .= " </ChunksRequiringTranslation>\n";
222
223 # Then do chunks requiring updating
224 if ($num_chunks_to_return > scalar(@target_file_keys_requiring_updating)) {
225 $xml_response .= " <ChunksRequiringUpdating size=\"" . scalar(@target_file_keys_requiring_updating) . "\">\n";
226 }
227 else {
228 $xml_response .= " <ChunksRequiringUpdating size=\"" . $num_chunks_to_return . "\">\n";
229 }
230
231 foreach my $chunk_key (@target_file_keys_requiring_updating) {
232 last if ($num_chunks_to_return == 0);
233
234 my $source_file_chunk_date = $source_file_key_to_last_update_date_mapping{$chunk_key};
235 my $source_file_chunk_text = &make_text_xml_safe($source_file_key_to_text_mapping{$chunk_key});
236 my $target_file_chunk_date = $target_file_key_to_last_update_date_mapping{$chunk_key};
237 my $target_file_chunk_text = &make_text_xml_safe($target_file_key_to_text_mapping{$chunk_key});
238
239 $xml_response .= " <Chunk key=\"$chunk_key\">\n";
240 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
241 $xml_response .= " <TargetFileText date=\"$target_file_chunk_date\">$target_file_chunk_text</TargetFileText>\n";
242 $xml_response .= " </Chunk>\n";
243
244 $num_chunks_to_return--;
245 }
246
247 $xml_response .= " </ChunksRequiringUpdating>\n";
248
249 $xml_response .= "</GTIResponse>\n";
250 return $xml_response;
251}
252
253
254sub get_language_status
255{
256 # The code of the target language (ensure it is lowercase)
257 my $target_language_code = lc(shift(@_));
258
259 # Check that the necessary arguments were supplied
260 if (!$target_language_code) {
261 &throw_fatal_error("Missing command argument.");
262 }
263
264 # Form an XML response to the command
265 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
266 $xml_response .= "<GTIResponse>\n";
267 $xml_response .= " <LanguageStatus code=\"$target_language_code\">\n";
268
269 foreach my $translation_file (@$gti_translation_files) {
270 # Get (and check) the translation configuration
271 my ($source_file, $target_file, $translation_file_type)
272 = &get_translation_configuration($target_language_code, $translation_file->{'key'});
273
274 # Parse the source language and target language files
275 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
276 my @source_file_lines = &read_file_lines($source_file_path);
277 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
278
279 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
280 my @target_file_lines = &read_file_lines($target_file_path);
281 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
282
283 # Filter out any automatically translated chunks
284 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
285 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
286 delete $source_file_key_to_line_mapping{$chunk_key};
287 delete $target_file_key_to_line_mapping{$chunk_key};
288 }
289 }
290
291 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
292 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
293 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
294 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
295
296 # Determine the target file chunks requiring translation
297 my @target_file_keys_requiring_translation = &determine_chunks_requiring_translation(\%source_file_key_to_text_mapping, \%target_file_key_to_text_mapping);
298 &log_message("Number of target chunks requiring translation: " . scalar(@target_file_keys_requiring_translation));
299
300 # Determine the target file chunks requiring updating
301 my @target_file_keys_requiring_updating = ();
302 if (-e $target_file_path) {
303 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);
304 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);
305 @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);
306 &log_message("Number of target chunks requiring updating: " . scalar(@target_file_keys_requiring_updating));
307 }
308
309 $xml_response .= " <TranslationFile"
310 . " key=\"" . $translation_file->{'key'} . "\""
311 . " target_file_path=\"" . $target_file . "\""
312 . " num_chunks_translated=\"" . (scalar(keys(%source_file_key_to_text_mapping)) - scalar(@target_file_keys_requiring_translation)) . "\""
313 . " num_chunks_requiring_translation=\"" . scalar(@target_file_keys_requiring_translation) . "\""
314 . " num_chunks_requiring_updating=\"" . scalar(@target_file_keys_requiring_updating) . "\"\/>\n";
315 }
316
317 $xml_response .= " </LanguageStatus>\n";
318
319 $xml_response .= "</GTIResponse>\n";
320 return $xml_response;
321}
322
323
324sub search_chunks
325{
326 # The code of the target language (ensure it is lowercase)
327 my $target_language_code = lc(shift(@_));
328 # The key of the file to translate (ensure it is lowercase)
329 my $translation_file_key = lc(shift(@_));
330 # The query string
331 my $query_string = join(' ', @_);
332
333 # Check that the necessary arguments were supplied
334 if (!$target_language_code || !$translation_file_key || !$query_string) {
335 &throw_fatal_error("Missing command argument.");
336 }
337
338 # Get (and check) the translation configuration
339 my ($source_file, $target_file, $translation_file_type)
340 = &get_translation_configuration($target_language_code, $translation_file_key);
341
342 # Parse the source language and target language files
343 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
344 my @source_file_lines = &read_file_lines($source_file_path);
345 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
346
347 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
348 my @target_file_lines = &read_file_lines($target_file_path);
349 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
350
351 # Filter out any automatically translated chunks
352 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
353 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
354 delete $source_file_key_to_line_mapping{$chunk_key};
355 delete $target_file_key_to_line_mapping{$chunk_key};
356 }
357 }
358
359 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
360 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
361 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
362 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
363
364 # Determine the target file chunks matching the query
365 my @target_file_keys_matching_query = ();
366 foreach my $chunk_key (keys(%target_file_key_to_text_mapping)) {
367 my $target_file_text = $target_file_key_to_text_mapping{$chunk_key};
368 if ($target_file_text =~ /$query_string/i) {
369 # &log_message("Chunk with key $chunk_key matches query.");
370 push(@target_file_keys_matching_query, $chunk_key);
371 }
372 }
373
374 # Form an XML response to the command
375 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
376 $xml_response .= "<GTIResponse>\n";
377
378 $xml_response .= " <ChunksMatchingQuery size=\"" . scalar(@target_file_keys_matching_query) . "\">\n";
379 foreach my $chunk_key (@target_file_keys_matching_query) {
380 my $target_file_chunk_text = &make_text_xml_safe($target_file_key_to_text_mapping{$chunk_key});
381
382 $xml_response .= " <Chunk key=\"$chunk_key\">\n";
383 $xml_response .= " <TargetFileText>$target_file_chunk_text</TargetFileText>\n";
384 $xml_response .= " </Chunk>\n";
385 }
386 $xml_response .= " </ChunksMatchingQuery>\n";
387
388 $xml_response .= "</GTIResponse>\n";
389 return $xml_response;
390}
391
392
393sub submit_translations
394{
395 # The code of the target language (ensure it is lowercase)
396 my $target_language_code = lc(shift(@_));
397 # The key of the file to translate (ensure it is lowercase)
398 my $translation_file_key = lc(shift(@_));
399 # Whether to submit a target chunk even if it hasn't changed
400 my $force_submission_flag = shift(@_);
401
402 # Check that the necessary arguments were supplied
403 if (!$target_language_code || !$translation_file_key) {
404 &log_message("Fatal error (but cannot be thrown): Missing command argument.");
405 die "\n";
406 }
407
408 # Get (and check) the translation configuration
409 my ($source_file, $target_file, $translation_file_type)
410 = &get_translation_configuration($target_language_code, $translation_file_key);
411
412 # Parse the source language and target language files
413 my @source_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $source_file));
414 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
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 %source_file_key_to_comment_date_mapping = &build_key_to_comment_date_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
417 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
418
419 my @target_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $target_file));
420 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
421 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
422 my %target_file_key_to_comment_date_mapping = &build_key_to_comment_date_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
423 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
424
425 # Submission date
426 my $day = (localtime)[3];
427 my $month = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")[(localtime)[4]];
428 my $year = (localtime)[5] + 1900;
429 my $submission_date = "$day-$month-$year";
430
431 open(SUBMISSION, "-");
432 my @submission_lines = <SUBMISSION>;
433 close(SUBMISSION);
434
435 # Remove any nasty carriage returns
436 &log_message("Submission:");
437 foreach my $submission_line (@submission_lines) {
438 $submission_line =~ s/\r$//;
439 &log_message(" $submission_line");
440 }
441
442 my %source_file_key_to_submission_mapping = ();
443 my %target_file_key_to_submission_mapping = ();
444 for (my $i = 0; $i < scalar(@submission_lines); $i++) {
445 # Read source file part of submission
446 if ($submission_lines[$i] =~ /^\<SourceFileText key=\"(.+)\"\>/) {
447 my $chunk_key = $1;
448
449 # Read the source file text
450 my $source_file_chunk_text = "";
451 $i++;
452 while ($i < scalar(@submission_lines) && $submission_lines[$i] !~ /^\<\/SourceFileText\>/) {
453 $source_file_chunk_text .= $submission_lines[$i];
454 $i++;
455 }
456 $source_file_chunk_text =~ s/\n$//; # Strip the extra newline character added
457
458 &log_message("Source file key: $chunk_key");
459 &log_message("Source file text: $source_file_chunk_text");
460 $source_file_key_to_submission_mapping{$chunk_key} = $source_file_chunk_text;
461 }
462
463 # Read target file part of submission
464 if ($submission_lines[$i] =~ /^\<TargetFileText key=\"(.+)\"\>/) {
465 my $chunk_key = $1;
466
467 # Read the target file text
468 my $target_file_chunk_text = "";
469 $i++;
470 while ($i < scalar(@submission_lines) && $submission_lines[$i] !~ /^\<\/TargetFileText\>/) {
471 $target_file_chunk_text .= $submission_lines[$i];
472 $i++;
473 }
474 $target_file_chunk_text =~ s/\n$//; # Strip the extra newline character added
475
476 &log_message("Target file key: $chunk_key");
477 &log_message("Target file text: $target_file_chunk_text");
478 $target_file_key_to_submission_mapping{$chunk_key} = $target_file_chunk_text;
479 }
480 }
481
482 # -----------------------------------------
483 # Validate the translation submissions
484 # -----------------------------------------
485
486 # Check that the translations are valid
487 foreach my $chunk_key (keys(%source_file_key_to_submission_mapping)) {
488 # Make sure the submitted chunk still exists in the source file
489 if (!defined($source_file_key_to_text_mapping{$chunk_key})) {
490 &log_message("Warning: Source chunk $chunk_key no longer exists (ignoring submission).");
491 delete $source_file_key_to_submission_mapping{$chunk_key};
492 delete $target_file_key_to_submission_mapping{$chunk_key};
493 next;
494 }
495
496 # Make sure the submitted source chunk matches the source file chunk
497 if ($source_file_key_to_submission_mapping{$chunk_key} ne $source_file_key_to_text_mapping{$chunk_key}) {
498 &log_message("Warning: Source chunk $chunk_key has changed (ignoring submission).");
499 &log_message("Submission source: $source_file_key_to_submission_mapping{$chunk_key}");
500 &log_message(" Source text: $source_file_key_to_text_mapping{$chunk_key}");
501 delete $source_file_key_to_submission_mapping{$chunk_key};
502 delete $target_file_key_to_submission_mapping{$chunk_key};
503 next;
504 }
505 }
506
507 # Apply the submitted translations
508 foreach my $chunk_key (keys(%target_file_key_to_submission_mapping)) {
509 # Only apply the submission if it is a change, unless -force_submission has been specified
510 if ($force_submission_flag || $target_file_key_to_submission_mapping{$chunk_key} ne $target_file_key_to_text_mapping{$chunk_key}) {
511 $target_file_key_to_text_mapping{$chunk_key} = $target_file_key_to_submission_mapping{$chunk_key};
512 $target_file_key_to_comment_date_mapping{$chunk_key} = $submission_date;
513 }
514 }
515
516 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_comment_date_mapping, \$target_language_code)";
517}
518
519
520sub get_translation_configuration
521{
522 # Get the code of the target language
523 my $target_language_code = shift(@_);
524 # Get the key of the file to translate
525 my $translation_file_key = shift(@_);
526
527 # Read the translation data from the gti.cfg file
528 my ($source_file, $target_file, $translation_file_type) =
529 &get_translation_data_for($target_language_code, $translation_file_key);
530
531 # Check that the file to translate is defined in the gti.cfg file
532 if (!$source_file || !$target_file || !$translation_file_type) {
533 &throw_fatal_error("Missing or incomplete specification for translation file \"$translation_file_key\" in gti.pl.");
534 }
535
536 # Check that the source file exists
537 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
538 if (!-e $source_file_path) {
539 &throw_fatal_error("Source file $source_file_path does not exist.");
540 }
541
542 # Check that the source file is up to date
543 # unless ($translation_file_is_not_in_cvs) {
544 my $source_file_cvs_status = `cd $gsdl_root_directory; cvs -d $anonymous_cvs_root update $source_file`;
545 if ($source_file_cvs_status =~ /^C /) {
546 &throw_fatal_error("Source file $source_file_path conflicts with the repository.");
547 }
548 if ($source_file_cvs_status =~ /^M /) {
549 &throw_fatal_error("Source file $source_file_path contains uncommitted changes.");
550 }
551 # }
552
553 return ($source_file, $target_file, $translation_file_type);
554}
555
556
557sub get_translation_data_for
558{
559 my ($target_language_code, $translation_file_key) = @_;
560
561 foreach my $translation_file (@$gti_translation_files) {
562 # If this isn't the correct translation file, move onto the next one
563 next if ($translation_file_key ne $translation_file->{'key'});
564
565 # Resolve the target language file
566 my $target_language_file = $translation_file->{'target_file'};
567 if ($target_language_file =~ /(\{.+\;.+\})/) {
568 my $unresolved_target_language_file_part = $1;
569
570 # Check for a special case for the target language code
571 if ($unresolved_target_language_file_part =~ /(\{|\;)$target_language_code:([^\;]+)(\;|\})/) {
572 my $resolved_target_language_file_part = $2;
573 $target_language_file =~ s/$unresolved_target_language_file_part/$resolved_target_language_file_part/;
574 }
575 # Otherwise use the last part as the default value
576 else {
577 my ($default_target_language_file_part) = $unresolved_target_language_file_part =~ /([^\;]+)\}/;
578 $target_language_file =~ s/$unresolved_target_language_file_part/\{$default_target_language_file_part\}/;
579 }
580 }
581
582 # Resolve instances of {iso_639_1_target_language_name}
583 my $iso_639_1_target_language_name = $iso639::fromiso639{$target_language_code};
584 $iso_639_1_target_language_name =~ tr/A-Z/a-z/ if $iso_639_1_target_language_name;
585 $target_language_file =~ s/\{iso_639_1_target_language_name\}/$iso_639_1_target_language_name/g;
586
587 # Resolve instances of {target_language_code}
588 $target_language_file =~ s/\{target_language_code\}/$target_language_code/g;
589
590 return ($translation_file->{'source_file'}, $target_language_file, $translation_file->{'file_type'});
591 }
592
593 return ();
594}
595
596
597sub read_file_lines
598{
599 my ($file_path) = @_;
600
601 if (!open(FILE_IN, "<$file_path")) {
602 &log_message("Note: Could not open file $file_path.");
603 return ();
604 }
605 my @file_lines = <FILE_IN>;
606 close(FILE_IN);
607
608 return @file_lines;
609}
610
611
612sub build_key_to_line_mapping
613{
614 my ($file_lines, $translation_file_type) = @_;
615 eval "return &build_key_to_line_mapping_for_${translation_file_type}(\@\$file_lines)";
616}
617
618
619sub build_key_to_text_mapping
620{
621 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
622
623 my %key_to_text_mapping = ();
624 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
625 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
626 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
627
628 my $chunk_text = @$file_lines[$chunk_starting_line];
629 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
630 $chunk_text .= @$file_lines[$l];
631 }
632
633 # Map from chunk key to text
634 eval "\$key_to_text_mapping{\${chunk_key}} = &import_chunk_from_${translation_file_type}(\$chunk_text)";
635 }
636
637 return %key_to_text_mapping;
638}
639
640
641sub build_key_to_last_update_date_mapping
642{
643 my ($file, $file_lines, $key_to_line_mapping, $translation_file_type) = @_;
644
645 # If the files aren't in CVS then we can't tell anything about what needs updating
646 # return () if ($translation_file_is_not_in_cvs);
647
648 # Build a mapping from key to CVS date
649 # Need to be careful with this mapping because the chunk keys won't necessarily all be valid
650 my %key_to_cvs_date_mapping = &build_key_to_cvs_date_mapping($file, $translation_file_type);
651
652 # Build a mapping from key to comment date
653 my %key_to_comment_date_mapping = &build_key_to_comment_date_mapping($file_lines, $key_to_line_mapping, $translation_file_type);
654
655 # Build a mapping from key to last update date (the latter of the CVS date and comment date)
656 my %key_to_last_update_date_mapping = ();
657 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
658 # Use the CVS date as a starting point
659 my $chunk_cvs_date = $key_to_cvs_date_mapping{$chunk_key};
660 $key_to_last_update_date_mapping{$chunk_key} = $chunk_cvs_date;
661
662 # If a comment date exists and it is after the CVS date, use that instead
663 my $chunk_comment_date = $key_to_comment_date_mapping{$chunk_key};
664 if (defined($chunk_comment_date) && (!defined($chunk_cvs_date) || &is_date_after($chunk_comment_date, $chunk_cvs_date))) {
665 $key_to_last_update_date_mapping{$chunk_key} = $chunk_comment_date;
666 }
667 }
668
669 return %key_to_last_update_date_mapping;
670}
671
672
673sub build_key_to_cvs_date_mapping
674{
675 my ($filename, $translation_file_type) = @_;
676
677 # Use CVS to annotate each line of the file with the date it was last edited
678 # The "2>/dev/null" is very important! If it is missing this will never return when run from the receptionist
679 my $cvs_annotated_file = `cd $gsdl_root_directory; cvs -d $anonymous_cvs_root annotate -F $filename 2>/dev/null`;
680 my @cvs_annotated_file_lines = split(/\n/, $cvs_annotated_file);
681
682 my @cvs_annotated_file_lines_date = ();
683 foreach my $cvs_annotated_file_line (@cvs_annotated_file_lines) {
684 # Extract the date from the CVS annotation at the front
685 $cvs_annotated_file_line =~ s/^\S+\s+\(\S+\s+(\S+)\):\s//;
686 push(@cvs_annotated_file_lines_date, $1);
687 }
688
689 # Build a key to line mapping for the CVS annotated file, for matching the chunk key to the CVS date
690 my %key_to_line_mapping = &build_key_to_line_mapping(\@cvs_annotated_file_lines, $translation_file_type);
691
692 my %key_to_cvs_date_mapping = ();
693 foreach my $chunk_key (keys(%key_to_line_mapping)) {
694 my $chunk_starting_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[0];
695 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[1];
696
697 # Find the date this chunk was last edited, from the CVS annotation
698 my $chunk_date = $cvs_annotated_file_lines_date[$chunk_starting_line];
699 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
700 if (&is_date_after($cvs_annotated_file_lines_date[$l], $chunk_date)) {
701 # This part of the chunk has been updated more recently
702 $chunk_date = $cvs_annotated_file_lines_date[$l];
703 }
704 }
705
706 # Map from chunk key to CVS date
707 $key_to_cvs_date_mapping{$chunk_key} = $chunk_date;
708 }
709
710 return %key_to_cvs_date_mapping;
711}
712
713
714sub build_key_to_comment_date_mapping
715{
716 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
717
718 my %key_to_comment_date_mapping = ();
719 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
720 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
721 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
722
723 my $chunk_text = @$file_lines[$chunk_starting_line];
724 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
725 $chunk_text .= @$file_lines[$l];
726 }
727
728 # Map from chunk key to comment date
729 my $chunk_comment_date;
730 eval "\$chunk_comment_date = &get_${translation_file_type}_chunk_comment_date(\$chunk_text)";
731 $key_to_comment_date_mapping{$chunk_key} = $chunk_comment_date if (defined($chunk_comment_date));
732 }
733
734 return %key_to_comment_date_mapping;
735}
736
737
738sub determine_chunks_requiring_translation
739{
740 my $source_file_key_to_text_mapping = shift(@_);
741 my $target_file_key_to_text_mapping = shift(@_);
742
743 # Chunks needing translation are those in the source file with no translation in the target file
744 my @target_file_keys_requiring_translation = ();
745 foreach my $chunk_key (keys(%$source_file_key_to_text_mapping)) {
746 if ($source_file_key_to_text_mapping->{$chunk_key} && !$target_file_key_to_text_mapping->{$chunk_key}) {
747 # &log_message("Chunk with key $chunk_key needs translating.");
748 push(@target_file_keys_requiring_translation, $chunk_key);
749 }
750 }
751
752 return @target_file_keys_requiring_translation;
753}
754
755
756sub determine_chunks_requiring_updating
757{
758 my $source_file_key_to_last_update_date_mapping = shift(@_);
759 my $target_file_key_to_last_update_date_mapping = shift(@_);
760
761 # Chunks needing updating are those in the target file that have been more recently edited in the source file
762 my @target_file_keys_requiring_updating = ();
763 foreach my $chunk_key (keys(%$source_file_key_to_last_update_date_mapping)) {
764 my $source_chunk_last_update_date = $source_file_key_to_last_update_date_mapping->{$chunk_key};
765 my $target_chunk_last_update_date = $target_file_key_to_last_update_date_mapping->{$chunk_key};
766 if (defined($target_chunk_last_update_date) && &is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date)) {
767 # &log_message("Chunk with key $chunk_key needs updating.");
768 push(@target_file_keys_requiring_updating, $chunk_key);
769 }
770 }
771
772 return @target_file_keys_requiring_updating;
773}
774
775
776sub is_chunk_automatically_translated
777{
778 my ($chunk_key, $translation_file_type) = @_;
779 eval "return &is_${translation_file_type}_chunk_automatically_translated(\$chunk_key)";
780}
781
782
783sub make_text_xml_safe
784{
785 my $text = shift(@_);
786 $text =~ s/\&/\&amp\;/g;
787 $text =~ s/</\&lt\;/g;
788 $text =~ s/>/\&gt\;/g;
789 return $text;
790}
791
792
793# Returns 1 if $date1 is after $date2, 0 otherwise
794sub is_date_after
795{
796 my ($date1, $date2) = @_;
797 my %months = ("Jan", 1, "Feb", 2, "Mar", 3, "Apr", 4, "May", 5, "Jun", 6,
798 "Jul", 7, "Aug", 8, "Sep", 9, "Oct", 10, "Nov", 11, "Dec", 12);
799
800 my @date1parts = split(/-/, $date1);
801 my @date2parts = split(/-/, $date2);
802
803 # Compare year - nasty because we have rolled over into a new century
804 my $year1 = $date1parts[2];
805 if ($year1 < 80) {
806 $year1 += 100;
807 }
808 my $year2 = $date2parts[2];
809 if ($year2 < 80) {
810 $year2 += 100;
811 }
812
813 # Compare year
814 if ($year1 > $year2) {
815 return 1;
816 }
817 elsif ($year1 == $year2) {
818 # Year is the same, so compare month
819 if ($months{$date1parts[1]} > $months{$date2parts[1]}) {
820 return 1;
821 }
822 elsif ($months{$date1parts[1]} == $months{$date2parts[1]}) {
823 # Month is the same, so compare day
824 if ($date1parts[0] > $date2parts[0]) {
825 return 1;
826 }
827 }
828 }
829
830 return 0;
831}
832
833
834# ==========================================================================================
835# MACROFILE FUNCTIONS
836
837sub build_key_to_line_mapping_for_macrofile
838{
839 my (@file_lines) = @_;
840
841 my $macro_package;
842 my %chunk_key_to_line_mapping = ();
843 # Process the contents of the file, line by line
844 for (my $i = 0; $i < scalar(@file_lines); $i++) {
845 my $line = $file_lines[$i];
846 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
847
848 # Check if a new package is being defined
849 if ($line =~ m/^package\s+(.+)/) {
850 $macro_package = $1;
851 }
852
853 # Line contains a macro name
854 elsif ($line =~ m/^(_\w+_)/) {
855 my $macro_key = $1;
856 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
857
858 # While there is still text of the macro to go...
859 my $startline = $i;
860 while ($line !~ /\}$/) {
861 $i++;
862 if ($i == scalar(@file_lines)) {
863 &throw_fatal_error("Could not find end of macro $macro_key.");
864 }
865 $line = $file_lines[$i];
866 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
867 }
868
869 # The chunk key consists of the package name and the macro key
870 my $chunk_key = $macro_package . "." . $macro_key;
871 # Map from chunk key to line
872 $chunk_key_to_line_mapping{$chunk_key} = $startline . "-" . $i;
873 }
874
875 # Icon: line in format ## "image text" ## image_type ## macro_name ##
876 elsif ($line =~ m/^\#\# .* \#\# .* \#\# (.*) \#\#/) {
877 # The chunk key consists of package name and macro key
878 my $chunk_key = $macro_package . "." . $1;
879 # Map from chunk key to line
880 $chunk_key_to_line_mapping{$chunk_key} = $i . "-" . $i;
881 }
882 }
883
884 return %chunk_key_to_line_mapping;
885}
886
887
888sub import_chunk_from_macrofile
889{
890 my ($chunk_text) = @_;
891
892 # Is this an icon macro??
893 if ($chunk_text =~ /^\#\# (.*)/) {
894 # Extract image macro text
895 $chunk_text =~ /^\#\#\s+([^\#]+)\s+\#\#/;
896 $chunk_text = $1;
897
898 # Remove enclosing quotes
899 $chunk_text =~ s/^\"//;
900 $chunk_text =~ s/\"$//;
901 }
902
903 # No, so it must be a text macro
904 else {
905 # Remove macro key
906 $chunk_text =~ s/^_([^_]+)_(\s*)//;
907
908 # Remove language specifier
909 $chunk_text =~ s/^\[l=.*\](\s*)//;
910
911 # Remove braces enclosing text
912 $chunk_text =~ s/^{(\s*)((.|\n)*)}(\s*)(\#.+\s*)?/$2/;
913 }
914
915 return $chunk_text;
916}
917
918
919sub get_macrofile_chunk_comment_date
920{
921 my ($chunk_text) = @_;
922
923 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
924 if ($chunk_text =~ /\#\s+Updated\s+(\d?\d-\D\D\D-\d\d\d\d)\s*$/i) {
925 return $1;
926 }
927
928 return undef;
929}
930
931
932sub is_macrofile_chunk_automatically_translated
933{
934 my ($chunk_key) = @_;
935
936 # The _httpiconX_, _widthX_ and _heightX_ image macros are automatically translated
937 if ($chunk_key =~ /\._(httpicon|width|height)/) {
938 return 1;
939 }
940
941 return 0;
942}
943
944
945# Use the source file to generate a target file that is formatted the same
946sub write_translated_macrofile
947{
948 my $source_file = shift(@_); # Not used
949 my $source_file_lines = shift(@_);
950 my $source_file_key_to_text_mapping = shift(@_);
951 my $target_file = shift(@_);
952 my $target_file_lines = shift(@_); # Not used
953 my $target_file_key_to_text_mapping = shift(@_);
954 my $target_file_key_to_comment_date_mapping = shift(@_);
955 my $target_language_code = shift(@_);
956
957 # Build a mapping from source file line to chunk key
958 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@$source_file_lines);
959 my %source_file_line_to_key_mapping = ();
960 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
961 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
962 }
963 my @source_file_line_keys = (sort sort_by_line (keys(%source_file_line_to_key_mapping)));
964 my $source_file_line_number = 0;
965
966 # Build a mapping from target file line to chunk key
967 my %target_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@$target_file_lines);
968 my %target_file_line_to_key_mapping = ();
969 foreach my $chunk_key (keys(%target_file_key_to_line_mapping)) {
970 $target_file_line_to_key_mapping{$target_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
971 }
972 my @target_file_line_keys = (sort sort_by_line (keys(%target_file_line_to_key_mapping)));
973
974 # Write the new target file
975 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
976 if (!open(TARGET_FILE, ">$target_file_path")) {
977 &throw_fatal_error("Could not write target file $target_file_path.");
978 }
979
980 # Use the header from the target file, to keep language and author information
981 if (scalar(@target_file_line_keys) > 0) {
982 my $target_file_line_number = 0;
983 my $target_file_chunk_starting_line_number = (split(/-/, $target_file_line_keys[0]))[0];
984 while ($target_file_line_number < $target_file_chunk_starting_line_number) {
985 my $target_file_line = @$target_file_lines[$target_file_line_number];
986 last if ($target_file_line =~ /^\# -- Missing translation: /); # We don't want to get into the macros
987 print TARGET_FILE $target_file_line;
988 $target_file_line_number++;
989 }
990
991 $source_file_line_number = (split(/-/, $source_file_line_keys[0]))[0];
992 }
993
994 # Model the new target file on the source file, with the target file translations
995 foreach my $line_key (@source_file_line_keys) {
996 # Fill in the gaps before this chunk starts
997 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
998 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
999 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1000 print TARGET_FILE @$source_file_lines[$source_file_line_number];
1001 $source_file_line_number++;
1002 }
1003 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1004
1005 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1006 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1007 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1008
1009 my $macrofile_key = $chunk_key;
1010 $macrofile_key =~ s/^(.+?)\.//;
1011
1012 # If no translation exists for this chunk, show this, and move on
1013 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1014 print TARGET_FILE "# -- Missing translation: $macrofile_key\n";
1015 next;
1016 }
1017
1018 # Grab the source chunk text
1019 my $source_file_chunk = @$source_file_lines[$source_file_chunk_starting_line_number];
1020 for (my $l = ($source_file_chunk_starting_line_number + 1); $l <= $source_file_chunk_finishing_line_number; $l++) {
1021 $source_file_chunk .= @$source_file_lines[$l];
1022 }
1023
1024 # Is this an icon macro??
1025 if ($source_file_chunk =~ /^\#\# (.*)/) {
1026 # Escape any newline and question mark characters so the source text is replaced correctly
1027 $source_file_chunk_text =~ s/\\/\\\\/g;
1028 $source_file_chunk_text =~ s/\?/\\\?/g;
1029
1030 # Build the new target chunk from the source chunk
1031 my $target_file_chunk = $source_file_chunk;
1032 $target_file_chunk =~ s/$source_file_chunk_text/$target_file_chunk_text/;
1033 $target_file_chunk =~ s/(\s)*$//;
1034 print TARGET_FILE "$target_file_chunk";
1035 }
1036
1037 # No, it is just a normal text macro
1038 else {
1039 print TARGET_FILE "$macrofile_key [l=$target_language_code] {$target_file_chunk_text}";
1040 }
1041
1042 # Add the update date, if one exists
1043 if ($target_file_key_to_comment_date_mapping->{$chunk_key}) {
1044 print TARGET_FILE " # Updated " . $target_file_key_to_comment_date_mapping->{$chunk_key};
1045 }
1046 print TARGET_FILE "\n";
1047 }
1048
1049 close(TARGET_FILE);
1050}
1051
1052
1053sub sort_by_line
1054{
1055 return ((split(/-/, $a))[0] <=> (split(/-/, $b))[0]);
1056}
1057
1058
1059# ==========================================================================================
1060# RESOURCE BUNDLE FUNCTIONS
1061
1062sub build_key_to_line_mapping_for_resource_bundle
1063{
1064 my (@file_lines) = @_;
1065
1066 my %key_to_line_mapping = ();
1067 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1068 my $line = $file_lines[$i];
1069 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1070
1071 # Line contains a dictionary string
1072 if ($line =~ /^(\S+?):(.*)$/) {
1073 my $chunk_key = $1;
1074
1075 # Map from chunk key to line
1076 $key_to_line_mapping{$chunk_key} = $i . "-" . $i;
1077 }
1078 }
1079
1080 return %key_to_line_mapping;
1081}
1082
1083
1084sub import_chunk_from_resource_bundle
1085{
1086 my ($chunk_text) = @_;
1087
1088 # Simple: just remove string key
1089 $chunk_text =~ s/^(\S+?)://;
1090 $chunk_text =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1091
1092 return $chunk_text;
1093}
1094
1095
1096sub get_resource_bundle_chunk_comment_date
1097{
1098 my ($chunk_text) = @_;
1099
1100 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1101 if ($chunk_text =~ /\#\s+Updated\s+(\d?\d-\D\D\D-\d\d\d\d)\s*$/i) {
1102 return $1;
1103 }
1104
1105 return undef;
1106}
1107
1108
1109sub is_resource_bundle_chunk_automatically_translated
1110{
1111 # No resource bundle chunks are automatically translated
1112 return 0;
1113}
1114
1115
1116sub write_translated_resource_bundle
1117{
1118 my $source_file = shift(@_); # Not used
1119 my $source_file_lines = shift(@_);
1120 my $source_file_key_to_text_mapping = shift(@_);
1121 my $target_file = shift(@_);
1122 my $target_file_lines = shift(@_); # Not used
1123 my $target_file_key_to_text_mapping = shift(@_);
1124 my $target_file_key_to_comment_date_mapping = shift(@_);
1125 my $target_language_code = shift(@_); # Not used
1126
1127 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1128 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_resource_bundle(@$source_file_lines);
1129 my %source_file_line_to_key_mapping = ();
1130 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1131 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1132 }
1133
1134 # Write the new target file
1135 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1136 if (!open(TARGET_FILE, ">$target_file_path")) {
1137 &throw_fatal_error("Could not write target file $target_file_path.");
1138 }
1139
1140 # Model the new target file on the source file, with the target file translations
1141 my $source_file_line_number = 0;
1142 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1143 # Fill in the gaps before this chunk starts
1144 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1145 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1146 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1147 print TARGET_FILE @$source_file_lines[$source_file_line_number];
1148 $source_file_line_number++;
1149 }
1150 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1151
1152 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1153 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1154 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1155
1156 # If no translation exists for this chunk, show this, and move on
1157 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1158 print TARGET_FILE "# -- Missing translation: $chunk_key\n";
1159 next;
1160 }
1161
1162 print TARGET_FILE "$chunk_key:$target_file_chunk_text";
1163 if ($target_file_key_to_comment_date_mapping->{$chunk_key}) {
1164 print TARGET_FILE " # Updated " . $target_file_key_to_comment_date_mapping->{$chunk_key};
1165 }
1166 print TARGET_FILE "\n";
1167 }
1168
1169 close(TARGET_FILE);
1170}
1171
1172
1173&main(@ARGV);
Note: See TracBrowser for help on using the repository browser.