#!/usr/bin/perl -s -w # __________ __ ___. # Open \______ \ ____ ____ | | _\_ |__ _______ ___ # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ # \/ \/ \/ \/ \/ # # Copyright (C) 2020 Solomon Peachy # use utf8; use File::Basename; sub trim { my ($string) = @_; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } sub parselangfile { my ($filename) = @_; my %phrases; my @order; my %empty = ( #'phrase' => {}, #'source' => {}, #'dest' => {}, #'voice' => {}, 'notes' => "", 'new' => 0 ); my %thisphrase = %empty; open(FH, "<$filename") || die ("Can't open $filename"); my @lines = ; close(FH); my $pos = 'lang'; my $id = ''; my @comments; foreach my $line (@lines) { $line = trim($line); if($line =~ /^ *###/) { # Filter out warnings from prior runs next; } elsif($line =~ /^ *#/) { push(@comments, "$line\n") if ($pos eq 'lang'); # comments are ignored! next; } elsif ($pos eq 'phrase' && $line =~ /^([^:]+): ?(.*)$/) { $thisphrase{$pos}->{$1} = $2; if ($1 eq 'id') { push(@order, $2); $id = $2; } } elsif ($pos ne 'phrase' && $line =~ /^([^:]+): ?\"?([^\"]*)\"?$/) { my @targets = split(',', $1); my $w = $2; foreach (@targets) { my $l = trim($_); # Convert some obsolete keys if ($l eq "swcodec") { $l = "*"; } elsif ($l eq "lcd_bitmap") { $l = "*"; } elsif ($l eq "recording_swcodec") { $l = "recording"; # } elsif ($id =~ /USB_MODE/ && $l =~ /ibassodx/) { # $l = "*"; } $thisphrase{$pos}->{$l} = $w; } } if ($line eq '' || $line eq '' || $line eq '' || $line eq '') { $pos = 'phrase'; } elsif ($line eq '') { my %copy = %thisphrase; $phrases{$id} = \%copy; %thisphrase = %empty; $pos = 'lang'; $id = ''; } elsif ($line eq '') { $pos = 'source'; } elsif ($line eq '') { $pos = 'dest'; } elsif ($line eq '') { $pos = 'voice'; } } $phrases{'HEADER'} = \@comments; $phrases{'ORDER'} = \@order; return %phrases; } sub combinetgts { my (%tgtmap) = (@_); my %strmap; my %combined; # Reverse-map things foreach my $tgt (sort(keys(%tgtmap))) { next if ($tgt eq '*'); # Do not combine anything with fallback if (defined($strmap{$tgtmap{$tgt}})) { $strmap{$tgtmap{$tgt}} .= ",$tgt"; } else { $strmap{$tgtmap{$tgt}} = "$tgt"; } } # Copy over default/fallback as it was skipped $combined{'*'} = $tgtmap{'*'}; foreach my $str (keys(%strmap)) { $combined{$strmap{$str}} = $str; } return %combined; } ################## if($#ARGV != 2) { print "Usage: updatelang \n"; exit; } # Parse master file my %english = parselangfile($ARGV[0]); my @englishorder = @{$english{'ORDER'}}; # Parse secondary file my %lang = parselangfile($ARGV[1]); my @langorder = @{$lang{'ORDER'}}; my @langheader = @{$lang{'HEADER'}}; # Clean up delete $english{'ORDER'}; delete $english{'HEADER'}; delete $lang{'ORDER'}; delete $lang{'HEADER'}; # Extract language names my @tmp = split(/\./, basename($ARGV[0])); my $f1 = $tmp[0]; @tmp = split(/\./, basename($ARGV[1])); my $f2 = $tmp[0]; undef @tmp; # Read in ignore list my $igname = dirname($0) . "/langignorelist.txt"; open (FH, "<$igname") || die ("Can't open $igname!"); my @ignorelist = ; close (FH); sub not_ignorelist { my ($key) = @_; foreach (@ignorelist) { chomp; if ($_ eq $key) { return 0; } } return 1; } undef $igname; # Do we care about notes? my $printnotes = 1; my $ignoredups = 0; if ($f1 eq $f2) { # Ignore all notes for master language $printnotes = 0; } if (index($f2, $f1) > -1) { # Ignore duplicates for sub-languages $ignoredups = 1; } # work out the missing phrases my %missing; my @missingorder; foreach (@englishorder) { $missing{$_} = 1; } foreach (@langorder) { if (!defined($english{$_})) { delete($lang{$_}); # print "#!! '$_' no longer needed\n"; next; } delete $missing{$_}; } foreach (@englishorder) { push(@missingorder, $_) if defined($missing{$_}); } # And add them to the phrase list. foreach (@missingorder) { # print "#!! '$_' missing\n"; push(@langorder, $_); $lang{$_} = $english{$_}; $lang{$_}{'notes'} .= "### This phrase is missing entirely, copying from english!\n"; $lang{$_}{'new'} = 1; } undef @missingorder; undef %missing; # Sanity-check a few things foreach my $id (@langorder) { if (!defined($english{$id})) { next; } my %ep = %{$english{$id}{'phrase'}}; my %lp = %{$lang{$id}{'phrase'}}; if ($lp{'desc'} ne $ep{'desc'} || $ep{'desc'} eq 'deprecated') { if ($ep{'desc'} eq 'deprecated') { # Nuke all deprecated targets; just copy from English # print "#!! '$id' deprecated, deleting\n"; $lang{$id} = $english{$id}; } else { $lang{$id}{'notes'} .= "### The 'desc' field for '$id' differs from the english!\n### the previously used desc is commented below:\n### desc: $lp{desc}\n"; $lang{$id}{'phrase'}{'desc'} = $english{$id}{'phrase'}{'desc'}; # print "#!! '$id' changed description\n"; } } if (!defined($ep{'user'}) || length($ep{'user'}) == 0) { $lp{'user'} = 'core'; } if (!defined($lp{'user'}) || $lp{'user'} ne $ep{'user'}) { $lang{$id}{'notes'} .= "### The 'user' field for '$id' differs from the english!\n### the previously used desc is commented below:\n### desc: $lp{user}\n"; if (!defined($lp{'user'}) || length($lp{'user'}) == 0) { $lp{'user'} = $ep{'user'}; } $lang{$id}{'phrase'}{'user'} = $english{$id}{'phrase'}{'user'}; # print "#!! '$id' changed user\n"; } } # Check sources foreach my $id (@langorder) { if (!defined($english{$id})) { next; } my %ep = %{$english{$id}{'source'}}; my %lp; if (defined($lang{$id}{'source'})) { %lp = %{$lang{$id}{'source'}}; } else { %lp = (); } foreach my $tgt (keys(%lp)) { if (!defined($ep{$tgt})) { # Delete any targets that have been nuked in master delete($lang{$id}{'source'}{$tgt}); } } foreach my $tgt (keys(%ep)) { if (!defined($lp{$tgt})) { # If it doesn't exist in the language, copy it from English if ($ep{$tgt} ne 'none' && $ep{$tgt} ne '' ) { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is missing! Copying from english!\n"; # print "#!! '$id:$tgt' source missing\n"; } $lang{$id}{'source'}{$tgt} = $english{$id}{'source'}{$tgt}; } elsif ($lp{$tgt} ne $ep{$tgt}) { # If the source string differs, complain, and copy from English $lang{$id}{'notes'} .= "### The section for '$id:$tgt' differs from the english!\n"; $lang{$id}{'notes'} .= "### the previously used one is commented below:\n"; $lang{$id}{'notes'} .= "### $english{$id}{source}{$tgt}\n"; # print "#!! '$id:$tgt' source changed ('$lp{$tgt}' vs '$ep{$tgt}')\n"; $lang{$id}{'source'}{$tgt} = $english{$id}{'source'}{$tgt}; } } } # Check dests foreach my $id (@langorder) { if (!defined($english{$id})) { next; } my %ep = %{$english{$id}{'dest'}}; my %lp; if (defined($lang{$id}{'dest'})) { %lp = %{$lang{$id}{'dest'}}; } else { %lp = (); } foreach my $tgt (keys(%lp)) { if (!defined($ep{$tgt})) { # Delete any targets that have been nuked in master delete($lang{$id}{'dest'}{$tgt}); } } foreach my $tgt (keys(%ep)) { if (!defined($lp{$tgt}) || ($lp{$tgt} eq 'none')) { # If it doesn't exist in the language, copy it from English if ($ep{$tgt} ne 'none' && $ep{$tgt} ne '' ) { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is missing! Copying from english!\n"; # print "#!! '$id:$tgt' dest missing\n"; } $lang{$id}{'dest'}{$tgt} = $english{$id}{'dest'}{$tgt}; } elsif ($lp{$tgt} ne $ep{$tgt}) { # If the source string differs, complain, and copy from English if ($lp{$tgt} eq '' && $ep{$tgt} ne '') { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is blank! Copying from english!\n"; # print "#!! '$id:$tgt' dest is blank ('$lp{$tgt}' vs '$ep{$tgt}')\n"; $lang{$id}{'dest'}{$tgt} = $english{$id}{'dest'}{$tgt}; } elsif ($lp{$tgt} ne '' && $ep{$tgt} eq '') { # It should be kept blank! $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is not blank!\n"; $lang{$id}{'notes'} .= "### the previously used one is commented below:\n"; $lang{$id}{'notes'} .= "### $english{$id}{dest}{$tgt}\n"; # print "#!! '$id:$tgt' dest not blank ('$lp{$tgt}' vs '$ep{$tgt}')\n"; $lang{$id}{'dest'}{$tgt} = $english{$id}{'dest'}{$tgt}; } } elsif ($lp{$tgt} ne 'none' && $lp{$tgt} ne '' && not_ignorelist($id) && !$lang{$id}{'new'} && !$ignoredups) { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is identical to english!\n"; # print "#!! '$id:$tgt' dest identical ('$lp{$tgt}')\n"; } } } # Check voices foreach my $id (@langorder) { if (!defined($english{$id})) { next; } my %ep = %{$english{$id}{'voice'}}; my %lp; if (defined($lang{$id}{'voice'})) { %lp = %{$lang{$id}{'voice'}}; } else { %lp = (); } foreach my $tgt (keys(%lp)) { if (!defined($ep{$tgt})) { # Delete any targets that have been nuked in master delete($lang{$id}{'voice'}{$tgt}); } } foreach my $tgt (keys(%ep)) { if (!defined($lp{$tgt}) || ($lp{$tgt} eq 'none')) { # If it doesn't exist in the language, copy it from English if ($ep{$tgt} ne 'none' && $ep{$tgt} ne '' ) { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is missing! Copying from english!\n"; # print "#!! '$id:$tgt' voice missing\n"; } $lang{$id}{'voice'}{$tgt} = $english{$id}{'voice'}{$tgt}; } elsif ($lp{$tgt} ne $ep{$tgt}) { if ($lp{$tgt} eq '' && $ep{$tgt} ne '') { # If the lang voice string is blank, complain, and copy from English # print "#!! '$id:$tgt' voice is blank ('$lp{$tgt}' vs '$ep{$tgt}')\n"; if ($lang{$id}{'dest'}{$tgt} ne '' && $lang{$id}{'dest'}{$tgt} ne $english{$id}{'dest'}{$tgt}) { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is blank! Copying from translated !\n"; $lang{$id}{'voice'}{$tgt} = $lang{$id}{'dest'}{$tgt}; } else { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is blank! Copying from english!\n"; $lang{$id}{'voice'}{$tgt} = $english{$id}{'voice'}{$tgt}; } } elsif ($lp{$tgt} ne '' && $ep{$tgt} eq '') { if ($id ne 'VOICE_NUMERIC_TENS_SWAP_SEPARATOR') { # If it's not blank, clear it and complain! $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is not blank!\n"; $lang{$id}{'notes'} .= "### the previously used one is commented below:\n"; $lang{$id}{'notes'} .= "### $english{$id}{voice}{$tgt}\n"; # print "#!! '$id:$tgt' voice not blank ('$lp{$tgt}' vs '$ep{$tgt}')\n"; $lang{$id}{'voice'}{$tgt} = $english{$id}{'voice'}{$tgt}; } } } elsif ($lp{$tgt} ne 'none' && $lp{$tgt} ne '' && not_ignorelist($id) && !$lang{$id}{'new'} && !$ignoredups) { # print "#!! '$id:$tgt' voice identical ('$lp{$tgt}')\n"; if ($lang{$id}{'dest'}{$tgt} ne '' && $lang{$id}{'dest'}{$tgt} ne $english{$id}{'dest'}{$tgt}) { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is identical to english, copying translated \n"; $lang{$id}{'voice'}{$tgt} = $lang{$id}{'dest'}{$tgt}; } else { $lang{$id}{'notes'} .= "### The section for '$id:$tgt' is identical to english!\n"; } } } } ########## Write new language file my $fh; if ($ARGV[2] ne '-') { open(FH, ">$ARGV[2]") || die ("Can't open $ARGV[2]"); $fh = *FH; } else { $fh = *STDOUT; } foreach (@langheader) { print $fh $_; } my @finalorder = @langorder; # TODO make configurable vs @englishorder foreach my $id (@finalorder) { if (!defined($english{$id})) { next; } my %lp; # phrase %lp = %{$lang{$id}{'phrase'}}; # Drop all deprecated phrases? # next if ($lp{'desc'} eq 'deprecated'); if (length($lang{$id}{'notes'}) && $printnotes) { print $fh "$lang{$id}{notes}"; } print $fh "\n"; print $fh " id: $lp{id}\n"; if ($lp{'desc'} ne '') { print $fh " desc: $lp{desc}\n"; } else { print $fh " desc:\n"; } print $fh " user: $lp{user}\n"; # source %lp = combinetgts(%{$lang{$id}{'source'}}); print $fh " \n"; foreach my $tgt (sort(keys(%lp))) { if ($lp{$tgt} eq 'none') { print $fh " $tgt: $lp{$tgt}\n"; } else { print $fh " $tgt: \"$lp{$tgt}\"\n"; } } print $fh " \n"; # dest %lp = combinetgts(%{$lang{$id}{'dest'}}); print $fh " \n"; foreach my $tgt (sort(keys(%lp))) { if ($lp{$tgt} eq 'none') { print $fh " $tgt: $lp{$tgt}\n"; } else { print $fh " $tgt: \"$lp{$tgt}\"\n"; } } print $fh " \n"; # voice %lp = combinetgts(%{$lang{$id}{'voice'}}); print $fh " \n"; foreach my $tgt (sort(keys(%lp))) { if ($lp{$tgt} eq 'none') { print $fh " $tgt: $lp{$tgt}\n"; } else { print $fh " $tgt: \"$lp{$tgt}\"\n"; } } print $fh " \n"; # FiN print $fh "\n"; } if ($ARGV[2] ne '-') { close(FH); }