3 # Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 # Copyright (C) 2006 - 2007 by Daniel Stenberg
13 # binary version for the binary lang file
14 my $langversion = 3; # 3 was the latest one used in the v1 format
16 # A note for future users and readers: The original v1 language system allowed
17 # the build to create and use a different language than english built-in. We
18 # removed that feature from our build-system, but the build scripts still had
19 # the ability. But, starting now, this ability is no longer provided since I
20 # figured it was boring and unnecessary to write support for now since we
21 # don't use it anymore.
25 Usage: genlang [options] <langv2 file>
28 Make the tool create a [prefix].c and [prefix].h file.
31 Make the tool create a binary language (.lng) file namaed [outfile].
32 The use of this option requires that you also use -e.
35 Update language file. Given the translated file and the most recent english
36 file, you\'ll get an updated version sent to stdout. Suitable action to do
37 when you intend to update a translation.
39 -e=<english lang file>
40 Point out the english (original source) file, to use that as master
41 language template. Used in combination with -b or -u.
44 Specify which target you want the translations/phrases for. Required when
47 The target can in fact be specified as numerous different strings,
48 separated with colons. This will make genlang to use all the specified
49 strings when searching for a matching phrase.
52 Voice mode output. Outputs all id: and voice: lines for the given target!
55 Enables verbose (debug) output.
63 # 1) scan the english file, keep the whole <phrase> for each phrase.
64 # 2) read the translated file, for each end of phrase, compare:
65 # A) all source strings, if there's any change there should be a comment about
69 # 3) output the phrase with the comments from above
70 # 4) check which phrases that the translated version didn't have, and spit out
71 # the english version of those
81 my $check = ($binary?
1:0) + ($prefix?
1:0) + ($update?
1:0) + ($voiceout?
1:0);
84 print "Please use only one of -p, -u, -o and -b\n";
88 print "Please use at least one of -p, -u, -o and -b\n";
91 if(($binary || $update || $voiceout) && !$english) {
92 print "Please use -e too when you use -b, -o or -u\n";
97 if(!$target && !$update) {
98 print "Please specify a target (with -t)!\n";
103 my %id; # string to num hash
104 my @idnum; # num to string array
106 my %source; # id string to source phrase hash
107 my %dest; # id string to dest phrase hash
108 my %voice; # id string to voice phrase hash
110 my $input = $ARGV[0];
116 my ($string, $pattern)=@_;
118 $pattern =~ s/\*/.?*/g;
119 $pattern =~ s/\?/./g;
121 return ($string =~ /^$pattern\z/);
130 my ($full, $n, $v)=@_;
136 my ($full, $n, $v)=@_;
141 my ($debug, $strref, $full, $n, $v)=@_;
143 my @all= split(" *, *", $n);
146 # print "TEST ($debug) $target for $test\n";
147 for my $part (split(":", $target)) {
148 if(match
($part, $test)) {
150 # print "MATCH: $test => $v\n";
160 parsetarget
("src", \
$src, @_);
165 parsetarget
("dest", \
$dest, @_);
170 parsetarget
("voice", \
$voice, @_);
176 # For the cases where the english file needs to be scanned/read, we do
177 # it before we read the translated file. For -b it isn't necessary, but for
178 # -u it is convenient.
180 my $idnum=0; # start with a true number
181 my $vidnum=0x8000; # first voice id
182 open(ENG
, "<$english") || die "can't open $english";
189 # get rid of DOS newlines
192 if($_ =~ /^ *\<phrase\>/) {
193 # this is the start of a phrase
195 elsif($_ =~ /^ *\<\/phrase\
>/) {
197 # if id is something, when we count and store this phrase
199 # voice-only entries get a difference range
200 if($id =~ /^VOICE_/) {
201 # Assign an ID number to this entry
206 # Assign an ID number to this entry
209 print STDERR
"DEST: bumped idnum to $idnum\n";
212 # this is the end of a phrase, add it to the english hash
213 $english{$id}=join("", @phrase);
219 # gather everything related to this phrase
221 if($_ =~ /^ *\<dest\>/i) {
224 elsif($withindest && ($_ =~ /^ *\<\/dest\
>/i
)) {
227 elsif($withindest && ($_ =~ / *([^:]+): *(.*)/)) {
228 my ($name, $val)=($1, $2);
229 $dest=""; # in case it is left untouched for when the
230 # model name isn't "our"
231 dest
($_, $name, $val);
233 print STDERR
"DEST: \"$dest\" for $name / $id\n";
235 if($update || ($dest && ($dest !~ /^none\z/i))) {
236 # we unconditionally always use all IDs when the "update"
239 print STDERR
"DEST: use this id $id\n";
244 if($_ =~ /^ *id: ([^ \t\n]+)/i) {
251 # a function that compares the english phrase with the translated one.
252 # compare source strings and desc
254 # Then output the updated version!
256 my ($idstr, $engref, $locref)=@_;
258 my ($esource, $lsource);
261 for my $l (@
$engref) {
262 if($l =~ /^ *desc: (.*)/) {
265 elsif($l =~ / *\<source\>/i) {
269 if($l =~ / *\<\/source\
>/i
) {
280 for my $l (@
$locref) {
281 if($l =~ /^ *desc: (.*)/) {
283 if($edesc ne $ldesc) {
284 $l = "### The 'desc' field differs from the english!\n### the previously used desc is commented below:\n### desc: $ldesc\n desc: $edesc\n";
288 elsif($l =~ / *\<source\>/i) {
293 if($l =~ / *\<\/source\
>/i
) {
296 if($esource ne $lsource) {
297 print "### The <source> section differs from the english!\n",
298 "### the previously used one is commented below:\n";
299 for(split("\n", $lsource)) {
307 undef @show; # start over
324 my $idcount; # counter for lang ID numbers
325 my $voiceid=0x8000; # counter for voice-only ID numbers
328 # Now start the scanning of the selected language string
331 open(LANG
, "<$input") || die "couldn't read language file named $input\n";
337 # get rid of DOS newlines
340 if($_ =~ /^( *\#|[ \t\n\r]*\z)/) {
341 # comment or empty line
351 # this is an XML-lookalike tag
352 if (/^(<|[^\"<]+<)([^>]*)>/) {
354 # print "P: $part\n";
357 # this was a closing tag
359 if($part eq "/phrase") {
362 my $idstr = $phrase{'id'};
365 if($dest =~ /^none\z/i) {
366 # "none" as dest (without quotes) means that this entire
367 # phrase is to be ignored
370 # we don't do the fully detailed analysis when we "update"
371 # since we don't do it for a particular target etc
373 # allow the keyword 'deprecated' to be used on dest and
374 # voice strings to mark that as deprecated. It will then
375 # be replaced with "".
377 $dest =~ s/^deprecate(|d)\z/\"\"/i;
378 $voice =~ s/^deprecate(|d)\z/\"\"/i;
380 # basic syntax error alerts, if there are no quotes we
381 # will assume an empty string was intended
383 print STDERR
"Warning: dest before $input line $line lacks quotes ($dest)!\n";
387 print STDERR
"Warning: source before $input line $line lacks quotes ($src)!\n";
390 if($voice !~ /^\"/) {
391 print STDERR
"Warning: voice before $input line $line lacks quotes ($voice)!\n";
395 # Use the ID name to figure out which id number range we
396 # should use for this phrase. Voice-only strings are
399 if($idstr =~ /^VOICE/) {
406 $id{$idstr} = $idnum;
407 $idnum[$idnum]=$idstr;
409 $source{$idstr}=$src;
411 $voice{$idstr}=$voice;
414 print "id: $phrase{id} ($idnum)\n";
415 print "source: $src\n";
416 print "dest: $dest\n";
417 print "voice: $voice\n";
427 my $e = $english{$idstr};
430 # compare original english with this!
431 my @eng = split("\n", $english{$idstr});
433 compare
($idstr, \
@eng, \
@phrase);
435 $english{$idstr}=""; # clear it
438 print "### $idstr: The phrase is not used. Skipped\n";
445 # starts with a slash, this _ends_ this section
446 $m = pop @m; # get back old value, the previous level's tag
450 # This is an opening (sub) tag
452 push @m, $m; # store old value
457 if(/^ *([^:]+): *(.*)/) {
458 my ($name, $val)=($1, $2);
459 &$m($_, $name, $val);
469 "### This phrase below was not present in the translated file\n",
478 # We create a .c and .h file
480 open(HFILE
, ">$prefix.h") ||
481 die "couldn't create file $prefix.h\n";
482 open(CFILE
, ">$prefix.c") ||
483 die "couldn't create file $prefix.c\n";
486 /* This file was automatically generated using genlang */
488 * The str() macro/functions is how to access strings that might be
489 * translated. Use it like str(MACRO) and expect a string to be
492 #define str(x) language_strings[x]
494 /* this is the array for holding the string pointers.
495 It will be initialized at runtime. */
496 extern unsigned char *language_strings[];
497 /* this contains the concatenation of all strings, separated by \\0 chars */
498 extern const unsigned char language_builtin[];
500 /* The enum below contains all available strings */
506 /* This file was automaticly generated using genlang, the strings come
511 unsigned char *language_strings[LANG_LAST_INDEX_IN_ARRAY];
512 const unsigned char language_builtin[] =
516 # Output the ID names for the enum in the header file
518 for $i (1 .. $idcount) {
519 my $name=$idnum[$i - 1]; # get the ID name
521 $name =~ s/\"//g; # cut off the quotes
523 printf HFILE
(" %s,\n", $name);
526 # Output separation marker for last string ID and the upcoming voice IDs
529 LANG_LAST_INDEX_IN_ARRAY, /* this is not a string, this is a marker */
530 /* --- below this follows voice-only strings --- */
531 VOICEONLY_DELIMITER = 0x8000,
535 # Output the ID names for the enum in the header file
536 for $i (0x8000 .. ($voiceid-1)) {
537 my $name=$idnum[$i]; # get the ID name
539 $name =~ s/\"//g; # cut off the quotes
541 printf HFILE
(" %s,\n", $name);
545 print HFILE
"\n};\n/* end of generated enum list */\n";
547 # Output the target phrases for the source file
548 for $i (1 .. $idcount) {
549 my $name=$idnum[$i - 1]; # get the ID
550 my $dest = $dest{$name}; # get the destination phrase
552 $dest =~ s
:\"$:\\0\":; # insert a \0 before the second quote
555 # this is just to be on the safe side
559 printf CFILE
(" %s\n", $dest);
562 # Output end of string chunk
565 /* end of generated string list */
571 } # end of the c/h file generation
573 # Creation of a binary lang file was requested
575 # We must first scan the english file to get the correct order of the id
576 # numbers used there, as that is what sets the id order for all language
577 # files. The english file is scanned before the translated file was
580 open(OUTF
, ">$binary") or die "Can't create $binary";
582 printf OUTF
("\x1a%c", $langversion); # magic lang file header
584 # loop over the target phrases
585 for $i (1 .. $idcount) {
586 my $name=$idnum[$i - 1]; # get the ID
587 my $dest = $dest{$name}; # get the destination phrase
590 $dest =~ s/^\"(.*)\"\s*$/$1/g; # cut off quotes
592 # Now, make sure we get the number from the english sort order:
593 $idnum = $idmap{$name};
595 printf OUTF
("%c%c%s\x00", ($idnum>>8), ($idnum&0xff), $dest);
600 # voice output requested, display id: and voice: strings in a v1-like
605 # This loops over the strings in the translated language file order
606 my @ids = ((0 .. ($idcount-1)));
607 push @ids, (0x8000 .. ($voiceid-1));
614 my $name=$idnum[$i]; # get the ID
615 my $dest = $voice{$name}; # get the destination voice string
618 $dest =~ s/^\"(.*)\"\s*$/$1/g; # cut off quotes
620 # Now, make sure we get the number from the english sort order:
621 $idnum = $idmap{$name};
625 # print "Input index $i output index $idnum\n";
633 my $name=$idnum[$o]; # get the ID
634 my $dest = $voice{$name}; # get the destination voice string
636 print "#$i\nid: $name\nvoice: $dest\n";
643 printf("%d ID strings scanned\n", $idcount);
647 printf "$_: %s\n", $head{$_};