dotstrings 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|