mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 13:06:59 -04:00
1fb3f15aff
Fixes #13. Removed IconMenu class, use add_menu_item/add_menu_item_tr instead. Added _tr variants that automatically do locale translation
225 lines
7.0 KiB
Perl
225 lines
7.0 KiB
Perl
#! /usr/bin/perl
|
|
|
|
# Validate a .mse-locale file by checking that the keys match those used in the source code, and that they have the right number of arguments.
|
|
|
|
use strict;
|
|
use File::Find;
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 0: arguments
|
|
# ------------------------------------------------------------------------------
|
|
|
|
if (scalar @ARGV < 1) {
|
|
die "Usage: $0 <SRCDIR> <LOCALE> [<LOCALE>...]"
|
|
}
|
|
my $indir = shift @ARGV;
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 1: find keys used in source code
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Determine the keys that should be in the locale file,
|
|
# and the number of arguments the keys should have
|
|
|
|
# Array of locale keys: maps type -> key -> info
|
|
# where info is {argc:arity, opt:optional(only used in commented code)}
|
|
|
|
our %locale_keys;
|
|
our $i=0;
|
|
|
|
sub arg_count {
|
|
return scalar split(/,/,$_[0]);
|
|
}
|
|
|
|
sub make_comment {
|
|
my $input = $_[0];
|
|
$input =~ s/(_[A-Z])/_COMMENT$1/g;
|
|
return $input;
|
|
}
|
|
|
|
sub normalize {
|
|
my $key = shift;
|
|
$key =~ s/_/ /g;
|
|
return $key;
|
|
}
|
|
|
|
# for each .cpp/.hpp file, collect locale calls
|
|
sub gather_locale_keys {
|
|
my $filename = $_;
|
|
my $full_name = $File::Find::name;
|
|
|
|
if (!($filename =~ /\..pp$/)) {
|
|
return;
|
|
}
|
|
|
|
# Read file
|
|
open(my $fh, "<", $filename) or die "Failed to open source file $full_name";
|
|
my $body = join('',<$fh>);
|
|
close $fh;
|
|
|
|
# Custom argument expansion
|
|
my $inparen;
|
|
$inparen = qr/[^()]|\((??{$inparen})*\)/; # recursive paren matching
|
|
$body =~ s/(?:String::Format|format_string)\((_[A-Z]+)(_\([^)]+\)),($inparen+)/
|
|
$1 . "_" . arg_count($3) . $2
|
|
/ge;
|
|
$body =~ s/action_name_for[(][^,]*,\s*(_[A-Z]+)(_\([^)]+\))/$1_1$2/g;
|
|
|
|
# Drop comments, mark found items as 'optional'
|
|
$body =~ s{//[^\n]*} {find_locale_calls($&, 1)}ge;
|
|
$body =~ s{/\*.*?\*/}{find_locale_calls($&, 1)}ge;
|
|
|
|
find_locale_calls($body, 0);
|
|
}
|
|
|
|
sub find_locale_calls {
|
|
my $body = shift;
|
|
my $in_comment = shift;
|
|
|
|
# Find calls to locale functions
|
|
while ($body =~ /_(COMMENT_)?(MENU|HELP|TOOL|TOOLTIP|LABEL|BUTTON|TITLE|TYPE|ACTION|ERROR)_(?:([1-9])_)?\(\s*\"([^\"]+)\"/g) {
|
|
my $type = $2;
|
|
my $argc = $3 ? $3 : 0;
|
|
my $key = normalize($4);
|
|
if (defined($locale_keys{$type}{$key}{'argc'}) && $locale_keys{$type}{$key}{'argc'} != $argc) {
|
|
die "ERROR: locale key _${type}_($key) used with different arities";
|
|
}
|
|
$locale_keys{$type}{$key}{'opt'} = defined($locale_keys{$type}{$key}{'opt'}) ? ($locale_keys{$type}{$key}{'opt'} && $in_comment) : $in_comment;
|
|
$locale_keys{$type}{$key}{'argc'} = $argc;
|
|
}
|
|
# addPanel/add_menu_tr/add_tool_tr use multiple locale types
|
|
my $ARG = qr{[^,]+};
|
|
my $STRARG = qr{\s*(?:_\()?\"([^\"]+)\"\)?};
|
|
find_multi_locale_calls($body, $in_comment, qr{addPanel\s*\((?:$ARG,){6} $STRARG \)}x, ["MENU","HELP","TOOL","TOOLTIP"]);
|
|
find_multi_locale_calls($body, $in_comment, qr{menu_item_tr\s*\((?:$ARG,){3} $STRARG }x, ["MENU","HELP"]);
|
|
while ($body =~ m{add_tool_tr\s*\((?:$ARG,){3} $STRARG \s*,?\s*(true|false)?}gx) {
|
|
if ($2 eq 'true') {
|
|
add_locale_keys(["TOOL","TOOLTIP","HELP"], normalize($1), $in_comment);
|
|
} else {
|
|
add_locale_keys(["TOOLTIP","HELP"], normalize($1), $in_comment);
|
|
}
|
|
}
|
|
}
|
|
|
|
sub add_locale_keys {
|
|
my $types = shift;
|
|
my $key = shift;
|
|
my $in_comment = shift;
|
|
foreach my $type (@{$types}) {
|
|
$locale_keys{$type}{$key}{'opt'} = $in_comment;
|
|
$locale_keys{$type}{$key}{'argc'} = 0;
|
|
}
|
|
}
|
|
|
|
sub find_multi_locale_calls {
|
|
my $body = shift;
|
|
my $in_comment = shift;
|
|
my $re = shift;
|
|
my $types = shift;
|
|
while ($body =~ m{$re}g) {
|
|
my $key = normalize($1);
|
|
add_locale_keys($types, $key, $in_comment);
|
|
}
|
|
}
|
|
|
|
#my $filename = 'src/code_template.cpp';
|
|
#open(my $fh, "<", $filename) or die "WTF Failed to open source file $filename".length($filename);
|
|
|
|
find(\&gather_locale_keys, $indir);
|
|
if (scalar(%locale_keys) == 0) {
|
|
die "Did not find any source files with locale keys"
|
|
}
|
|
print $locale_keys{'ERROR'}{'successful instalal'} . "\n";
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 2: validate a locale
|
|
# ------------------------------------------------------------------------------
|
|
|
|
sub parse_locale {
|
|
# Load and parse a .mse-locale file
|
|
# Return a mapping type -> key -> value
|
|
my $locale_file = shift;
|
|
open(my $fh, "<", "$locale_file/locale") or
|
|
die "Error: locale file not found: $locale_file/locale";
|
|
# Get lines from file
|
|
my $type = undef;
|
|
my $key = undef;
|
|
my %locale;
|
|
for my $line (<$fh>) {
|
|
if ($line =~ /^\s*#|^\s*$/) {
|
|
# comment
|
|
} elsif ($line =~ /^([^:\t]+):/) {
|
|
$type = uc $1;
|
|
$key = undef;
|
|
} elsif ($line =~ /^\t([^:\t]+):(.*)/) {
|
|
$key = normalize($1);
|
|
if (defined($locale{$type}{$key})) {
|
|
die "Locale key already defined: $type: $key";
|
|
}
|
|
$locale{$type}{$key} = $2;
|
|
} elsif ($line =~ /^\t\t(.*)/) {
|
|
$locale{$type}{$key} .= $1;
|
|
} else {
|
|
die "Unknown line in locale file: $line\n";
|
|
}
|
|
}
|
|
close $fh;
|
|
return %locale
|
|
}
|
|
|
|
sub validate_locale {
|
|
my $locale_file = shift;
|
|
my %locale = parse_locale($locale_file);
|
|
# Validate locale:
|
|
# The set of keys should match exactly, so every key expected by the program should be in the locale file, and vice-versa
|
|
my $ok = 1;
|
|
foreach my $type (sort keys %locale_keys) {
|
|
if (!defined($locale{$type})) {
|
|
print "Missing key in locale: $type\n have keys " . "[" . join(', ', keys %locale) . "]\n";
|
|
$ok = 0;
|
|
next;
|
|
}
|
|
foreach my $key (sort keys %{$locale_keys{$type}}) {
|
|
if (!defined($locale{$type}{$key})) {
|
|
if (!$locale_keys{$type}{$key}{'opt'}) {
|
|
print "Missing key in locale: $type: $key\n";
|
|
$ok = 0;
|
|
}
|
|
next;
|
|
}
|
|
# Count the number of printf style arguments in the value
|
|
my @args = $locale{$type}{$key} =~ /%[sd]/g;
|
|
my $argc = scalar(@args);
|
|
if ($argc != $locale_keys{$type}{$key}{'argc'}) {
|
|
print "Incorrect number of arguments for $type: $key. Expected $locale_keys{$type}{$key}{'argc'}, got $argc\n";
|
|
$ok = 0;
|
|
}
|
|
}
|
|
}
|
|
foreach my $type (sort keys %locale) {
|
|
next if $type eq 'PACKAGE'; # Ignore package specific locale keys
|
|
if (!defined($locale_keys{$type})) {
|
|
print "Unknown key in locale: $type\n expected keys " . "[" . join(', ', keys %locale_keys) . "]\n";
|
|
$ok = 0;
|
|
next;
|
|
}
|
|
foreach my $key (sort keys %{$locale{$type}}) {
|
|
if (!defined($locale_keys{$type}{$key})) {
|
|
print "Unknown key in locale: $type: $key\n";
|
|
$ok = 0;
|
|
}
|
|
}
|
|
}
|
|
return $ok;
|
|
}
|
|
|
|
if (scalar @ARGV < 1) {
|
|
die "No locales found to validate";
|
|
}
|
|
|
|
for my $locale (@ARGV) {
|
|
if (!validate_locale($locale)) {
|
|
exit 1;
|
|
}
|
|
}
|