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 = 4; # 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 named [outfile].
32 The use of this option requires that you also use -e, -t and -i.
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 The target id number, needed for -b.
55 Voice mode output. Outputs all id: and voice: lines for the given target!
58 Enables verbose (debug) output.
66 # 1) scan the english file, keep the whole <phrase> for each phrase.
67 # 2) read the translated file, for each end of phrase, compare:
68 # A) all source strings, if there's any change there should be a comment about
72 # 3) output the phrase with the comments from above
73 # 4) check which phrases that the translated version didn't have, and spit out
74 # the english version of those
84 my $check = ($binary?
1:0) + ($prefix?
1:0) + ($update?
1:0) + ($voiceout?
1:0);
87 print "Please use only one of -p, -u, -o and -b\n";
91 print "Please use at least one of -p, -u, -o and -b\n";
95 if(($binary || $update || $voiceout) && !$english) {
96 print "Please use -e too when you use -b, -o or -u\n";
101 if($binary && !$target_id) {
102 print "Please specify a target id number (with -i)!\n";
107 if(!$target && !$update) {
108 print "Please specify a target (with -t)!\n";
113 my %id; # string to num hash
114 my @idnum; # num to string array
116 my %source; # id string to source phrase hash
117 my %dest; # id string to dest phrase hash
118 my %voice; # id string to voice phrase hash
120 my $input = $ARGV[0];
126 my ($string, $pattern)=@_;
128 $pattern =~ s/\*/.?*/g;
129 $pattern =~ s/\?/./g;
131 return ($string =~ /^$pattern\z/);
140 my ($full, $n, $v)=@_;
146 my ($full, $n, $v)=@_;
151 my ($debug, $strref, $full, $n, $v)=@_;
153 my @all= split(" *, *", $n);
156 # print "TEST ($debug) $target for $test\n";
157 for my $part (split(":", $target)) {
158 if(match
($part, $test)) {
160 # print "MATCH: $test => $v\n";
170 parsetarget
("src", \
$src, @_);
175 parsetarget
("dest", \
$dest, @_);
180 parsetarget
("voice", \
$voice, @_);
186 # For the cases where the english file needs to be scanned/read, we do
187 # it before we read the translated file. For -b it isn't necessary, but for
188 # -u it is convenient.
190 my $idnum=0; # start with a true number
191 my $vidnum=0x8000; # first voice id
192 open(ENG
, "<$english") || die "can't open $english";
199 # get rid of DOS newlines
202 if($_ =~ /^ *\<phrase\>/) {
203 # this is the start of a phrase
205 elsif($_ =~ /^ *\<\/phrase\
>/) {
207 # if id is something, when we count and store this phrase
209 # voice-only entries get a difference range
210 if($id =~ /^VOICE_/) {
211 # Assign an ID number to this entry
216 # Assign an ID number to this entry
219 # print STDERR "DEST: bumped idnum to $idnum\n";
222 # this is the end of a phrase, add it to the english hash
223 $english{$id}=join("", @phrase);
229 # gather everything related to this phrase
231 if($_ =~ /^ *\<dest\>/i) {
234 elsif($withindest && ($_ =~ /^ *\<\/dest\
>/i
)) {
237 elsif($withindest && ($_ =~ / *([^:]+): *(.*)/)) {
238 my ($name, $val)=($1, $2);
239 $dest=""; # in case it is left untouched for when the
240 # model name isn't "our"
241 dest
($_, $name, $val);
243 # print STDERR "DEST: \"$dest\" for $name / $id\n";
245 if($update || ($dest && ($dest !~ /^none\z/i))) {
246 # we unconditionally always use all IDs when the "update"
249 # print STDERR "DEST: use this id $id\n";
254 if($_ =~ /^ *id: ([^ \t\n]+)/i) {
261 # a function that compares the english phrase with the translated one.
262 # compare source strings and desc
264 # Then output the updated version!
266 my ($idstr, $engref, $locref)=@_;
268 my ($esource, $lsource);
271 for my $l (@
$engref) {
276 if($l =~ /^ *desc: (.*)/) {
279 elsif($l =~ / *\<source\>/i) {
283 if($l =~ / *\<\/source\
>/i
) {
294 for my $l (@
$locref) {
295 if($l =~ /^ *desc: (.*)/) {
297 if($edesc ne $ldesc) {
298 $l = "### The 'desc' field differs from the english!\n### the previously used desc is commented below:\n### desc: $ldesc\n desc: $edesc\n";
302 elsif($l =~ / *\<source\>/i) {
307 if($l =~ / *\<\/source\
>/i
) {
310 if($esource ne $lsource) {
311 print "### The <source> section differs from the english!\n",
312 "### the previously used one is commented below:\n";
313 for(split("\n", $lsource)) {
321 undef @show; # start over
338 my $idcount; # counter for lang ID numbers
339 my $voiceid=0x8000; # counter for voice-only ID numbers
342 # Now start the scanning of the selected language string
345 open(LANG
, "<$input") || die "couldn't read language file named $input\n";
351 # get rid of DOS newlines
354 if($_ =~ /^( *\#|[ \t\n\r]*\z)/) {
355 # comment or empty line
365 # this is an XML-lookalike tag
366 if (/^(<|[^\"<]+<)([^>]*)>/) {
368 # print "P: $part\n";
371 # this was a closing tag
373 if($part eq "/phrase") {
376 my $idstr = $phrase{'id'};
379 if($binary && !$english{$idstr}) {
380 # $idstr doesn't exist for english, skip it\n";
382 elsif($dest =~ /^none\z/i) {
383 # "none" as dest (without quotes) means that this entire
384 # phrase is to be ignored
387 # we don't do the fully detailed analysis when we "update"
388 # since we don't do it for a particular target etc
390 # allow the keyword 'deprecated' to be used on dest and
391 # voice strings to mark that as deprecated. It will then
392 # be replaced with "".
394 $dest =~ s/^deprecate(|d)\z/\"\"/i;
395 $voice =~ s/^deprecate(|d)\z/\"\"/i;
397 # basic syntax error alerts, if there are no quotes we
398 # will assume an empty string was intended
400 print STDERR
"Warning: dest before $input line $line lacks quotes ($dest)!\n";
404 print STDERR
"Warning: source before $input line $line lacks quotes ($src)!\n";
407 if($voice !~ /^\"/) {
408 print STDERR
"Warning: voice before $input line $line lacks quotes ($voice)!\n";
412 # Use the ID name to figure out which id number range we
413 # should use for this phrase. Voice-only strings are
416 if($idstr =~ /^VOICE/) {
423 $id{$idstr} = $idnum;
424 $idnum[$idnum]=$idstr;
426 $source{$idstr}=$src;
428 $voice{$idstr}=$voice;
431 print "id: $phrase{id} ($idnum)\n";
432 print "source: $src\n";
433 print "dest: $dest\n";
434 print "voice: $voice\n";
444 my $e = $english{$idstr};
447 # compare original english with this!
448 my @eng = split("\n", $english{$idstr});
450 compare
($idstr, \
@eng, \
@phrase);
452 $english{$idstr}=""; # clear it
455 print "### $idstr: The phrase is not used. Skipped\n";
462 # starts with a slash, this _ends_ this section
463 $m = pop @m; # get back old value, the previous level's tag
467 # This is an opening (sub) tag
469 push @m, $m; # store old value
474 if(/^ *([^:]+): *(.*)/) {
475 my ($name, $val)=($1, $2);
476 &$m($_, $name, $val);
486 "### This phrase below was not present in the translated file\n",
495 # We create a .c and .h file
497 open(HFILE
, ">$prefix.h") ||
498 die "couldn't create file $prefix.h\n";
499 open(CFILE
, ">$prefix.c") ||
500 die "couldn't create file $prefix.c\n";
503 /* This file was automatically generated using genlang */
505 * The str() macro/functions is how to access strings that might be
506 * translated. Use it like str(MACRO) and expect a string to be
509 #define str(x) language_strings[x]
511 /* this is the array for holding the string pointers.
512 It will be initialized at runtime. */
513 extern unsigned char *language_strings[];
514 /* this contains the concatenation of all strings, separated by \\0 chars */
515 extern const unsigned char language_builtin[];
517 /* The enum below contains all available strings */
523 /* This file was automaticly generated using genlang, the strings come
528 unsigned char *language_strings[LANG_LAST_INDEX_IN_ARRAY];
529 const unsigned char language_builtin[] =
533 # Output the ID names for the enum in the header file
535 for $i (1 .. $idcount) {
536 my $name=$idnum[$i - 1]; # get the ID name
538 $name =~ s/\"//g; # cut off the quotes
540 printf HFILE
(" %s,\n", $name);
543 # Output separation marker for last string ID and the upcoming voice IDs
546 LANG_LAST_INDEX_IN_ARRAY, /* this is not a string, this is a marker */
547 /* --- below this follows voice-only strings --- */
548 VOICEONLY_DELIMITER = 0x8000,
552 # Output the ID names for the enum in the header file
553 for $i (0x8000 .. ($voiceid-1)) {
554 my $name=$idnum[$i]; # get the ID name
556 $name =~ s/\"//g; # cut off the quotes
558 printf HFILE
(" %s,\n", $name);
562 print HFILE
"\n};\n/* end of generated enum list */\n";
564 # Output the target phrases for the source file
565 for $i (1 .. $idcount) {
566 my $name=$idnum[$i - 1]; # get the ID
567 my $dest = $dest{$name}; # get the destination phrase
569 $dest =~ s
:\"$:\\0\":; # insert a \0 before the second quote
572 # this is just to be on the safe side
576 printf CFILE
(" %s\n", $dest);
579 # Output end of string chunk
582 /* end of generated string list */
588 } # end of the c/h file generation
590 # Creation of a binary lang file was requested
592 # We must first scan the english file to get the correct order of the id
593 # numbers used there, as that is what sets the id order for all language
594 # files. The english file is scanned before the translated file was
597 open(OUTF
, ">$binary") or die "Can't create $binary";
599 printf OUTF
("\x1a%c%c", $langversion, $target_id); # magic lang file header
601 # loop over the target phrases
602 for $i (1 .. $idcount) {
603 my $name=$idnum[$i - 1]; # get the ID
604 my $dest = $dest{$name}; # get the destination phrase
607 $dest =~ s/^\"(.*)\"\s*$/$1/g; # cut off quotes
609 # Now, make sure we get the number from the english sort order:
610 $idnum = $idmap{$name};
612 printf OUTF
("%c%c%s\x00", ($idnum>>8), ($idnum&0xff), $dest);
617 # voice output requested, display id: and voice: strings in a v1-like
622 # This loops over the strings in the translated language file order
623 my @ids = ((0 .. ($idcount-1)));
624 push @ids, (0x8000 .. ($voiceid-1));
631 my $name=$idnum[$i]; # get the ID
632 my $dest = $voice{$name}; # get the destination voice string
635 $dest =~ s/^\"(.*)\"\s*$/$1/g; # cut off quotes
637 # Now, make sure we get the number from the english sort order:
638 $idnum = $idmap{$name};
642 # print "Input index $i output index $idnum\n";
650 my $name=$idnum[$o]; # get the ID
651 my $dest = $voice{$name}; # get the destination voice string
653 print "#$i\nid: $name\nvoice: $dest\n";
660 printf("%d ID strings scanned\n", $idcount);
664 printf "$_: %s\n", $head{$_};