loco_strings 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 567e3981149f49dd55a99138ca51993030011f886c227f3cd8809c5a0c1e48c5
4
- data.tar.gz: d9424623eeb635a2798a4f858701be4fa1897714af4181caa5863dbcb8d4bf4d
3
+ metadata.gz: 36daf9f28f2ff56c824968da9a23422d2a3d50e8260154469cb93b8637097350
4
+ data.tar.gz: 948f62e57518bc34201c1f6ae67f66ca2f8a0b10662389caeaf908505a12e2e5
5
5
  SHA512:
6
- metadata.gz: 50404d9b25812983c3b6f537d4220572f7df87c837b19411fd22293cc6c1abae4550ec3e630bc359853b8092802cf8324a2b4db8b753ba830d20e0d42bae82c3
7
- data.tar.gz: f650a177e645204d68e6cd65fa87214976b5c258c266d90bcdf61a1061bd49466a442dd74d3bac22b1fe7a86b13ecacedb90a32bff5fbf0db3e4c70fc4dd63e4
6
+ metadata.gz: '028dc48c692c84b7e6fc97b0bbd572faedb686a59c50084e2aa9bd0cc95b7f88d6a82cad905fd984a44bdf7a6b2de1166ba00a1390bf115131ca5ddb8af0246b'
7
+ data.tar.gz: a88259d18e7893c48bea667080de5d2ccd2ee4904fc7a83f7a219abcc70d47cf7a1004658a06b6f83673b2cea0c5532352e6712c30d6b3a599a0a7f256176c9c
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.6
3
+ NewCops: enable
3
4
 
4
5
  Style/StringLiterals:
