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

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

Added rule for gaelic macrofiles.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 45.7 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;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" },
52 { 'key' => "auxdm",
53 'file_type' => "macrofile",
54 'source_file' => "macros/english2.dm",
55 '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" },
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 # The "2>/dev/null" is very important! If it is missing this will never return when run from the receptionist
544 # unless ($translation_file_is_not_in_cvs) {
545 my $source_file_cvs_status = `cd $gsdl_root_directory; cvs -d $anonymous_cvs_root update $source_file 2>/dev/null`;
546 if ($source_file_cvs_status =~ /^C /) {
547 &throw_fatal_error("Source file $source_file_path conflicts with the repository.");
548 }
549 if ($source_file_cvs_status =~ /^M /) {
550 &throw_fatal_error("Source file $source_file_path contains uncommitted changes.");
551 }
552 # }
553
554 return ($source_file, $target_file, $translation_file_type);
555}
556
557
558sub get_translation_data_for
559{
560 my ($target_language_code, $translation_file_key) = @_;
561
562 foreach my $translation_file (@$gti_translation_files) {
563 # If this isn't the correct translation file, move onto the next one
564 next if ($translation_file_key ne $translation_file->{'key'});
565
566 # Resolve the target language file
567 my $target_language_file = $translation_file->{'target_file'};
568 if ($target_language_file =~ /(\{.+\;.+\})/) {
569 my $unresolved_target_language_file_part = $1;
570
571 # Check for a special case for the target language code
572 if ($unresolved_target_language_file_part =~ /(\{|\;)$target_language_code:([^\;]+)(\;|\})/) {
573 my $resolved_target_language_file_part = $2;
574 $target_language_file =~ s/$unresolved_target_language_file_part/$resolved_target_language_file_part/;
575 }
576 # Otherwise use the last part as the default value
577 else {
578 my ($default_target_language_file_part) = $unresolved_target_language_file_part =~ /([^\;]+)\}/;
579 $target_language_file =~ s/$unresolved_target_language_file_part/\{$default_target_language_file_part\}/;
580 }
581 }
582
583 # Resolve instances of {iso_639_1_target_language_name}
584 my $iso_639_1_target_language_name = $iso639::fromiso639{$target_language_code};
585 $iso_639_1_target_language_name =~ tr/A-Z/a-z/ if $iso_639_1_target_language_name;
586 $target_language_file =~ s/\{iso_639_1_target_language_name\}/$iso_639_1_target_language_name/g;
587
588 # Resolve instances of {target_language_code}
589 $target_language_file =~ s/\{target_language_code\}/$target_language_code/g;
590
591 return ($translation_file->{'source_file'}, $target_language_file, $translation_file->{'file_type'});
592 }
593
594 return ();
595}
596
597
598sub read_file_lines
599{
600 my ($file_path) = @_;
601
602 if (!open(FILE_IN, "<$file_path")) {
603 &log_message("Note: Could not open file $file_path.");
604 return ();
605 }
606 my @file_lines = <FILE_IN>;
607 close(FILE_IN);
608
609 return @file_lines;
610}
611
612
613sub build_key_to_line_mapping
614{
615 my ($file_lines, $translation_file_type) = @_;
616 eval "return &build_key_to_line_mapping_for_${translation_file_type}(\@\$file_lines)";
617}
618
619
620sub build_key_to_text_mapping
621{
622 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
623
624 my %key_to_text_mapping = ();
625 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
626 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
627 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
628
629 my $chunk_text = @$file_lines[$chunk_starting_line];
630 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
631 $chunk_text .= @$file_lines[$l];
632 }
633
634 # Map from chunk key to text
635 eval "\$key_to_text_mapping{\${chunk_key}} = &import_chunk_from_${translation_file_type}(\$chunk_text)";
636 }
637
638 return %key_to_text_mapping;
639}
640
641
642sub build_key_to_last_update_date_mapping
643{
644 my ($file, $file_lines, $key_to_line_mapping, $translation_file_type) = @_;
645
646 # If the files aren't in CVS then we can't tell anything about what needs updating
647 # return () if ($translation_file_is_not_in_cvs);
648
649 # Build a mapping from key to CVS date
650 # Need to be careful with this mapping because the chunk keys won't necessarily all be valid
651 my %key_to_cvs_date_mapping = &build_key_to_cvs_date_mapping($file, $translation_file_type);
652
653 # Build a mapping from key to comment date
654 my %key_to_comment_date_mapping = &build_key_to_comment_date_mapping($file_lines, $key_to_line_mapping, $translation_file_type);
655
656 # Build a mapping from key to last update date (the latter of the CVS date and comment date)
657 my %key_to_last_update_date_mapping = ();
658 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
659 # Use the CVS date as a starting point
660 my $chunk_cvs_date = $key_to_cvs_date_mapping{$chunk_key};
661 $key_to_last_update_date_mapping{$chunk_key} = $chunk_cvs_date;
662
663 # If a comment date exists and it is after the CVS date, use that instead
664 my $chunk_comment_date = $key_to_comment_date_mapping{$chunk_key};
665 if (defined($chunk_comment_date) && (!defined($chunk_cvs_date) || &is_date_after($chunk_comment_date, $chunk_cvs_date))) {
666 $key_to_last_update_date_mapping{$chunk_key} = $chunk_comment_date;
667 }
668 }
669
670 return %key_to_last_update_date_mapping;
671}
672
673
674sub build_key_to_cvs_date_mapping
675{
676 my ($filename, $translation_file_type) = @_;
677
678 # Use CVS to annotate each line of the file with the date it was last edited
679 # The "2>/dev/null" is very important! If it is missing this will never return when run from the receptionist
680 my $cvs_annotated_file = `cd $gsdl_root_directory; cvs -d $anonymous_cvs_root annotate -F $filename 2>/dev/null`;
681 my @cvs_annotated_file_lines = split(/\n/, $cvs_annotated_file);
682
683 my @cvs_annotated_file_lines_date = ();
684 foreach my $cvs_annotated_file_line (@cvs_annotated_file_lines) {
685 # Extract the date from the CVS annotation at the front
686 $cvs_annotated_file_line =~ s/^\S+\s+\(\S+\s+(\S+)\):\s//;
687 push(@cvs_annotated_file_lines_date, $1);
688 }
689
690 # Build a key to line mapping for the CVS annotated file, for matching the chunk key to the CVS date
691 my %key_to_line_mapping = &build_key_to_line_mapping(\@cvs_annotated_file_lines, $translation_file_type);
692
693 my %key_to_cvs_date_mapping = ();
694 foreach my $chunk_key (keys(%key_to_line_mapping)) {
695 my $chunk_starting_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[0];
696 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[1];
697
698 # Find the date this chunk was last edited, from the CVS annotation
699 my $chunk_date = $cvs_annotated_file_lines_date[$chunk_starting_line];
700 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
701 if (&is_date_after($cvs_annotated_file_lines_date[$l], $chunk_date)) {
702 # This part of the chunk has been updated more recently
703 $chunk_date = $cvs_annotated_file_lines_date[$l];
704 }
705 }
706
707 # Map from chunk key to CVS date
708 $key_to_cvs_date_mapping{$chunk_key} = $chunk_date;
709 }
710
711 return %key_to_cvs_date_mapping;
712}
713
714
715sub build_key_to_comment_date_mapping
716{
717 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
718
719 my %key_to_comment_date_mapping = ();
720 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
721 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
722 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
723
724 my $chunk_text = @$file_lines[$chunk_starting_line];
725 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
726 $chunk_text .= @$file_lines[$l];
727 }
728
729 # Map from chunk key to comment date
730 my $chunk_comment_date;
731 eval "\$chunk_comment_date = &get_${translation_file_type}_chunk_comment_date(\$chunk_text)";
732 $key_to_comment_date_mapping{$chunk_key} = $chunk_comment_date if (defined($chunk_comment_date));
733 }
734
735 return %key_to_comment_date_mapping;
736}
737
738
739sub determine_chunks_requiring_translation
740{
741 my $source_file_key_to_text_mapping = shift(@_);
742 my $target_file_key_to_text_mapping = shift(@_);
743
744 # Chunks needing translation are those in the source file with no translation in the target file
745 my @target_file_keys_requiring_translation = ();
746 foreach my $chunk_key (keys(%$source_file_key_to_text_mapping)) {
747 if ($source_file_key_to_text_mapping->{$chunk_key} && !$target_file_key_to_text_mapping->{$chunk_key}) {
748 # &log_message("Chunk with key $chunk_key needs translating.");
749 push(@target_file_keys_requiring_translation, $chunk_key);
750 }
751 }
752
753 return @target_file_keys_requiring_translation;
754}
755
756
757sub determine_chunks_requiring_updating
758{
759 my $source_file_key_to_last_update_date_mapping = shift(@_);
760 my $target_file_key_to_last_update_date_mapping = shift(@_);
761
762 # Chunks needing updating are those in the target file that have been more recently edited in the source file
763 my @target_file_keys_requiring_updating = ();
764 foreach my $chunk_key (keys(%$source_file_key_to_last_update_date_mapping)) {
765 my $source_chunk_last_update_date = $source_file_key_to_last_update_date_mapping->{$chunk_key};
766 my $target_chunk_last_update_date = $target_file_key_to_last_update_date_mapping->{$chunk_key};
767 if (defined($target_chunk_last_update_date) && &is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date)) {
768 # &log_message("Chunk with key $chunk_key needs updating.");
769 push(@target_file_keys_requiring_updating, $chunk_key);
770 }
771 }
772
773 return @target_file_keys_requiring_updating;
774}
775
776
777sub is_chunk_automatically_translated
778{
779 my ($chunk_key, $translation_file_type) = @_;
780 eval "return &is_${translation_file_type}_chunk_automatically_translated(\$chunk_key)";
781}
782
783
784sub make_text_xml_safe
785{
786 my $text = shift(@_);
787 $text =~ s/\&/\&amp\;/g;
788 $text =~ s/</\&lt\;/g;
789 $text =~ s/>/\&gt\;/g;
790 return $text;
791}
792
793
794# Returns 1 if $date1 is after $date2, 0 otherwise
795sub is_date_after
796{
797 my ($date1, $date2) = @_;
798 my %months = ("Jan", 1, "Feb", 2, "Mar", 3, "Apr", 4, "May", 5, "Jun", 6,
799 "Jul", 7, "Aug", 8, "Sep", 9, "Oct", 10, "Nov", 11, "Dec", 12);
800
801 my @date1parts = split(/-/, $date1);
802 my @date2parts = split(/-/, $date2);
803
804 # Compare year - nasty because we have rolled over into a new century
805 my $year1 = $date1parts[2];
806 if ($year1 < 80) {
807 $year1 += 100;
808 }
809 my $year2 = $date2parts[2];
810 if ($year2 < 80) {
811 $year2 += 100;
812 }
813
814 # Compare year
815 if ($year1 > $year2) {
816 return 1;
817 }
818 elsif ($year1 == $year2) {
819 # Year is the same, so compare month
820 if ($months{$date1parts[1]} > $months{$date2parts[1]}) {
821 return 1;
822 }
823 elsif ($months{$date1parts[1]} == $months{$date2parts[1]}) {
824 # Month is the same, so compare day
825 if ($date1parts[0] > $date2parts[0]) {
826 return 1;
827 }
828 }
829 }
830
831 return 0;
832}
833
834
835# ==========================================================================================
836# MACROFILE FUNCTIONS
837
838sub build_key_to_line_mapping_for_macrofile
839{
840 my (@file_lines) = @_;
841
842 my $macro_package;
843 my %chunk_key_to_line_mapping = ();
844 # Process the contents of the file, line by line
845 for (my $i = 0; $i < scalar(@file_lines); $i++) {
846 my $line = $file_lines[$i];
847 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
848
849 # Check if a new package is being defined
850 if ($line =~ m/^package\s+(.+)/) {
851 $macro_package = $1;
852 }
853
854 # Line contains a macro name
855 elsif ($line =~ m/^(_\w+_)/) {
856 my $macro_key = $1;
857 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
858
859 # While there is still text of the macro to go...
860 my $startline = $i;
861 while ($line !~ /\}$/) {
862 $i++;
863 if ($i == scalar(@file_lines)) {
864 &throw_fatal_error("Could not find end of macro $macro_key.");
865 }
866 $line = $file_lines[$i];
867 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
868 }
869
870 # The chunk key consists of the package name and the macro key
871 my $chunk_key = $macro_package . "." . $macro_key;
872 # Map from chunk key to line
873 $chunk_key_to_line_mapping{$chunk_key} = $startline . "-" . $i;
874 }
875
876 # Icon: line in format ## "image text" ## image_type ## macro_name ##
877 elsif ($line =~ m/^\#\# .* \#\# .* \#\# (.*) \#\#/) {
878 # The chunk key consists of package name and macro key
879 my $chunk_key = $macro_package . "." . $1;
880 # Map from chunk key to line
881 $chunk_key_to_line_mapping{$chunk_key} = $i . "-" . $i;
882 }
883 }
884
885 return %chunk_key_to_line_mapping;
886}
887
888
889sub import_chunk_from_macrofile
890{
891 my ($chunk_text) = @_;
892
893 # Is this an icon macro??
894 if ($chunk_text =~ /^\#\# (.*)/) {
895 # Extract image macro text
896 $chunk_text =~ /^\#\#\s+([^\#]+)\s+\#\#/;
897 $chunk_text = $1;
898
899 # Remove enclosing quotes
900 $chunk_text =~ s/^\"//;
901 $chunk_text =~ s/\"$//;
902 }
903
904 # No, so it must be a text macro
905 else {
906 # Remove macro key
907 $chunk_text =~ s/^_([^_]+)_(\s*)//;
908
909 # Remove language specifier
910 $chunk_text =~ s/^\[l=.*\](\s*)//;
911
912 # Remove braces enclosing text
913 $chunk_text =~ s/^{(\s*)((.|\n)*)}(\s*)(\#.+\s*)?/$2/;
914 }
915
916 return $chunk_text;
917}
918
919
920sub get_macrofile_chunk_comment_date
921{
922 my ($chunk_text) = @_;
923
924 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
925 if ($chunk_text =~ /\#\s+Updated\s+(\d?\d-\D\D\D-\d\d\d\d)\s*$/i) {
926 return $1;
927 }
928
929 return undef;
930}
931
932
933sub is_macrofile_chunk_automatically_translated
934{
935 my ($chunk_key) = @_;
936
937 # The _httpiconX_, _widthX_ and _heightX_ image macros are automatically translated
938 if ($chunk_key =~ /\._(httpicon|width|height)/) {
939 return 1;
940 }
941
942 return 0;
943}
944
945
946# Use the source file to generate a target file that is formatted the same
947sub write_translated_macrofile
948{
949 my $source_file = shift(@_); # Not used
950 my $source_file_lines = shift(@_);
951 my $source_file_key_to_text_mapping = shift(@_);
952 my $target_file = shift(@_);
953 my $target_file_lines = shift(@_); # Not used
954 my $target_file_key_to_text_mapping = shift(@_);
955 my $target_file_key_to_comment_date_mapping = shift(@_);
956 my $target_language_code = shift(@_);
957
958 # Build a mapping from source file line to chunk key
959 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@$source_file_lines);
960 my %source_file_line_to_key_mapping = ();
961 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
962 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
963 }
964 my @source_file_line_keys = (sort sort_by_line (keys(%source_file_line_to_key_mapping)));
965 my $source_file_line_number = 0;
966
967 # Build a mapping from target file line to chunk key
968 my %target_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@$target_file_lines);
969 my %target_file_line_to_key_mapping = ();
970 foreach my $chunk_key (keys(%target_file_key_to_line_mapping)) {
971 $target_file_line_to_key_mapping{$target_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
972 }
973 my @target_file_line_keys = (sort sort_by_line (keys(%target_file_line_to_key_mapping)));
974
975 # Write the new target file
976 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
977 if (!open(TARGET_FILE, ">$target_file_path")) {
978 &throw_fatal_error("Could not write target file $target_file_path.");
979 }
980
981 # Use the header from the target file, to keep language and author information
982 if (scalar(@target_file_line_keys) > 0) {
983 my $target_file_line_number = 0;
984 my $target_file_chunk_starting_line_number = (split(/-/, $target_file_line_keys[0]))[0];
985 while ($target_file_line_number < $target_file_chunk_starting_line_number) {
986 my $target_file_line = @$target_file_lines[$target_file_line_number];
987 last if ($target_file_line =~ /^\# -- Missing translation: /); # We don't want to get into the macros
988 print TARGET_FILE $target_file_line;
989 $target_file_line_number++;
990 }
991
992 $source_file_line_number = (split(/-/, $source_file_line_keys[0]))[0];
993 }
994
995 # Model the new target file on the source file, with the target file translations
996 foreach my $line_key (@source_file_line_keys) {
997 # Fill in the gaps before this chunk starts
998 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
999 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1000 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1001 print TARGET_FILE @$source_file_lines[$source_file_line_number];
1002 $source_file_line_number++;
1003 }
1004 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1005
1006 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1007 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1008 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1009
1010 my $macrofile_key = $chunk_key;
1011 $macrofile_key =~ s/^(.+?)\.//;
1012
1013 # If no translation exists for this chunk, show this, and move on
1014 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1015 print TARGET_FILE "# -- Missing translation: $macrofile_key\n";
1016 next;
1017 }
1018
1019 # Grab the source chunk text
1020 my $source_file_chunk = @$source_file_lines[$source_file_chunk_starting_line_number];
1021 for (my $l = ($source_file_chunk_starting_line_number + 1); $l <= $source_file_chunk_finishing_line_number; $l++) {
1022 $source_file_chunk .= @$source_file_lines[$l];
1023 }
1024
1025 # Is this an icon macro??
1026 if ($source_file_chunk =~ /^\#\# (.*)/) {
1027 # Escape any newline and question mark characters so the source text is replaced correctly
1028 $source_file_chunk_text =~ s/\\/\\\\/g;
1029 $source_file_chunk_text =~ s/\?/\\\?/g;
1030
1031 # Build the new target chunk from the source chunk
1032 my $target_file_chunk = $source_file_chunk;
1033 $target_file_chunk =~ s/$source_file_chunk_text/$target_file_chunk_text/;
1034 $target_file_chunk =~ s/(\s)*$//;
1035 print TARGET_FILE "$target_file_chunk";
1036 }
1037
1038 # No, it is just a normal text macro
1039 else {
1040 print TARGET_FILE "$macrofile_key [l=$target_language_code] {$target_file_chunk_text}";
1041 }
1042
1043 # Add the update date, if one exists
1044 if ($target_file_key_to_comment_date_mapping->{$chunk_key}) {
1045 print TARGET_FILE " # Updated " . $target_file_key_to_comment_date_mapping->{$chunk_key};
1046 }
1047 print TARGET_FILE "\n";
1048 }
1049
1050 close(TARGET_FILE);
1051}
1052
1053
1054sub sort_by_line
1055{
1056 return ((split(/-/, $a))[0] <=> (split(/-/, $b))[0]);
1057}
1058
1059
1060# ==========================================================================================
1061# RESOURCE BUNDLE FUNCTIONS
1062
1063sub build_key_to_line_mapping_for_resource_bundle
1064{
1065 my (@file_lines) = @_;
1066
1067 my %key_to_line_mapping = ();
1068 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1069 my $line = $file_lines[$i];
1070 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1071
1072 # Line contains a dictionary string
1073 if ($line =~ /^(\S+?):(.*)$/) {
1074 my $chunk_key = $1;
1075
1076 # Map from chunk key to line
1077 $key_to_line_mapping{$chunk_key} = $i . "-" . $i;
1078 }
1079 }
1080
1081 return %key_to_line_mapping;
1082}
1083
1084
1085sub import_chunk_from_resource_bundle
1086{
1087 my ($chunk_text) = @_;
1088
1089 # Simple: just remove string key
1090 $chunk_text =~ s/^(\S+?):(\s*)//;
1091 $chunk_text =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1092 $chunk_text =~ s/(\s*)\#\s+Updated\s+(\d?\d-\D\D\D-\d\d\d\d)\s*$//i;
1093
1094 return $chunk_text;
1095}
1096
1097
1098sub get_resource_bundle_chunk_comment_date
1099{
1100 my ($chunk_text) = @_;
1101
1102 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1103 if ($chunk_text =~ /\#\s+Updated\s+(\d?\d-\D\D\D-\d\d\d\d)\s*$/i) {
1104 return $1;
1105 }
1106
1107 return undef;
1108}
1109
1110
1111sub is_resource_bundle_chunk_automatically_translated
1112{
1113 # No resource bundle chunks are automatically translated
1114 return 0;
1115}
1116
1117
1118sub write_translated_resource_bundle
1119{
1120 my $source_file = shift(@_); # Not used
1121 my $source_file_lines = shift(@_);
1122 my $source_file_key_to_text_mapping = shift(@_);
1123 my $target_file = shift(@_);
1124 my $target_file_lines = shift(@_); # Not used
1125 my $target_file_key_to_text_mapping = shift(@_);
1126 my $target_file_key_to_comment_date_mapping = shift(@_);
1127 my $target_language_code = shift(@_); # Not used
1128
1129 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1130 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_resource_bundle(@$source_file_lines);
1131 my %source_file_line_to_key_mapping = ();
1132 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1133 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1134 }
1135
1136 # Write the new target file
1137 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1138 if (!open(TARGET_FILE, ">$target_file_path")) {
1139 &throw_fatal_error("Could not write target file $target_file_path.");
1140 }
1141
1142 # Model the new target file on the source file, with the target file translations
1143 my $source_file_line_number = 0;
1144 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1145 # Fill in the gaps before this chunk starts
1146 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1147 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1148 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1149 print TARGET_FILE @$source_file_lines[$source_file_line_number];
1150 $source_file_line_number++;
1151 }
1152 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1153
1154 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1155 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1156 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1157
1158 # If no translation exists for this chunk, show this, and move on
1159 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1160 print TARGET_FILE "# -- Missing translation: $chunk_key\n";
1161 next;
1162 }
1163
1164 print TARGET_FILE "$chunk_key:$target_file_chunk_text";
1165 if ($target_file_key_to_comment_date_mapping->{$chunk_key}) {
1166 print TARGET_FILE " # Updated " . $target_file_key_to_comment_date_mapping->{$chunk_key};
1167 }
1168 print TARGET_FILE "\n";
1169 }
1170
1171 close(TARGET_FILE);
1172}
1173
1174
1175&main(@ARGV);
Note: See TracBrowser for help on using the repository browser.