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

Last change on this file since 29413 was 29413, checked in by ak19, 9 years ago

Forgot to add an important comment to the previous commit

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