Created
May 4, 2018 14:56
-
-
Save MaximBazarov/9ee0d4c868e933bb1b9a7264ee2e411c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# HOW TO USE: | |
# script suppose to run as one of the build phases in a Xcode project | |
# (For example like this: ${SOURCE_ROOT}/scripts/check_strings.pl ${PRODUCT_NAME}) | |
# HOW IT WORKS | |
# script parse the Localizable file for the specific target (get target name from parameter ${PRODUCT_NAME}) | |
# and .m files in the project (except KDSLocalization.m) | |
# script generates xcode error messages during a build process | |
# WHAT SCRIPT TESTS: | |
# - duplicates in the Localizable file | |
# - Unused keys in the Localizable file | |
# - Missed translations (key used in code but doesn't exist in a Localizable file) | |
# - Format of the I18N command - must be I18N(@"something") | |
# ADDITIONAL FEATURES | |
# - script ignores lines staring with "//" | |
# - script supports plurals (__one, __many) | |
# - script supports two method names, I18N and I18N_PluralString | |
# Preparations... | |
our $project_path_global = $ENV{'PROJECT_DIR'}; | |
die_with_error("Wrong PROJECT_DIR: \"$project_path_global\"") unless -d $project_path_global; | |
$target_name = ""; | |
$country_code = "en"; | |
# $supported_langs = "en", "cs", "da", "pl", "pt", "pt-BR", "ru", "sv", "de", "zh", "es", "fr", "it", "nl" | |
our $localizable_file_global = $project_path_global . "/" .$target_name ."/Supporting Files/en.lproj/Localizable.strings"; | |
die_with_error("Wrong localizable file : \"$localizable_file_global\"") unless -e $localizable_file_global; | |
# Start comparing strings ! | |
compare_strings($project_path_global, $localizable_file_global); | |
check_wrong_brand_names($localizable_file_global, $target_name); | |
exit; | |
# -------------------------------------- main sub | |
sub compare_strings | |
{ | |
my ($project_path, $localizable_file) = @_; | |
# 1. parse strings file and find duplicates in strings file | |
my %keys_from_strings = %{parse_localizable_file($localizable_file)}; | |
my $count = keys %keys_from_strings; | |
print "Found $count keys in $localizable_file\n"; | |
# 2. parse code (all .m files in the project dir) and find missed translations | |
my %keys_from_code = %{parse_source_files($project_path, \%keys_from_strings)}; | |
# 3. find unused keys | |
my %unused_keys = %keys_from_strings; # take all keys | |
foreach $key (keys %keys_from_code) { | |
delete $unused_keys{$key} if exists $keys_from_code{$key}; #remove keys we use in the code from hash | |
} | |
foreach $key (keys %unused_keys) { #every key left in the hash - is unused | |
error($localizable_file_global, $unused_keys{$key}, "Unused key: \"$key\""); # !!! | |
} | |
} | |
# ------------------------------------ Parse source files (*.m) and report missing translation keys | |
# ------------------------------------ returns hash with keys used in code | |
sub parse_source_files | |
{ | |
my $path = @_[0]; | |
my %localized_strings = %{@_[1]}; | |
my %keys_in_code = (); | |
@files_in_project = `find $path -iname "*.m" -o -iname "*.swift"`; | |
@m_files = (@files_in_project, @files_in_submodules); | |
print "Found .m files: " . scalar(@m_files), "in $path folder (including ../submodules) \n"; | |
foreach $file (@m_files) { | |
chomp($file); | |
open FILE, "$file"; | |
my $line_number = 0; | |
while($line= <FILE> ){ | |
$line_number++; | |
next if $line =~ /^\/\//; # skip lines started with "//" | |
check_code_conventions($file, $line_number, $line); | |
#find all I18N(@"bla-bla") or I18N_PluralString(bla-bla); | |
# while ($line =~ /(I18N|I18N_PluralString)\((.+?)\)/g) { | |
while ($line =~ /(NSLocalizedString|I18n.pluralString)\((.+?)\)/g) { | |
$method_name = $1; | |
$inside_brackets = $2; | |
if ($method_name eq "I18n.PluralString") { | |
if ($inside_brackets =~ /^\@?\"(.*?)\"/) { | |
$i18n_key = $1; | |
check_missing_translation($file, $line_number, $i18n_key, \%localized_strings); | |
check_missing_translation($file, $line_number, $i18n_key."__one", \%localized_strings); | |
check_missing_translation($file, $line_number, $i18n_key."__many", \%localized_strings); | |
# Add keys to the list of keys found in code | |
$keys_in_code{$i18n_key} = $line_number; | |
$keys_in_code{$i18n_key."__one"} = $line_number; | |
$keys_in_code{$i18n_key."__many"} = $line_number; | |
} | |
} | |
else { # else - method name is I18N | |
if ($inside_brackets =~ /^\@?\"(.*?)\"$/) { | |
# correct I18N format | |
$i18n_key = $1; | |
check_missing_translation($file, $line_number, $i18n_key, \%localized_strings); | |
# Add key to the list of keys found in code | |
$keys_in_code{$i18n_key} = $line_number; | |
} | |
else { | |
# wrong I18N format | |
error($file, $line_number, "Wrong I18N format ! Must be I18N(@\"something\")."); # !!! | |
} | |
} | |
} | |
} | |
close FILE; | |
} | |
return \%keys_in_code; | |
} | |
sub check_missing_translation | |
{ | |
my $file = @_[0]; | |
my $line_number = @_[1]; | |
my $i18n_key = @_[2]; | |
my %localized_strings = %{@_[3]}; | |
if (!$localized_strings{$i18n_key}) { | |
error($file, $line_number, "Missing translation for key \"$i18n_key\""); # !!! | |
} | |
} | |
# ------------------------------------ parse apple strings file (e.g. Localizable.strings) and report duplicates | |
# get: one parameter - file name | |
# returns: hash with keys and values | |
sub parse_localizable_file | |
{ | |
$filename = shift; | |
die_with_error("file doesn't exist \"$filename\"") unless -e $filename; | |
open FILE, $filename or die_with_error ("Couldn't open file: $filename"); | |
my %keys_in_strings = (); | |
my $line_number = 0; | |
while ($line= <FILE>) { # using perl variable $_ | |
$line_number++; | |
next if $line =~ /^\/\//; # skip lines started with "//" | |
# parce key-value | |
if ($line =~ /\"([^\"]*?)\"[^\"]*?\"([^\"]*?)\"/ ) { | |
($key, $value) = ($1, $2); | |
if ($keys_in_strings{$key}) { | |
error($filename, $line_number, "key already defined \"$key\""); # !!! | |
} | |
$keys_in_strings{$key} = $line_number; | |
} | |
} | |
close FILE; | |
return \%keys_in_strings; | |
} | |
# BACKUP: it works: ## find . -iname \*.m -exec egrep -i "I18N\(.*\)" {} \; -print |wc -l | |
# BACKUP: convert to the right encoding using system util (don't need anymore with POEditor utf-8) | |
# BACKUP: system("iconv -f UTF-16LE -t UTF-8 $filename > $filename.utf8"); | |
# BACKUP: $filename_utf8 = "$filename.utf8"; | |
# ---------------------------- Helper | |
sub warning | |
{ | |
my ($filename, $line_number, $message) = @_; | |
print "$filename:$line_number: warning: $message\n"; | |
} | |
sub error | |
{ | |
my ($filename, $line_number, $message) = @_; | |
print "$filename:$line_number: error: $message\n"; | |
} | |
# Dies with Xcode Error Message | |
sub die_with_error | |
{ | |
$message = shift; | |
error("","", "SCRIPT FATAL ERROR: $message"); | |
die; | |
} | |
# print hash for debug | |
sub print_hash | |
{ | |
my %hash = %{@_[0]}; | |
my $count = keys %hash; | |
print "number of keys in hash = $count\n"; | |
print "======================= \n"; | |
foreach $key (keys %hash) { | |
print "[" . $i++ . "]: ($key) = ($hash{$key})\n"; | |
} | |
print "======================= \n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment