source: main/tags/2.40/gsdl/bin/script/translator.pl@ 31150

Last change on this file since 31150 was 4234, checked in by jrm21, 21 years ago

oops... [:)] - need to return 0 on non-error from main()

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1#!/usr/bin/perl -w
2
3###########################################################################
4#
5# translator.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) 1999 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 if (!defined($ENV{'GSDLHOME'})) {
32 print STDERR "GSDLHOME not set\n";
33 return 1;
34 }
35 unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
36}
37
38
39use util;
40use GDBM_File;
41use CGI;
42
43
44my $translationdir;
45my $translationdb;
46my %translationdata = ();
47my $updatedb;
48my %updatedata = ();
49
50
51sub main
52{
53 # Get the name of the source (base) language
54 my $sourcelang = shift(@_);
55 # Get the name of the target language
56 my $targetlang = shift(@_);
57
58 # Check that both arguments were supplied
59 if (!$sourcelang) {
60 print STDERR "Error: You didn't supply the name of the source language!\n";
61 return 2;
62 }
63 if (!$targetlang) {
64 print STDERR "Error: You didn't supply the name of the target language!\n";
65 return 2;
66 }
67
68 # Casefold both language names
69 $sourcelang =~ tr/A-Z/a-z/;
70 $targetlang =~ tr/A-Z/a-z/;
71
72 my $gsdldir = "$ENV{'GSDLHOME'}";
73 my $macrosdir = util::filename_cat($gsdldir, "macros");
74 my $langdir = util::filename_cat($gsdldir, "tmp", "lang");
75
76 # Make sure this is a continuation of a previously initialised translation process
77 $translationdir = util::filename_cat($langdir, "$sourcelang-$targetlang");
78 if (! -e $translationdir) {
79 print STDERR "Error: Translation has not been initialised (exiting).\n";
80 return 3;
81 }
82
83 # Parse the source language macro files
84 my $sourcedmname1 = &get_macrofile_name($sourcelang, "");
85 my $sourcehash1 = &parse_macrofile($macrosdir, $sourcedmname1);
86 my $sourcedmname2 = &get_macrofile_name($sourcelang, "2");
87 my $sourcehash2 = &parse_macrofile($macrosdir, $sourcedmname2);
88
89 # Make sure some macros exist to be translated
90 if (!$sourcehash1 && !$sourcehash2) {
91 print STDERR "Error: No source macro information exists.\n";
92 return 4;
93 }
94
95 # Combine the two source hashes
96 my $sourcehash = &combine_hashes($sourcehash1, $sourcehash2);
97
98 # Open the existing translation and update databases
99 $translationdb = util::filename_cat($translationdir, "translation.db");
100 $updatedb = util::filename_cat($translationdir, "update.db");
101
102 # This translation is a continuation of an old one, so open the database files for it
103 tie(%translationdata, "GDBM_File", $translationdb, 1, 0640);
104 tie(%updatedata, "GDBM_File", $updatedb, 1, 0640);
105
106 # Parse the target language macro files
107 my $targetdmname1 = &get_macrofile_name($targetlang, "");
108 my $targethash1 = &parse_macrofile($macrosdir, $targetdmname1);
109 my $targetdmname2 = &get_macrofile_name($targetlang, "2");
110 my $targethash2 = &parse_macrofile($macrosdir, $targetdmname2);
111
112 # Combine the two target hashes
113 my $targethash = &combine_hashes($targethash1, $targethash2);
114
115 # Determine the macros that need translating
116 my $needstranslating = &find_macros_needing_translation($sourcelang, $sourcehash,
117 $targetlang, $targethash);
118
119 # Generates HTML code to display user interface on webpage with what needs translation
120 my $targetcode = $translationdata{"*target_lang_code*"};
121 print STDERR "Target language code: $targetcode\n";
122 &generate_pages($sourcelang, $targetlang, $targetcode, $needstranslating);
123
124 # Clean up
125 untie %translationdata;
126 untie %updatedata;
127
128 return 0;
129}
130
131
132sub get_macrofile_name
133{
134 my ($language, $suffix) = @_;
135
136 my $macrofilename = $language;
137
138 # Handles cases where the macro file name is different to the language name
139 $macrofilename = "port" if ($language =~ m/portuguese/);
140 $macrofilename = "indo" if ($language =~ m/indonesian/);
141
142 # Add suffix (if any) and file extension, and return
143 $macrofilename = $macrofilename . $suffix . ".dm";
144 return $macrofilename;
145}
146
147
148sub parse_macrofile
149{
150 my ($macrosdir, $macrofile) = @_;
151
152 # Opens macro file or returns
153 my $macropath = util::filename_cat($macrosdir, $macrofile);
154 open(IN, "<$macropath") or return;
155
156 # Initialises some local variables
157 my $currpackage;
158 my %macros = ();
159
160 # Reads in contents of macro file, line by line
161 while (<IN>) {
162 # Check if a new package is being defined
163 if (s/^package //) {
164 $currpackage = $_;
165 chomp($currpackage);
166 }
167
168 # Line contains a macro name
169 elsif (/^(_\w+_)/) {
170 # gets the name of the macro ($1 contains text matched by corresponding
171 # set of parentheses in the last matched pattern within the dynamic scope
172 # which here matches (_\w+_) )
173 my $macroname = $1;
174 my $macrotext;
175
176 # get the first line of the macro
177 $_ =~ s/^_\w+_ *//;
178 # while there is still text of the macro to go...
179 while ($_ !~ /.*\}/) {
180 # ... adds it to the macrotext variable
181 $macrotext .= $_;
182 $_ = <IN>;
183 }
184 $macrotext .= $_;
185
186 # The key consists of macro file, package name, and macro name
187 my $key = $macrofile . "::" . $currpackage . "::" . $macroname;
188 # Store the macro text in the database
189 $macros{$key} = $macrotext;
190 }
191
192 # Icon: line in format ## "sometext" ## macro ## macroname ##
193 elsif (/^\#\# .*/) {
194 my $macroname = $_;
195 my $macrotext;
196
197 #if the macro text contains a new line will run over two lines
198 unless ($macroname =~ m/^\#\# .*\#\#/) {
199 $macroname = $_;
200 chomp($macroname);
201 $_ = <IN>;
202 $macroname .= $_;
203 }
204
205 #split the image macro header on ##
206 my @names = split(/\s*\#\#\s*/, $macroname);
207 #save current contents of macroname into macrotext
208 $macrotext .= $macroname;
209 $_ = <IN>;
210 # overwrite macroname with macroname from ## ... ## ... ## HERE ###
211 $macroname = $names[(scalar @names) - 1];
212
213 # read in the rest of the text associated with the image macro
214 while ($_ !~ /^\s+/) {
215 $macrotext .= $_;
216 $_ = <IN>;
217 }
218
219 # key to the hash and the database
220 my $key = $macrofile . "::" . $currpackage . "::" . $macroname;
221 # print "Icon, key: $key\n text: $macrotext\n";
222 # hashes macroname and macrotext
223 $macros{$key} = $macrotext;
224 }
225 }
226 close IN;
227
228 return (\%macros);
229}
230
231
232# Combines two existing hashes of the same format together
233sub combine_hashes
234{
235 my ($hash1, $hash2) = @_;
236 my %combined = ();
237
238 foreach $key (sort (keys %$hash1)) {
239 $combined{$key} = $hash1->{$key};
240 }
241 foreach $key (sort (keys %$hash2)) {
242 $combined{$key} = $hash2->{$key};
243 }
244
245 return (\%combined);
246}
247
248
249sub find_macros_needing_translation
250{
251 my ($sourcelang, $sourcehash, $targetlang, $targethash) = @_;
252
253 # Find source macros whose text has changed
254 foreach $sourcekey (sort keys(%$sourcehash)) {
255 if ($translationdata{$sourcekey}) {
256 if ($translationdata{$sourcekey} ne $sourcehash->{$sourcekey}) {
257 my $targetkey = &get_macroname_equivalent($sourcelang, $targetlang,
258 $sourcekey);
259 if ($targethash->{$targetkey}) {
260 $updatedata{$targetkey} = $targethash->{$targetkey};
261 }
262 else {
263 $updatedata{$targetkey} = "";
264 }
265 }
266 }
267 }
268
269 # Clear out the translation database
270 my $targetcode = $translationdata{"*target_lang_code*"};
271 untie %translationdata;
272 unlink($translationdb);
273 %translationdata = ();
274 tie(%translationdata, "GDBM_File", $translationdb, GDBM_WRCREAT, 0640);
275 $translationdata{"*target_lang_code*"} = $targetcode;
276
277 # Re-add source and target macros
278 foreach $sourcekey (sort keys(%$sourcehash)) {
279 $translationdata{$sourcekey} = $sourcehash->{$sourcekey};
280 }
281 foreach $targetkey (sort keys(%$targethash)) {
282 $translationdata{$targetkey} = $targethash->{$targetkey};
283 }
284
285 my %needstranslating = ();
286
287 # Macros that need translating are those that are in the source language macro file
288 # but no translation exists in the translation database...
289 foreach $sourcekey (sort keys(%$sourcehash)) {
290 my $targetkey = &get_macroname_equivalent($sourcelang, $targetlang, $sourcekey);
291 if (!$translationdata{$targetkey}) {
292 $needstranslating{$sourcekey}[0] = $sourcehash->{$sourcekey};
293 $needstranslating{$sourcekey}[1] = "";
294 }
295 }
296
297 # ...and those in the database of macros to update
298 foreach $targetkey (sort keys(%updatedata)) {
299 my $sourcekey = &get_macroname_equivalent($targetlang, $sourcelang, $targetkey);
300 $needstranslating{$sourcekey}[0] = $sourcehash->{$sourcekey};
301 $needstranslating{$sourcekey}[1] = $updatedata{$targetkey};
302 }
303
304 return (\%needstranslating);
305}
306
307
308sub get_macroname_equivalent
309{
310 my ($languageA, $languageB, $languageAkey) = @_;
311
312 my $macrofile = $languageAkey;
313 $macrofile =~ s/^([^:]+)::(.*)/$1/;
314 my $key = $languageAkey;
315 $key =~ s/^([^:])+::(.*)/$2/;
316
317 my $languageBkey;
318 if ($macrofile eq &get_macrofile_name($languageA, "")) {
319 $languageBkey = &get_macrofile_name($languageB, "") . "::" . $key;
320 }
321 if ($macrofile eq &get_macrofile_name($languageA, "2")) {
322 $languageBkey = &get_macrofile_name($languageB, "2") . "::" . $key;
323 }
324
325 return ($languageBkey);
326}
327
328
329# generates whole website by seperating hash of differences into groups
330# of at most 15 macros and then generating a page from each group
331sub generate_pages
332{
333 my ($sourcelang, $targetlang, $targetcode, $needstranslating) = @_;
334
335 my %pagehash = ();
336
337 $pageno = 1;
338 $keysperpage = 0;
339 $limit = 15;
340
341 # goes through hash of differences between base language and translation
342 # sorted first by whether it is from the core or auxiliary macrofile then
343 # alphabetically by macroname
344 foreach $sourcekey (sort keys(%$needstranslating)) {
345 #nested hash. The pagehash contains a hash of all of the translation pages.
346 #Then each page is itself contains a hash of all the macros on that page, where
347 #each macro is associated with some macrotext and whether it was from the core
348 #or auxiliary macrofile
349 $pagehash->{$pageno}->{$sourcekey} = $needstranslating->{$sourcekey};
350 $keysperpage++;
351
352 # If have enough keys to generate a page with
353 if ($keysperpage == $limit) {
354 #opens a file to write the HTML code to for current page
355 my $transpagefile = util::filename_cat($translationdir, $pageno . ".lang");
356 open HTMLFILE, ">$transpagefile";
357
358 #passes hash of keys for current page and filehandle, returns CGI data
359 &generate_form($sourcelang, $targetlang, $targetcode,
360 $pagehash->{$pageno}, \*HTMLFILE);
361 close HTMLFILE;
362 $pageno++;
363
364 #resets key counting variable
365 $keysperpage = 0;
366 $limit = 15;
367 }
368 }
369
370 # if you exit the loop with keys still to write
371 if ($keysperpage != 0) {
372 # writes out the remaining keys, same format as above
373 my $transpagefile = util::filename_cat($translationdir, $pageno . ".lang");
374 open HTMLFILE, ">$transpagefile";
375 &generate_form($sourcelang, $targetlang, $targetcode, $pagehash->{$pageno}, \*HTMLFILE);
376 close HTMLFILE;
377 $pageno++;
378 }
379
380 # Write the number of pages to a file for use by the receptionist
381 my $numpagesfile = util::filename_cat($translationdir, "numpages.log");
382 if (!open NUMPAGESLOG, ">$numpagesfile") {
383 print STDERR "Error: Could not write $numpagesfile.\n";
384 return 5;
385 }
386 print NUMPAGESLOG ($pageno - 1);
387 close NUMPAGESLOG;
388
389 # Write thankyou page for language translator once translation is complete
390 my $thankyoufile = util::filename_cat($translationdir, "thankyou.lang");
391 if (!open THANKYOU, ">$thankyoufile") {
392 print STDERR "Error: Could not write $thankyoufile.\n";
393 return 6;
394 }
395 print THANKYOU ("<center> _textthanks_ $targetlang _texttrans_ ",
396 "<br> _textviewtranslation_ ",
397 "<a href=\"_gwcgi_?a=p&p=home&l=$targetcode\">_texthere_</a>.",
398 # "_textgetdmfiles_ ",
399 # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . ".dm\">_texthere_</a>",
400 # " &amp; ",
401 # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . "2.dm\">_texthere_</a>.",
402 "<p></center>\n");
403 close THANKYOU;
404}
405
406
407sub generate_form
408{
409 my $sourcelang = shift(@_);
410 my $targetlang = shift(@_);
411 my $targetcode = shift(@_);
412 my $formhash = shift(@_);
413 my $fh = shift(@_);
414
415 # common gateway interface for writing the stuff to the web
416 my $query = new CGI;
417 my $keynamecount;
418
419 $first = "true";
420 foreach $key (sort (keys(%$formhash))) {
421
422 my $text = $formhash->{$key}[0];
423 my $default = $formhash->{$key}[1];
424 # print "Text: $text Default: $default\n";
425
426 # whole lot of formatting on strings
427 # escape all the '_' with '\' so that Greenstone doesn't substitute
428 # the macro names for the contents of the macro itself
429 my $keyname = $key;
430 $keyname =~ s/_/\\_/g;
431
432 $text =~ s/_/\\_/g;
433 $text =~ s/(\[l=.*\])//;
434 $default =~ s/_/\\_/g;
435 $default =~ s/(\[l=.*\] )//;
436
437 # must take care of image macros single and multiple line ones
438 $text =~ s/(\<br\>\#\#.*\#\#\s*\<br\>).*/$1/g;
439 $default =~ s/(\<br\>\#\#.*\#\#\s*\<br\>).*/$1/g;
440
441 # format the text for displaying in the browser
442 $text =~ s/[\{\}]//g unless($text =~ m/\#\# /);
443 $default =~ s/[\{\}]//g unless($text =~ m/\#\# /);
444
445 $text =~ s/(\A\n)//;
446 $text =~ s/(\n\Z)//;
447 $default =~ s/(\A\n)//;
448 $default =~ s/(\n\Z)//;
449
450 #tells whether is a core or auxiliary macro
451 my $priority = &core_or_auxiliary_macro($sourcelang, $keyname);
452
453 if ($first eq "true") {
454 print $fh ($query->hidden(-name=>"tlng",-default=>"$targetlang"),
455 $query->hidden(-name=>"bl",-default=>"$sourcelang"));
456
457 # If it is a core macro, table header displays core
458 if ($priority eq "1") {
459 print $fh "</center><img src=\"_httpimg_/core.gif\">";
460
461 #shows that have seen first macro of core type
462 $first = "been";
463 }
464
465 # If it is an auxiliary macro, table header displays auxiliary
466 if ($priority eq "2") {
467 print $fh "</center><img src=\"_httpimg_/auxiliary.gif\">";
468 $first = "false";
469 }
470
471 print $fh ("<table cellpadding=\"10\">\n<tr>",
472 "<td><strong><center>". uc ($sourcelang) . "</center></strong></td>\n",
473 "<td><strong><center>". uc ($targetlang) . "</center></strong></td>\n",
474 "</tr>\n");
475 }
476
477 # If this is the first auxiliary macro, but not the first macro, finish the
478 # current table and start a new auxiliary table underneath
479 if($priority eq "2" and $first eq "been") {
480 print $fh ("</table><br></center><img src=\"_httpimg_/auxiliary.gif\">",
481 "<table cellpadding=\"10\">\n<tr>",
482 "<td><strong><center>". uc ($sourcelang) . "</strong></center></td>\n",
483 "<td><strong><center>". uc ($targetlang) . "</strong></center></td>\n",
484 "</tr>\n");
485 $first = "false";
486 }
487
488 # so that when it is an image only get the text to translate not coding stuff aswell
489 if ($text =~ m/\#\# (^|.)*/) {
490 # print STDERR "\nIcon -- Key: $keyname\nText: $text\n"; # Default: $default\n";
491 if ($& =~ m/".*"/) {
492 #prints to the file two text fields, the left one contains the base language
493 #macro text, the right contains the translation language macro text if any exists
494 my $icontext = $&;
495 $icontext =~ s/"(.*)"/$1/;
496 # print "Text to translate: $icontext\n";
497
498 if ($default =~ m/\#\# (^|.)*/) {
499 if ($& =~ m/".*"/) {
500 $default = $&;
501 $default =~ s/"(.*)"/$1/;
502 }
503 }
504 # print "Default: $default\n";
505
506 my $remainder = $text;
507 $remainder =~ s/^\#\# ([^\#]*)//;
508 # print "Remainder: $remainder\n";
509
510 $rows = 1;
511
512 my $textbox1;
513 if (1) {
514 $textbox1 = "<textarea name=\"whocaresh\" rows=\"$rows\" cols=\"50\" readonly=\"1\">";
515 $textbox1 .= "$icontext</textarea>\n";
516 }
517 else {
518 $textbox1 = $query->textfield(-name=>"whocares", -default=>"$icontext",
519 -size=>50, -readonly=>1);
520 }
521 print $fh ("<tr><td align=center>", $textbox1, "</td><td align=center>");
522
523 my $textbox2;
524 if (1) {
525 $textbox2 = "<textarea name=\"$keyname\" rows=\"$rows\" cols=\"50\">";
526 $textbox2 .= "$default</textarea><br>\n";
527 }
528 else {
529 $textbox2 = $query->textfield(-name=>"$keyname", -default=>"$default",
530 -size=>50);
531 }
532 print $fh $textbox2;
533
534 print $fh $query->hidden(-name=>"$keyname" . "::icontext",
535 -default=>"$remainder");
536 print $fh "</td></tr>\n";
537 }
538 }
539 # Text macro
540 elsif ($text =~ m/\S+/) {
541 $text =~ s/\s+/ /g;
542
543 my @words = split(/ /, $text);
544 my $words = scalar(@words);
545 my $rows = sprintf("%.0f", $words/5);
546 print $fh ("<tr><td align=center>\n");
547 #determines how many rows of text are required to display the base language
548 #macro text then use this value to determine whether we should use a text area
549 #or a text field
550
551 # Experimenting with textfield and text area
552 # for now use exclusively textareas.
553
554 # if ($rows >= 0) {
555 if (1) {
556 $rows = 1 if ($rows < 1);
557
558 #prints to the file two text areas, the left one contains the base language
559 #macro text, the right contains the translation language macro text if any exists
560
561 $text =~ s/<p>/<p>\n/g;
562 $text =~ s/<br>/<br>\n/g;
563 $text =~ s/<\/td>/<\/td>\n/g;
564
565 print $fh ("<textarea name=\"whocaresh\" rows=\"$rows\" cols=\"50\" readonly=\"1\">",
566 "$text</textarea>\n",
567 "</td><td align=center>\n",
568 "<textarea name=\"$keyname\" rows=\"$rows\" cols=\"50\">",
569 "$default</textarea><br></td></tr>\n");
570 }
571 else {
572 #prints to the file two text fields, the left one contains the base language
573 #macro text, the right contains the translation language macro text if any exists
574 print $fh ($query->textfield(-name=>"whocares",
575 -default=>"$text",
576 -size=>50,
577 -readonly=>1),
578 "</td><td align=center>\n",
579 $query->textfield(-name=>"$keyname" . "$keynamecount",
580 -default=>"$default",
581 -size=>50),
582 "<br></td></tr>\n");
583 }
584 }
585 }
586
587 #finishes table and adds a SUBMIT CHANGES option on the end
588 print $fh ("</table><br><center>\n",
589 "<table><tr><td align=left width=\"50%\">",
590 $query->reset("RESET FORM"),
591 "</td><td align=right width=\"50%\">",
592 $query->submit($pageno, "_textsubmit_"),
593 "</td></tr><tr><td width=\"50%\"></td><td align=right width=\"50%\">",
594 "<br> _textsubmittext_ <br> _textviewtranslation_ ",
595 "<a href=\"_gwcgi_?a=p&p=home&l=$targetcode\">_texthere_</a>.",
596 # "_textgetdmfiles_ ",
597 # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . ".dm\">_texthere_</a>",
598 # " &amp; ",
599 # "<a href=\"_httpprefix_/gsdl/macros/$targetlang" . "2.dm\">_texthere_</a>.",
600 "</td></tr></table>");
601}
602
603
604sub core_or_auxiliary_macro
605{
606 my ($language, $sourcekey) = @_;
607
608 my $macrofile = $sourcekey;
609 $macrofile =~ s/^([^:]+)::(.*)/$1/;
610
611 if ($macrofile eq ($language . ".dm")) {
612 return "1"; # Core macro
613 }
614 if ($macrofile eq ($language . "2.dm")) {
615 return "2"; # Auxiliary macro
616 }
617}
618
619
620sub display_hash
621{
622 my ($hash) = @_;
623
624 foreach $key (sort (keys %$hash)) {
625 print $key . "\n";
626 print $hash->{$key} . "\n";
627 }
628}
629
630exit &main(@ARGV);
Note: See TracBrowser for help on using the repository browser.