twine 1.0.2 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +45 -6
- data/lib/twine/cli.rb +24 -2
- data/lib/twine/formatters/abstract.rb +10 -3
- data/lib/twine/formatters/android.rb +21 -12
- data/lib/twine/formatters/apple.rb +1 -1
- data/lib/twine/formatters/django.rb +7 -16
- data/lib/twine/formatters/flash.rb +0 -5
- data/lib/twine/formatters/gettext.rb +4 -14
- data/lib/twine/formatters/jquery.rb +5 -10
- data/lib/twine/placeholders.rb +7 -1
- data/lib/twine/plugin.rb +2 -1
- data/lib/twine/runner.rb +33 -14
- data/lib/twine/twine_file.rb +1 -1
- data/lib/twine/version.rb +1 -1
- data/test/fixtures/consume_localization_archive.zip +0 -0
- data/test/fixtures/formatter_django.po +5 -6
- data/test/fixtures/formatter_gettext.po +2 -2
- data/test/fixtures/formatter_gettext_quotes.po +10 -0
- data/test/test_cli.rb +27 -3
- data/test/test_consume_localization_archive.rb +13 -7
- data/test/test_formatters.rb +203 -11
- data/test/test_generate_all_localization_files.rb +24 -14
- data/test/test_generate_localization_archive.rb +9 -4
- data/test/test_placeholders.rb +21 -0
- data/test/test_validate_twine_file.rb +8 -0
- data/test/twine_test.rb +2 -2
- metadata +17 -17
data/lib/twine/placeholders.rb
CHANGED
@@ -24,7 +24,7 @@ module Twine
|
|
24
24
|
# %@ -> %s
|
25
25
|
value = convert_twine_string_placeholder(input)
|
26
26
|
|
27
|
-
number_of_placeholders = number_of_twine_placeholders(
|
27
|
+
number_of_placeholders = number_of_twine_placeholders(value)
|
28
28
|
|
29
29
|
return value if number_of_placeholders == 0
|
30
30
|
|
@@ -72,5 +72,11 @@ module Twine
|
|
72
72
|
def convert_placeholders_from_flash_to_twine(input)
|
73
73
|
input.gsub /\{\d+\}/, '%@'
|
74
74
|
end
|
75
|
+
|
76
|
+
# Python supports placeholders in the form of `%(amount)03d`
|
77
|
+
# see https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
|
78
|
+
def contains_python_specific_placeholder(input)
|
79
|
+
/%\([a-zA-Z0-9_-]+\)#{PLACEHOLDER_PARAMETER_FLAGS_WIDTH_PRECISION_LENGTH}#{PLACEHOLDER_TYPES}/.match(input) != nil
|
80
|
+
end
|
75
81
|
end
|
76
82
|
end
|
data/lib/twine/plugin.rb
CHANGED
data/lib/twine/runner.rb
CHANGED
@@ -5,6 +5,14 @@ Twine::Plugin.new # Initialize plugins first in Runner.
|
|
5
5
|
|
6
6
|
module Twine
|
7
7
|
class Runner
|
8
|
+
class NullOutput
|
9
|
+
def puts(message)
|
10
|
+
end
|
11
|
+
def string
|
12
|
+
""
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
8
16
|
def self.run(args)
|
9
17
|
options = CLI.parse(args)
|
10
18
|
|
@@ -35,6 +43,9 @@ module Twine
|
|
35
43
|
def initialize(options = {}, twine_file = TwineFile.new)
|
36
44
|
@options = options
|
37
45
|
@twine_file = twine_file
|
46
|
+
if @options[:quite]
|
47
|
+
Twine::stdout = NullOutput.new
|
48
|
+
end
|
38
49
|
end
|
39
50
|
|
40
51
|
def write_twine_data(path)
|
@@ -76,7 +87,7 @@ module Twine
|
|
76
87
|
end
|
77
88
|
|
78
89
|
unless formatter
|
79
|
-
raise Twine::Error.new "Could not determine format given the contents of #{@options[:output_path]}"
|
90
|
+
raise Twine::Error.new "Could not determine format given the contents of #{@options[:output_path]}. Try using `--format`."
|
80
91
|
end
|
81
92
|
|
82
93
|
file_name = @options[:file_name] || formatter.default_file_name
|
@@ -90,7 +101,7 @@ module Twine
|
|
90
101
|
|
91
102
|
output = formatter.format_file(lang)
|
92
103
|
unless output
|
93
|
-
Twine::
|
104
|
+
Twine::stdout.puts "Skipping file at path #{file_path} since it would not contain any translations."
|
94
105
|
next
|
95
106
|
end
|
96
107
|
|
@@ -112,7 +123,7 @@ module Twine
|
|
112
123
|
file_path = File.join(output_path, file_name)
|
113
124
|
output = formatter.format_file(lang)
|
114
125
|
unless output
|
115
|
-
Twine::
|
126
|
+
Twine::stdout.puts "Skipping file at path #{file_path} since it would not contain any translations."
|
116
127
|
next
|
117
128
|
end
|
118
129
|
|
@@ -148,7 +159,7 @@ module Twine
|
|
148
159
|
|
149
160
|
output = formatter.format_file(lang)
|
150
161
|
unless output
|
151
|
-
Twine::
|
162
|
+
Twine::stdout.puts "Skipping file #{file_name} since it would not contain any translations."
|
152
163
|
next
|
153
164
|
end
|
154
165
|
|
@@ -197,6 +208,7 @@ module Twine
|
|
197
208
|
raise Twine::Error.new("File does not exist: #{@options[:input_path]}")
|
198
209
|
end
|
199
210
|
|
211
|
+
error_encountered = false
|
200
212
|
Dir.mktmpdir do |temp_dir|
|
201
213
|
Zip::File.open(@options[:input_path]) do |zipfile|
|
202
214
|
zipfile.each do |entry|
|
@@ -209,6 +221,7 @@ module Twine
|
|
209
221
|
read_localization_file(real_path)
|
210
222
|
rescue Twine::Error => e
|
211
223
|
Twine::stderr.puts "#{e.message}"
|
224
|
+
error_encountered = true
|
212
225
|
end
|
213
226
|
end
|
214
227
|
end
|
@@ -216,6 +229,10 @@ module Twine
|
|
216
229
|
|
217
230
|
output_path = @options[:output_path] || @options[:twine_file]
|
218
231
|
write_twine_data(output_path)
|
232
|
+
|
233
|
+
if error_encountered
|
234
|
+
raise Twine::Error.new("At least one file could not be consumed")
|
235
|
+
end
|
219
236
|
end
|
220
237
|
|
221
238
|
def validate_twine_file
|
@@ -224,6 +241,7 @@ module Twine
|
|
224
241
|
duplicate_keys = Set.new
|
225
242
|
keys_without_tags = Set.new
|
226
243
|
invalid_keys = Set.new
|
244
|
+
keys_with_python_only_placeholders = Set.new
|
227
245
|
valid_key_regex = /^[A-Za-z0-9_]+$/
|
228
246
|
|
229
247
|
@twine_file.sections.each do |section|
|
@@ -236,6 +254,8 @@ module Twine
|
|
236
254
|
keys_without_tags.add(definition.key) if definition.tags == nil or definition.tags.length == 0
|
237
255
|
|
238
256
|
invalid_keys << definition.key unless definition.key =~ valid_key_regex
|
257
|
+
|
258
|
+
keys_with_python_only_placeholders << definition.key if definition.translations.values.any? { |v| Placeholders.contains_python_specific_placeholder(v) }
|
239
259
|
end
|
240
260
|
end
|
241
261
|
|
@@ -258,6 +278,10 @@ module Twine
|
|
258
278
|
errors << "Found key(s) with invalid characters:\n#{join_keys.call(invalid_keys)}"
|
259
279
|
end
|
260
280
|
|
281
|
+
unless keys_with_python_only_placeholders.empty?
|
282
|
+
errors << "Found key(s) with placeholders that are only supported by Python:\n#{join_keys.call(keys_with_python_only_placeholders)}"
|
283
|
+
end
|
284
|
+
|
261
285
|
raise Twine::Error.new errors.join("\n\n") unless errors.empty?
|
262
286
|
|
263
287
|
Twine::stdout.puts "#{@options[:twine_file]} is valid."
|
@@ -277,21 +301,16 @@ module Twine
|
|
277
301
|
end
|
278
302
|
end
|
279
303
|
|
280
|
-
def determine_language_given_path(path)
|
281
|
-
code = File.basename(path, File.extname(path))
|
282
|
-
return code if @twine_file.language_codes.include? code
|
283
|
-
end
|
284
|
-
|
285
304
|
def formatter_for_format(format)
|
286
305
|
find_formatter { |f| f.format_name == format }
|
287
306
|
end
|
288
307
|
|
289
308
|
def find_formatter(&block)
|
290
|
-
formatters = Formatters.formatters.select
|
309
|
+
formatters = Formatters.formatters.select(&block)
|
291
310
|
if formatters.empty?
|
292
311
|
return nil
|
293
312
|
elsif formatters.size > 1
|
294
|
-
raise Twine::Error.new("Unable to determine format. Candidates are: #{formatters.map(&:format_name).join(', ')}. Please specify the format you want using
|
313
|
+
raise Twine::Error.new("Unable to determine format. Candidates are: #{formatters.map(&:format_name).join(', ')}. Please specify the format you want using `--format`")
|
295
314
|
end
|
296
315
|
formatter = formatters.first
|
297
316
|
formatter.twine_file = @twine_file
|
@@ -322,12 +341,12 @@ module Twine
|
|
322
341
|
end
|
323
342
|
|
324
343
|
unless formatter
|
325
|
-
raise Twine::Error.new "Unable to determine format of #{path}"
|
344
|
+
raise Twine::Error.new "Unable to determine format of #{path}. Try using `--format`."
|
326
345
|
end
|
327
346
|
|
328
|
-
lang = lang ||
|
347
|
+
lang = lang || formatter.determine_language_given_path(path)
|
329
348
|
unless lang
|
330
|
-
raise Twine::Error.new "Unable to determine language for #{path}"
|
349
|
+
raise Twine::Error.new "Unable to determine language for #{path}. Try using `--lang`."
|
331
350
|
end
|
332
351
|
|
333
352
|
@twine_file.language_codes << lang unless @twine_file.language_codes.include? lang
|
data/lib/twine/twine_file.rb
CHANGED
@@ -190,7 +190,7 @@ module Twine
|
|
190
190
|
|
191
191
|
value = write_value(definition, dev_lang, f)
|
192
192
|
if !value && !definition.reference_key
|
193
|
-
puts "
|
193
|
+
Twine::stdout.puts "WARNING: #{definition.key} does not exist in developer language '#{dev_lang}'"
|
194
194
|
end
|
195
195
|
|
196
196
|
if definition.reference_key
|
data/lib/twine/version.rb
CHANGED
Binary file
|
@@ -1,12 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
# Language: en
|
1
|
+
# Django Strings File
|
2
|
+
# Generated by Twine <%= Twine::VERSION %>
|
3
|
+
# Language: en
|
5
4
|
msgid ""
|
6
5
|
msgstr ""
|
7
6
|
"Content-Type: text/plain; charset=UTF-8\n"
|
8
7
|
|
9
|
-
|
8
|
+
# --------- Section 1 --------- #
|
10
9
|
|
11
10
|
#. comment key1
|
12
11
|
# base translation: "value1-english"
|
@@ -18,7 +17,7 @@ msgid "key2"
|
|
18
17
|
msgstr "value2-english"
|
19
18
|
|
20
19
|
|
21
|
-
|
20
|
+
# --------- Section 2 --------- #
|
22
21
|
|
23
22
|
# base translation: "value3-english"
|
24
23
|
msgid "key3"
|
data/test/test_cli.rb
CHANGED
@@ -48,6 +48,13 @@ class CLITest < TwineTest
|
|
48
48
|
assert_equal 'UTF16', @options[:encoding]
|
49
49
|
end
|
50
50
|
|
51
|
+
def assert_option_escape_all_tags
|
52
|
+
parse_with "--escape-all-tags"
|
53
|
+
assert @options[:escape_all_tags]
|
54
|
+
parse_with "--no-escape-all-tags"
|
55
|
+
refute @options[:escape_all_tags]
|
56
|
+
end
|
57
|
+
|
51
58
|
def assert_option_format
|
52
59
|
random_format = Twine::Formatters.formatters.sample.format_name.downcase
|
53
60
|
parse_with "--format #{random_format}"
|
@@ -82,6 +89,13 @@ class CLITest < TwineTest
|
|
82
89
|
assert_equal @output_path, @options[:output_path]
|
83
90
|
end
|
84
91
|
|
92
|
+
def assert_option_quiet
|
93
|
+
parse_with '--quiet'
|
94
|
+
assert @options[:quiet]
|
95
|
+
parse_with '--no-quiet'
|
96
|
+
refute @options[:quiet]
|
97
|
+
end
|
98
|
+
|
85
99
|
def assert_option_tags
|
86
100
|
# single tag
|
87
101
|
random_tag = "tag#{rand(100)}"
|
@@ -156,7 +170,7 @@ class TestGenerateLocalizationFileCLI < CLITest
|
|
156
170
|
|
157
171
|
def test_missing_argument
|
158
172
|
assert_raises Twine::Error do
|
159
|
-
parse "generate-localization-file #{@
|
173
|
+
parse "generate-localization-file #{@twine_file_path}"
|
160
174
|
end
|
161
175
|
end
|
162
176
|
|
@@ -170,10 +184,12 @@ class TestGenerateLocalizationFileCLI < CLITest
|
|
170
184
|
assert_help
|
171
185
|
assert_option_developer_language
|
172
186
|
assert_option_encoding
|
187
|
+
assert_option_escape_all_tags
|
173
188
|
assert_option_format
|
174
189
|
assert_option_include
|
175
190
|
assert_option_single_language
|
176
191
|
assert_raises(Twine::Error) { assert_option_multiple_languages }
|
192
|
+
assert_option_quiet
|
177
193
|
assert_option_tags
|
178
194
|
assert_option_untagged
|
179
195
|
assert_option_validate
|
@@ -209,8 +225,10 @@ class TestGenerateAllLocalizationFilesCLI < CLITest
|
|
209
225
|
assert_help
|
210
226
|
assert_option_developer_language
|
211
227
|
assert_option_encoding
|
228
|
+
assert_option_escape_all_tags
|
212
229
|
assert_option_format
|
213
230
|
assert_option_include
|
231
|
+
assert_option_quiet
|
214
232
|
assert_option_tags
|
215
233
|
assert_option_untagged
|
216
234
|
assert_option_validate
|
@@ -259,7 +277,9 @@ class TestGenerateLocalizationArchiveCLI < CLITest
|
|
259
277
|
assert_help
|
260
278
|
assert_option_developer_language
|
261
279
|
assert_option_encoding
|
280
|
+
assert_option_escape_all_tags
|
262
281
|
assert_option_include
|
282
|
+
assert_option_quiet
|
263
283
|
assert_option_tags
|
264
284
|
assert_option_untagged
|
265
285
|
assert_option_validate
|
@@ -278,7 +298,7 @@ class TestGenerateLocalizationArchiveCLI < CLITest
|
|
278
298
|
|
279
299
|
def test_deprecated_command_prints_warning
|
280
300
|
parse "generate-loc-drop #{@twine_file_path} #{@output_path} --format apple"
|
281
|
-
assert_match "WARNING: Twine commands names have changed.", Twine::
|
301
|
+
assert_match "WARNING: Twine commands names have changed.", Twine::stdout.string
|
282
302
|
end
|
283
303
|
end
|
284
304
|
|
@@ -317,6 +337,7 @@ class TestConsumeLocalizationFileCLI < CLITest
|
|
317
337
|
assert_option_single_language
|
318
338
|
assert_raises(Twine::Error) { assert_option_multiple_languages }
|
319
339
|
assert_option_output_path
|
340
|
+
assert_option_quiet
|
320
341
|
assert_option_tags
|
321
342
|
end
|
322
343
|
end
|
@@ -354,6 +375,7 @@ class TestConsumeAllLocalizationFilesCLI < CLITest
|
|
354
375
|
assert_option_encoding
|
355
376
|
assert_option_format
|
356
377
|
assert_option_output_path
|
378
|
+
assert_option_quiet
|
357
379
|
assert_option_tags
|
358
380
|
end
|
359
381
|
end
|
@@ -391,6 +413,7 @@ class TestConsumeLocalizationArchiveCLI < CLITest
|
|
391
413
|
assert_option_encoding
|
392
414
|
assert_option_format
|
393
415
|
assert_option_output_path
|
416
|
+
assert_option_quiet
|
394
417
|
assert_option_tags
|
395
418
|
end
|
396
419
|
|
@@ -401,7 +424,7 @@ class TestConsumeLocalizationArchiveCLI < CLITest
|
|
401
424
|
|
402
425
|
def test_deprecated_command_prints_warning
|
403
426
|
parse "consume-loc-drop #{@twine_file_path} #{@input_path}"
|
404
|
-
assert_match "WARNING: Twine commands names have changed.", Twine::
|
427
|
+
assert_match "WARNING: Twine commands names have changed.", Twine::stdout.string
|
405
428
|
end
|
406
429
|
end
|
407
430
|
|
@@ -432,6 +455,7 @@ class TestValidateTwineFileCLI < CLITest
|
|
432
455
|
def test_options
|
433
456
|
assert_help
|
434
457
|
assert_option_developer_language
|
458
|
+
assert_option_quiet
|
435
459
|
end
|
436
460
|
|
437
461
|
def test_option_pedantic
|
@@ -4,24 +4,30 @@ class TestConsumeLocalizationArchive < CommandTest
|
|
4
4
|
def setup
|
5
5
|
super
|
6
6
|
|
7
|
-
options = {}
|
8
|
-
options[:input_path] = fixture_path 'consume_localization_archive.zip'
|
9
|
-
options[:output_path] = @output_path
|
10
|
-
options[:format] = 'apple'
|
11
|
-
|
12
7
|
@twine_file = build_twine_file 'en', 'es' do
|
13
8
|
add_section 'Section' do
|
14
9
|
add_definition key1: 'value1'
|
15
10
|
end
|
16
11
|
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def new_runner(options = {})
|
15
|
+
options[:input_path] = fixture_path 'consume_localization_archive.zip'
|
16
|
+
options[:output_path] = @output_path
|
17
17
|
|
18
|
-
|
18
|
+
Twine::Runner.new(options, @twine_file)
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_consumes_zip_file
|
22
|
-
|
22
|
+
new_runner(format: 'android').consume_localization_archive
|
23
23
|
|
24
24
|
assert @twine_file.definitions_by_key['key1'].translations['en'], 'value1-english'
|
25
25
|
assert @twine_file.definitions_by_key['key1'].translations['es'], 'value1-spanish'
|
26
26
|
end
|
27
|
+
|
28
|
+
def test_raises_error_if_format_ambiguous
|
29
|
+
assert_raises Twine::Error do
|
30
|
+
new_runner.consume_localization_archive
|
31
|
+
end
|
32
|
+
end
|
27
33
|
end
|
data/test/test_formatters.rb
CHANGED
@@ -47,26 +47,87 @@ class TestAndroidFormatter < FormatterTest
|
|
47
47
|
'a "good" way' => 'a \"good\" way',
|
48
48
|
|
49
49
|
'<b>bold</b>' => '<b>bold</b>',
|
50
|
+
'<em>bold</em>' => '<em>bold</em>',
|
51
|
+
|
50
52
|
'<i>italic</i>' => '<i>italic</i>',
|
53
|
+
'<cite>italic</cite>' => '<cite>italic</cite>',
|
54
|
+
'<dfn>italic</dfn>' => '<dfn>italic</dfn>',
|
55
|
+
|
56
|
+
'<big>larger</big>' => '<big>larger</big>',
|
57
|
+
'<small>smaller</small>' => '<small>smaller</small>',
|
58
|
+
|
59
|
+
'<font color="#45C1D0">F</font>' => '<font color="#45C1D0">F</font>',
|
60
|
+
|
61
|
+
'<tt>monospaced</tt>' => '<tt>monospaced</tt>',
|
62
|
+
|
63
|
+
'<s>strike</s>' => '<s>strike</s>',
|
64
|
+
'<strike>strike</strike>' => '<strike>strike</strike>',
|
65
|
+
'<del>strike</del>' => '<del>strike</del>',
|
66
|
+
|
51
67
|
'<u>underline</u>' => '<u>underline</u>',
|
52
68
|
|
69
|
+
'<super>superscript</super>'=> '<super>superscript</super>',
|
70
|
+
|
71
|
+
'<sub>subscript</sub>' => '<sub>subscript</sub>',
|
72
|
+
|
73
|
+
'<ul>bullet point</ul>' => '<ul>bullet point</ul>',
|
74
|
+
'<li>bullet point</li>' => '<li>bullet point</li>',
|
75
|
+
|
76
|
+
'<br>line break' => '<br>line break',
|
77
|
+
|
78
|
+
'<div>division</div>' => '<div>division</div>',
|
79
|
+
|
80
|
+
'<span style="color:#45C1D0">inline</span>' => '<span style="color:#45C1D0">inline</span>',
|
81
|
+
|
82
|
+
'<p>para</p>' => '<p>para</p>',
|
83
|
+
'<p dir="ltr">para</p>' => '<p dir="ltr">para</p>',
|
84
|
+
|
53
85
|
'<b>%@</b>' => '<b>%s</b>',
|
86
|
+
'<em>%@</em>' => '<em>%s</em>',
|
87
|
+
|
54
88
|
'<i>%@</i>' => '<i>%s</i>',
|
89
|
+
'<cite>%@</cite>' => '<cite>%s</cite>',
|
90
|
+
'<dfn>%@</dfn>' => '<dfn>%s</dfn>',
|
91
|
+
|
92
|
+
'<big>%@</big>' => '<big>%s</big>',
|
93
|
+
'<small>%@</small>' => '<small>%s</small>',
|
94
|
+
|
95
|
+
'<font color="#45C1D0>%@</font>' => '<font color="#45C1D0>%s</font>',
|
96
|
+
|
97
|
+
'<tt>%@</tt>' => '<tt>%s</tt>',
|
98
|
+
|
99
|
+
'<s>%@</s>' => '<s>%s</s>',
|
100
|
+
'<strike>%@</strike>' => '<strike>%s</strike>',
|
101
|
+
'<del>%@</del>' => '<del>%s</del>',
|
102
|
+
|
55
103
|
'<u>%@</u>' => '<u>%s</u>',
|
56
104
|
|
57
|
-
'<
|
58
|
-
|
105
|
+
'<super>%@</super>' => '<super>%s</super>',
|
106
|
+
|
107
|
+
'<sub>%@</sub>' => '<sub>%s</sub>',
|
108
|
+
|
109
|
+
'<ul>%@</ul>' => '<ul>%s</ul>',
|
110
|
+
'<li>%@</li>' => '<li>%s</li>',
|
111
|
+
|
112
|
+
'<br>%@' => '<br>%s',
|
113
|
+
|
114
|
+
'<div>%@</div>' => '<div>%s</div>',
|
115
|
+
|
116
|
+
'<span style="color:#45C1D0">%@</span>' => '<span style="color:#45C1D0">%s</span>',
|
117
|
+
|
118
|
+
'<p>%@</p>' => '<p>%s</p>',
|
119
|
+
'<p dir="ltr">%@</p>' => '<p dir="ltr">%s</p>',
|
59
120
|
|
60
121
|
'<a href="target">link</a>' => '<a href="target">link</a>',
|
61
122
|
'<a href="target">"link"</a>' => '<a href="target">\"link\"</a>',
|
62
123
|
'<a href="target"></a>"out"' => '<a href="target"></a>\"out\"',
|
63
124
|
'<a href="http://url.com?param=1¶m2=3¶m3=%20">link</a>' => '<a href="http://url.com?param=1¶m2=3¶m3=%20">link</a>',
|
64
125
|
|
65
|
-
'<
|
66
|
-
'<![CDATA[]]><
|
67
|
-
'<![CDATA[<
|
68
|
-
'<![CDATA[<
|
69
|
-
'<![CDATA[]]><![CDATA[<
|
126
|
+
'<q>escaped</q><![CDATA[]]>' => '<q>escaped</q><![CDATA[]]>',
|
127
|
+
'<![CDATA[]]><q>escaped</q>' => '<![CDATA[]]><q>escaped</q>',
|
128
|
+
'<![CDATA[<q>unescaped</q>]]>' => '<![CDATA[<q>unescaped</q>]]>',
|
129
|
+
'<![CDATA[<q>unescaped with %@</q>]]>' => '<![CDATA[<q>unescaped with %s</q>]]>',
|
130
|
+
'<![CDATA[]]><![CDATA[<q>unescaped</q>]]>' => '<![CDATA[]]><![CDATA[<q>unescaped</q>]]>',
|
70
131
|
|
71
132
|
'<![CDATA[&]]>' => '<![CDATA[&]]>',
|
72
133
|
'<![CDATA[\']]>' => '<![CDATA[\']]>',
|
@@ -77,6 +138,11 @@ class TestAndroidFormatter < FormatterTest
|
|
77
138
|
'<xliff:g id="42">untouched</xliff:g>' => '<xliff:g id="42">untouched</xliff:g>',
|
78
139
|
'<xliff:g id="1">first</xliff:g> inbetween <xliff:g id="2">second</xliff:g>' => '<xliff:g id="1">first</xliff:g> inbetween <xliff:g id="2">second</xliff:g>'
|
79
140
|
}
|
141
|
+
@escape_all_test_values = {
|
142
|
+
'<b>bold</b>' => '<b>bold</b>',
|
143
|
+
'<i>italic</i>' => '<i>italic</i>',
|
144
|
+
'<u>underline</u>' => '<u>underline</u>'
|
145
|
+
}
|
80
146
|
end
|
81
147
|
|
82
148
|
def test_read_format
|
@@ -101,6 +167,36 @@ class TestAndroidFormatter < FormatterTest
|
|
101
167
|
assert_equal 'This is\n a string', @empty_twine_file.definitions_by_key["foo"].translations['en']
|
102
168
|
end
|
103
169
|
|
170
|
+
def test_read_html_tags
|
171
|
+
content = <<-EOCONTENT
|
172
|
+
<?xml version="1.0" encoding="utf-8"?>
|
173
|
+
<resources>
|
174
|
+
<string name="foo">Hello, <b>BOLD</b></string>
|
175
|
+
</resources>
|
176
|
+
EOCONTENT
|
177
|
+
|
178
|
+
io = StringIO.new(content)
|
179
|
+
|
180
|
+
@formatter.read io, 'en'
|
181
|
+
|
182
|
+
assert_equal 'Hello, <b>BOLD</b>', @empty_twine_file.definitions_by_key["foo"].translations['en']
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_double_quotes_are_not_modified
|
186
|
+
content = <<-EOCONTENT
|
187
|
+
<?xml version="1.0" encoding="utf-8"?>
|
188
|
+
<resources>
|
189
|
+
<string name="foo">Hello, <a href="http://www.foo.com">BOLD</a></string>
|
190
|
+
</resources>
|
191
|
+
EOCONTENT
|
192
|
+
|
193
|
+
io = StringIO.new(content)
|
194
|
+
|
195
|
+
@formatter.read io, 'en'
|
196
|
+
|
197
|
+
assert_equal 'Hello, <a href="http://www.foo.com">BOLD</a>', @empty_twine_file.definitions_by_key["foo"].translations['en']
|
198
|
+
end
|
199
|
+
|
104
200
|
def test_set_translation_converts_leading_spaces
|
105
201
|
@formatter.set_translation_for_key 'key1', 'en', "\u0020value"
|
106
202
|
assert_equal ' value', @empty_twine_file.definitions_by_key['key1'].translations['en']
|
@@ -126,6 +222,11 @@ class TestAndroidFormatter < FormatterTest
|
|
126
222
|
@formatter.set_translation_for_key 'key1', 'en', input
|
127
223
|
assert_equal expected, @empty_twine_file.definitions_by_key['key1'].translations['en']
|
128
224
|
end
|
225
|
+
|
226
|
+
@escape_all_test_values.each do |expected, input|
|
227
|
+
@formatter.set_translation_for_key 'key1', 'en', input
|
228
|
+
assert_equal expected, @empty_twine_file.definitions_by_key['key1'].translations['en']
|
229
|
+
end
|
129
230
|
end
|
130
231
|
|
131
232
|
def test_format_file
|
@@ -154,6 +255,11 @@ class TestAndroidFormatter < FormatterTest
|
|
154
255
|
@escape_test_values.each do |input, expected|
|
155
256
|
assert_equal expected, @formatter.format_value(input)
|
156
257
|
end
|
258
|
+
|
259
|
+
@formatter.options.merge!({ escape_all_tags: true })
|
260
|
+
@escape_all_test_values.each do |input, expected|
|
261
|
+
assert_equal expected, @formatter.format_value(input)
|
262
|
+
end
|
157
263
|
end
|
158
264
|
|
159
265
|
def test_format_value_escapes_non_resource_identifier_at_signs
|
@@ -165,8 +271,24 @@ class TestAndroidFormatter < FormatterTest
|
|
165
271
|
assert_equal identifier, @formatter.format_value(identifier)
|
166
272
|
end
|
167
273
|
|
274
|
+
def test_deducts_language_from_filename
|
275
|
+
language = KNOWN_LANGUAGES.sample
|
276
|
+
assert_equal language, @formatter.determine_language_given_path("#{language}.xml")
|
277
|
+
end
|
278
|
+
|
279
|
+
def test_recognize_every_twine_language_from_filename
|
280
|
+
twine_file = build_twine_file "not-a-lang-code" do
|
281
|
+
add_section "Section" do
|
282
|
+
add_definition key: "value"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
@formatter.twine_file = twine_file
|
287
|
+
assert_equal "not-a-lang-code", @formatter.determine_language_given_path("not-a-lang-code.xml")
|
288
|
+
end
|
289
|
+
|
168
290
|
def test_deducts_language_from_resource_folder
|
169
|
-
language =
|
291
|
+
language = KNOWN_LANGUAGES.sample
|
170
292
|
assert_equal language, @formatter.determine_language_given_path("res/values-#{language}")
|
171
293
|
end
|
172
294
|
|
@@ -185,6 +307,13 @@ class TestAndroidFormatter < FormatterTest
|
|
185
307
|
def test_output_path_with_region
|
186
308
|
assert_equal 'values-en-rGB', @formatter.output_path_for_language('en-GB')
|
187
309
|
end
|
310
|
+
|
311
|
+
def test_output_path_respects_default_lang
|
312
|
+
@formatter.twine_file.language_codes.concat KNOWN_LANGUAGES
|
313
|
+
non_default_language = KNOWN_LANGUAGES[1..-1].sample
|
314
|
+
assert_equal 'values', @formatter.output_path_for_language(KNOWN_LANGUAGES[0])
|
315
|
+
assert_equal "values-#{non_default_language}", @formatter.output_path_for_language(non_default_language)
|
316
|
+
end
|
188
317
|
end
|
189
318
|
|
190
319
|
class TestAppleFormatter < FormatterTest
|
@@ -198,6 +327,22 @@ class TestAppleFormatter < FormatterTest
|
|
198
327
|
assert_file_contents_read_correctly
|
199
328
|
end
|
200
329
|
|
330
|
+
def test_deducts_language_from_filename
|
331
|
+
language = KNOWN_LANGUAGES.sample
|
332
|
+
assert_equal language, @formatter.determine_language_given_path("#{language}.strings")
|
333
|
+
end
|
334
|
+
|
335
|
+
def test_recognize_every_twine_language_from_filename
|
336
|
+
twine_file = build_twine_file "not-a-lang-code" do
|
337
|
+
add_section "Section" do
|
338
|
+
add_definition key: "value"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
@formatter.twine_file = twine_file
|
343
|
+
assert_equal "not-a-lang-code", @formatter.determine_language_given_path("not-a-lang-code.strings")
|
344
|
+
end
|
345
|
+
|
201
346
|
def test_deducts_language_from_resource_folder
|
202
347
|
language = %w(en de fr).sample
|
203
348
|
assert_equal language, @formatter.determine_language_given_path("#{language}.lproj/Localizable.strings")
|
@@ -302,6 +447,21 @@ class TestJQueryFormatter < FormatterTest
|
|
302
447
|
def test_format_value_with_newline
|
303
448
|
assert_equal "value\nwith\nline\nbreaks", @formatter.format_value("value\nwith\nline\nbreaks")
|
304
449
|
end
|
450
|
+
|
451
|
+
def test_deducts_language_from_filename
|
452
|
+
language = KNOWN_LANGUAGES.sample
|
453
|
+
assert_equal language, @formatter.determine_language_given_path("#{language}.json")
|
454
|
+
end
|
455
|
+
|
456
|
+
def test_deducts_language_from_extended_filename
|
457
|
+
language = KNOWN_LANGUAGES.sample
|
458
|
+
assert_equal language, @formatter.determine_language_given_path("something-#{language}.json")
|
459
|
+
end
|
460
|
+
|
461
|
+
def test_deducts_language_from_path
|
462
|
+
language = %w(en-GB de fr).sample
|
463
|
+
assert_equal language, @formatter.determine_language_given_path("/output/#{language}/#{@formatter.default_file_name}")
|
464
|
+
end
|
305
465
|
end
|
306
466
|
|
307
467
|
class TestGettextFormatter < FormatterTest
|
@@ -331,6 +491,21 @@ class TestGettextFormatter < FormatterTest
|
|
331
491
|
language = "en-GB"
|
332
492
|
assert_equal language, @formatter.determine_language_given_path("#{language}.po")
|
333
493
|
end
|
494
|
+
|
495
|
+
def test_deducts_language_from_path
|
496
|
+
language = %w(en-GB de fr).sample
|
497
|
+
assert_equal language, @formatter.determine_language_given_path("/output/#{language}/#{@formatter.default_file_name}")
|
498
|
+
end
|
499
|
+
|
500
|
+
def test_quoted_strings
|
501
|
+
formatter = Twine::Formatters::Gettext.new
|
502
|
+
formatter.twine_file = build_twine_file "not-a-lang-code" do
|
503
|
+
add_section "Section" do
|
504
|
+
add_definition key: "foo \"bar\" baz"
|
505
|
+
end
|
506
|
+
end
|
507
|
+
assert_equal content('formatter_gettext_quotes.po'), formatter.format_file('en')
|
508
|
+
end
|
334
509
|
end
|
335
510
|
|
336
511
|
class TestTizenFormatter < FormatterTest
|
@@ -351,7 +526,6 @@ class TestTizenFormatter < FormatterTest
|
|
351
526
|
formatter.twine_file = @twine_file
|
352
527
|
assert_equal content('formatter_tizen.xml'), formatter.format_file('en')
|
353
528
|
end
|
354
|
-
|
355
529
|
end
|
356
530
|
|
357
531
|
class TestDjangoFormatter < FormatterTest
|
@@ -375,6 +549,24 @@ class TestDjangoFormatter < FormatterTest
|
|
375
549
|
language = "en-GB"
|
376
550
|
assert_equal language, @formatter.determine_language_given_path("#{language}.po")
|
377
551
|
end
|
552
|
+
|
553
|
+
def test_deducts_language_from_path
|
554
|
+
language = %w(en-GB de fr).sample
|
555
|
+
assert_equal language, @formatter.determine_language_given_path("/output/#{language}/#{@formatter.default_file_name}")
|
556
|
+
end
|
557
|
+
|
558
|
+
def test_ignores_commented_out_strings
|
559
|
+
content = <<-EOCONTENT
|
560
|
+
#~ msgid "foo"
|
561
|
+
#~ msgstr "This should be ignored"
|
562
|
+
EOCONTENT
|
563
|
+
|
564
|
+
io = StringIO.new(content)
|
565
|
+
|
566
|
+
@formatter.read io, 'en'
|
567
|
+
|
568
|
+
assert_nil @empty_twine_file.definitions_by_key["foo"]
|
569
|
+
end
|
378
570
|
end
|
379
571
|
|
380
572
|
class TestFlashFormatter < FormatterTest
|
@@ -405,10 +597,10 @@ class TestFlashFormatter < FormatterTest
|
|
405
597
|
|
406
598
|
def test_deducts_language_from_resource_folder
|
407
599
|
language = %w(en de fr).sample
|
408
|
-
assert_equal language, @formatter.determine_language_given_path("locale/#{language}")
|
600
|
+
assert_equal language, @formatter.determine_language_given_path("locale/#{language}/#{@formatter.default_file_name}")
|
409
601
|
end
|
410
602
|
|
411
603
|
def test_deducts_language_and_region_from_resource_folder
|
412
|
-
assert_equal 'de-AT', @formatter.determine_language_given_path("locale/de-AT")
|
604
|
+
assert_equal 'de-AT', @formatter.determine_language_given_path("locale/de-AT/#{@formatter.default_file_name}")
|
413
605
|
end
|
414
606
|
end
|