twine 1.0.2 → 1.1
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.
- 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
|