forked from dcordero/Rubustrings
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rubustrings
executable file
·173 lines (139 loc) · 5.45 KB
/
rubustrings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#!/usr/bin/env ruby
# Rubustrings
# A format validator for Localizable.strings files.
# The MIT License (MIT) Copyright (c) 2014 @dcordero
# https://github.com/dcordero/Rubustrings
%w[colored].each do |this_gem|
begin
require this_gem
rescue LoadError
abort "Please install the '#{this_gem}' gem."
end
end
# Possible levels are :error, :result_error, :warning, :result_success, :info
def log_output(level, file_name, line_number, xcode_output, message)
if xcode_output
message = message.chomp
case level
when :error, :result_error
puts "#{file_name}:#{line_number}: error: #{message}"
when :warning
puts "#{file_name}:#{line_number}: warning: #{message}"
when :result_success
puts "✓ #{message}"
else
puts "#{message}"
end
else
case level
when :error
puts "✘ Error, #{message}".red
when :result_error
puts "\nResult: ✘ #{message}".bold.red
when :warning
puts "⊗ Warning, #{message}".yellow
when :result_success
puts "\nResult: ✓ #{message}".bold.green
when :info
puts "#{message}".blue
end
end
end
def add_line_numbers(file_data)
line_num = 0
result = ""
file_data.each_line { |line|
line_num += 1
result += "#{line_num} #{line}"
}
return result
end
def open_and_read_file(file_name)
return nil unless File.exist?(file_name)
begin
File.open(file_name, 'rb:utf-16:utf-8').read
rescue
File.open(file_name, 'rb:utf-8:utf-8').read
end
end
def remove_comments_and_empty_lines(file_data)
multiline_comments_regex = /\/\*.*?\*\//m
empty_lines_regex = /^[1-9]\d* $\n/
file_data_with_lines = add_line_numbers file_data
file_data_with_lines.gsub(multiline_comments_regex, "").gsub(empty_lines_regex, "") if file_data
end
def validate_format(line)
localizable_strings_format_regex = /^\"((?:\\.|[^\\"])*?)\"\s=\s\"((?:\\.|[^\\"])*?)\";/
match = localizable_strings_format_regex.match line
end
def validate_special_characters(translation_key, translation_value)
variables_regex = /%[hlqLztj]?[@%dDuUxXoOfeEgGcCsSpaAF]/
key_variables = translation_key.scan(variables_regex)
value_variables = translation_value.scan(variables_regex)
return key_variables.sort == value_variables.sort
end
def validate_special_beginning(translation_key, translation_value)
beginning_regex = /^(?:\s|\n|\r)/
return true unless translation_key =~ beginning_regex || translation_value =~ beginning_regex
translation_key.chars.first == translation_value.chars.first
end
def validate_special_ending(translation_key, translation_value)
ending_regex = /(?:\s|\n|\r)$/
return true unless translation_key =~ ending_regex || translation_value =~ ending_regex
translation_key.chars.last == translation_value.chars.last
end
def check_translation_length(translation_key, translation_value)
translation_value.length / translation_key.length < 3
end
def validate_translation_line(file_name, line, use_xcode)
line_number = 0
empty_regex = /^\d+\s*\n?$/
if empty_regex.match line
return true
end
numbered_line_regex = /^(\d+) (.*)/
numbered_line_match = numbered_line_regex.match line
return log_output(:error, file_name, line_number, use_xcode, "internal error") unless numbered_line_match
line_number = numbered_line_match[1]
line = numbered_line_match[2]
match = validate_format line
return log_output(:error, file_name, line_number, use_xcode, "invalid format: _#{line}_") unless match
match_key = match[1]
match_value = match[2]
log_output(:warning, file_name, line_number, use_xcode, "no translated string: #{line}") unless match_value.length > 0
log_output(:warning, file_name, line_number, use_xcode, "translation significantly large: #{line}") unless check_translation_length match_key, match_value
validation_special_characters = validate_special_characters match_key,match_value
log_output(:error, file_name, line_number, use_xcode, "number of variables mismatch: #{line}") unless validation_special_characters
validation_special_beginning = validate_special_beginning match_key,match_value
log_output(:error, file_name, line_number, use_xcode, "beginning mismatch: #{line}") unless validation_special_beginning
validation_special_ending = validate_special_ending match_key,match_value
log_output(:error, file_name, line_number, use_xcode, "ending mismatch: #{line}") unless validation_special_ending
return validation_special_characters && validation_special_beginning && validation_special_ending
end
def validate_localizable_string_file (file_name, use_xcode)
file_data = open_and_read_file file_name
cleaned_strings = remove_comments_and_empty_lines file_data
return log_output(:error, file_name, 0, use_xcode, "no translations found in file: #{file_name}") if cleaned_strings.empty?
validation_result = true
cleaned_strings.each_line do |line|
validation_result &= validate_translation_line file_name,line,use_xcode
end
return validation_result
end
use_xcode=false
abort "No strings file provided" if ARGV.empty?
ARGV.each do |file_name|
if file_name == "-xcode"
use_xcode=true
next
end
log_output(:info, "", 0, use_xcode, "Processing file: \"#{file_name}\"\n")
result = validate_localizable_string_file file_name,use_xcode
if result
log_output(:result_success, file_name, 0, use_xcode, "Strings file validated succesfully")
exit 0
else
log_output(:result_error, file_name, 0, use_xcode, "Some errors detected")
exit 1
end
end