5
6
  Enabled: true
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module LocoStrings
6
+ # The XCStringsDecoder class is responsible for decoding the XCStrings file format.
7
+ class XCStringsDecoder
8
+ attr_reader :language, :strings, :translations, :languages
9
+
10
+ def initialize(file_path)
11
+ @file_path = file_path
12
+ @strings = {}
13
+ @translations = {}
14
+ @languages = []
15
+ @language = nil
16
+ end
17
+
18
+ def decode
19
+ return @strings unless File.exist?(@file_path)
20
+
21
+ file = File.read(@file_path)
22
+ json = JSON.parse(file)
23
+ extract_languages(json)
24
+ @language = json["sourceLanguage"]
25
+ decode_strings(json)
26
+ @strings
27
+ end
28
+
29
+ private
30
+
31
+ def extract_languages(json)
32
+ @languages = []
33
+ json["strings"].each do |_key, value|
34
+ next unless value.key?("localizations")
35
+
36
+ value["localizations"].each do |lang, _loc|
37
+ @languages << lang
38
+ end
39
+ end
40
+ @languages.uniq!
41
+ end
42
+
43
+ def decode_strings(json)
44
+ strings = json["strings"]
45
+ strings.each do |key, value|
46
+ @strings[key] = decode_string(key, value, @language)
47
+ decode_translations(key, value)
48
+ end
49
+ end
50
+
51
+ def decode_string(key, value, language)
52
+ return LocoString.new(key, key, value["comment"], "new") unless value.key?("localizations")
53
+
54
+ loc = value.dig("localizations", language)
55
+ return if loc.nil?
56
+
57
+ if loc.key?("stringUnit")
58
+ decode_string_unit(key, loc, value["comment"])
59
+ elsif loc.key?("variations")
60
+ decode_variations(key, loc, value["comment"])
61
+ else
62
+ LocoString.new(key, key, value["comment"], "new")
63
+ end
64
+ end
65
+
66
+ def decode_string_unit(key, value, comment)
67
+ return nil unless value.key?("stringUnit")
68
+
69
+ unit = value["stringUnit"]
70
+ translation = unit["value"]
71
+ LocoString.new(key, translation, comment, unit["state"]) unless translation.nil?
72
+ end
73
+
74
+ def decode_variations(key, value, comment)
75
+ variations = value["variations"]
76
+ plural = decode_plural(variations, comment)
77
+ return nil if plural.empty?
78
+
79
+ variation = LocoVariantions.new(key, comment)
80
+ plural.each do |unit|
81
+ variation.append_string(unit)
82
+ end
83
+ variation
84
+ end
85
+
86
+ def decode_plural(variation, comment)
87
+ plural = variation["plural"]
88
+ return [] if plural.nil?
89
+
90
+ result = []
91
+ plural.each do |key, value|
92
+ unit = decode_string_unit(key, value, comment)
93
+ result << unit unless unit.nil?
94
+ end
95
+ result
96
+ end
97
+
98
+ def decode_translations(key, value)
99
+ @languages.each do |language|
100
+ string = decode_string(key, value, language)
101
+ @translations[language] ||= {}
102
+ @translations[language][key] = string
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module LocoStrings
6
+ # The XCStringsEncoder class is responsible for encoding the LocoStrings data to the XCStrings format.
7
+ class XCStringsEncoder
8
+ attr_reader :language, :strings, :translations, :languages
9
+
10
+ def initialize(strings, translations, languages, language)
11
+ @strings = strings
12
+ @translations = translations
13
+ @languages = languages
14
+ @language = language
15
+ end
16
+
17
+ def encode
18
+ raise Error, "The base language is not defined" if @language.nil?
19
+
20
+ json = {
21
+ "sourceLanguage" => @language,
22
+ "strings" => {},
23
+ "version" => "1.0"
24
+ }
25
+ generate_keys.each do |key|
26
+ json["strings"][key] = encode_key(key)
27
+ end
28
+ JSON.pretty_generate(json, { space_before: " " })
29
+ end
30
+
31
+ private
32
+
33
+ def generate_keys
34
+ keys = []
35
+ @translations.each do |_, translation|
36
+ keys += translation.keys
37
+ end
38
+ keys.uniq
39
+ end
40
+
41
+ def encode_key(key)
42
+ row = {}
43
+ @translations.each do |language, translation|
44
+ next unless translation.key?(key)
45
+
46
+ value = translation[key]
47
+ next if value.nil?
48
+
49
+ row["comment"] = value.comment unless row.key?("comment") || value.comment.nil?
50
+ row["localizations"] ||= {}
51
+ row["localizations"][language] = encode_value(value)
52
+ end
53
+ row
54
+ end
55
+
56
+ def encode_value(value)
57
+ if value.is_a?(LocoVariantions)
58
+ encode_variations(value)
59
+ else
60
+ encode_string_unit(value)
61
+ end
62
+ end
63
+
64
+ def encode_string_unit(unit)
65
+ res = { "stringUnit" => {} }
66
+ res["stringUnit"]["state"] = unit.state unless unit.state.nil?
67
+ res["stringUnit"]["value"] = unit.value
68
+ res
69
+ end
70
+
71
+ def encode_variations(variations)
72
+ plural = {}
73
+ variations.strings.each do |key, string|
74
+ plural[key] = encode_string_unit(string) unless string.nil?
75
+ end
76
+ {
77
+ "variations" => {
78
+ "plural" => plural
79
+ }
80
+ }
81
+ end
82
+ end
83
+ end
@@ -29,7 +29,7 @@ module LocoStrings
29
29
  end
30
30
  end
31
31
  end
32
- File.open(@file_path, "w") { |file| file.write(builder.to_xml) }
32
+ File.write(@file_path, builder.to_xml)
33
33
  end
34
34
 
35
35
  private
@@ -25,7 +25,7 @@ module LocoStrings
25
25
  output += "/* #{value.comment} */\n" if value.comment
26
26
  output += "\"#{key}\" = \"#{value.value}\";\n"
27
27
  end
28
- File.open(@file_path, "w") { |file| file.write(output) }
28
+ File.write(@file_path, output)
29
29
  end
30
30
 
31
31
  private
@@ -23,7 +23,7 @@ module LocoStrings
23
23
  end
24
24
 
25
25
  def update(key, value, comment = nil)
