dotstrings 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +3 -2
- data/lib/dotstrings/errors.rb +2 -0
- data/lib/dotstrings/file.rb +133 -0
- data/lib/dotstrings/item.rb +18 -0
- data/lib/dotstrings/parser.rb +44 -16
- data/lib/dotstrings/version.rb +1 -1
- data/lib/dotstrings.rb +14 -0
- metadata +19 -44
- data/.editorconfig +0 -13
- data/.github/workflows/ci.yml +0 -33
- data/.gitignore +0 -2
- data/.rubocop.yml +0 -41
- data/Gemfile +0 -5
- data/Gemfile.lock +0 -49
- data/Rakefile +0 -10
- data/dotstrings.gemspec +0 -28
- data/test/fixtures/escaped_backslashes.strings +0 -2
- data/test/fixtures/escaped_carriage_returns.strings +0 -2
- data/test/fixtures/escaped_new_lines.strings +0 -2
- data/test/fixtures/escaped_nil.strings +0 -2
- data/test/fixtures/escaped_quotes.strings +0 -2
- data/test/fixtures/escaped_single_quotes.strings +0 -2
- data/test/fixtures/escaped_tabs.strings +0 -2
- data/test/fixtures/escaped_unicode.strings +0 -2
- data/test/fixtures/utf16be_bom.strings +0 -0
- data/test/fixtures/utf16le_bom.strings +0 -0
- data/test/fixtures/utf8_bom.strings +0 -2
- data/test/fixtures/valid.strings +0 -8
- data/test/helper.rb +0 -8
- data/test/test_dotstrings.rb +0 -107
- data/test/test_file.rb +0 -94
- data/test/test_item.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9c320ba9bef4d63af10c9c9fd3d91ad41da99b3663538f0b89ebdc6fc780d31
|
4
|
+
data.tar.gz: 24e762927cd739f3475a8a550a751f808dd81d73292e47f7449c657edeaa88db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f24ad0efde29b52a33f28cd0e434970bde12556d9236c632495eebdfd089ed550e54a41f67fb844c70677c53bbdaccdbb93f593cafd535062532f062297ab7b4
|
7
|
+
data.tar.gz: d61a17f762188162a431c6289b96e23d4374ca2a29a82e84b58e92bf097d79746a352418859e12c2d1dcf13c8844a53a9975360be6c0a98f65d046c16aba45cc
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.4.0] - 2022-09-18
|
4
|
+
### Added
|
5
|
+
* Added `DotStrings::File#each`, `DotStrings::File#length`, `DotStrings::File#count`, and `DotStrings::File#empty?` methods.
|
6
|
+
* Allow comparing `DotStrings::File` objects.
|
7
|
+
|
8
|
+
## [v0.3.0] - 2022-08-07
|
9
|
+
### Changed
|
10
|
+
* Improved unicode code point parsing and validation.
|
11
|
+
* Added `DotStrings::File#sort`, `DotStrings::File#sort!`, and `DotStrings::File#delete_if` methods.
|
12
|
+
* Improved documentation.
|
13
|
+
|
3
14
|
## [v0.2.0] - 2022-07-17
|
4
15
|
### Changed
|
5
16
|
* Made some state transitions more strict.
|
@@ -13,6 +24,8 @@
|
|
13
24
|
### Added
|
14
25
|
* Initial release.
|
15
26
|
|
27
|
+
[v0.4.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.4.0
|
28
|
+
[v0.3.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.3.0
|
16
29
|
[v0.2.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.2.0
|
17
30
|
[v0.1.1]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.1.1
|
18
31
|
[v0.1.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.1.0
|
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/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,130 @@ 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
|
+
# Calls the given block once for each item in the file.
|
136
|
+
#
|
137
|
+
# @param block [Proc] The block to call.
|
138
|
+
# @example
|
139
|
+
# file.each do |item|
|
140
|
+
# puts "#{item.key} > #{item.value}"
|
141
|
+
# end
|
142
|
+
def each(&block)
|
143
|
+
@items.each(&block)
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Returns the number of items in the file.
|
149
|
+
def length
|
150
|
+
@items.length
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Returns the number of items in the file.
|
155
|
+
#
|
156
|
+
# If a block is given, it will count the number of items for which the block returns true.
|
157
|
+
#
|
158
|
+
# @example
|
159
|
+
# file.count # => 10
|
160
|
+
# file.count { |item| item.key.start_with?('button.') } # => 3
|
161
|
+
def count(&block)
|
162
|
+
@items.count(&block)
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# Returns `true` if the file doen't contain any items.
|
167
|
+
def empty?
|
168
|
+
@items.empty?
|
169
|
+
end
|
170
|
+
|
171
|
+
def ==(other)
|
172
|
+
eql?(other)
|
173
|
+
end
|
174
|
+
|
175
|
+
def eql?(other)
|
176
|
+
other.is_a?(self.class) && @items.eql?(other.items)
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# Serializes the file to a string.
|
181
|
+
#
|
182
|
+
# @param escape_single_quotes [Boolean] whether to escape single quotes.
|
183
|
+
# @param comments [Boolean] whether to include comments.
|
51
184
|
def to_s(escape_single_quotes: false, comments: true)
|
52
185
|
result = []
|
53
186
|
|
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,22 @@ module DotStrings
|
|
10
12
|
@value = value
|
11
13
|
end
|
12
14
|
|
15
|
+
def ==(other)
|
16
|
+
eql?(other)
|
17
|
+
end
|
18
|
+
|
19
|
+
def eql?(other)
|
20
|
+
other.is_a?(Item) &&
|
21
|
+
comment == other.comment &&
|
22
|
+
key == other.key &&
|
23
|
+
value == other.value
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Serializes the item to string.
|
28
|
+
#
|
29
|
+
# @param escape_single_quotes [Boolean] Whether to escape single quotes.
|
30
|
+
# @param include_comment [Boolean] Whether to include the comment.
|
13
31
|
def to_s(escape_single_quotes: false, include_comment: true)
|
14
32
|
result = []
|
15
33
|
|
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
|
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.4.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-
|
11
|
+
date: 2022-09-18 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:
|
@@ -102,39 +116,15 @@ executables: []
|
|
102
116
|
extensions: []
|
103
117
|
extra_rdoc_files: []
|
104
118
|
files:
|
105
|
-
- ".editorconfig"
|
106
|
-
- ".github/workflows/ci.yml"
|
107
|
-
- ".gitignore"
|
108
|
-
- ".rubocop.yml"
|
109
119
|
- CHANGELOG.md
|
110
|
-
- Gemfile
|
111
|
-
- Gemfile.lock
|
112
120
|
- LICENSE
|
113
121
|
- README.md
|
114
|
-
- Rakefile
|
115
|
-
- dotstrings.gemspec
|
116
122
|
- lib/dotstrings.rb
|
117
123
|
- lib/dotstrings/errors.rb
|
118
124
|
- lib/dotstrings/file.rb
|
119
125
|
- lib/dotstrings/item.rb
|
120
126
|
- lib/dotstrings/parser.rb
|
121
127
|
- lib/dotstrings/version.rb
|
122
|
-
- test/fixtures/escaped_backslashes.strings
|
123
|
-
- test/fixtures/escaped_carriage_returns.strings
|
124
|
-
- test/fixtures/escaped_new_lines.strings
|
125
|
-
- test/fixtures/escaped_nil.strings
|
126
|
-
- test/fixtures/escaped_quotes.strings
|
127
|
-
- test/fixtures/escaped_single_quotes.strings
|
128
|
-
- test/fixtures/escaped_tabs.strings
|
129
|
-
- test/fixtures/escaped_unicode.strings
|
130
|
-
- test/fixtures/utf16be_bom.strings
|
131
|
-
- test/fixtures/utf16le_bom.strings
|
132
|
-
- test/fixtures/utf8_bom.strings
|
133
|
-
- test/fixtures/valid.strings
|
134
|
-
- test/helper.rb
|
135
|
-
- test/test_dotstrings.rb
|
136
|
-
- test/test_file.rb
|
137
|
-
- test/test_item.rb
|
138
128
|
homepage: https://github.com/raymondjavaxx/dotstrings
|
139
129
|
licenses:
|
140
130
|
- MIT
|
@@ -155,24 +145,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
145
|
- !ruby/object:Gem::Version
|
156
146
|
version: '0'
|
157
147
|
requirements: []
|
158
|
-
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 2.7.6
|
159
150
|
signing_key:
|
160
151
|
specification_version: 4
|
161
152
|
summary: Parse and create .strings files used in localization of iOS and macOS apps.
|
162
|
-
test_files:
|
163
|
-
- test/fixtures/escaped_backslashes.strings
|
164
|
-
- test/fixtures/escaped_carriage_returns.strings
|
165
|
-
- test/fixtures/escaped_new_lines.strings
|
166
|
-
- test/fixtures/escaped_nil.strings
|
167
|
-
- test/fixtures/escaped_quotes.strings
|
168
|
-
- test/fixtures/escaped_single_quotes.strings
|
169
|
-
- test/fixtures/escaped_tabs.strings
|
170
|
-
- test/fixtures/escaped_unicode.strings
|
171
|
-
- test/fixtures/utf16be_bom.strings
|
172
|
-
- test/fixtures/utf16le_bom.strings
|
173
|
-
- test/fixtures/utf8_bom.strings
|
174
|
-
- test/fixtures/valid.strings
|
175
|
-
- test/helper.rb
|
176
|
-
- test/test_dotstrings.rb
|
177
|
-
- test/test_file.rb
|
178
|
-
- test/test_item.rb
|
153
|
+
test_files: []
|
data/.editorconfig
DELETED
data/.github/workflows/ci.yml
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
name: CI
|
2
|
-
|
3
|
-
on:
|
4
|
-
push:
|
5
|
-
branches: "*"
|
6
|
-
pull_request:
|
7
|
-
branches: "*"
|
8
|
-
|
9
|
-
jobs:
|
10
|
-
test:
|
11
|
-
runs-on: ubuntu-latest
|
12
|
-
strategy:
|
13
|
-
matrix:
|
14
|
-
ruby-version:
|
15
|
-
- "2.5"
|
16
|
-
- "2.6"
|
17
|
-
- "2.7"
|
18
|
-
- "3.0"
|
19
|
-
- "3.1"
|
20
|
-
|
21
|
-
steps:
|
22
|
-
- uses: actions/checkout@v2
|
23
|
-
- name: Set up Ruby
|
24
|
-
uses: ruby/setup-ruby@v1
|
25
|
-
with:
|
26
|
-
ruby-version: ${{ matrix.ruby-version }}
|
27
|
-
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
28
|
-
- name: Lint
|
29
|
-
run: bundle exec rake rubocop
|
30
|
-
- name: Run tests
|
31
|
-
run: bundle exec rake
|
32
|
-
- name: Install
|
33
|
-
run: bundle exec rake install
|
data/.gitignore
DELETED
data/.rubocop.yml
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require:
|
2
|
-
- rubocop-rake
|
3
|
-
- rubocop-minitest
|
4
|
-
|
5
|
-
AllCops:
|
6
|
-
TargetRubyVersion: 2.5
|
7
|
-
NewCops: enable
|
8
|
-
|
9
|
-
Style/StringLiterals:
|
10
|
-
Enabled: true
|
11
|
-
EnforcedStyle: single_quotes
|
12
|
-
|
13
|
-
Layout/FirstHashElementIndentation:
|
14
|
-
Enabled: true
|
15
|
-
EnforcedStyle: consistent
|
16
|
-
|
17
|
-
Layout/FirstArrayElementIndentation:
|
18
|
-
Enabled: true
|
19
|
-
EnforcedStyle: consistent
|
20
|
-
|
21
|
-
Metrics/ClassLength:
|
22
|
-
Enabled: true
|
23
|
-
Max: 150
|
24
|
-
|
25
|
-
Naming/MethodParameterName:
|
26
|
-
Enabled: false
|
27
|
-
|
28
|
-
Metrics/MethodLength:
|
29
|
-
Enabled: false
|
30
|
-
|
31
|
-
Metrics/AbcSize:
|
32
|
-
Enabled: false
|
33
|
-
|
34
|
-
Style/Documentation:
|
35
|
-
Enabled: false
|
36
|
-
|
37
|
-
Style/ParallelAssignment:
|
38
|
-
Enabled: false
|
39
|
-
|
40
|
-
Minitest/AssertInDelta:
|
41
|
-
Enabled: false
|
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
dotstrings (0.2.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
ast (2.4.2)
|
10
|
-
minitest (5.15.0)
|
11
|
-
parallel (1.22.1)
|
12
|
-
parser (3.1.2.0)
|
13
|
-
ast (~> 2.4.1)
|
14
|
-
rainbow (3.1.1)
|
15
|
-
rake (13.0.6)
|
16
|
-
regexp_parser (2.5.0)
|
17
|
-
rexml (3.2.5)
|
18
|
-
rubocop (1.28.2)
|
19
|
-
parallel (~> 1.10)
|
20
|
-
parser (>= 3.1.0.0)
|
21
|
-
rainbow (>= 2.2.2, < 4.0)
|
22
|
-
regexp_parser (>= 1.8, < 3.0)
|
23
|
-
rexml
|
24
|
-
rubocop-ast (>= 1.17.0, < 2.0)
|
25
|
-
ruby-progressbar (~> 1.7)
|
26
|
-
unicode-display_width (>= 1.4.0, < 3.0)
|
27
|
-
rubocop-ast (1.17.0)
|
28
|
-
parser (>= 3.1.1.0)
|
29
|
-
rubocop-minitest (0.19.1)
|
30
|
-
rubocop (>= 0.90, < 2.0)
|
31
|
-
rubocop-rake (0.6.0)
|
32
|
-
rubocop (~> 1.0)
|
33
|
-
ruby-progressbar (1.11.0)
|
34
|
-
unicode-display_width (2.2.0)
|
35
|
-
|
36
|
-
PLATFORMS
|
37
|
-
ruby
|
38
|
-
|
39
|
-
DEPENDENCIES
|
40
|
-
bundler
|
41
|
-
dotstrings!
|
42
|
-
minitest (~> 5.14)
|
43
|
-
rake
|
44
|
-
rubocop
|
45
|
-
rubocop-minitest
|
46
|
-
rubocop-rake
|
47
|
-
|
48
|
-
BUNDLED WITH
|
49
|
-
2.2.18
|
data/Rakefile
DELETED
data/dotstrings.gemspec
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require './lib/dotstrings/version'
|
4
|
-
|
5
|
-
Gem::Specification.new do |s|
|
6
|
-
s.name = 'dotstrings'
|
7
|
-
s.version = DotStrings::VERSION
|
8
|
-
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ['Ramon Torres']
|
10
|
-
s.email = ['raymondjavaxx@gmail.com']
|
11
|
-
s.homepage = 'https://github.com/raymondjavaxx/dotstrings'
|
12
|
-
s.description = s.summary = 'Parse and create .strings files used in localization of iOS and macOS apps.'
|
13
|
-
s.files = `git ls-files`.split("\n")
|
14
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
16
|
-
s.require_paths = ['lib']
|
17
|
-
s.license = 'MIT'
|
18
|
-
|
19
|
-
s.add_development_dependency 'bundler'
|
20
|
-
s.add_development_dependency 'minitest', '~> 5.14'
|
21
|
-
s.add_development_dependency 'rake'
|
22
|
-
s.add_development_dependency 'rubocop'
|
23
|
-
s.add_development_dependency 'rubocop-minitest'
|
24
|
-
s.add_development_dependency 'rubocop-rake'
|
25
|
-
|
26
|
-
s.required_ruby_version = '>= 2.5.0'
|
27
|
-
s.metadata['rubygems_mfa_required'] = 'true'
|
28
|
-
end
|
Binary file
|
Binary file
|
data/test/fixtures/valid.strings
DELETED
data/test/helper.rb
DELETED
data/test/test_dotstrings.rb
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'helper'
|
4
|
-
|
5
|
-
class TestDotStrings < MiniTest::Test
|
6
|
-
def test_parse_can_parse_valid_files
|
7
|
-
file = DotStrings.parse_file('test/fixtures/valid.strings')
|
8
|
-
|
9
|
-
assert_equal 3, file.items.size
|
10
|
-
|
11
|
-
assert_equal 'Single line comment', file.items[0].comment
|
12
|
-
assert_equal 'key 1', file.items[0].key
|
13
|
-
assert_equal 'value 1', file.items[0].value
|
14
|
-
|
15
|
-
assert_equal "Multi line\ncomment", file.items[1].comment
|
16
|
-
assert_equal 'key 2', file.items[1].key
|
17
|
-
assert_equal 'value 2', file.items[1].value
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_can_parse_file_with_escaped_quotes
|
21
|
-
file = DotStrings.parse_file('test/fixtures/escaped_quotes.strings')
|
22
|
-
|
23
|
-
assert_equal 1, file.items.size
|
24
|
-
assert_equal 'some "key"', file.items[0].key
|
25
|
-
assert_equal 'some "value"', file.items[0].value
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_can_parse_file_with_escaped_single_quotes
|
29
|
-
file = DotStrings.parse_file('test/fixtures/escaped_single_quotes.strings')
|
30
|
-
|
31
|
-
assert_equal 1, file.items.size
|
32
|
-
assert_equal 'some \'key\'', file.items[0].key
|
33
|
-
assert_equal 'some \'value\'', file.items[0].value
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_can_parse_file_with_escaped_tabs
|
37
|
-
file = DotStrings.parse_file('test/fixtures/escaped_tabs.strings')
|
38
|
-
|
39
|
-
assert_equal 1, file.items.size
|
40
|
-
assert_equal "some\tkey", file.items[0].key
|
41
|
-
assert_equal "some\tvalue", file.items[0].value
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_can_parse_files_with_escaped_carriage_returns
|
45
|
-
file = DotStrings.parse_file('test/fixtures/escaped_carriage_returns.strings')
|
46
|
-
|
47
|
-
assert_equal 1, file.items.size
|
48
|
-
assert_equal "some\rkey", file.items[0].key
|
49
|
-
assert_equal "some\rvalue", file.items[0].value
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_can_parse_files_with_escaped_nil
|
53
|
-
file = DotStrings.parse_file('test/fixtures/escaped_nil.strings')
|
54
|
-
|
55
|
-
assert_equal 1, file.items.size
|
56
|
-
assert_equal "key\0", file.items[0].key
|
57
|
-
assert_equal "value\0", file.items[0].value
|
58
|
-
end
|
59
|
-
|
60
|
-
def test_can_parse_files_with_escaped_new_lines
|
61
|
-
file = DotStrings.parse_file('test/fixtures/escaped_new_lines.strings')
|
62
|
-
|
63
|
-
assert_equal 1, file.items.size
|
64
|
-
assert_equal "some\nkey", file.items[0].key
|
65
|
-
assert_equal "some\nvalue", file.items[0].value
|
66
|
-
end
|
67
|
-
|
68
|
-
def test_can_parse_files_with_escaped_backslashes
|
69
|
-
file = DotStrings.parse_file('test/fixtures/escaped_backslashes.strings')
|
70
|
-
|
71
|
-
assert_equal 1, file.items.size
|
72
|
-
assert_equal 'some\\key', file.items[0].key
|
73
|
-
assert_equal 'some\\value', file.items[0].value
|
74
|
-
end
|
75
|
-
|
76
|
-
def test_can_parse_files_with_escaped_unicode
|
77
|
-
file = DotStrings.parse_file('test/fixtures/escaped_unicode.strings')
|
78
|
-
|
79
|
-
assert_equal 1, file.items.size
|
80
|
-
assert_equal '$', file.items[0].key
|
81
|
-
assert_equal '⚡👻', file.items[0].value
|
82
|
-
end
|
83
|
-
|
84
|
-
def test_can_parse_utf16le_files_with_bom
|
85
|
-
file = DotStrings.parse_file('test/fixtures/utf16le_bom.strings')
|
86
|
-
|
87
|
-
assert_equal 1, file.items.size
|
88
|
-
assert_equal 'key', file.items[0].key
|
89
|
-
assert_equal 'value', file.items[0].value
|
90
|
-
end
|
91
|
-
|
92
|
-
def test_can_parse_utf16be_files_with_bom
|
93
|
-
file = DotStrings.parse_file('test/fixtures/utf16be_bom.strings')
|
94
|
-
|
95
|
-
assert_equal 1, file.items.size
|
96
|
-
assert_equal 'key', file.items[0].key
|
97
|
-
assert_equal 'value', file.items[0].value
|
98
|
-
end
|
99
|
-
|
100
|
-
def test_can_parse_utf8_files_with_bom
|
101
|
-
file = DotStrings.parse_file('test/fixtures/utf8_bom.strings')
|
102
|
-
|
103
|
-
assert_equal 1, file.items.size
|
104
|
-
assert_equal 'key', file.items[0].key
|
105
|
-
assert_equal 'value', file.items[0].value
|
106
|
-
end
|
107
|
-
end
|
data/test/test_file.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'helper'
|
4
|
-
|
5
|
-
class TestFile < MiniTest::Test
|
6
|
-
def test_delete
|
7
|
-
items = [
|
8
|
-
DotStrings::Item.new(key: 'key 1', value: 'value 1'),
|
9
|
-
DotStrings::Item.new(key: 'key 2', value: 'value 2'),
|
10
|
-
DotStrings::Item.new(key: 'key 3', value: 'value 3')
|
11
|
-
]
|
12
|
-
|
13
|
-
file = DotStrings::File.new(items)
|
14
|
-
|
15
|
-
file.delete('key 2')
|
16
|
-
assert_equal 2, file.items.size
|
17
|
-
assert_equal ['key 1', 'key 3'], file.keys
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_access_by_key
|
21
|
-
items = [
|
22
|
-
DotStrings::Item.new(key: 'key 1', value: 'value 1'),
|
23
|
-
DotStrings::Item.new(key: 'key 2', value: 'value 2'),
|
24
|
-
DotStrings::Item.new(key: 'key 3', value: 'value 3')
|
25
|
-
]
|
26
|
-
|
27
|
-
file = DotStrings::File.new(items)
|
28
|
-
|
29
|
-
assert_equal 'value 1', file['key 1'].value
|
30
|
-
assert_equal 'value 2', file['key 2'].value
|
31
|
-
assert_equal 'value 3', file['key 3'].value
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_append
|
35
|
-
file = DotStrings::File.new
|
36
|
-
file.append(DotStrings::Item.new(key: 'key 1', value: 'value 1'))
|
37
|
-
assert_equal 1, file.items.size
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_to_string
|
41
|
-
file = DotStrings::File.new([
|
42
|
-
DotStrings::Item.new(comment: 'Comment 1', key: 'key 1', value: 'value 1'),
|
43
|
-
DotStrings::Item.new(comment: 'Comment 2', key: 'key 2', value: 'value 2'),
|
44
|
-
DotStrings::Item.new(comment: 'Comment 3', key: 'key 3', value: '👻'),
|
45
|
-
DotStrings::Item.new(comment: 'Comment 4', key: "\"'\t\n\r\0", value: "\"'\t\n\r\0")
|
46
|
-
])
|
47
|
-
|
48
|
-
expected = <<~'END_OF_DOCUMENT'
|
49
|
-
/* Comment 1 */
|
50
|
-
"key 1" = "value 1";
|
51
|
-
|
52
|
-
/* Comment 2 */
|
53
|
-
"key 2" = "value 2";
|
54
|
-
|
55
|
-
/* Comment 3 */
|
56
|
-
"key 3" = "👻";
|
57
|
-
|
58
|
-
/* Comment 4 */
|
59
|
-
"\"'\t\n\r\0" = "\"'\t\n\r\0";
|
60
|
-
END_OF_DOCUMENT
|
61
|
-
|
62
|
-
assert_equal expected, file.to_s
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_to_string_no_comments
|
66
|
-
file = DotStrings::File.new([
|
67
|
-
DotStrings::Item.new(comment: 'Comment 1', key: 'key 1', value: 'value 1'),
|
68
|
-
DotStrings::Item.new(comment: 'Comment 2', key: 'key 2', value: 'value 2')
|
69
|
-
])
|
70
|
-
|
71
|
-
expected = <<~'END_OF_DOCUMENT'
|
72
|
-
"key 1" = "value 1";
|
73
|
-
|
74
|
-
"key 2" = "value 2";
|
75
|
-
END_OF_DOCUMENT
|
76
|
-
|
77
|
-
assert_equal expected, file.to_s(comments: false)
|
78
|
-
end
|
79
|
-
|
80
|
-
def test_to_string_can_escape_single_quotes
|
81
|
-
items = [
|
82
|
-
DotStrings::Item.new(comment: 'Comment', key: "key'", value: "value'")
|
83
|
-
]
|
84
|
-
|
85
|
-
file = DotStrings::File.new(items)
|
86
|
-
|
87
|
-
expected = <<~'END_OF_DOCUMENT'
|
88
|
-
/* Comment */
|
89
|
-
"key\'" = "value\'";
|
90
|
-
END_OF_DOCUMENT
|
91
|
-
|
92
|
-
assert_equal expected, file.to_s(escape_single_quotes: true)
|
93
|
-
end
|
94
|
-
end
|
data/test/test_item.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'helper'
|
4
|
-
|
5
|
-
class TestFile < MiniTest::Test
|
6
|
-
def test_to_s
|
7
|
-
item = DotStrings::Item.new(comment: 'Comment', key: 'key 1', value: 'value 1')
|
8
|
-
assert_equal "/* Comment */\n\"key 1\" = \"value 1\";", item.to_s
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_to_s_with_nil_comment
|
12
|
-
item = DotStrings::Item.new(comment: nil, key: 'key 1', value: 'value 1')
|
13
|
-
assert_equal '"key 1" = "value 1";', item.to_s
|
14
|
-
end
|
15
|
-
end
|