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

Last change on this file since 29411 was 29411, checked in by ak19, 6 years ago

There is rudimentary support for translating Greenstone 3 online via the GTI, activating that support, with slight modifications.

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