26
- comment = @strings[key].comment if comment.nil? && @strings.has_key?(key)
26
+ comment = @strings[key].comment if comment.nil? && @strings.key?(key)
27
27
  @strings[key] = LocoString.new key, value, comment
28
28
  end
29
29
 
@@ -1,102 +1,58 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "loco_file"
4
+ require_relative "../encoders/xcstrings_encoder"
5
+ require_relative "../decoders/xcstrings_decoder"
2
6
  require "json"
3
7
 
4
8
  module LocoStrings
9
+ # The XCStringsFile class is responsible for reading and writing the XCStrings file format.
5
10
  class XCStringsFile < LocoFile
6
11
  def read
7
12
  clean
8
13
  return @strings unless File.exist?(@file_path)
9
14
 
10
- comment = nil
11
- file = File.read(@file_path)
12
- json = JSON.parse(file)
13
- extract_languages(json)
14
- @language = json["sourceLanguage"]
15
- @translations = {}
16
- strings = json["strings"]
17
- strings.each do |key, value|
18
- comment = value["comment"]
19
- translation = key
20
- if value.has_key?("localizations") && value["localizations"].has_key?(@language)
21
- loc = value["localizations"][@language]
22
- end
23
- translation = loc["stringUnit"]["value"] unless loc.nil?
24
- @strings[key] = LocoString.new key, translation, comment unless translation.nil?
25
- for language in @languages
26
- next unless value.has_key?("localizations") && value["localizations"].has_key?(language)
27
-
28
- loc = value["localizations"][language]
29
- translation = loc["stringUnit"]["value"] unless loc.nil?
30
- @translations[language] = {} unless @translations.has_key?(language)
31
- @translations[language][key] = translation unless translation.nil?
32
- end
33
- end
15
+ decoder = XCStringsDecoder.new(@file_path)
16
+ decoder.decode
17
+
18
+ @language = decoder.language
19
+ @strings = decoder.strings
20
+ @translations = decoder.translations
21
+ @languages = decoder.languages
34
22
  @strings
35
23
  end
36
24
 
37
25
  def write
38
26
  raise Error, "The base language is not defined" if @language.nil?
39
27
 
40
- json = {}
41
- json["sourceLanguage"] = @language
42
- json["strings"] = {}
43
- @strings.each do |key, value|
44
- row = {}
45
- row["comment"] = value.comment unless value.comment.nil?
46
- localizations = {}
47
- if value.value != key && !value.value.nil?
48
- localizations[@language] = {
49
- "stringUnit" => {
50
- "state" => "translated",
51
- "value" => value.value
52
- }
53
- }
54
- end
55
- @languages.each do |language|
56
- next if language == @language
57
- next unless @translations.has_key?(language) && @translations[language].has_key?(key)
58
-
59
- localizations[language] = {
60
- "stringUnit" => {
61
- "state" => "translated",
62
- "value" => @translations[language][key]
63
- }
64
- }
65
- end
66
- row["localizations"] = localizations unless localizations.empty?
67
- json["strings"][key] = row
68
- end
69
- json["version"] = "1.0"
70
- File.open(@file_path, "w") { |file| file.write(JSON.pretty_generate(json)) }
28
+ json = XCStringsEncoder.new(@strings, @translations, @languages, @language).encode
29
+ File.write(@file_path, json)
71
30
  end
72
31
 
73
- def update(key, value, comment = nil, language = @language)
32
+ def update(key, value, comment = nil, stage = nil, language = @language)
74
33
  raise Error, "The base language is not defined" if @language.nil?
75
34
 
76
- if @language != language
77
- @languages << language unless @languages.include?(language)
78
- @translations[language] = {} unless @translations.has_key?(language)
79
- @translations[language][key] = value
80
- @strings[key] = LocoString.new key, key, comment unless @strings.has_key?(key)
81
- return
82
- end
83
- comment = @strings[key].comment if @strings.has_key?(key) && (comment.nil? && @strings.has_key?(key))
84
- @strings[key] = LocoString.new key, value, comment
85
- end
35
+ stage = "translated" if stage.nil?
36
+ string = make_strings(key, value, comment, stage, language)
37
+ return if string.nil?
86
38
 
