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

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

Forgot to add an important comment to the previous commit

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