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

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

Now processes multi-line properties in properties files (resource bundles). Discovered the need for this change when testing submissions for GS3 translations, otherwise all except the first line of any property in an English GS3 prop file, ends up in the matching language property file after the submission step.

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