87
- def value(key, language = nil)
88
- return @strings[key] if language.nil? || language == @language
89
- return @translations[language][key] if @translations.has_key?(language) && @translations[language].has_key?(key)
39
+ @translations[language] ||= {}
40
+ @translations[language][key] = string
41
+ @strings[key] = string if @language == language
90
42
  end
91
43
 
92
- def clean
93
- @strings = {}
94
- @translations = {}
95
- @languages = []
96
- @language = nil
44
+ def update_variation(key, variant, strings, comment = nil, state = nil, language = @language) # rubocop:disable Metrics/ParameterLists
45
+ raise Error, "The base language is not defined" if @language.nil?
46
+
47
+ variations = make_variations(key, variant, strings, comment, state, language)
48
+ return if variations.nil?
49
+
50
+ @translations[language] ||= {}
51
+ @translations[language][key] = variations
52
+ @strings[key] = variations if @language == language
97
53
  end
98
54
 
99
- def set_language(language)
55
+ def select_language(language)
100
56
  raise Error, "The base language is aready defined" unless @language.nil?
101
57
 
102
58
  @language = language
@@ -113,18 +69,39 @@ module LocoStrings
113
69
  result
114
70
  end
115
71
 
72
+ def clean
73
+ @strings = {}
74
+ @translations = {}
75
+ @languages = []
76
+ @language = nil
77
+ end
78
+
116
79
  private
117
80
 
118
- def extract_languages(json)
119
- @languages = []
120
- json["strings"].each do |_key, value|
121
- next unless value.has_key?("localizations")
81
+ def make_strings(key, value, comment = nil, state = nil, language = @language)
82
+ unit = @translations.dig(language, key)
83
+ return LocoString.new(key, value, comment, state) if unit.nil?
122
84
 
123
- value["localizations"].each do |lang, _loc|
124
- @languages << lang
125
- end
85
+ if unit.is_a?(LocoVariantions)
86
+ puts "Variations not supported through this method"
87
+ return nil
126
88
  end
127
- @languages.uniq!
89
+ unit.update(value, comment, state)
90
+ unit
91
+ end
92
+
93
+ def make_variations(key, variant, value, comment = nil, state = nil, language = @language) # rubocop:disable Metrics/ParameterLists
94
+ variants = @translations.dig(language, key)
95
+
96
+ return make_strings(key, value, comment, state, language) if variants.is_a?(LocoString)
97
+
98
+ if variants.nil?
99
+ state = "new" if state.nil?
100
+ return LocoVariantions.new(key, { variant => LocoString.new(variant, value, comment, state) })
101
+ end
102
+
103
+ variants.update_variant(variant, value, comment, state)
104
+ variants
128
105
  end
129
106
  end
130
107
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LocoStrings
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
data/lib/loco_strings.rb CHANGED
@@ -9,17 +9,46 @@ require_relative "loco_strings/parsers/xcstrings_file"
9
9
  module LocoStrings
10
10
  class Error < StandardError; end
11
11
 
12
- LocoString = Struct.new(:key, :value, :comment) do
13
- def initialize(key, value, comment = nil)
12
+ LocoString = Struct.new(:key, :value, :comment, :state) do
13
+ def initialize(key, value, comment = nil, state = nil)
14
14
  super
15
15
  end
16
16
 
17
+ def update(value, comment = nil, state = nil)
18
+ self.value = value
19
+ self.comment = comment unless comment.nil?
20
+ self.state = state
21
+ end
22
+
23
+ def to_s
24
+ "Key: #{key}, Value: #{value}, Comment: #{comment || "None"}, State: #{state || "None"}"
25
+ end
26
+ end
27
+
28
+ LocoVariantions = Struct.new(:key, :strings, :comment) do
29
+ def initialize(key, strings = nil, comment = nil)
30
+ super
31
+ self.strings = strings || {}
32
+ end
33
+
34
+ def append_string(string)
35
+ strings[string.key] = string
36
+ end
37
+
38
+ def update_variant(key, value, comment = nil, state = nil)
39
+ if strings.key? key
40
+ strings[key].update(value, comment, state)
41
+ else
42
+ strings[key] = LocoString.new(key, value, comment, state)
43
+ end
44
+ end
45
+
17
46
  def to_s
