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

Last change on this file since 30548 was 30548, checked in by ak19, 8 years ago

Fixing gti.pl: previously for just the gs3interface module, if any strings that required translating got translated, they'd immediately be added to the list of strings that require updating. This was due to an oversight in build_gs3_configuration() which was not getting the last_updated_mapping when it needed, but always getting the gti_comments instead. Now it gets whichever of these is appropriate, and things work at last.

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