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

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

Added greenstone.org translation.

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