traduco 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,301 @@
1
+ require 'tmpdir'
2
+
3
+ module Traduco
4
+ VALID_COMMANDS = ['generate-string-file', 'generate-all-string-files', 'consume-string-file', 'consume-all-string-files', 'generate-loc-drop', 'consume-loc-drop', 'generate-report']
5
+
6
+ class Runner
7
+ def initialize(args)
8
+ @options = {}
9
+ @args = args
10
+ end
11
+
12
+ def self.run(args)
13
+ new(args).run
14
+ end
15
+
16
+ def run
17
+ # Parse all CLI arguments.
18
+ CLI::parse_args(@args, @options)
19
+ read_strings_data
20
+ execute_command
21
+ end
22
+
23
+ def read_strings_data
24
+ @strings = StringsFile.new
25
+ @strings.read @options[:strings_file]
26
+ end
27
+
28
+ def write_strings_data(path)
29
+ if @options[:developer_language]
30
+ @strings.set_developer_language_code(@options[:developer_language])
31
+ end
32
+ @strings.write(path)
33
+ end
34
+
35
+ def execute_command
36
+ case @options[:command]
37
+ when 'generate-string-file'
38
+ generate_string_file
39
+ when 'generate-all-string-files'
40
+ generate_all_string_files
41
+ when 'consume-string-file'
42
+ consume_string_file
43
+ when 'consume-all-string-files'
44
+ consume_all_string_files
45
+ when 'generate-loc-drop'
46
+ generate_loc_drop
47
+ when 'consume-loc-drop'
48
+ consume_loc_drop
49
+ when 'generate-report'
50
+ generate_report
51
+ end
52
+ end
53
+
54
+ def generate_string_file
55
+ lang = nil
56
+ if @options[:languages]
57
+ lang = @options[:languages][0]
58
+ end
59
+
60
+ read_write_string_file(@options[:output_path], false, lang)
61
+ end
62
+
63
+ def generate_all_string_files
64
+ if !File.directory?(@options[:output_path])
65
+ raise Traduco::Error.new("Directory does not exist: #{@options[:output_path]}")
66
+ end
67
+
68
+ format = @options[:format]
69
+ if !format
70
+ format = determine_format_given_directory(@options[:output_path])
71
+ end
72
+ if !format
73
+ raise Traduco::Error.new "Could not determine format given the contents of #{@options[:output_path]}"
74
+ end
75
+
76
+ formatter = formatter_for_format(format)
77
+
78
+ formatter.write_all_files(@options[:output_path])
79
+ end
80
+
81
+ def consume_string_file
82
+ lang = nil
83
+ if @options[:languages]
84
+ lang = @options[:languages][0]
85
+ end
86
+
87
+ read_write_string_file(@options[:input_path], true, lang)
88
+ output_path = @options[:output_path] || @options[:strings_file]
89
+ write_strings_data(output_path)
90
+ end
91
+
92
+ def consume_all_string_files
93
+ if !File.directory?(@options[:input_path])
94
+ raise Traduco::Error.new("Directory does not exist: #{@options[:output_path]}")
95
+ end
96
+
97
+ Dir.glob(File.join(@options[:input_path], "**/*")) do |item|
98
+ if File.file?(item)
99
+ begin
100
+ read_write_string_file(item, true, nil)
101
+ rescue Traduco::Error => e
102
+ STDERR.puts "#{e.message}"
103
+ end
104
+ end
105
+ end
106
+
107
+ output_path = @options[:output_path] || @options[:strings_file]
108
+ write_strings_data(output_path)
109
+ end
110
+
111
+ def read_write_string_file(path, is_read, lang)
112
+ if is_read && !File.file?(path)
113
+ raise Traduco::Error.new("File does not exist: #{path}")
114
+ end
115
+
116
+ format = @options[:format]
117
+ if !format
118
+ format = determine_format_given_path(path)
119
+ end
120
+ if !format
121
+ raise Traduco::Error.new "Unable to determine format of #{path}"
122
+ end
123
+
124
+ formatter = formatter_for_format(format)
125
+
126
+ if !lang
127
+ lang = determine_language_given_path(path)
128
+ end
129
+ if !lang
130
+ lang = formatter.determine_language_given_path(path)
131
+ end
132
+ if !lang
133
+ raise Traduco::Error.new "Unable to determine language for #{path}"
134
+ end
135
+
136
+ if !@strings.language_codes.include? lang
137
+ @strings.language_codes << lang
138
+ end
139
+
140
+ if is_read
141
+ formatter.read_file(path, lang)
142
+ else
143
+ formatter.write_file(path, lang)
144
+ end
145
+ end
146
+
147
+ def generate_loc_drop
148
+ begin
149
+ require 'zip/zip'
150
+ rescue LoadError
151
+ raise Traduco::Error.new "You must run 'gem install rubyzip' in order to create or consume localization drops."
152
+ end
153
+
154
+ if File.file?(@options[:output_path])
155
+ File.delete(@options[:output_path])
156
+ end
157
+
158
+ Dir.mktmpdir do |dir|
159
+ Zip::ZipFile.open(@options[:output_path], Zip::ZipFile::CREATE) do |zipfile|
160
+ zipfile.mkdir('Locales')
161
+
162
+ formatter = formatter_for_format(@options[:format])
163
+ @strings.language_codes.each do |lang|
164
+ if @options[:languages] == nil || @options[:languages].length == 0 || @options[:languages].include?(lang)
165
+ file_name = lang + formatter.class::EXTENSION
166
+ real_path = File.join(dir, file_name)
167
+ zip_path = File.join('Locales', file_name)
168
+ formatter.write_file(real_path, lang)
169
+ zipfile.add(zip_path, real_path)
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ def consume_loc_drop
177
+ if !File.file?(@options[:input_path])
178
+ raise Traduco::Error.new("File does not exist: #{@options[:input_path]}")
179
+ end
180
+
181
+ begin
182
+ require 'zip/zip'
183
+ rescue LoadError
184
+ raise Traduco::Error.new "You must run 'gem install rubyzip' in order to create or consume localization drops."
185
+ end
186
+
187
+ Dir.mktmpdir do |dir|
188
+ Zip::ZipFile.open(@options[:input_path]) do |zipfile|
189
+ zipfile.each do |entry|
190
+ if !entry.name.end_with?'/' and !File.basename(entry.name).start_with?'.'
191
+ real_path = File.join(dir, entry.name)
192
+ FileUtils.mkdir_p(File.dirname(real_path))
193
+ zipfile.extract(entry.name, real_path)
194
+ begin
195
+ read_write_string_file(real_path, true, nil)
196
+ rescue Traduco::Error => e
197
+ STDERR.puts "#{e.message}"
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ output_path = @options[:output_path] || @options[:strings_file]
205
+ write_strings_data(output_path)
206
+ end
207
+
208
+ def generate_report
209
+ total_strings = 0
210
+ strings_per_lang = {}
211
+ all_keys = Set.new
212
+ duplicate_keys = Set.new
213
+ keys_without_tags = Set.new
214
+ @strings.language_codes.each do |code|
215
+ strings_per_lang[code] = 0
216
+ end
217
+
218
+ @strings.sections.each do |section|
219
+ section.rows.each do |row|
220
+ total_strings += 1
221
+
222
+ if all_keys.include? row.key
223
+ duplicate_keys.add(row.key)
224
+ else
225
+ all_keys.add(row.key)
226
+ end
227
+
228
+ row.translations.each_key do |code|
229
+ strings_per_lang[code] += 1
230
+ end
231
+
232
+ if row.tags == nil || row.tags.length == 0
233
+ keys_without_tags.add(row.key)
234
+ end
235
+ end
236
+ end
237
+
238
+ # Print the report.
239
+ puts "Total number of strings = #{total_strings}"
240
+ @strings.language_codes.each do |code|
241
+ puts "#{code}: #{strings_per_lang[code]}"
242
+ end
243
+
244
+ if duplicate_keys.length > 0
245
+ puts "\nDuplicate string keys:"
246
+ duplicate_keys.each do |key|
247
+ puts key
248
+ end
249
+ end
250
+
251
+ if keys_without_tags.length == total_strings
252
+ puts "\nNone of your strings have tags."
253
+ elsif keys_without_tags.length > 0
254
+ puts "\nStrings without tags:"
255
+ keys_without_tags.each do |key|
256
+ puts key
257
+ end
258
+ end
259
+ end
260
+
261
+ def determine_language_given_path(path)
262
+ code = File.basename(path, File.extname(path))
263
+ if !@strings.language_codes.include? code
264
+ code = nil
265
+ end
266
+
267
+ code
268
+ end
269
+
270
+ def determine_format_given_path(path)
271
+ ext = File.extname(path)
272
+ Formatters::FORMATTERS.each do |formatter|
273
+ if formatter::EXTENSION == ext
274
+ return formatter::FORMAT_NAME
275
+ end
276
+ end
277
+
278
+ return
279
+ end
280
+
281
+ def determine_format_given_directory(directory)
282
+ Formatters::FORMATTERS.each do |formatter|
283
+ if formatter.can_handle_directory?(directory)
284
+ return formatter::FORMAT_NAME
285
+ end
286
+ end
287
+
288
+ return
289
+ end
290
+
291
+ def formatter_for_format(format)
292
+ Formatters::FORMATTERS.each do |formatter|
293
+ if formatter::FORMAT_NAME == format
294
+ return formatter.new(@strings, @options)
295
+ end
296
+ end
297
+
298
+ return
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,12 @@
1
+ /**
2
+ * JQuery Language File
3
+ * Generated by Twine
4
+ * Language: en
5
+ */
6
+ {
7
+
8
+ /* My Strings */
9
+ "key1":"key1-english",
10
+ "key3":"key3-english",
11
+ "key5":"A new string"
12
+ }
@@ -0,0 +1,16 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Language: en\n"
4
+ "X-Generator: Twine\n"
5
+
6
+ msgctxt "key1"
7
+ msgid "key1-english"
8
+ msgstr "key1-english"
9
+
10
+ msgctxt "key3"
11
+ msgid "key3-english"
12
+ msgstr ""
13
+
14
+ msgctxt "key5"
15
+ msgid "A new string"
16
+ msgstr "A new string"
@@ -0,0 +1,10 @@
1
+ /**
2
+ * iOS Strings File
3
+ * Generated by Twine
4
+ * Language: en
5
+ */
6
+
7
+ /* My Strings */
8
+ "key1" = "key1-english";
9
+ "key3" = "key3-english";
10
+ "key5" = "A new string";
@@ -0,0 +1,23 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Language: en\n"
4
+ "X-Generator: Twine\n"
5
+
6
+ msgctxt "key1"
7
+ msgid "key1-english"
8
+ msgstr "key1-english"
9
+
10
+ msgctxt "key3"
11
+ msgid "key3-english"
12
+ msgstr ""
13
+
14
+ msgctxt "key4"
15
+ msgid "key4"
16
+ "multiline"
17
+ msgstr "A multi"
18
+ "line string\n"
19
+ "can occur"
20
+
21
+ msgctxt "key5"
22
+ msgid "A new string"
23
+ msgstr "A new string"
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Android Strings File -->
3
+ <!-- Generated by Twine -->
4
+ <!-- Language: fr -->
5
+ <resources>
6
+ <!-- This is a comment -->
7
+ <string name="key1">key1-french</string>
8
+ <string name="key2">key2-french</string>
9
+ <string name="key3">key3-french</string>
10
+ </resources>
@@ -0,0 +1,17 @@
1
+ [[My Strings]]
2
+ [key1]
3
+ en = key1-english
4
+ tags = tag1
5
+ comment = This is a comment
6
+ es = key1-spanish
7
+ fr = key1-french
8
+ [key2]
9
+ en = key2-english
10
+ tags = tag2
11
+ fr = key2-french
12
+ [key3]
13
+ en = key3-english
14
+ tags = tag1,tag2
15
+ es = key3-spanish
16
+ [key4]
17
+ en = key4-english
@@ -0,0 +1,5 @@
1
+ [[My Strings]]
2
+ [key with space ]
3
+ en = `string with space `
4
+ tags = tag1
5
+ comment = String ends with space
@@ -0,0 +1,5 @@
1
+ [[My Strings]]
2
+ [parameterized_string]
3
+ en = The %@ brown fox jumps over the %@ dog %d times.
4
+ [percentage_string]
5
+ en = This product is %d%% off.
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Android Strings File -->
3
+ <!-- Generated by Twine <%= Twine::VERSION %> -->
4
+ <!-- Language: fr -->
5
+ <resources>
6
+ <!-- SECTION: My Strings -->
7
+ <!-- This is a comment -->
8
+ <string name="key1">key1-french</string>
9
+ <string name="key2">key2-french</string>
10
+ <string name="key3">key3-english</string>
11
+ <string name="key4">key4-english</string>
12
+ </resources>
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Apple Strings File
3
+ * Generated by Twine <%= Twine::VERSION %>
4
+ * Language: en
5
+ */
6
+
7
+ /********** My Strings **********/
8
+
9
+ /* This is a comment */
10
+ "key1" = "key1-english";
11
+
12
+ "key3" = "key3-english";
@@ -0,0 +1,18 @@
1
+ [[My Strings]]
2
+ [key1]
3
+ en = key1-english
4
+ tags = tag1
5
+ comment = This is a comment
6
+ es = key1-spanish
7
+ fr = key1-french
8
+ [key2]
9
+ en = key2-english
10
+ tags = tag2
11
+ fr = key2-french
12
+ [key3]
13
+ en = key3-english
14
+ tags = tag1,tag2
15
+ es = key3-spanish
16
+ fr = key3-french
17
+ [key4]
18
+ en = key4-english