dotstrings 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +9 -1
- data/README.md +3 -2
- data/dotstrings.gemspec +1 -0
- data/lib/dotstrings/errors.rb +2 -0
- data/lib/dotstrings/file.rb +88 -0
- data/lib/dotstrings/item.rb +7 -0
- data/lib/dotstrings/parser.rb +44 -16
- data/lib/dotstrings/version.rb +1 -1
- data/lib/dotstrings.rb +14 -0
- data/test/fixtures/escaped_unicode~bad_surrogate_order.strings +1 -0
- data/test/fixtures/escaped_unicode~duplicated_high_surrogate.strings +1 -0
- data/test/fixtures/escaped_unicode~incomplete_surrogate_pair.strings +1 -0
- data/test/fixtures/escaped_unicode~non_surrogate_after_high_surrogate.strings +1 -0
- data/test/test_dotstrings.rb +20 -1
- data/test/test_file.rb +23 -1
- data/test/{helper.rb → test_helper.rb} +3 -1
- data/test/test_item.rb +1 -1
- data/test/test_parser.rb +50 -0
- metadata +28 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7f215ea68d4b6ce65de6f15811800b976eb722b89a4618bce4503338508df51
|
4
|
+
data.tar.gz: 51a72c22c7233b00df29f97d876abb3e59517de4bf0b78797ab8897d122250fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d9247c103eb79e3e908a55af5661de97cae316cc36660c7c872a869f077a9875060eab7bab7caa0a9aa8c2ef82d6ce7c5b9acede49fa1227790b4aad53b205d
|
7
|
+
data.tar.gz: af144c8d6b3f9f552fc71db2df31db95f1189575fa8eb9e2c5f97ab0a15234024bcd97692218be4bbd04f7352f6a3d63966b4ff4948df84379158733583920ad
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.3.0] - 2022-08-07
|
4
|
+
### Changed
|
5
|
+
* Improved unicode code point parsing and validation.
|
6
|
+
* Added `DotStrings::File#sort`, `DotStrings::File#sort!`, and `DotStrings::File#delete_if` methods.
|
7
|
+
* Improved documentation.
|
8
|
+
|
3
9
|
## [v0.2.0] - 2022-07-17
|
4
10
|
### Changed
|
5
11
|
* Made some state transitions more strict.
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dotstrings (0.
|
4
|
+
dotstrings (0.3.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
ast (2.4.2)
|
10
|
+
docile (1.4.0)
|
10
11
|
minitest (5.15.0)
|
11
12
|
parallel (1.22.1)
|
12
13
|
parser (3.1.2.0)
|
@@ -31,6 +32,12 @@ GEM
|
|
31
32
|
rubocop-rake (0.6.0)
|
32
33
|
rubocop (~> 1.0)
|
33
34
|
ruby-progressbar (1.11.0)
|
35
|
+
simplecov (0.21.2)
|
36
|
+
docile (~> 1.1)
|
37
|
+
simplecov-html (~> 0.11)
|
38
|
+
simplecov_json_formatter (~> 0.1)
|
39
|
+
simplecov-html (0.12.3)
|
40
|
+
simplecov_json_formatter (0.1.4)
|
34
41
|
unicode-display_width (2.2.0)
|
35
42
|
|
36
43
|
PLATFORMS
|
@@ -44,6 +51,7 @@ DEPENDENCIES
|
|
44
51
|
rubocop
|
45
52
|
rubocop-minitest
|
46
53
|
rubocop-rake
|
54
|
+
simplecov
|
47
55
|
|
48
56
|
BUNDLED WITH
|
49
57
|
2.2.18
|
data/README.md
CHANGED
@@ -7,6 +7,7 @@ A parser for Apple *strings* files (`.strings`) written in Ruby. Some of the fea
|
|
7
7
|
* An API for creating strings files programmatically.
|
8
8
|
* Handles Unicode and escaped characters.
|
9
9
|
* Helpful error messages: know which line and column fail to parse and why.
|
10
|
+
* Well [tested](test) and [documented](https://www.rubydoc.info/gems/dotstrings/DotStrings).
|
10
11
|
|
11
12
|
## Installing
|
12
13
|
|
@@ -19,12 +20,12 @@ $ gem install dotstrings
|
|
19
20
|
Or by adding the following entry to your [Gemfile](https://guides.cocoapods.org/using/a-gemfile.html), then running `$ bundle install`.
|
20
21
|
|
21
22
|
```ruby
|
22
|
-
gem
|
23
|
+
gem 'dotstrings'
|
23
24
|
```
|
24
25
|
|
25
26
|
## Usage
|
26
27
|
|
27
|
-
You can load `.strings` files using the `DotString.parse()` utility method. This method returns a `DotStrings::File` object or raises an exception if the file
|
28
|
+
You can load `.strings` files using the `DotString.parse()` utility method. This method returns a `DotStrings::File` object or raises an exception if the file cannot be parsed.
|
28
29
|
|
29
30
|
```ruby
|
30
31
|
file = DotStrings.parse_file('en-US/Localizable.strings')
|
data/dotstrings.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.add_development_dependency 'rubocop'
|
23
23
|
s.add_development_dependency 'rubocop-minitest'
|
24
24
|
s.add_development_dependency 'rubocop-rake'
|
25
|
+
s.add_development_dependency 'simplecov'
|
25
26
|
|
26
27
|
s.required_ruby_version = '>= 2.5.0'
|
27
28
|
s.metadata['rubygems_mfa_required'] = 'true'
|
data/lib/dotstrings/errors.rb
CHANGED
data/lib/dotstrings/file.rb
CHANGED
@@ -5,13 +5,48 @@ require 'dotstrings/errors'
|
|
5
5
|
require 'dotstrings/item'
|
6
6
|
|
7
7
|
module DotStrings
|
8
|
+
##
|
9
|
+
# Represents a .strings file.
|
10
|
+
#
|
11
|
+
# It provides methods to parse .strings, as well as methods for accessing and
|
12
|
+
# manipulating localized string items.
|
8
13
|
class File
|
14
|
+
##
|
15
|
+
# All items in the file.
|
9
16
|
attr_reader :items
|
10
17
|
|
11
18
|
def initialize(items = [])
|
12
19
|
@items = items
|
13
20
|
end
|
14
21
|
|
22
|
+
##
|
23
|
+
# Returns a new File with the items sorted using the given comparator block.
|
24
|
+
#
|
25
|
+
# If no block is given, the items will be sorted by key.
|
26
|
+
def sort(&block)
|
27
|
+
new_file = dup
|
28
|
+
new_file.sort!(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Sort the items using the given block.
|
33
|
+
#
|
34
|
+
# If no block is given, the items will be sorted by key.
|
35
|
+
def sort!(&block)
|
36
|
+
@items.sort!(&block || ->(a, b) { a.key <=> b.key })
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Parses a file from the given IO object.
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# io = Zlib::GzipReader.open('path/to/en.lproj/Localizable.strings.gz')
|
45
|
+
# file = DotStrings::File.parse(io)
|
46
|
+
#
|
47
|
+
# @param io [IO] The IO object to parse.
|
48
|
+
# @return [DotStrings::File] The parsed file.
|
49
|
+
# @raise [DotStrings::ParsingError] if the file could not be parsed.
|
15
50
|
def self.parse(io)
|
16
51
|
items = []
|
17
52
|
|
@@ -22,32 +57,85 @@ module DotStrings
|
|
22
57
|
File.new(items)
|
23
58
|
end
|
24
59
|
|
60
|
+
##
|
61
|
+
# Parses the file at the given path.
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# file = DotStrings::File.parse_file('path/to/en.lproj/Localizable.strings')
|
65
|
+
#
|
66
|
+
# @param path [String] The path to the file to parse.
|
67
|
+
# @return [DotStrings::File] The parsed file.
|
68
|
+
# @raise [DotStrings::ParsingError] if the file could not be parsed.
|
25
69
|
def self.parse_file(path)
|
26
70
|
::File.open(path, 'r') do |file|
|
27
71
|
parse(file)
|
28
72
|
end
|
29
73
|
end
|
30
74
|
|
75
|
+
##
|
76
|
+
# Returns all keys in the file.
|
31
77
|
def keys
|
32
78
|
@items.map(&:key)
|
33
79
|
end
|
34
80
|
|
81
|
+
##
|
82
|
+
# Returns an item by key, if it exists, otherwise nil.
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# item = file['button.title']
|
86
|
+
# unless item.nil?
|
87
|
+
# puts item.value # => 'Submit'
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @param key [String] The key of the item to return.
|
91
|
+
# @return [DotStrings::Item] The item, if it exists.
|
35
92
|
def [](key)
|
36
93
|
@items.find { |item| item.key == key }
|
37
94
|
end
|
38
95
|
|
96
|
+
##
|
97
|
+
# Appends an item to the file.
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# file << DotStrings::Item.new(key: 'button.title', value: 'Submit')
|
101
|
+
#
|
102
|
+
# @param item [DotStrings::Item] The item to append.
|
103
|
+
# @return [DotStrings::Item] The item that was appended.
|
39
104
|
def <<(item)
|
40
105
|
@items << item
|
106
|
+
self
|
41
107
|
end
|
42
108
|
|
109
|
+
##
|
110
|
+
# Appends an item to the file.
|
111
|
+
#
|
112
|
+
# @example
|
113
|
+
# file.append(DotStrings::Item.new(key: 'button.title', value: 'Submit'))
|
43
114
|
def append(item)
|
44
115
|
self << item
|
45
116
|
end
|
46
117
|
|
118
|
+
##
|
119
|
+
# Deletes an item by key.
|
47
120
|
def delete(key)
|
48
121
|
@items.delete_if { |item| item.key == key }
|
49
122
|
end
|
50
123
|
|
124
|
+
##
|
125
|
+
# Deletes all items for which the block returns true.
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
# file.delete_if { |item| item.key.start_with?('button.') }
|
129
|
+
def delete_if(&block)
|
130
|
+
@items.delete_if(&block)
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# Serializes the file to a string.
|
136
|
+
#
|
137
|
+
# @param escape_single_quotes [Boolean] whether to escape single quotes.
|
138
|
+
# @param comments [Boolean] whether to include comments.
|
51
139
|
def to_s(escape_single_quotes: false, comments: true)
|
52
140
|
result = []
|
53
141
|
|
data/lib/dotstrings/item.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DotStrings
|
4
|
+
##
|
5
|
+
# Represents a localized string item.
|
4
6
|
class Item
|
5
7
|
attr_reader :comment, :key, :value
|
6
8
|
|
@@ -10,6 +12,11 @@ module DotStrings
|
|
10
12
|
@value = value
|
11
13
|
end
|
12
14
|
|
15
|
+
##
|
16
|
+
# Serializes the item to string.
|
17
|
+
#
|
18
|
+
# @param escape_single_quotes [Boolean] Whether to escape single quotes.
|
19
|
+
# @param include_comment [Boolean] Whether to include the comment.
|
13
20
|
def to_s(escape_single_quotes: false, include_comment: true)
|
14
21
|
result = []
|
15
22
|
|
data/lib/dotstrings/parser.rb
CHANGED
@@ -4,6 +4,9 @@ require 'dotstrings/errors'
|
|
4
4
|
|
5
5
|
module DotStrings
|
6
6
|
# rubocop:disable Metrics/ClassLength
|
7
|
+
|
8
|
+
##
|
9
|
+
# Parser for .strings files.
|
7
10
|
class Parser
|
8
11
|
# Special tokens
|
9
12
|
TOK_SLASH = '/'
|
@@ -36,8 +39,6 @@ module DotStrings
|
|
36
39
|
STATE_UNICODE_SURROGATE = 11
|
37
40
|
STATE_UNICODE_SURROGATE_U = 12
|
38
41
|
|
39
|
-
attr_reader :items
|
40
|
-
|
41
42
|
def initialize
|
42
43
|
@state = STATE_START
|
43
44
|
@temp_state = nil
|
@@ -59,11 +60,16 @@ module DotStrings
|
|
59
60
|
@column = 1
|
60
61
|
end
|
61
62
|
|
63
|
+
##
|
64
|
+
# Specifies a block to be called when a new item is parsed.
|
62
65
|
def on_item(&block)
|
63
66
|
@item_block = block
|
64
67
|
end
|
65
68
|
|
66
69
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength
|
70
|
+
|
71
|
+
##
|
72
|
+
# Feeds data to the parser.
|
67
73
|
def <<(data)
|
68
74
|
data.each_char do |ch|
|
69
75
|
case @state
|
@@ -157,6 +163,7 @@ module DotStrings
|
|
157
163
|
update_position(ch)
|
158
164
|
end
|
159
165
|
end
|
166
|
+
|
160
167
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength
|
161
168
|
|
162
169
|
private
|
@@ -203,30 +210,51 @@ module DotStrings
|
|
203
210
|
end
|
204
211
|
end
|
205
212
|
|
206
|
-
# rubocop:disable
|
213
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
207
214
|
def parse_unicode(ch, &block)
|
208
215
|
raise_error("Unexpected character '#{ch}', expecting a hex digit") unless ch =~ TOK_HEX_DIGIT
|
209
216
|
|
210
217
|
@unicode_buffer << ch
|
211
218
|
|
212
|
-
if
|
213
|
-
|
219
|
+
# Check if we have enough digits to form a codepoint.
|
220
|
+
return if @unicode_buffer.length < 4
|
214
221
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
block.call(codepoint.chr('UTF-8'))
|
222
|
+
codepoint = @unicode_buffer.join.hex
|
223
|
+
|
224
|
+
if codepoint >= 0xD800 && codepoint <= 0xDBFF
|
225
|
+
unless @high_surrogate.nil?
|
226
|
+
raise_error(
|
227
|
+
'Found a high surrogate code point after another high surrogate'
|
228
|
+
)
|
223
229
|
end
|
224
230
|
|
225
|
-
|
226
|
-
@
|
231
|
+
@high_surrogate = codepoint
|
232
|
+
@state = STATE_UNICODE_SURROGATE
|
233
|
+
elsif codepoint >= 0xDC00 && codepoint <= 0xDFFF
|
234
|
+
if @high_surrogate.nil?
|
235
|
+
raise_error(
|
236
|
+
'Found a low surrogate code point before a high surrogate'
|
237
|
+
)
|
238
|
+
end
|
239
|
+
|
240
|
+
character = ((@high_surrogate - 0xD800) * 0x400) + (codepoint - 0xDC00) + 0x10000
|
241
|
+
@high_surrogate = nil
|
242
|
+
|
243
|
+
block.call(character.chr('UTF-8'))
|
244
|
+
else
|
245
|
+
unless @high_surrogate.nil?
|
246
|
+
raise_error(
|
247
|
+
"Invalid unicode codepoint '\\U#{codepoint.to_s(16).upcase}' after a high surrogate code point"
|
248
|
+
)
|
249
|
+
end
|
250
|
+
|
251
|
+
block.call(codepoint.chr('UTF-8'))
|
227
252
|
end
|
253
|
+
|
254
|
+
# Clear buffer after codepoint is parsed
|
255
|
+
@unicode_buffer.clear
|
228
256
|
end
|
229
|
-
# rubocop:enable
|
257
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
230
258
|
|
231
259
|
def update_position(ch)
|
232
260
|
@offset += 1
|
data/lib/dotstrings/version.rb
CHANGED
data/lib/dotstrings.rb
CHANGED
@@ -6,10 +6,24 @@ require 'dotstrings/item'
|
|
6
6
|
require 'dotstrings/errors'
|
7
7
|
|
8
8
|
module DotStrings
|
9
|
+
##
|
10
|
+
# Parses a file from the given IO object.
|
11
|
+
#
|
12
|
+
# This is a convenience method for {DotStrings::File.parse}.
|
13
|
+
#
|
14
|
+
# @param io [IO] The IO object to parse.
|
15
|
+
# @return [DotStrings::File] The parsed file.
|
9
16
|
def self.parse(io)
|
10
17
|
File.parse(io)
|
11
18
|
end
|
12
19
|
|
20
|
+
##
|
21
|
+
# Parses a .strings file at the given path.
|
22
|
+
#
|
23
|
+
# This is a convenience method for {DotStrings::File.parse_file}.
|
24
|
+
#
|
25
|
+
# @param path [String] The path to the .strings file to parse.
|
26
|
+
# @return [DotStrings::File] The parsed file.
|
13
27
|
def self.parse_file(path)
|
14
28
|
File.parse_file(path)
|
15
29
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
"key" = "\UDC7B\UD83D";
|
@@ -0,0 +1 @@
|
|
1
|
+
"key" = "\UD83D\UD83D";
|
@@ -0,0 +1 @@
|
|
1
|
+
"key" = "\UD83D";
|
@@ -0,0 +1 @@
|
|
1
|
+
"key" = "\UD83D\U26A1";
|
data/test/test_dotstrings.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'test_helper'
|
4
4
|
|
5
5
|
class TestDotStrings < MiniTest::Test
|
6
6
|
def test_parse_can_parse_valid_files
|
@@ -81,6 +81,25 @@ class TestDotStrings < MiniTest::Test
|
|
81
81
|
assert_equal '⚡👻', file.items[0].value
|
82
82
|
end
|
83
83
|
|
84
|
+
def test_raises_error_when_bad_surrogate_pair_is_found
|
85
|
+
# rubocop:disable Layout/LineLength
|
86
|
+
test_cases = {
|
87
|
+
'escaped_unicode~bad_surrogate_order.strings' => 'Found a low surrogate code point before a high surrogate at line 1, column 15 (offset: 14)',
|
88
|
+
'escaped_unicode~duplicated_high_surrogate.strings' => 'Found a high surrogate code point after another high surrogate at line 1, column 21 (offset: 20)',
|
89
|
+
'escaped_unicode~incomplete_surrogate_pair.strings' => 'Unexpected character \'"\', expecting another unicode codepoint at line 1, column 16 (offset: 15)',
|
90
|
+
'escaped_unicode~non_surrogate_after_high_surrogate.strings' => 'Invalid unicode codepoint \'\U26A1\' after a high surrogate code point at line 1, column 21 (offset: 20)'
|
91
|
+
}
|
92
|
+
# rubocop:enable Layout/LineLength
|
93
|
+
|
94
|
+
test_cases.each do |filename, error_message|
|
95
|
+
error = assert_raises DotStrings::ParsingError do
|
96
|
+
DotStrings.parse_file("test/fixtures/#{filename}")
|
97
|
+
end
|
98
|
+
|
99
|
+
assert_equal error_message, error.message
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
84
103
|
def test_can_parse_utf16le_files_with_bom
|
85
104
|
file = DotStrings.parse_file('test/fixtures/utf16le_bom.strings')
|
86
105
|
|
data/test/test_file.rb
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'test_helper'
|
4
4
|
|
5
5
|
class TestFile < MiniTest::Test
|
6
|
+
def test_sort
|
7
|
+
file = DotStrings::File.new([
|
8
|
+
DotStrings::Item.new(key: 'key 3', value: 'value 3'),
|
9
|
+
DotStrings::Item.new(key: 'key 2', value: 'value 2'),
|
10
|
+
DotStrings::Item.new(key: 'key 1', value: 'value 1')
|
11
|
+
])
|
12
|
+
|
13
|
+
sorted = file.sort
|
14
|
+
assert_equal ['key 1', 'key 2', 'key 3'], sorted.keys
|
15
|
+
end
|
16
|
+
|
6
17
|
def test_delete
|
7
18
|
items = [
|
8
19
|
DotStrings::Item.new(key: 'key 1', value: 'value 1'),
|
@@ -17,6 +28,17 @@ class TestFile < MiniTest::Test
|
|
17
28
|
assert_equal ['key 1', 'key 3'], file.keys
|
18
29
|
end
|
19
30
|
|
31
|
+
def test_delete_if
|
32
|
+
file = DotStrings::File.new([
|
33
|
+
DotStrings::Item.new(key: 'key 1', value: 'value 1'),
|
34
|
+
DotStrings::Item.new(key: 'key 2', value: 'value 2'),
|
35
|
+
DotStrings::Item.new(key: 'key 3', value: 'value 3')
|
36
|
+
])
|
37
|
+
|
38
|
+
file.delete_if { |item| item.key == 'key 2' }
|
39
|
+
assert_equal ['key 1', 'key 3'], file.keys
|
40
|
+
end
|
41
|
+
|
20
42
|
def test_access_by_key
|
21
43
|
items = [
|
22
44
|
DotStrings::Item.new(key: 'key 1', value: 'value 1'),
|
data/test/test_item.rb
CHANGED
data/test/test_parser.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'test_helper'
|
4
|
+
|
5
|
+
class TestParser < MiniTest::Test
|
6
|
+
def test_handles_extraneous_characters_at_start_of_file
|
7
|
+
error = assert_raises DotStrings::ParsingError do
|
8
|
+
parser = DotStrings::Parser.new
|
9
|
+
parser << '$'
|
10
|
+
end
|
11
|
+
|
12
|
+
assert_equal "Unexpected character '$' at line 1, column 1 (offset: 0)", error.message
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_handles_malformed_comments
|
16
|
+
error = assert_raises DotStrings::ParsingError do
|
17
|
+
parser = DotStrings::Parser.new
|
18
|
+
parser << '/@ test'
|
19
|
+
end
|
20
|
+
|
21
|
+
assert_equal "Unexpected character '@' at line 1, column 2 (offset: 1)", error.message
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_raises_error_when_escaping_invalid_character
|
25
|
+
error = assert_raises DotStrings::ParsingError do
|
26
|
+
parser = DotStrings::Parser.new
|
27
|
+
parser << '"\\z" = "value";'
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_equal "Unexpected character 'z' at line 1, column 3 (offset: 2)", error.message
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_raises_error_when_items_are_not_separated_by_semicolon
|
34
|
+
error = assert_raises DotStrings::ParsingError do
|
35
|
+
parser = DotStrings::Parser.new
|
36
|
+
parser << '"key_1" = "value_1" "key_2" = "value_2"'
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_equal "Unexpected character '\"', expecting ';' at line 1, column 21 (offset: 20)", error.message
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_raises_error_if_low_surrogate_is_not_formatted_correctly
|
43
|
+
error = assert_raises DotStrings::ParsingError do
|
44
|
+
parser = DotStrings::Parser.new
|
45
|
+
parser << '"key" = "\UD83D\$DC7B";'
|
46
|
+
end
|
47
|
+
|
48
|
+
assert_equal "Unexpected character '$', expecting 'U' at line 1, column 17 (offset: 16)", error.message
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dotstrings
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ramon Torres
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-07
|
11
|
+
date: 2022-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Parse and create .strings files used in localization of iOS and macOS
|
98
112
|
apps.
|
99
113
|
email:
|
@@ -127,14 +141,19 @@ files:
|
|
127
141
|
- test/fixtures/escaped_single_quotes.strings
|
128
142
|
- test/fixtures/escaped_tabs.strings
|
129
143
|
- test/fixtures/escaped_unicode.strings
|
144
|
+
- test/fixtures/escaped_unicode~bad_surrogate_order.strings
|
145
|
+
- test/fixtures/escaped_unicode~duplicated_high_surrogate.strings
|
146
|
+
- test/fixtures/escaped_unicode~incomplete_surrogate_pair.strings
|
147
|
+
- test/fixtures/escaped_unicode~non_surrogate_after_high_surrogate.strings
|
130
148
|
- test/fixtures/utf16be_bom.strings
|
131
149
|
- test/fixtures/utf16le_bom.strings
|
132
150
|
- test/fixtures/utf8_bom.strings
|
133
151
|
- test/fixtures/valid.strings
|
134
|
-
- test/helper.rb
|
135
152
|
- test/test_dotstrings.rb
|
136
153
|
- test/test_file.rb
|
154
|
+
- test/test_helper.rb
|
137
155
|
- test/test_item.rb
|
156
|
+
- test/test_parser.rb
|
138
157
|
homepage: https://github.com/raymondjavaxx/dotstrings
|
139
158
|
licenses:
|
140
159
|
- MIT
|
@@ -168,11 +187,16 @@ test_files:
|
|
168
187
|
- test/fixtures/escaped_single_quotes.strings
|
169
188
|
- test/fixtures/escaped_tabs.strings
|
170
189
|
- test/fixtures/escaped_unicode.strings
|
190
|
+
- test/fixtures/escaped_unicode~bad_surrogate_order.strings
|
191
|
+
- test/fixtures/escaped_unicode~duplicated_high_surrogate.strings
|
192
|
+
- test/fixtures/escaped_unicode~incomplete_surrogate_pair.strings
|
193
|
+
- test/fixtures/escaped_unicode~non_surrogate_after_high_surrogate.strings
|
171
194
|
- test/fixtures/utf16be_bom.strings
|
172
195
|
- test/fixtures/utf16le_bom.strings
|
173
196
|
- test/fixtures/utf8_bom.strings
|
174
197
|
- test/fixtures/valid.strings
|
175
|
-
- test/helper.rb
|
176
198
|
- test/test_dotstrings.rb
|
177
199
|
- test/test_file.rb
|
200
|
+
- test/test_helper.rb
|
178
201
|
- test/test_item.rb
|
202
|
+
- test/test_parser.rb
|