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

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

Add in new key for processing Greenstone installer language files

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 84.7 KB
Line 
1#!/usr/bin/perl -w
2
3###########################################################################
4#
5# gti.pl
6#
7# A component of the Greenstone digital library software
8# from the New Zealand Digital Library Project at the
9# University of Waikato, New Zealand.
10#
11# Copyright (C) 2005 New Zealand Digital Library Project
12#
13# This program is free software; you can redistribute it and/or modify
14# it under the terms of the GNU General Public License as published by
15# the Free Software Foundation; either version 2 of the License, or
16# (at your option) any later version.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program; if not, write to the Free Software
25# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26#
27###########################################################################
28
29
30BEGIN {
31 die "GSDLHOME not set\n" unless defined $ENV{'GSDLHOME'};
32 unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
33}
34
35
36use iso639;
37use strict;
38use util;
39
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 ($source_file_key_to_submission_mapping{$chunk_key} ne &unmake_text_xml_safe($source_file_key_to_text_mapping{$chunk_key})) {
672 &log_message("Warning: Source chunk $chunk_key has changed (ignoring submission).");
673 &log_message("Submission source: $source_file_key_to_submission_mapping{$chunk_key}");
674 &log_message(" Source text: $source_file_key_to_text_mapping{$chunk_key}");
675 delete $source_file_key_to_submission_mapping{$chunk_key};
676 delete $target_file_key_to_submission_mapping{$chunk_key};
677 next;
678 }
679 }
680
681 # Apply the submitted translations
682 foreach my $chunk_key (keys(%target_file_key_to_submission_mapping)) {
683 # Only apply the submission if it is a change, unless -force_submission has been specified
684 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}) {
685 $target_file_key_to_text_mapping{$chunk_key} = $target_file_key_to_submission_mapping{$chunk_key};
686 $target_file_key_to_gti_comment_mapping{$chunk_key} = "Updated $submission_date by $submitter_username";
687 }
688 }
689
690 if ($translation_file_key ne "gs3interface") {
691 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)";
692 } else {
693 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)";
694 }
695}
696
697
698sub create_glihelp_zip_file
699{
700 my $target_language_code = shift(@_);
701 my $translation_file_key = "glihelp";
702
703 &log_message("Creating GLI Help zip file for $target_language_code");
704
705 my ($source_file, $target_file, $translation_file_type) = &get_translation_data_for($target_language_code, $translation_file_key);
706
707 my $classpath = &util::filename_cat($gsdl_root_directory, "gti-lib");
708 if ( ! -e $classpath) {
709 &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");
710 }
711
712 my $gli_help_directory = &util::filename_cat($gsdl_root_directory, "gli");
713 $gli_help_directory = &util::filename_cat($gli_help_directory, "help");
714
715 my $gen_many_html_xsl_filepath = &util::filename_cat($gli_help_directory, "gen-many-html.xsl");
716 if ( ! -e $gen_many_html_xsl_filepath) {
717 &throw_fatal_error("$gen_many_html_xsl_filepath doesn't exist! Need this file to create the zip file for GLI Help");
718 }
719
720 my $gen_index_xml_xsl_filepath = &util::filename_cat($gli_help_directory, "gen-index-xml.xsl");
721 my $split_script_filepath = &util::filename_cat($gli_help_directory, "splithelpdocument.pl");
722
723 my $target_file_directory = &util::filename_cat($gli_help_directory, $target_language_code);
724 $target_file_directory = $target_file_directory."/";
725
726 my $target_filepath = &util::filename_cat($gsdl_root_directory, $target_file);
727
728 my $perl_exec = &util::get_perl_exec();
729 my $java_exec = "java";
730 if(defined($ENV{'JAVA_HOME'}) && $ENV{'JAVA_HOME'} ne ""){
731 $java_exec = &util::filename_cat($ENV{'JAVA_HOME'}, "bin", "java");
732 }
733
734 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";
735 my $response = `$cmd`;
736
737 $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";
738 $response = `$cmd`;
739
740 my $zip_file_path = "/greenstone/custom/gti/" . $target_language_code . "_GLIHelp.zip";
741 $cmd = "zip -rj $zip_file_path $target_file_directory -i \*.htm \*.xml";
742 $response = `$cmd`;
743}
744
745
746sub get_translation_configuration
747{
748 # Get the code of the target language
749 my $target_language_code = shift(@_);
750 # Get the key of the file to translate
751 my $translation_file_key = shift(@_);
752
753 # Read the translation data from the gti.cfg file
754 my ($source_file, $target_file, $translation_file_type) =
755 &get_translation_data_for($target_language_code, $translation_file_key);
756
757 # Check that the file to translate is defined in the gti.cfg file
758 if (!$source_file || !$target_file || !$translation_file_type) {
759 &throw_fatal_error("Missing or incomplete specification for translation file \"$translation_file_key\" in gti.pl.");
760 }
761
762 # Check that the source file exists
763 my $source_file_path = &util::filename_cat($gsdl_root_directory, $source_file);
764 if (!-e $source_file_path) {
765 &throw_fatal_error("Source file $source_file_path does not exist.");
766 }
767
768 # Check that the source file is up to date
769 # The "2>/dev/null" is very important! If it is missing this will never return when run from the receptionist
770 # unless ($translation_file_is_not_in_cvs) {
771 #my $source_file_cvs_status = `cd $gsdl_root_directory; cvs -d $anonymous_cvs_root update $source_file 2>/dev/null`;
772 my $source_file_cvs_status = `cd $gsdl_root_directory; svn status $source_file 2>/dev/null`;
773 if ($source_file_cvs_status =~ /^C /) {
774 &throw_fatal_error("Source file $source_file_path conflicts with the repository.");
775 }
776 if ($source_file_cvs_status =~ /^M /) {
777 &throw_fatal_error("Source file $source_file_path contains uncommitted changes.");
778 }
779 # }
780
781 return ($source_file, $target_file, $translation_file_type);
782}
783
784
785sub get_translation_data_for
786{
787 my ($target_language_code, $translation_file_key) = @_;
788
789 foreach my $translation_file (@$gti_translation_files) {
790 # If this isn't the correct translation file, move onto the next one
791 next if ($translation_file_key ne $translation_file->{'key'});
792
793 # Resolve the target language file
794 my $target_language_file = $translation_file->{'target_file'};
795 if ($target_language_file =~ /(\{.+\;.+\})/) {
796 my $unresolved_target_language_file_part = $1;
797
798 # Check for a special case for the target language code
799 if ($unresolved_target_language_file_part =~ /(\{|\;)$target_language_code:([^\;]+)(\;|\})/) {
800 my $resolved_target_language_file_part = $2;
801 $target_language_file =~ s/$unresolved_target_language_file_part/$resolved_target_language_file_part/;
802 }
803 # Otherwise use the last part as the default value
804 else {
805 my ($default_target_language_file_part) = $unresolved_target_language_file_part =~ /([^\;]+)\}/;
806 $target_language_file =~ s/$unresolved_target_language_file_part/\{$default_target_language_file_part\}/;
807 }
808 }
809
810 # Resolve instances of {iso_639_1_target_language_name}
811 my $iso_639_1_target_language_name = $iso639::fromiso639{$target_language_code};
812 $iso_639_1_target_language_name =~ tr/A-Z/a-z/ if $iso_639_1_target_language_name;
813 $target_language_file =~ s/\{iso_639_1_target_language_name\}/$iso_639_1_target_language_name/g;
814
815 # Resolve instances of {target_language_code}
816 $target_language_file =~ s/\{target_language_code\}/$target_language_code/g;
817
818 return ($translation_file->{'source_file'}, $target_language_file, $translation_file->{'file_type'});
819}
820
821return ();
822}
823
824
825sub read_file_lines
826{
827 my ($file_path) = @_;
828
829 if (!open(FILE_IN, "<$file_path")) {
830 &log_message("Note: Could not open file $file_path.");
831 return ();
832 }
833 my @file_lines = <FILE_IN>;
834 close(FILE_IN);
835
836 return @file_lines;
837}
838
839
840sub build_key_to_line_mapping
841{
842 my ($file_lines, $translation_file_type) = @_;
843 eval "return &build_key_to_line_mapping_for_${translation_file_type}(\@\$file_lines)";
844}
845
846
847sub build_key_to_text_mapping
848{
849 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
850
851 my %key_to_text_mapping = ();
852 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
853 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
854 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
855
856 my $chunk_text = @$file_lines[$chunk_starting_line];
857 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
858 $chunk_text .= @$file_lines[$l];
859 }
860
861 # Map from chunk key to text
862 eval "\$key_to_text_mapping{\${chunk_key}} = &import_chunk_from_${translation_file_type}(\$chunk_text)";
863 }
864
865 return %key_to_text_mapping;
866}
867
868
869sub build_key_to_last_update_date_mapping
870{
871 my ($file, $file_lines, $key_to_line_mapping, $translation_file_type) = @_;
872
873 # If the files aren't in CVS then we can't tell anything about what needs updating
874 # return () if ($translation_file_is_not_in_cvs);
875
876 # Build a mapping from key to CVS date
877 # Need to be careful with this mapping because the chunk keys won't necessarily all be valid
878 my %key_to_cvs_date_mapping = &build_key_to_cvs_date_mapping($file, $translation_file_type);
879
880 # Build a mapping from key to comment date
881 my %key_to_gti_comment_mapping = &build_key_to_gti_comment_mapping($file_lines, $key_to_line_mapping, $translation_file_type);
882
883 # Build a mapping from key to last update date (the latter of the CVS date and comment date)
884 my %key_to_last_update_date_mapping = ();
885 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
886 # Use the CVS date as a starting point
887 my $chunk_cvs_date = $key_to_cvs_date_mapping{$chunk_key};
888 $key_to_last_update_date_mapping{$chunk_key} = $chunk_cvs_date;
889
890 # If a comment date exists and it is after the CVS date, use that instead
891 # need to convert the comment date format to SVN format
892 my $chunk_gti_comment = $key_to_gti_comment_mapping{$chunk_key};
893 if (defined($chunk_gti_comment) && $chunk_gti_comment =~ /(\d?\d-\D\D\D-\d\d\d\d)/) {
894 my $chunk_comment_date = $1;
895 if ((!defined($chunk_cvs_date) || &is_date_after($chunk_comment_date, $chunk_cvs_date))) {
896 $key_to_last_update_date_mapping{$chunk_key} = $chunk_comment_date;
897 }
898 }
899 }
900
901 return %key_to_last_update_date_mapping;
902}
903
904
905sub build_key_to_cvs_date_mapping
906{
907 my ($filename, $translation_file_type) = @_;
908
909 # Use SVN to annotate each line of the file with the date it was last edited
910 # The "2>/dev/null" is very important! If it is missing this will never return when run from the receptionist
911 my $cvs_annotated_file = `cd $gsdl_root_directory; svn annotate -v --force $filename 2>/dev/null`;
912
913 my @cvs_annotated_file_lines = split(/\n/, $cvs_annotated_file);
914
915 my @cvs_annotated_file_lines_date = ();
916 foreach my $cvs_annotated_file_line (@cvs_annotated_file_lines) {
917 # Extract the date from the SVN annotation at the front
918 # svn format : 2007-07-16
919 $cvs_annotated_file_line =~ s/^\s+\S+\s+\S+\s(\S+)//;
920
921 push(@cvs_annotated_file_lines_date, $1);
922
923 # trim extra date information in svn annotation format
924 # 15:42:49 +1200 (Wed, 21 Jun 2006)
925 $cvs_annotated_file_line =~ s/^\s+\S+\s\S+\s\((.+?)\)\s//;
926 }
927
928 # Build a key to line mapping for the CVS annotated file, for matching the chunk key to the CVS date
929 my %key_to_line_mapping = &build_key_to_line_mapping(\@cvs_annotated_file_lines, $translation_file_type);
930
931 my %key_to_cvs_date_mapping = ();
932 foreach my $chunk_key (keys(%key_to_line_mapping)) {
933 my $chunk_starting_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[0];
934 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping{$chunk_key}))[1];
935
936 # Find the date this chunk was last edited, from the CVS annotation
937 my $chunk_date = $cvs_annotated_file_lines_date[$chunk_starting_line];
938 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
939 if (&is_date_after($cvs_annotated_file_lines_date[$l], $chunk_date)) {
940 # This part of the chunk has been updated more recently
941 $chunk_date = $cvs_annotated_file_lines_date[$l];
942
943 }
944 }
945
946 # Map from chunk key to CVS date
947 $key_to_cvs_date_mapping{$chunk_key} = $chunk_date;
948 }
949
950 return %key_to_cvs_date_mapping;
951}
952
953
954sub build_key_to_gti_comment_mapping
955{
956 my ($file_lines, $key_to_line_mapping, $translation_file_type) = @_;
957
958 my %key_to_gti_comment_mapping = ();
959 foreach my $chunk_key (keys(%$key_to_line_mapping)) {
960 my $chunk_starting_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[0];
961 my $chunk_finishing_line = (split(/-/, $key_to_line_mapping->{$chunk_key}))[1];
962
963 my $chunk_text = @$file_lines[$chunk_starting_line];
964 for (my $l = ($chunk_starting_line + 1); $l <= $chunk_finishing_line; $l++) {
965 $chunk_text .= @$file_lines[$l];
966 }
967
968 # Map from chunk key to GTI comment
969 my $chunk_gti_comment;
970 eval "\$chunk_gti_comment = &get_${translation_file_type}_chunk_gti_comment(\$chunk_text)";
971 $key_to_gti_comment_mapping{$chunk_key} = $chunk_gti_comment if (defined($chunk_gti_comment));
972 }
973
974 return %key_to_gti_comment_mapping;
975}
976
977
978sub determine_chunks_requiring_translation
979{
980 my $source_file_key_to_text_mapping = shift(@_);
981 my $target_file_key_to_text_mapping = shift(@_);
982
983 # Chunks needing translation are those in the source file with no translation in the target file
984 my @target_file_keys_requiring_translation = ();
985 foreach my $chunk_key (keys(%$source_file_key_to_text_mapping)) {
986 if ($source_file_key_to_text_mapping->{$chunk_key} && !$target_file_key_to_text_mapping->{$chunk_key}) {
987 # &log_message("Chunk with key $chunk_key needs translating.");
988 push(@target_file_keys_requiring_translation, $chunk_key);
989 }
990 }
991
992 return @target_file_keys_requiring_translation;
993}
994
995
996sub determine_chunks_requiring_updating
997{
998 my $source_file_key_to_last_update_date_mapping = shift(@_);
999 my $target_file_key_to_last_update_date_mapping = shift(@_);
1000
1001 # Chunks needing updating are those in the target file that have been more recently edited in the source file
1002 my @target_file_keys_requiring_updating = ();
1003 foreach my $chunk_key (keys(%$source_file_key_to_last_update_date_mapping)) {
1004 my $source_chunk_last_update_date = $source_file_key_to_last_update_date_mapping->{$chunk_key};
1005 my $target_chunk_last_update_date = $target_file_key_to_last_update_date_mapping->{$chunk_key};
1006
1007 # 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";
1008
1009 if (defined($target_chunk_last_update_date) && &is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date)) {
1010 # &log_message("Chunk with key $chunk_key needs updating.");
1011 push(@target_file_keys_requiring_updating, $chunk_key);
1012 }
1013 }
1014
1015 return @target_file_keys_requiring_updating;
1016}
1017
1018
1019sub is_chunk_automatically_translated
1020{
1021 my ($chunk_key, $translation_file_type) = @_;
1022 eval "return &is_${translation_file_type}_chunk_automatically_translated(\$chunk_key)";
1023}
1024
1025
1026sub make_text_xml_safe
1027{
1028 my $text = shift(@_);
1029 $text =~ s/\&/\&amp\;/g;
1030 $text =~ s/\&amp\;lt\;/\&amp\;amp\;lt\;/g;
1031 $text =~ s/\&amp\;gt\;/\&amp\;amp\;gt\;/g;
1032 $text =~ s/\&amp\;rarr\;/\&amp\;amp\;rarr\;/g;
1033 $text =~ s/\&amp\;mdash\;/\&amp\;amp\;mdash\;/g;
1034 $text =~ s/</\&lt\;/g;
1035 $text =~ s/>/\&gt\;/g;
1036 return $text;
1037}
1038
1039
1040sub unmake_text_xml_safe
1041{
1042 my $text = shift(@_);
1043 $text =~ s/\&lt\;/</g;
1044 $text =~ s/\&gt\;/>/g;
1045 $text =~ s/\&amp\;/\&/g;
1046 return $text;
1047}
1048
1049
1050# Returns 1 if $date1 is after $date2, 0 otherwise
1051sub is_date_after_cvs
1052{
1053 my ($date1, $date2) = @_;
1054 my %months = ("Jan", 1, "Feb", 2, "Mar", 3, "Apr", 4, "May", 5, "Jun", 6,
1055 "Jul", 7, "Aug", 8, "Sep", 9, "Oct", 10, "Nov", 11, "Dec", 12);
1056
1057 if(!defined $date1) {
1058 return 1;
1059 }
1060
1061 my @date1parts = split(/-/, $date1);
1062 my @date2parts = split(/-/, $date2);
1063
1064 # Compare year - nasty because we have rolled over into a new century
1065 my $year1 = $date1parts[2];
1066 if ($year1 < 80) {
1067 $year1 += 2000;
1068 }
1069 my $year2 = $date2parts[2];
1070 if ($year2 < 80) {
1071 $year2 += 2000;
1072 }
1073
1074 # Compare year
1075 if ($year1 > $year2) {
1076 return 1;
1077 }
1078 elsif ($year1 == $year2) {
1079 # Year is the same, so compare month
1080 if ($months{$date1parts[1]} > $months{$date2parts[1]}) {
1081 return 1;
1082 }
1083 elsif ($months{$date1parts[1]} == $months{$date2parts[1]}) {
1084 # Month is the same, so compare day
1085 if ($date1parts[0] > $date2parts[0]) {
1086 return 1;
1087 }
1088 }
1089 }
1090
1091 return 0;
1092}
1093
1094sub is_date_after
1095{
1096 my ($date1, $date2) = @_;
1097
1098 if(!defined $date1) {
1099 return 1;
1100 }
1101 if(!defined $date2) {
1102 return 0;
1103 }
1104
1105 # 16-Aug-2006
1106 if($date1=~ /(\d+?)-(\S\S\S)-(\d\d\d\d)/){
1107 my %months = ("Jan", "01", "Feb", "02", "Mar", "03", "Apr", "04", "May", "05", "Jun", "06",
1108 "Jul", "07", "Aug", "08", "Sep", "09", "Oct", "10", "Nov", "11", "Dec", "12");
1109 $date1=$3 . "-" . $months{$2} . "-" . $1;
1110 # print "** converted date1: $date1\n";
1111 }
1112 if($date2=~ /(\d+?)-(\S\S\S)-(\d\d\d\d)/){
1113 my %months = ("Jan", "01", "Feb", "02", "Mar", "03", "Apr", "04", "May", "05", "Jun", "06",
1114 "Jul", "07", "Aug", "08", "Sep", "09", "Oct", "10", "Nov", "11", "Dec", "12");
1115 $date2=$3 . "-" . $months{$2} . "-" . $1;
1116 # print "** converted date2: $date2\n";
1117 }
1118
1119
1120 # 2006-08-16
1121 my @date1parts = split(/-/, $date1);
1122 my @date2parts = split(/-/, $date2);
1123
1124 # Compare year
1125 if ($date1parts[0] > $date2parts[0]) {
1126 return 1;
1127 }
1128 elsif ($date1parts[0] == $date2parts[0]) {
1129 # Year is the same, so compare month
1130 if ($date1parts[1] > $date2parts[1]) {
1131 return 1;
1132 }
1133 elsif ($date1parts[1] == $date2parts[1]) {
1134 # Month is the same, so compare day
1135 if ($date1parts[2] > $date2parts[2]) {
1136 return 1;
1137 }
1138 }
1139 }
1140
1141 return 0;
1142}
1143
1144
1145sub create_xml_response_for_chunks_requiring_work
1146{
1147 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) = @_;
1148
1149 # Form an XML response to the command
1150 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
1151 $xml_response .= "<GTIResponse>\n";
1152 $xml_response .= " <TranslationFile"
1153 . " key=\"" . $translation_file_key . "\""
1154 . " target_file_path=\"" . $target_file . "\""
1155 . " num_chunks_translated=\"" . ($total_num_chunks - scalar(@$target_files_keys_requiring_translation)) . "\""
1156 . " num_chunks_requiring_translation=\"" . scalar(@$target_files_keys_requiring_translation) . "\""
1157 . " num_chunks_requiring_updating=\"" . scalar(@$target_files_keys_requiring_updating) . "\"\/>\n";
1158
1159 # Do chunks requiring translation first
1160 if ($num_chunks_to_return > scalar(@$target_files_keys_requiring_translation)) {
1161 $xml_response .= " <ChunksRequiringTranslation size=\"" . scalar(@$target_files_keys_requiring_translation) . "\">\n";
1162 }
1163 else {
1164 $xml_response .= " <ChunksRequiringTranslation size=\"" . $num_chunks_to_return . "\">\n";
1165 }
1166
1167 my @sorted_chunk_keys = sort (@$target_files_keys_requiring_translation);
1168 foreach my $chunk_key (@sorted_chunk_keys) {
1169 last if ($num_chunks_to_return == 0);
1170
1171 my $source_file_chunk_date = $source_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1172 my $source_file_chunk_text = &make_text_xml_safe($source_files_key_to_text_mapping->{$chunk_key});
1173
1174 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1175 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1176 $xml_response .= " <TargetFileText></TargetFileText>\n";
1177 $xml_response .= " </Chunk>\n";
1178
1179 $num_chunks_to_return--;
1180 }
1181
1182 $xml_response .= " </ChunksRequiringTranslation>\n";
1183
1184 # Then do chunks requiring updating
1185 if ($num_chunks_to_return > scalar(@$target_files_keys_requiring_updating)) {
1186 $xml_response .= " <ChunksRequiringUpdating size=\"" . scalar(@$target_files_keys_requiring_updating) . "\">\n";
1187 }
1188 else {
1189 $xml_response .= " <ChunksRequiringUpdating size=\"" . $num_chunks_to_return . "\">\n";
1190 }
1191
1192 # foreach my $chunk_key (@target_file_keys_requiring_updating) {
1193 @sorted_chunk_keys = sort (@$target_files_keys_requiring_updating);
1194 foreach my $chunk_key (@sorted_chunk_keys) {
1195 last if ($num_chunks_to_return == 0);
1196
1197 my $source_file_chunk_date = $source_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1198 my $source_file_chunk_text = &make_text_xml_safe($source_files_key_to_text_mapping->{$chunk_key});
1199 my $target_file_chunk_date = $target_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1200 my $target_file_chunk_text = &make_text_xml_safe($target_files_key_to_text_mapping->{$chunk_key});
1201
1202 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1203 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1204 $xml_response .= " <TargetFileText date=\"$target_file_chunk_date\">$target_file_chunk_text</TargetFileText>\n";
1205 $xml_response .= " </Chunk>\n";
1206
1207 $num_chunks_to_return--;
1208 }
1209
1210 $xml_response .= " </ChunksRequiringUpdating>\n";
1211
1212 $xml_response .= "</GTIResponse>\n";
1213
1214 return $xml_response;
1215}
1216
1217sub create_xml_response_for_uptodate_chunks
1218{
1219 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) = @_;
1220
1221 # Form an XML response to the command
1222 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
1223 $xml_response .= "<GTIResponse>\n";
1224 $xml_response .= " <TranslationFile"
1225 . " key=\"" . $translation_file_key . "\""
1226 . " target_file_path=\"" . $target_file . "\""
1227 . " num_chunks_uptodate=\"" . scalar(@$uptodate_target_files_keys) . "\"\/>\n";
1228
1229
1230 # Then do chunks requiring updating
1231 $xml_response .= " <UptodateChunks size=\"" . scalar(@$uptodate_target_files_keys) . "\">\n";
1232
1233
1234 # foreach my $chunk_key (@uptodate_target_file_keys) {
1235 my @sorted_chunk_keys = sort (@$uptodate_target_files_keys);
1236 foreach my $chunk_key (@sorted_chunk_keys) {
1237
1238 my $source_file_chunk_date = $source_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1239 my $source_file_chunk_text = &make_text_xml_safe($source_files_key_to_text_mapping->{$chunk_key});
1240 my $target_file_chunk_date = $target_files_key_to_last_update_date_mapping->{$chunk_key} || "";
1241 my $target_file_chunk_text = &make_text_xml_safe($target_files_key_to_text_mapping->{$chunk_key});
1242
1243 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1244 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1245 $xml_response .= " <TargetFileText date=\"$target_file_chunk_date\">$target_file_chunk_text</TargetFileText>\n";
1246 $xml_response .= " </Chunk>\n";
1247
1248 }
1249
1250 $xml_response .= " </UptodateChunks>\n";
1251
1252 $xml_response .= "</GTIResponse>\n";
1253
1254 return $xml_response;
1255}
1256
1257sub create_xml_response_for_all_chunks
1258{
1259 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) = @_;
1260
1261 # Form an XML response to the command
1262 my $xml_response = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
1263 $xml_response .= "<GTIResponse>\n";
1264 $xml_response .= " <TranslationFile"
1265 . " key=\"" . $translation_file_key . "\""
1266 . " target_file_path=\"" . $target_file . "\"\/>\n";
1267
1268 # Do all the chunks
1269 $xml_response .= " <Chunks size=\"" . scalar(keys(%$source_file_key_to_text_mapping)) . "\">\n";
1270
1271 my @sorted_chunk_keys = sort (keys(%$source_file_key_to_text_mapping));
1272 foreach my $chunk_key (@sorted_chunk_keys) {
1273 my $source_file_chunk_date = $source_file_key_to_last_update_date_mapping->{$chunk_key} || "";
1274 my $source_file_chunk_text = &make_text_xml_safe($source_file_key_to_text_mapping->{$chunk_key});
1275
1276 $xml_response .= " <Chunk key=\"" . &make_text_xml_safe($chunk_key) . "\">\n";
1277 $xml_response .= " <SourceFileText date=\"$source_file_chunk_date\">$source_file_chunk_text</SourceFileText>\n";
1278 if (defined($target_file_key_to_text_mapping->{$chunk_key})) {
1279 my $target_file_chunk_date = $target_file_key_to_last_update_date_mapping->{$chunk_key} || "";
1280 my $target_file_chunk_text = &make_text_xml_safe($target_file_key_to_text_mapping->{$chunk_key});
1281 $xml_response .= " <TargetFileText date=\"$target_file_chunk_date\">$target_file_chunk_text</TargetFileText>\n";
1282 }
1283 else {
1284 $xml_response .= " <TargetFileText></TargetFileText>\n";
1285 }
1286
1287 $xml_response .= " </Chunk>\n";
1288 }
1289 $xml_response .= " </Chunks>\n";
1290
1291 $xml_response .= "</GTIResponse>\n";
1292 return $xml_response;
1293}
1294
1295
1296
1297# ==========================================================================================
1298# MACROFILE FUNCTIONS
1299
1300sub build_key_to_line_mapping_for_macrofile
1301{
1302 my (@file_lines) = @_;
1303
1304 my $macro_package;
1305 my %chunk_key_to_line_mapping = ();
1306 # Process the contents of the file, line by line
1307 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1308 my $line = $file_lines[$i];
1309 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1310
1311 # Check if a new package is being defined
1312 if ($line =~ m/^package\s+(.+)/) {
1313 $macro_package = $1;
1314 }
1315
1316 # Line contains a macro name
1317 elsif ($line =~ m/^(_\w+_)/) {
1318 my $macro_key = $1;
1319 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
1320
1321 # While there is still text of the macro to go...
1322 my $startline = $i;
1323 while ($line !~ /\}$/) {
1324 $i++;
1325 if ($i == scalar(@file_lines)) {
1326 &throw_fatal_error("Could not find end of macro $macro_key.");
1327 }
1328 $line = $file_lines[$i];
1329 $line =~ s/\s*([^\\]\#[^\}]+)?$//; # Remove any comments and nasty whitespace
1330 }
1331
1332 # The chunk key consists of the package name and the macro key
1333 my $chunk_key = $macro_package . "." . $macro_key;
1334 # Map from chunk key to line
1335 $chunk_key_to_line_mapping{$chunk_key} = $startline . "-" . $i;
1336 }
1337
1338 # Icon: line in format ## "image text" ## image_type ## macro_name ##
1339 elsif ($line =~ m/^\#\# .* \#\# .* \#\# (.*) \#\#/) {
1340 # The chunk key consists of package name and macro key
1341 my $chunk_key = $macro_package . "." . $1;
1342 # Map from chunk key to line
1343 $chunk_key_to_line_mapping{$chunk_key} = $i . "-" . $i;
1344}
1345}
1346
1347return %chunk_key_to_line_mapping;
1348}
1349
1350
1351sub import_chunk_from_macrofile
1352{
1353 my ($chunk_text) = @_;
1354
1355 # Is this an icon macro??
1356 if ($chunk_text =~ /^\#\# (.*)/) {
1357 # Extract image macro text
1358 $chunk_text =~ /^\#\#\s+([^\#]+)\s+\#\#/;
1359 $chunk_text = $1;
1360
1361 # Remove enclosing quotes
1362 $chunk_text =~ s/^\"//;
1363 $chunk_text =~ s/\"$//;
1364}
1365
1366# No, so it must be a text macro
1367else {
1368 # Remove macro key
1369 $chunk_text =~ s/^_([^_]+)_(\s*)//;
1370
1371 # Remove language specifier
1372 $chunk_text =~ s/^\[l=.*\](\s*)//;
1373
1374 # Remove braces enclosing text
1375 $chunk_text =~ s/^{(\s*)((.|\n)*)}(\s*)(\#.+\s*)?/$2/;
1376}
1377
1378return $chunk_text;
1379}
1380
1381
1382sub get_macrofile_chunk_gti_comment
1383{
1384 my ($chunk_text) = @_;
1385
1386 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1387 if ($chunk_text =~ /\#\s+(Updated\s+\d?\d-\D\D\D-\d\d\d\d.*)\s*$/i) {
1388 return $1;
1389}
1390
1391return undef;
1392}
1393
1394
1395sub is_macrofile_chunk_automatically_translated
1396{
1397 my ($chunk_key) = @_;
1398
1399 # The _httpiconX_, _widthX_ and _heightX_ image macros are automatically translated
1400 if ($chunk_key =~ /\._(httpicon|width|height)/) {
1401 return 1;
1402 }
1403
1404 return 0;
1405}
1406
1407
1408# Use the source file to generate a target file that is formatted the same
1409sub write_translated_macrofile
1410{
1411 my $source_file = shift(@_); # Not used
1412 my @source_file_lines = @{shift(@_)};
1413 my $source_file_key_to_text_mapping = shift(@_);
1414 my $target_file = shift(@_);
1415 my @target_file_lines = @{shift(@_)};
1416 my $target_file_key_to_text_mapping = shift(@_);
1417 my $target_file_key_to_gti_comment_mapping = shift(@_);
1418 my $target_language_code = shift(@_);
1419
1420 # Build a mapping from source file line to chunk key
1421 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@source_file_lines);
1422 my %source_file_line_to_key_mapping = ();
1423 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1424 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1425 }
1426 my @source_file_line_keys = (sort sort_by_line (keys(%source_file_line_to_key_mapping)));
1427 my $source_file_line_number = 0;
1428
1429 # Build a mapping from target file line to chunk key
1430 my %target_file_key_to_line_mapping = &build_key_to_line_mapping_for_macrofile(@target_file_lines);
1431 my %target_file_line_to_key_mapping = ();
1432 foreach my $chunk_key (keys(%target_file_key_to_line_mapping)) {
1433 $target_file_line_to_key_mapping{$target_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1434 }
1435 my @target_file_line_keys = (sort sort_by_line (keys(%target_file_line_to_key_mapping)));
1436
1437 # Write the new target file
1438 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1439 if (!open(TARGET_FILE, ">$target_file_path")) {
1440 &throw_fatal_error("Could not write target file $target_file_path.");
1441 }
1442
1443 # Use the header from the target file, to keep language and author information
1444 if (scalar(@target_file_line_keys) > 0) {
1445 my $target_file_line_number = 0;
1446 my $target_file_chunk_starting_line_number = (split(/-/, $target_file_line_keys[0]))[0];
1447 while ($target_file_line_number < $target_file_chunk_starting_line_number) {
1448 my $target_file_line = $target_file_lines[$target_file_line_number];
1449 last if ($target_file_line =~ /^\# -- Missing translation: /); # We don't want to get into the macros
1450 print TARGET_FILE $target_file_line;
1451 $target_file_line_number++;
1452 }
1453
1454 $source_file_line_number = (split(/-/, $source_file_line_keys[0]))[0];
1455 }
1456
1457 # Model the new target file on the source file, with the target file translations
1458 foreach my $line_key (@source_file_line_keys) {
1459 # Fill in the gaps before this chunk starts
1460 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1461 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1462 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1463 print TARGET_FILE $source_file_lines[$source_file_line_number];
1464 $source_file_line_number++;
1465 }
1466 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1467
1468 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1469 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1470 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1471
1472 my $macrofile_key = $chunk_key;
1473 $macrofile_key =~ s/^(.+?)\.//;
1474
1475 # If no translation exists for this chunk, show this, and move on
1476 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1477 print TARGET_FILE "# -- Missing translation: $macrofile_key\n";
1478 next;
1479 }
1480
1481 # Grab the source chunk text
1482 my $source_file_chunk = $source_file_lines[$source_file_chunk_starting_line_number];
1483 for (my $l = ($source_file_chunk_starting_line_number + 1); $l <= $source_file_chunk_finishing_line_number; $l++) {
1484 $source_file_chunk .= $source_file_lines[$l];
1485 }
1486
1487 # Is this an icon macro??
1488 if ($source_file_chunk =~ /^\#\# (.*)/) {
1489 # Escape any newline and question mark characters so the source text is replaced correctly
1490 $source_file_chunk_text =~ s/\\/\\\\/g;
1491 $source_file_chunk_text =~ s/\?/\\\?/g;
1492
1493 # Build the new target chunk from the source chunk
1494 my $target_file_chunk = $source_file_chunk;
1495 $target_file_chunk =~ s/$source_file_chunk_text/$target_file_chunk_text/;
1496 $target_file_chunk =~ s/(\s)*$//;
1497 print TARGET_FILE "$target_file_chunk";
1498 }
1499
1500 # No, it is just a normal text macro
1501 else {
1502 print TARGET_FILE "$macrofile_key [l=$target_language_code] {$target_file_chunk_text}";
1503 }
1504
1505 # Add the "updated" comment, if one exists
1506 if ($target_file_key_to_gti_comment_mapping->{$chunk_key}) {
1507 print TARGET_FILE " # " . $target_file_key_to_gti_comment_mapping->{$chunk_key};
1508 }
1509 print TARGET_FILE "\n";
1510}
1511
1512close(TARGET_FILE);
1513}
1514
1515
1516sub sort_by_line
1517{
1518 return ((split(/-/, $a))[0] <=> (split(/-/, $b))[0]);
1519}
1520
1521
1522# ==========================================================================================
1523# RESOURCE BUNDLE FUNCTIONS
1524
1525sub build_key_to_line_mapping_for_resource_bundle
1526{
1527 my (@file_lines) = @_;
1528
1529 my %chunk_key_to_line_mapping = ();
1530 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1531 my $line = $file_lines[$i];
1532 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1533
1534 # Line contains a dictionary string
1535 if ($line =~ /^(\S+?)[:|=](.*)$/) {
1536 my $chunk_key = $1;
1537
1538 # Map from chunk key to line
1539 $chunk_key_to_line_mapping{$chunk_key} = $i . "-" . $i;
1540 }
1541 }
1542
1543 return %chunk_key_to_line_mapping;
1544}
1545
1546
1547sub import_chunk_from_resource_bundle
1548{
1549 my ($chunk_text) = @_;
1550
1551 # Simple: just remove string key
1552 $chunk_text =~ s/^(\S+?)[:|=](\s*)//;
1553 $chunk_text =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1554 $chunk_text =~ s/(\s*)\#\s+Updated\s+(\d?\d-\D\D\D-\d\d\d\d.*)\s*$//i;
1555
1556 return $chunk_text;
1557}
1558
1559
1560sub get_resource_bundle_chunk_gti_comment
1561{
1562 my ($chunk_text) = @_;
1563
1564 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1565 if ($chunk_text =~ /\#\s+(Updated\s+\d?\d-\D\D\D-\d\d\d\d.*)\s*$/i) {
1566 return $1;
1567}
1568
1569return undef;
1570}
1571
1572
1573sub is_resource_bundle_chunk_automatically_translated
1574{
1575 # No resource bundle chunks are automatically translated
1576 return 0;
1577}
1578
1579
1580sub write_translated_resource_bundle
1581{
1582 my $source_file = shift(@_); # Not used
1583 my @source_file_lines = @{shift(@_)};
1584 my $source_file_key_to_text_mapping = shift(@_);
1585 my $target_file = shift(@_);
1586 my @target_file_lines = @{shift(@_)}; # Not used
1587 my $target_file_key_to_text_mapping = shift(@_);
1588 my $target_file_key_to_gti_comment_mapping = shift(@_);
1589 my $target_language_code = shift(@_); # Not used
1590
1591 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1592 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_resource_bundle(@source_file_lines);
1593 my %source_file_line_to_key_mapping = ();
1594 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1595 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1596 }
1597
1598 # Write the new target file
1599 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1600 if (!open(TARGET_FILE, ">$target_file_path")) {
1601 &throw_fatal_error("Could not write target file $target_file_path.");
1602 }
1603
1604 # Model the new target file on the source file, with the target file translations
1605 my $source_file_line_number = 0;
1606 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1607 # Fill in the gaps before this chunk starts
1608 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1609 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1610 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1611 print TARGET_FILE $source_file_lines[$source_file_line_number];
1612 $source_file_line_number++;
1613 }
1614 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1615
1616 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1617 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1618 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1619
1620 # If no translation exists for this chunk, show this, and move on
1621 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1622 print TARGET_FILE "# -- Missing translation: $chunk_key\n";
1623 next;
1624 }
1625
1626 print TARGET_FILE "$chunk_key:$target_file_chunk_text";
1627 if ($target_file_key_to_gti_comment_mapping->{$chunk_key}) {
1628 print TARGET_FILE " # " . $target_file_key_to_gti_comment_mapping->{$chunk_key};
1629 }
1630 print TARGET_FILE "\n";
1631 }
1632
1633 close(TARGET_FILE);
1634}
1635
1636
1637# ==========================================================================================
1638# GREENSTONE XML FUNCTIONS
1639
1640sub build_key_to_line_mapping_for_greenstone_xml
1641{
1642 my (@file_lines) = @_;
1643
1644 my %chunk_key_to_line_mapping = ();
1645 for (my $i = 0; $i < scalar(@file_lines); $i++) {
1646 my $line = $file_lines[$i];
1647 $line =~ s/(\s*)$//; # Remove any nasty whitespace, carriage returns etc.
1648
1649 # Line contains a string to translate
1650 if ($line =~ /^\s*<Text id=\"(.*?)\">/) {
1651 my $chunk_key = $1;
1652 $line =~ s/\s*$//; # Remove any nasty whitespace
1653 $line =~ s/<Updated date=\"\d?\d-\D\D\D-\d\d\d\d.*\"\/>$//;
1654
1655 # While there is still text of the string to go...
1656 my $startline = $i;
1657 while ($line !~ /<\/Text>$/) {
1658 $i++;
1659 if ($i == scalar(@file_lines)) {
1660 &throw_fatal_error("Could not find end of string $chunk_key.");
1661 }
1662 $line = $file_lines[$i];
1663 $line =~ s/\s*$//; # Remove any nasty whitespace
1664 $line =~ s/<Updated date=\"\d?\d-\D\D\D-\d\d\d\d.*\"\/>$//;
1665 }
1666
1667 # Map from chunk key to line
1668 if (!defined($chunk_key_to_line_mapping{$chunk_key})) {
1669 $chunk_key_to_line_mapping{$chunk_key} = $startline . "-" . $i;
1670 }
1671 else {
1672 &throw_fatal_error("Duplicate key $chunk_key.");
1673 }
1674 }
1675 }
1676
1677 return %chunk_key_to_line_mapping;
1678}
1679
1680
1681sub import_chunk_from_greenstone_xml
1682{
1683 my ($chunk_text) = @_;
1684
1685 # Simple: just remove the Text tags
1686 $chunk_text =~ s/^\s*<Text id=\"(.*?)\">(\s*)//;
1687 $chunk_text =~ s/<Updated date=\"\d?\d-\D\D\D-\d\d\d\d.*\"\/>$//;
1688 $chunk_text =~ s/<\/Text>$//;
1689
1690 return $chunk_text;
1691}
1692
1693
1694sub get_greenstone_xml_chunk_gti_comment
1695{
1696 my ($chunk_text) = @_;
1697
1698 # Check for an "Updated DD-MMM-YYYY" comment at the end of the chunk
1699 if ($chunk_text =~ /<Updated date=\"(\d?\d-\D\D\D-\d\d\d\d.*)\"\/>$/i) {
1700 return $1;
1701 }
1702
1703 return undef;
1704}
1705
1706
1707sub is_greenstone_xml_chunk_automatically_translated
1708{
1709 # No greenstone XML chunks are automatically translated
1710 return 0;
1711}
1712
1713
1714sub write_translated_greenstone_xml
1715{
1716 my $source_file = shift(@_); # Not used
1717 my @source_file_lines = @{shift(@_)};
1718 my $source_file_key_to_text_mapping = shift(@_);
1719 my $target_file = shift(@_);
1720 my @target_file_lines = @{shift(@_)}; # Not used
1721 my $target_file_key_to_text_mapping = shift(@_);
1722 my $target_file_key_to_gti_comment_mapping = shift(@_);
1723 my $target_language_code = shift(@_); # Not used
1724
1725 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1726 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_greenstone_xml(@source_file_lines);
1727 my %source_file_line_to_key_mapping = ();
1728 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1729 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1730 }
1731
1732 # Write the new target file
1733 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1734 if (!open(TARGET_FILE, ">$target_file_path")) {
1735 &throw_fatal_error("Could not write target file $target_file_path.");
1736 }
1737
1738 # Model the new target file on the source file, with the target file translations
1739 my $source_file_line_number = 0;
1740 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1741 # Fill in the gaps before this chunk starts
1742 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1743 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1744 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1745 print TARGET_FILE $source_file_lines[$source_file_line_number];
1746 $source_file_line_number++;
1747 }
1748 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1749
1750 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1751 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$chunk_key};
1752 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$chunk_key} || "";
1753 $target_file_chunk_text =~ s/(\n)*$//g;
1754
1755 # If no translation exists for this chunk, show this, and move on
1756 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
1757 print TARGET_FILE "<!-- Missing translation: $chunk_key -->\n";
1758 next;
1759 }
1760
1761 print TARGET_FILE "<Text id=\"$chunk_key\">$target_file_chunk_text</Text>";
1762 if ($target_file_key_to_gti_comment_mapping->{$chunk_key}) {
1763 my $chunk_gti_comment = $target_file_key_to_gti_comment_mapping->{$chunk_key};
1764 $chunk_gti_comment =~ s/^Updated //;
1765 print TARGET_FILE "<Updated date=\"" . $chunk_gti_comment . "\"\/>";
1766 }
1767 print TARGET_FILE "\n";
1768 }
1769
1770 # Fill in the end of the file
1771 while ($source_file_line_number < scalar(@source_file_lines)) {
1772 print TARGET_FILE $source_file_lines[$source_file_line_number];
1773 $source_file_line_number++;
1774 }
1775
1776 close(TARGET_FILE);
1777}
1778
1779
1780# ==========================================================================================
1781# GREENSTONE3 FUNCTIONS
1782
1783sub get_all_chunks_for_gs3
1784{
1785 # The code of the target language (ensure it is lowercase)
1786 my $target_language_code = lc(shift(@_));
1787 my $translation_file_key = lc(shift(@_));
1788
1789 # Check that the necessary arguments were supplied
1790 if (!$target_language_code) {
1791 &throw_fatal_error("Missing command argument.");
1792 }
1793
1794 # Get (and check) the translation configuration
1795 # my ($source_file_dir, $target_file, $translation_file_type) = &get_translation_configuration($target_language_code, $translation_file_key);
1796
1797 my %source_files_key_to_text_mapping = ();
1798 my %target_files_key_to_text_mapping = ();
1799 my %source_files_key_to_last_update_date_mapping = ();
1800 my %target_files_key_to_last_update_date_mapping = ();
1801
1802 &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);
1803
1804 &log_message("Total number of source chunks: " . scalar(keys(%source_files_key_to_text_mapping)));
1805 &log_message("Total number of target chunks: " . scalar(keys(%target_files_key_to_text_mapping)));
1806
1807 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);
1808 return $xml_response;
1809}
1810
1811
1812sub get_first_n_chunks_requiring_work_for_gs3
1813{
1814 # The code of the target language (ensure it is lowercase)
1815 my $target_language_code = lc(shift(@_));
1816 # The key of the file to translate (ensure it is lowercase)
1817 my $translation_file_key = lc(shift(@_));
1818 # The number of chunks to return (defaults to one if not specified)
1819 my $num_chunks_to_return = shift(@_) || "1";
1820
1821 # Check that the necessary arguments were supplied
1822 if (!$target_language_code || !$translation_file_key) {
1823 &throw_fatal_error("Missing command argument.");
1824 }
1825
1826 my %source_files_key_to_text_mapping = ();
1827 my %target_files_key_to_text_mapping = ();
1828 my %source_files_key_to_last_update_date_mapping = ();
1829 my %target_files_key_to_last_update_date_mapping = ();
1830
1831 &build_gs3_configuration($target_language_code, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping,
1832 \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1833
1834 # Determine the target file chunks requiring translation
1835 my @target_files_keys_requiring_translation = &determine_chunks_requiring_translation(\%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping);
1836 # Determine the target file chunks requiring updating
1837 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);
1838 &log_message("Total number of target chunks requiring translation: " . scalar(@target_files_keys_requiring_translation));
1839 &log_message("Total number of target chunks requiring updating: " . scalar(@target_files_keys_requiring_updating));
1840
1841 my $xml_response = &create_xml_response_for_chunks_requiring_work($translation_file_key, "", scalar(keys(%source_files_key_to_text_mapping)),
1842 \@target_files_keys_requiring_translation, \@target_files_keys_requiring_updating,
1843 $num_chunks_to_return, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping,
1844 \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1845
1846 return $xml_response;
1847}
1848
1849sub get_uptodate_chunks_for_gs3
1850{
1851 # The code of the target language (ensure it is lowercase)
1852 my $target_language_code = lc(shift(@_));
1853 # The key of the file to translate (ensure it is lowercase)
1854 my $translation_file_key = lc(shift(@_));
1855 # The number of chunks to return (defaults to one if not specified)
1856 my $num_chunks_to_return = shift(@_) || "1";
1857
1858 # Check that the necessary arguments were supplied
1859 if (!$target_language_code || !$translation_file_key) {
1860 &throw_fatal_error("Missing command argument.");
1861 }
1862
1863 my %source_files_key_to_text_mapping = ();
1864 my %target_files_key_to_text_mapping = ();
1865 my %source_files_key_to_last_update_date_mapping = ();
1866 my %target_files_key_to_last_update_date_mapping = ();
1867
1868 &build_gs3_configuration($target_language_code, \%source_files_key_to_text_mapping, \%target_files_key_to_text_mapping,
1869 \%source_files_key_to_last_update_date_mapping, \%target_files_key_to_last_update_date_mapping);
1870
1871
1872 # Chunks needing updating are those in the target file that have been more recently edited in the source file
1873 # All others are uptodate (which implies that they have certainly been translated at some point and would not be empty)
1874 my @uptodate_target_file_keys = ();
1875 foreach my $chunk_key (keys(%source_files_key_to_last_update_date_mapping)) {
1876 my $source_chunk_last_update_date = $source_files_key_to_last_update_date_mapping{$chunk_key};
1877 my $target_chunk_last_update_date = $target_files_key_to_last_update_date_mapping{$chunk_key};
1878
1879 # 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";
1880
1881 if (defined($target_chunk_last_update_date) && !&is_date_after($source_chunk_last_update_date, $target_chunk_last_update_date)) {
1882 # &log_message("Chunk with key $chunk_key needs updating.");
1883 push(@uptodate_target_file_keys, $chunk_key);
1884 }
1885 }
1886
1887 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);
1888
1889 return $xml_response;
1890}
1891
1892
1893sub build_gs3_configuration
1894{
1895 my ($target_language_code, $source_files_key_to_text_mapping, $target_files_key_to_text_mapping,
1896 $source_files_key_to_gti_comment_mapping, $target_files_key_to_gti_comment_mapping) = @_;
1897
1898 my $source_file_directory = &util::filename_cat("WEB-INF","classes"); # my $source_file_directory = "greenstone3";
1899 my $translation_file_type = "resource_bundle";
1900
1901 foreach my $interface_file_key (@gs3_interface_files) {
1902
1903 &log_message("Greenstone 3 interface file: " . $interface_file_key);
1904
1905 # Parse the source language and target language files
1906 my $source_file = &util::filename_cat($source_file_directory, $interface_file_key.".properties");
1907 my @source_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $source_file));
1908 my %source_file_key_to_line_mapping = &build_key_to_line_mapping(\@source_file_lines, $translation_file_type);
1909 my %source_file_key_to_text_mapping = &build_key_to_text_mapping(\@source_file_lines, \%source_file_key_to_line_mapping, $translation_file_type);
1910 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);
1911
1912 my $target_file = &util::filename_cat($source_file_directory, $interface_file_key."_".$target_language_code.".properties");
1913 my @target_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $target_file));
1914 my %target_file_key_to_line_mapping = &build_key_to_line_mapping(\@target_file_lines, $translation_file_type);
1915 my %target_file_key_to_text_mapping = &build_key_to_text_mapping(\@target_file_lines, \%target_file_key_to_line_mapping, $translation_file_type);
1916 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);
1917
1918
1919 # Filter out any automatically translated chunks
1920 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1921 if (&is_chunk_automatically_translated($chunk_key, $translation_file_type)) {
1922 delete $source_file_key_to_line_mapping{$chunk_key};
1923 delete $target_file_key_to_line_mapping{$chunk_key};
1924 }
1925 }
1926
1927 &log_message("Number of source chunks: " . scalar(keys(%source_file_key_to_text_mapping)));
1928 &log_message("Number of target chunks: " . scalar(keys(%target_file_key_to_text_mapping)));
1929
1930 foreach my $chunk_key (keys(%source_file_key_to_text_mapping)) {
1931 my $global_chunk_key = "$interface_file_key.$chunk_key";
1932 $source_files_key_to_text_mapping->{$global_chunk_key} = $source_file_key_to_text_mapping{$chunk_key};
1933 $source_files_key_to_gti_comment_mapping->{$global_chunk_key} = $source_file_key_to_gti_comment_mapping{$chunk_key};
1934
1935 if (defined $target_file_key_to_text_mapping{$chunk_key}) {
1936 $target_files_key_to_text_mapping->{$global_chunk_key} = $target_file_key_to_text_mapping{$chunk_key};
1937 $target_files_key_to_gti_comment_mapping->{$global_chunk_key} = $target_file_key_to_gti_comment_mapping{$chunk_key};
1938 }
1939 }
1940 }
1941}
1942
1943
1944sub write_translated_gs3interface
1945{
1946 my $source_file_key_to_text_mapping = shift(@_);
1947 my $target_file_key_to_text_mapping = shift(@_);
1948 my $target_file_key_to_gti_comment_mapping = shift(@_);
1949 my $target_language_code = shift(@_);
1950
1951 my @sorted_chunk_keys = sort (keys(%$source_file_key_to_text_mapping));
1952
1953 my %translated_interface_file_keys = ();
1954 foreach my $chunk_key (keys(%$target_file_key_to_text_mapping)) {
1955 $chunk_key =~ /^([^\.]+)?\.(.*)$/;
1956 if (!defined $translated_interface_file_keys{$1}) {
1957 &log_message("Updated interface file: " . $1);
1958 $translated_interface_file_keys{$1}="";
1959 }
1960 }
1961 &log_message("Updated interface files: " . scalar(keys(%translated_interface_file_keys)));
1962
1963 my $source_file_directory = "greenstone3";
1964
1965 foreach my $interface_file_key (keys(%translated_interface_file_keys)) {
1966
1967 # Build a mapping from chunk key to source file line, and from source file line to chunk key
1968 my $source_file = &util::filename_cat($source_file_directory, "$interface_file_key.properties");
1969 my @source_file_lines = &read_file_lines(&util::filename_cat($gsdl_root_directory, $source_file));
1970 my %source_file_key_to_line_mapping = &build_key_to_line_mapping_for_resource_bundle(@source_file_lines);
1971 my %source_file_line_to_key_mapping = ();
1972 foreach my $chunk_key (keys(%source_file_key_to_line_mapping)) {
1973 $source_file_line_to_key_mapping{$source_file_key_to_line_mapping{$chunk_key}} = $chunk_key;
1974 }
1975
1976 # Write the new target file
1977 my $target_file = &util::filename_cat($source_file_directory, $interface_file_key . "_" . $target_language_code . ".properties");
1978 my $target_file_path = &util::filename_cat($gsdl_root_directory, $target_file);
1979 if (!open(TARGET_FILE, ">$target_file_path")) {
1980 &throw_fatal_error("Could not write target file $target_file_path.");
1981 }
1982
1983 # Model the new target file on the source file, with the target file translations
1984 my $source_file_line_number = 0;
1985 foreach my $line_key (sort sort_by_line (keys(%source_file_line_to_key_mapping))) {
1986 # Fill in the gaps before this chunk starts
1987 my $source_file_chunk_starting_line_number = (split(/-/, $line_key))[0];
1988 my $source_file_chunk_finishing_line_number = (split(/-/, $line_key))[1];
1989 while ($source_file_line_number < $source_file_chunk_starting_line_number) {
1990 print TARGET_FILE $source_file_lines[$source_file_line_number];
1991 $source_file_line_number++;
1992 }
1993 $source_file_line_number = $source_file_chunk_finishing_line_number + 1;
1994
1995 my $chunk_key = $source_file_line_to_key_mapping{$line_key};
1996 my $global_chunk_key = "$interface_file_key.$chunk_key";
1997 my $source_file_chunk_text = $source_file_key_to_text_mapping->{$global_chunk_key};
1998 my $target_file_chunk_text = $target_file_key_to_text_mapping->{$global_chunk_key} || "";
1999
2000 # If no translation exists for this chunk, show this, and move on
2001 if ($source_file_chunk_text ne "" && $target_file_chunk_text eq "") {
2002 print TARGET_FILE "# -- Missing translation: $chunk_key\n";
2003 next;
2004 }
2005
2006 print TARGET_FILE "$chunk_key:$target_file_chunk_text";
2007 if ($target_file_key_to_gti_comment_mapping->{$global_chunk_key}) {
2008 print TARGET_FILE " # " . $target_file_key_to_gti_comment_mapping->{$global_chunk_key};
2009 }
2010 print TARGET_FILE "\n";
2011 }
2012
2013 close(TARGET_FILE);
2014 }
2015}
2016
2017&main(@ARGV);
Note: See TracBrowser for help on using the repository browser.