abroad 1.0.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +142 -0
  3. data/abroad.gemspec +25 -0
  4. data/lib/abroad/extractors/extractor.rb +46 -0
  5. data/lib/abroad/extractors/json/json_extractor.rb +17 -0
  6. data/lib/abroad/extractors/json/key_value_extractor.rb +38 -0
  7. data/lib/abroad/extractors/json.rb +8 -0
  8. data/lib/abroad/extractors/xml/android_extractor.rb +113 -0
  9. data/lib/abroad/extractors/xml/xml_extractor.rb +28 -0
  10. data/lib/abroad/extractors/xml.rb +8 -0
  11. data/lib/abroad/extractors/yaml/dotted_key_extractor.rb +40 -0
  12. data/lib/abroad/extractors/yaml/jruby_compat.rb +170 -0
  13. data/lib/abroad/extractors/yaml/rails_extractor.rb +15 -0
  14. data/lib/abroad/extractors/yaml/yaml_extractor.rb +28 -0
  15. data/lib/abroad/extractors/yaml.rb +9 -0
  16. data/lib/abroad/extractors.rb +28 -0
  17. data/lib/abroad/serializers/json/json_serializer.rb +27 -0
  18. data/lib/abroad/serializers/json/key_value_serializer.rb +20 -0
  19. data/lib/abroad/serializers/json.rb +8 -0
  20. data/lib/abroad/serializers/serializer.rb +52 -0
  21. data/lib/abroad/serializers/trie.rb +76 -0
  22. data/lib/abroad/serializers/xml/android_serializer.rb +143 -0
  23. data/lib/abroad/serializers/xml/xml_serializer.rb +23 -0
  24. data/lib/abroad/serializers/xml.rb +8 -0
  25. data/lib/abroad/serializers/yaml/rails_serializer.rb +110 -0
  26. data/lib/abroad/serializers/yaml/yaml_serializer.rb +19 -0
  27. data/lib/abroad/serializers/yaml.rb +8 -0
  28. data/lib/abroad/serializers.rb +29 -0
  29. data/lib/abroad/version.rb +3 -0
  30. data/lib/abroad.rb +37 -0
  31. data/lib/ext/htmlentities/android_xml_decoder.rb +15 -0
  32. data/lib/ext/htmlentities/android_xml_encoder.rb +23 -0
  33. data/spec/abroad_spec.rb +35 -0
  34. data/spec/extractors/json/fixtures/arrays.json +9 -0
  35. data/spec/extractors/json/fixtures/basic.json +5 -0
  36. data/spec/extractors/json/fixtures/objects.json +11 -0
  37. data/spec/extractors/json/fixtures.yml +22 -0
  38. data/spec/extractors/json/json_extractor_spec.rb +6 -0
  39. data/spec/extractors/xml/fixtures/basic_arrays.xml +9 -0
  40. data/spec/extractors/xml/fixtures/basic_plurals.xml +8 -0
  41. data/spec/extractors/xml/fixtures/basic_strings.xml +8 -0
  42. data/spec/extractors/xml/fixtures/entities.xml +4 -0
  43. data/spec/extractors/xml/fixtures/markup.xml +5 -0
  44. data/spec/extractors/xml/fixtures/newlines.xml +6 -0
  45. data/spec/extractors/xml/fixtures/quotes.xml +9 -0
  46. data/spec/extractors/xml/fixtures.yml +54 -0
  47. data/spec/extractors/xml/xml_extractor_spec.rb +6 -0
  48. data/spec/extractors/yaml/fixtures/arrays.yml +9 -0
  49. data/spec/extractors/yaml/fixtures/arrays_and_hashes.yml +29 -0
  50. data/spec/extractors/yaml/fixtures/invalid_single_quote_escape.yml +2 -0
  51. data/spec/extractors/yaml/fixtures/invalid_single_quote_escape_array.yml +3 -0
  52. data/spec/extractors/yaml/fixtures/nesting.yml +19 -0
  53. data/spec/extractors/yaml/fixtures/short.yml +2 -0
  54. data/spec/extractors/yaml/fixtures.yml +75 -0
  55. data/spec/extractors/yaml/jruby_compat_spec.rb +54 -0
  56. data/spec/extractors/yaml/yaml_extractor_spec.rb +6 -0
  57. data/spec/serializers/json/key_value_serializer_spec.rb +26 -0
  58. data/spec/serializers/xml/android_serializer_spec.rb +165 -0
  59. data/spec/serializers/yaml/rails_serializer_spec.rb +171 -0
  60. data/spec/spec_helper.rb +43 -0
  61. metadata +186 -0
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Abroad do
4
+ describe '.extractors' do
5
+ it 'lists all the registered extractors' do
6
+ extractors = Abroad.extractors
7
+ expect(extractors).to be_a(Array)
8
+ expect(extractors).to include('yaml/rails')
9
+ end
10
+ end
11
+
12
+ describe '.serializers' do
13
+ it 'lists all the registered serializers' do
14
+ serializers = Abroad.serializers
15
+ expect(serializers).to be_a(Array)
16
+ expect(serializers).to include('yaml/rails')
17
+ end
18
+ end
19
+
20
+ describe '.extractor' do
21
+ it 'retrieves the extractor class by id' do
22
+ expect(Abroad.extractor('yaml/rails')).to eq(
23
+ Abroad::Extractors::Yaml::RailsExtractor
24
+ )
25
+ end
26
+ end
27
+
28
+ describe '.serializer' do
29
+ it 'retrieves the serializer class by id' do
30
+ expect(Abroad.serializer('yaml/rails')).to eq(
31
+ Abroad::Serializers::Yaml::RailsSerializer
32
+ )
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ {
2
+ "array1": ["1", "2", "3"],
3
+ "arrayObj": {
4
+ "array2": ["4", "5"]
5
+ },
6
+ "hello": "cool string",
7
+ "foo.bar": "baz",
8
+ "jelly.beans": "popcorn"
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "hello": "cool string",
3
+ "foo.bar": "baz",
4
+ "jelly.beans": "popcorn"
5
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "smartling" : {
3
+ "translate_paths" : ["/*", "/*/*"],
4
+ "source_key_paths" : ["/{*}"],
5
+ "variants_enabled" : true,
6
+ "placeholder_format_custom" : ["%s"]
7
+ },
8
+ "hello": "cool string",
9
+ "foo.bar": "baz",
10
+ "jelly.beans": "popcorn"
11
+ }
@@ -0,0 +1,22 @@
1
+ :json/key-value:
2
+ basic.json:
3
+ - key: foo.bar
4
+ string: baz
5
+ - key: hello
6
+ string: cool string
7
+ - key: jelly.beans
8
+ string: popcorn
9
+ objects.json:
10
+ - key: foo.bar
11
+ string: baz
12
+ - key: hello
13
+ string: cool string
14
+ - key: jelly.beans
15
+ string: popcorn
16
+ arrays.json:
17
+ - key: foo.bar
18
+ string: baz
19
+ - key: hello
20
+ string: cool string
21
+ - key: jelly.beans
22
+ string: popcorn
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'JsonExtractor' do
4
+ fixture_dir = File.expand_path('./', File.dirname(__FILE__))
5
+ it_behaves_like 'a fixture-based extractor', fixture_dir, Abroad::Extractors::Json
6
+ end
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <string-array name="captains">
4
+ <item>Janeway</item>
5
+ <item>Sisko</item>
6
+ <item>Picard</item>
7
+ <item>Kirk</item>
8
+ </string-array>
9
+ </resources>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <plurals name="horses">
4
+ <item quantity="zero">There are no horses</item>
5
+ <item quantity="one">There is one horse</item>
6
+ <item quantity="other">There are %d horses</item>
7
+ </plurals>
8
+ </resources>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <resources>
3
+ <string name="foobar">baz</string>
4
+ <string name="password_length_too_short">Password is too short</string>
5
+ <string name="password_length_too_long">Password is too long</string>
6
+ <string name="password_empty">Please enter your password.</string>
7
+ <string name="username_empty">Please enter your email address.</string>
8
+ </resources>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <resources>
3
+ <string name="states">Alaska &amp; Washington</string>
4
+ </resources>
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <resources>
3
+ <string name="internal_markup">I <b>have</b> <a href="foo">internal</a> markup</string>
4
+ <string name="quoted_markup">"I <b>have</b> <a href="foo">quoted</a> markup"</string>
5
+ </resources>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <resources>
3
+ <string name="foobar">baz</string>
4
+ <string name="password_length_too_short">Password is
5
+ too short</string>
6
+ </resources>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <resources>
3
+ <string name="quotes">"I have quotes"</string>
4
+ <string name="leading_space">" I have a leading space"</string>
5
+ <string name="internal_quotes">"I have \"internal\" quotes"</string>
6
+ <string name="apostrophes">That\'s the ticket</string>
7
+ <string name="apostrophes_in_quotes">"That's the ticket"</string>
8
+ <string name="escaped_apostrophes_in_quotes">"That\'s the ticket"</string>
9
+ </resources>
@@ -0,0 +1,54 @@
1
+ :xml/android:
2
+ basic_strings.xml:
3
+ - key: foobar
4
+ string: baz
5
+ - key: password_length_too_short
6
+ string: Password is too short
7
+ - key: password_length_too_long
8
+ string: Password is too long
9
+ - key: password_empty
10
+ string: Please enter your password.
11
+ - key: username_empty
12
+ string: Please enter your email address.
13
+ basic_arrays.xml:
14
+ - key: captains.0
15
+ string: Janeway
16
+ - key: captains.1
17
+ string: Sisko
18
+ - key: captains.2
19
+ string: Picard
20
+ - key: captains.3
21
+ string: Kirk
22
+ basic_plurals.xml:
23
+ - key: horses.zero
24
+ string: There are no horses
25
+ - key: horses.one
26
+ string: There is one horse
27
+ - key: horses.other
28
+ string: There are %d horses
29
+ quotes.xml:
30
+ - key: quotes
31
+ string: I have quotes
32
+ - key: leading_space
33
+ string: " I have a leading space"
34
+ - key: internal_quotes
35
+ string: I have "internal" quotes
36
+ - key: apostrophes
37
+ string: That's the ticket
38
+ - key: apostrophes_in_quotes
39
+ string: That's the ticket
40
+ - key: escaped_apostrophes_in_quotes
41
+ string: That's the ticket
42
+ markup.xml:
43
+ - key: internal_markup
44
+ string: I <b>have</b> <a href="foo">internal</a> markup
45
+ - key: quoted_markup
46
+ string: I <b>have</b> <a href="foo">quoted</a> markup
47
+ newlines.xml:
48
+ - key: foobar
49
+ string: baz
50
+ - key: password_length_too_short
51
+ string: "Password is\ntoo short"
52
+ entities.xml:
53
+ - key: states
54
+ string: "Alaska & Washington"
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'XmlExtractor' do
4
+ fixture_dir = File.expand_path('./', File.dirname(__FILE__))
5
+ it_behaves_like 'a fixture-based extractor', fixture_dir, Abroad::Extractors::Xml
6
+ end
@@ -0,0 +1,9 @@
1
+ en:
2
+ jobs:
3
+ benefits:
4
+ - Benefits to write home about
5
+ - Stress-free commute
6
+ - Brand-new gadgets
7
+ other:
8
+ - Stay happy and healthy
9
+ - Playtime anytime
@@ -0,0 +1,29 @@
1
+ en:
2
+ jobs:
3
+ benefits:
4
+ -
5
+ title: Benefits to write home about
6
+ body:
7
+ - Competitive salaries and bonuses
8
+ - Medical, dental, vision, and life insurance plans
9
+ -
10
+ title: Stress-free commute
11
+ body:
12
+ - Downtown SF location right next to BART and MUNI lines
13
+ - Free cabs and meals for those late nights
14
+ -
15
+ title: Brand-new gadgets
16
+ body:
17
+ - Pick out a new Mac or PC desktop or laptop
18
+ - Big monitors, fancy headphones, and more
19
+ -
20
+ title: Stay happy and healthy
21
+ body:
22
+ - Catered lunches and a pantry with every snack, drink, and Bi-Rite meal
23
+ - Gym membership at the Sports Club LA (2 blocks from the office)
24
+ -
25
+ title: Playtime anytime
26
+ body:
27
+ - Unlimited paid time off
28
+ - A game room with video games, board games, foosball, ping-pong, and shuffleboard
29
+ - A Culture Team behind office happy hours and Hawai'i holiday parties
@@ -0,0 +1,2 @@
1
+ foo:
2
+ bar: "baz\'boo"
@@ -0,0 +1,3 @@
1
+ foo:
2
+ bar:
3
+ - "baz\'boo"
@@ -0,0 +1,19 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ user:
5
+ unique_name: "Username"
6
+ password: "Password"
7
+ email_address: "Email address"
8
+ email_address:
9
+ address: "Email address"
10
+ testimonial:
11
+ original_text: "Your Story"
12
+ errors:
13
+ custom:
14
+ user:
15
+ uniqueness_of_unique_name: has already been taken
16
+ password_does_not_match_unique_name: cannot match username
17
+ cannot_disable_if_full_access: Cannot disable a user who has full access
18
+ current_password_blank: Please enter your current password
19
+ current_password_mismatch: The password you entered does not match our records
@@ -0,0 +1,2 @@
1
+ foo:
2
+ bar: baz
@@ -0,0 +1,75 @@
1
+ :yaml/dotted-key:
2
+ invalid_single_quote_escape_array.yml:
3
+ - key: foo.bar.0
4
+ string: baz'boo
5
+ invalid_single_quote_escape.yml:
6
+ - key: foo.bar
7
+ string: baz'boo
8
+ short.yml:
9
+ - key: foo.bar
10
+ string: baz
11
+ nesting.yml:
12
+ - key: en.activerecord.attributes.user.unique_name
13
+ string: Username
14
+ - key: en.activerecord.attributes.user.password
15
+ string: Password
16
+ - key: en.activerecord.attributes.user.email_address
17
+ string: Email address
18
+ - key: en.activerecord.attributes.email_address.address
19
+ string: Email address
20
+ - key: en.activerecord.attributes.testimonial.original_text
21
+ string: Your Story
22
+ - key: en.activerecord.errors.custom.user.uniqueness_of_unique_name
23
+ string: has already been taken
24
+ - key: en.activerecord.errors.custom.user.password_does_not_match_unique_name
25
+ string: cannot match username
26
+ - key: en.activerecord.errors.custom.user.cannot_disable_if_full_access
27
+ string: Cannot disable a user who has full access
28
+ - key: en.activerecord.errors.custom.user.current_password_blank
29
+ string: Please enter your current password
30
+ - key: en.activerecord.errors.custom.user.current_password_mismatch
31
+ string: The password you entered does not match our records
32
+ arrays.yml:
33
+ - key: en.jobs.benefits.0
34
+ string: Benefits to write home about
35
+ - key: en.jobs.benefits.1
36
+ string: Stress-free commute
37
+ - key: en.jobs.benefits.2
38
+ string: Brand-new gadgets
39
+ - key: en.jobs.other.0
40
+ string: Stay happy and healthy
41
+ - key: en.jobs.other.1
42
+ string: Playtime anytime
43
+ arrays_and_hashes.yml:
44
+ - key: en.jobs.benefits.0.title
45
+ string: Benefits to write home about
46
+ - key: en.jobs.benefits.0.body.0
47
+ string: Competitive salaries and bonuses
48
+ - key: en.jobs.benefits.0.body.1
49
+ string: Medical, dental, vision, and life insurance plans
50
+ - key: en.jobs.benefits.1.title
51
+ string: Stress-free commute
52
+ - key: en.jobs.benefits.1.body.0
53
+ string: Downtown SF location right next to BART and MUNI lines
54
+ - key: en.jobs.benefits.1.body.1
55
+ string: Free cabs and meals for those late nights
56
+ - key: en.jobs.benefits.2.title
57
+ string: Brand-new gadgets
58
+ - key: en.jobs.benefits.2.body.0
59
+ string: Pick out a new Mac or PC desktop or laptop
60
+ - key: en.jobs.benefits.2.body.1
61
+ string: Big monitors, fancy headphones, and more
62
+ - key: en.jobs.benefits.3.title
63
+ string: Stay happy and healthy
64
+ - key: en.jobs.benefits.3.body.0
65
+ string: Catered lunches and a pantry with every snack, drink, and Bi-Rite meal
66
+ - key: en.jobs.benefits.3.body.1
67
+ string: Gym membership at the Sports Club LA (2 blocks from the office)
68
+ - key: en.jobs.benefits.4.title
69
+ string: Playtime anytime
70
+ - key: en.jobs.benefits.4.body.0
71
+ string: Unlimited paid time off
72
+ - key: en.jobs.benefits.4.body.1
73
+ string: A game room with video games, board games, foosball, ping-pong, and shuffleboard
74
+ - key: en.jobs.benefits.4.body.2
75
+ string: A Culture Team behind office happy hours and Hawai'i holiday parties
@@ -0,0 +1,54 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ if Abroad.jruby?
6
+ require 'abroad/extractors/yaml/jruby_compat'
7
+
8
+ describe 'JRubyCompat' do
9
+ let(:compat) { Abroad::Extractors::Yaml::JRubyCompat }
10
+
11
+ describe 'clean' do
12
+ it 'replaces escaped single quotes in key/value pairs' do
13
+ yaml = "foo:\n" +
14
+ " bar: \"baz\\'boo\""
15
+
16
+ expect { YAML.load(yaml) }.to raise_error(Psych::SyntaxError)
17
+ expect(YAML.load(compat.clean(yaml))).to eq({
18
+ 'foo' => { 'bar' => "baz'boo" }
19
+ })
20
+ end
21
+
22
+ it 'replaces escaped single quotes in array elements' do
23
+ yaml = "foo:\n" +
24
+ " - abc\n" +
25
+ " - \"def\\'ghi\""
26
+
27
+ expect { YAML.load(yaml) }.to raise_error(Psych::SyntaxError)
28
+ expect(YAML.load(compat.clean(yaml))).to eq({
29
+ 'foo' => ['abc', "def'ghi"]
30
+ })
31
+ end
32
+
33
+ it 'does not replace escaped single quotes that occur outside of double quotes' do
34
+ yaml = "foo:\n" +
35
+ " bar: abc\\'def"
36
+
37
+ expect { YAML.load(yaml) }.to_not raise_error
38
+ expect(YAML.load(compat.clean(yaml))).to eq({
39
+ 'foo' => { 'bar' => "abc\\'def" }
40
+ })
41
+ end
42
+
43
+ it 'does not replace escaped single quotes that occur outside of double quotes in arrays' do
44
+ yaml = "foo:\n" +
45
+ " - def\\'ghi"
46
+
47
+ expect { YAML.load(yaml) }.to_not raise_error
48
+ expect(YAML.load(compat.clean(yaml))).to eq({
49
+ 'foo' => ["def\\'ghi"]
50
+ })
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'YamlExtractor' do
4
+ fixture_dir = File.expand_path('./', File.dirname(__FILE__))
5
+ it_behaves_like 'a fixture-based extractor', fixture_dir, Abroad::Extractors::Yaml
6
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ include Abroad::Serializers
4
+
5
+ describe Json::KeyValueSerializer do
6
+ let(:stream) { StringIO.new }
7
+ let(:locale) { 'fr-FR' }
8
+
9
+ let(:serializer) do
10
+ Json::KeyValueSerializer.new(stream, locale)
11
+ end
12
+
13
+ def serialize
14
+ yield
15
+ serializer.flush
16
+ stream.string
17
+ end
18
+
19
+ it 'writes key/value pairs' do
20
+ result = serialize do
21
+ serializer.write_key_value('foo', 'bar')
22
+ end
23
+
24
+ expect(result).to eq('{"foo":"bar"}')
25
+ end
26
+ end
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+
4
+ include Abroad::Serializers
5
+
6
+ # NOTE: The use of YAML in these specs is only to make writing multi-line
7
+ # indented strings easier to read and write
8
+ describe Xml::AndroidSerializer do
9
+ let(:stream) { StringIO.new }
10
+ let(:locale) { 'fr-FR' }
11
+
12
+ let(:serializer) do
13
+ Xml::AndroidSerializer.new(stream, locale)
14
+ end
15
+
16
+ def serialize
17
+ yield
18
+ serializer.flush
19
+ stream.string
20
+ end
21
+
22
+ it 'writes key/value pairs' do
23
+ result = serialize do
24
+ serializer.write_key_value('foo', 'bar')
25
+ end
26
+
27
+ expect(result).to eq(YAML.load(<<-EOM
28
+ |
29
+ <?xml version="1.0" encoding="UTF-8"?>
30
+ <resources>
31
+ <string name="foo">bar</string>
32
+ </resources>
33
+ EOM
34
+ ).strip)
35
+ end
36
+
37
+ it 'detects and writes plurals' do
38
+ result = serialize do
39
+ serializer.write_key_value('horses.one', 'There is one horse')
40
+ serializer.write_key_value('horses.other', 'There are %d horses')
41
+ end
42
+
43
+ expect(result).to eq(YAML.load(<<-EOM
44
+ |
45
+ <?xml version="1.0" encoding="UTF-8"?>
46
+ <resources>
47
+ <plurals name="horses">
48
+ <item quantity="one">There is one horse</item>
49
+ <item quantity="other">There are %d horses</item>
50
+ </plurals>
51
+ </resources>
52
+ EOM
53
+ ).strip)
54
+ end
55
+
56
+ it 'detects and writes arrays in any order' do
57
+ result = serialize do
58
+ serializer.write_key_value('captains.1', 'Kirk')
59
+ serializer.write_key_value('captains.3', 'Sisko')
60
+ serializer.write_key_value('captains.0', 'Janeway')
61
+ serializer.write_key_value('captains.2', 'Picard')
62
+ end
63
+
64
+ expect(result).to eq(YAML.load(<<-EOM
65
+ |
66
+ <?xml version="1.0" encoding="UTF-8"?>
67
+ <resources>
68
+ <string-array name="captains">
69
+ <item>Janeway</item>
70
+ <item>Kirk</item>
71
+ <item>Picard</item>
72
+ <item>Sisko</item>
73
+ </string-array>
74
+ </resources>
75
+ EOM
76
+ ).strip)
77
+ end
78
+
79
+ it "doesn't escape entities" do
80
+ result = serialize do
81
+ serializer.write_key_value('states', "Alaska & Hawai'i")
82
+ end
83
+
84
+ expect(result).to eq(YAML.load(<<-EOM
85
+ |
86
+ <?xml version="1.0" encoding="UTF-8"?>
87
+ <resources>
88
+ <string name="states">Alaska &amp; Hawai\\'i</string>
89
+ </resources>
90
+ EOM
91
+ ).strip)
92
+ end
93
+
94
+ it "doesn't escape html elements" do
95
+ result = serialize do
96
+ serializer.write_key_value('captain', "<b>Jean Luc</b> Picard")
97
+ end
98
+
99
+ expect(result).to eq(YAML.load(<<-EOM
100
+ |
101
+ <?xml version="1.0" encoding="UTF-8"?>
102
+ <resources>
103
+ <string name="captain"><b>Jean Luc</b> Picard</string>
104
+ </resources>
105
+ EOM
106
+ ).strip)
107
+ end
108
+
109
+ it 'escapes double quotes' do
110
+ result = serialize do
111
+ serializer.write_key_value('wow', 'And I said "cool"')
112
+ end
113
+
114
+ expect(result).to eq(YAML.load(<<-EOM
115
+ |
116
+ <?xml version="1.0" encoding="UTF-8"?>
117
+ <resources>
118
+ <string name="wow">And I said \\"cool\\"</string>
119
+ </resources>
120
+ EOM
121
+ ).strip)
122
+ end
123
+
124
+ it 'escapes double quotes at the beginning of a string' do
125
+ result = serialize do
126
+ serializer.write_key_value('wow', '"Elvis" is cool')
127
+ end
128
+
129
+ expect(result).to eq(YAML.load(<<-EOM
130
+ |
131
+ <?xml version="1.0" encoding="UTF-8"?>
132
+ <resources>
133
+ <string name="wow">\\"Elvis\\" is cool</string>
134
+ </resources>
135
+ EOM
136
+ ).strip)
137
+ end
138
+
139
+ it 'writes a mixture of all types of string' do
140
+ result = serialize do
141
+ serializer.write_key_value('justaplural.many', 'Many plurals')
142
+ serializer.write_key_value('justanarray.1', 'Second in line')
143
+ serializer.write_key_value('justanarray.0', 'First in line')
144
+ serializer.write_key_value('justastring', "I'm very basic")
145
+ serializer.write_key_value('justaplural.one', 'One plural')
146
+ end
147
+
148
+ expect(result).to eq(YAML.load(<<-EOM
149
+ |
150
+ <?xml version="1.0" encoding="UTF-8"?>
151
+ <resources>
152
+ <string name="justastring">I\\'m very basic</string>
153
+ <plurals name="justaplural">
154
+ <item quantity="many">Many plurals</item>
155
+ <item quantity="one">One plural</item>
156
+ </plurals>
157
+ <string-array name="justanarray">
158
+ <item>First in line</item>
159
+ <item>Second in line</item>
160
+ </string-array>
161
+ </resources>
162
+ EOM
163
+ ).strip)
164
+ end
165
+ end