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

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