18
- "Key: #{key}, Value: #{value}, Comment: #{comment || "None"}"
47
+ "Key: #{key}, Strings: #{strings}, Comment: #{comment || "None"}"
19
48
  end
20
49
  end
21
50
 
22
- def self.load(file_path)
51
+ def self.load(file_path) # rubocop:disable Metrics/MethodLength
23
52
  ext = File.extname(file_path)
24
53
  raise Error, "Unsupported file format: #{ext}" unless [".strings", ".xml", ".xcstrings"].include? ext
25
54
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loco_strings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksei Cherepanov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-16 00:00:00.000000000 Z
11
+ date: 2024-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -53,12 +53,13 @@ files:
53
53
  - README.md
54
54
  - Rakefile
55
55
  - lib/loco_strings.rb
56
+ - lib/loco_strings/decoders/xcstrings_decoder.rb
57
+ - lib/loco_strings/encoders/xcstrings_encoder.rb
56
58
  - lib/loco_strings/parsers/android_file.rb
57
59
  - lib/loco_strings/parsers/ios_file.rb
58
60
  - lib/loco_strings/parsers/loco_file.rb
59
61
  - lib/loco_strings/parsers/xcstrings_file.rb
60
62
  - lib/loco_strings/version.rb
61
- - loco_strings.gemspec
62
63
  - sig/loco_strings.rbs
63
64
  homepage: https://github.com/ftp27/loco_strings
64
65
  licenses:
@@ -66,6 +67,7 @@ licenses:
66
67
  metadata:
67
68
  homepage_uri: https://github.com/ftp27/loco_strings
68
69
  source_code_uri: https://github.com/ftp27/loco_strings
70
+ rubygems_mfa_required: 'true'
69
71
  post_install_message:
70
72
  rdoc_options: []
71
73
  require_paths:
data/loco_strings.gemspec DELETED
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Layout/LineLength
4
-
5
- require_relative "lib/loco_strings/version"
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = "loco_strings"
9
- spec.version = LocoStrings::VERSION
10
- spec.authors = ["Aleksei Cherepanov"]
11
- spec.email = ["ftp27host@gmail.com"]
12
-
13
- spec.summary = "LocoStrings is a Ruby gem for working with iOS and Android localization strings."
14
- spec.description = "LocoStrings is a powerful and easy-to-use Ruby gem that simplifies the process of managing localization strings for iOS and Android apps. With LocoStrings, you can easily extract, organize, and update your app's localized strings in one place, making it easy to maintain consistency across all of your app's translations. LocoStrings supports multiple file formats, including XLIFF and CSV, and provides a simple and intuitive API for working with your app's strings in code. Whether you're a solo developer or part of a team, LocoStrings makes managing your app's localization a breeze."
15
- spec.homepage = "https://github.com/ftp27/loco_strings"
16
- spec.license = "MIT"
17
- spec.required_ruby_version = ">= 2.6.0"
18
-
19
- spec.metadata["homepage_uri"] = spec.homepage
20
- spec.metadata["source_code_uri"] = spec.homepage
21
-
22
- # Specify which files should be added to the gem when it is released.
23
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
- spec.files = Dir.chdir(__dir__) do
25
- `git ls-files -z`.split("\x0").reject do |f|
26
- (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
27
- end
28
- end
29
- spec.bindir = "exe"
30
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
- spec.require_paths = ["lib"]
32
-
33
- # Uncomment to register a new dependency of your gem
34
- spec.add_dependency "nokogiri", "~> 1.13", ">= 1.13.8"
35
-
36
- # For more information and examples about making a new gem, check out our
37
- # guide at: https://bundler.io/guides/creating_gem.html
38
- end
39
-
40
- # rubocop:enable Layout/LineLength