i18nize 0.4.5 → 0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 554c7500faf755b35e18493fdb4f69b2a14e3554448644979c8065f5e8ba4d3e
4
- data.tar.gz: 50c639f8e40929e7376ecc207e6a422e071c5526de8c100ee01c10c03e6cefc9
3
+ metadata.gz: 592b298cbac45e06745596fe3b02025797d39f572214e2dd345ef6f9d39abe33
4
+ data.tar.gz: a661e6474225964f79d6d80a38c23f91d531e6c6c58b5c52e3b7b11de211d652
5
5
  SHA512:
6
- metadata.gz: e3430a473e47b1d4837d1f0f5343c461695ac1be8ca8225e6a02be4326754dae424ccb6477555e258487aaffef0d7e389b1873908c27bd67f0f8f9995fb3811e
7
- data.tar.gz: be813068492685523dd09fa531fe174642a7850f193b9c36082448744764350e45ff1d14ff49386a7d04dd06afc302b8c4073ddcf0ee4c6c57a4f81ced6160fe
6
+ metadata.gz: 210f54125ca483b27318abcdcb0647d947a83639d11e62ce4ca34506974d3cb0f65d2357954b81106eafd28874c64b1e774338548f48f3898d9e2fac84a1b85c
7
+ data.tar.gz: 21ed727606ee36a27b10ab94d7625f4b5deda616b3e4afd2fb745db7b69945b3a5688f0d6ba75d407d7fc4a82ee89eda49475c659f413c75bf03321594550ef0
@@ -15,15 +15,14 @@ module I18nize
15
15
  dirname = File.dirname(from_path)
16
16
  filename = File.basename(from_path)
17
17
 
18
- expected_to_filename =
19
- filename.sub(/(^|[._])#{Regexp.escape(from_locale)}\.yml$/, "\\1#{to_locale}.yml")
20
-
21
- to_path =
22
- if dirname == "." || dirname.empty?
23
- expected_to_filename
24
- else
25
- File.join(dirname, expected_to_filename)
26
- end
18
+ locale_re = /(^|[._])#{Regexp.escape(from_locale)}\.(ya?ml)$/
19
+ expected_to_filename = filename.sub(locale_re) { "#{Regexp.last_match(1)}#{to_locale}.#{Regexp.last_match(2)}" }
20
+
21
+ to_path = if dirname == "." || dirname.empty?
22
+ expected_to_filename
23
+ else
24
+ File.join(dirname, expected_to_filename)
25
+ end
27
26
 
28
27
  to_keys = to_data[to_path] || {}
29
28
 
@@ -6,32 +6,32 @@ require_relative "comparer"
6
6
 
7
7
  module I18nize
8
8
  class Inserter
9
+ CONFLICT_POLICY = :overwrite # :overwrite | :skip
10
+
9
11
  def self.insert_missing(from_locale: "en", to_locale:, base_path: "config/locales", translator: nil)
10
12
  missing_data = Comparer.missing_translations(from_locale: from_locale, to_locale: to_locale)
11
13
 
12
14
  if translator
13
15
  char_count = missing_data.character_count
14
16
  puts "\n\e[33m[i18nize] Estimated character count: #{char_count}\e[0m"
15
-
16
- if char_count > 500_000
17
- puts "\e[31m[i18nize] Warning: DeepL Free has a 500,000 character monthly limit.\e[0m"
18
- end
19
-
17
+ puts "\e[31m[i18nize] Warning: DeepL Free has a 500,000 character monthly limit.\e[0m" if char_count > 500_000
20
18
  print "\e[36mProceed with translation? [y/N]: \e[0m"
21
- answer = $stdin.gets.strip.downcase
22
-
19
+ answer = $stdin.gets.to_s.strip.downcase
23
20
  unless answer == "y"
24
21
  puts "\e[90m[i18nize] Aborted by user.\e[0m"
25
22
  return
26
23
  end
27
24
  end
28
25
 
26
+ overwrites = []
27
+
29
28
  missing_data.data.each do |from_rel_path, translations|
30
29
  dirname = File.dirname(from_rel_path)
31
30
  filename = File.basename(from_rel_path)
32
31
 
33
- new_filename = filename.sub(/^#{from_locale}\.yml$/, "#{to_locale}.yml")
34
- to_rel_path = File.join(dirname, new_filename)
32
+ locale_re = /(^|[._])#{Regexp.escape(from_locale)}\.(ya?ml)$/
33
+ new_filename = filename.sub(locale_re) { "#{Regexp.last_match(1)}#{to_locale}.#{Regexp.last_match(2)}" }
34
+ to_rel_path = dirname == "." || dirname.empty? ? new_filename : File.join(dirname, new_filename)
35
35
  to_full_path = File.join(base_path, to_rel_path)
36
36
 
37
37
  existing = File.exist?(to_full_path) ? YAML.load_file(to_full_path) : {}
@@ -39,16 +39,16 @@ module I18nize
39
39
 
40
40
  translation_map =
41
41
  if translator
42
- en_values = translations.values
43
- translated_values = translator.translate_texts(en_values)
44
- translations.keys.zip(translated_values.values).to_h
42
+ src_values = translations.values
43
+ translated_values = translator.translate_texts(src_values) # => pole!
44
+ translations.keys.zip(translated_values).to_h
45
45
  else
46
46
  translations
47
47
  end
48
48
 
49
- translation_map.each do |key, value|
50
- path = key.sub(/^#{from_locale}\./, "").split(".")
51
- insert_nested(existing[to_locale], path, value)
49
+ translation_map.each do |full_key, value|
50
+ path = full_key.sub(/^#{Regexp.escape(from_locale)}\./, "").split(".")
51
+ insert_nested(existing[to_locale], path, value, overwrites, to_rel_path, to_locale, CONFLICT_POLICY)
52
52
  end
53
53
 
54
54
  FileUtils.mkdir_p(File.dirname(to_full_path))
@@ -56,16 +56,41 @@ module I18nize
56
56
 
57
57
  puts "\e[32m[i18nize] Inserted #{translation_map.size} keys into #{to_full_path}\e[0m"
58
58
  end
59
+
60
+ if overwrites.any?
61
+ puts "\n\e[33m[i18nize] Overwrote #{overwrites.size} scalar node(s) to create nested structure:\e[0m"
62
+ overwrites.each do |c|
63
+ puts " \e[33m- #{c[:file]}: #{c[:locale]}.#{c[:path].join('.')}\e[0m (was: #{c[:existing_class]})"
64
+ end
65
+ end
59
66
  end
60
67
 
61
- def self.insert_nested(base, keys, value)
68
+ # conflict_policy: :overwrite (replace scalar with {}), :skip (leave as is)
69
+ def self.insert_nested(base, keys, value, overwrites, file, locale, conflict_policy)
62
70
  key = keys.shift
63
- base[key] ||= {}
64
71
 
65
72
  if keys.empty?
66
73
  base[key] = value
74
+ return true
75
+ end
76
+
77
+ cur = base[key]
78
+
79
+ case cur
80
+ when nil
81
+ base[key] = {}
82
+ insert_nested(base[key], keys, value, overwrites, file, locale, conflict_policy)
83
+ when Hash
84
+ insert_nested(cur, keys, value, overwrites, file, locale, conflict_policy)
67
85
  else
68
- insert_nested(base[key], keys, value)
86
+ if conflict_policy == :overwrite
87
+ overwrites << { file: file, locale: locale, path: [key, *keys], existing_class: cur.class.to_s }
88
+ base[key] = {}
89
+ insert_nested(base[key], keys, value, overwrites, file, locale, conflict_policy)
90
+ else
91
+ # :skip
92
+ false
93
+ end
69
94
  end
70
95
  end
71
96
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  require "net/http"
4
3
  require "uri"
5
4
  require "json"
@@ -7,42 +6,44 @@ require "json"
7
6
  module I18nize
8
7
  class Translator
9
8
  ENDPOINT = ENV.fetch("DEEPL_ENDPOINT", "https://api-free.deepl.com/v2/translate")
9
+ PLACEHOLDER_RE = /%{\s*[^}]+\s*}/
10
10
 
11
11
  def initialize(auth_key:, from_lang: "EN", to_lang:)
12
- @auth_key = auth_key
12
+ @auth_key = auth_key
13
13
  @from_lang = from_lang
14
- @to_lang = to_lang
14
+ @to_lang = to_lang
15
15
  end
16
16
 
17
17
  def translate_texts(texts)
18
- return {} if texts.empty?
18
+ return [] if texts.empty?
19
+
20
+ placeholders = []
21
+ protected = texts.map do |t|
22
+ t.to_s.gsub(PLACEHOLDER_RE) do |m|
23
+ idx = placeholders.size
24
+ placeholders << m
25
+ "__I18NIZE_PLH_#{idx}__"
26
+ end
27
+ end
19
28
 
20
29
  uri = URI(ENDPOINT)
21
-
22
- # Opakující se text parametry
23
30
  body_params = [
24
31
  ["auth_key", @auth_key],
25
32
  ["source_lang", @from_lang],
26
33
  ["target_lang", @to_lang],
27
- ] + texts.map { |text| ["text", text] }
34
+ ] + protected.map { |text| ["text", text] }
28
35
 
29
- request = Net::HTTP::Post.new(uri)
30
- request["Content-Type"] = "application/x-www-form-urlencoded"
31
- request.body = URI.encode_www_form(body_params)
36
+ req = Net::HTTP::Post.new(uri)
37
+ req["Content-Type"] = "application/x-www-form-urlencoded"
38
+ req.body = URI.encode_www_form(body_params)
32
39
 
33
- response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
34
- http.request(request)
35
- end
40
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
41
+ raise "DeepL API Error: #{res.code} - #{res.body}" unless res.is_a?(Net::HTTPSuccess)
36
42
 
37
- unless response.is_a?(Net::HTTPSuccess)
38
- raise "DeepL API Error: #{response.code} - #{response.body}"
39
- end
40
-
41
- json = JSON.parse(response.body)
42
- translated = json["translations"].map { |t| t["text"] }
43
+ json = JSON.parse(res.body)
44
+ out = json["translations"].map { |t| t["text"] }
43
45
 
44
- Hash[texts.zip(translated)]
46
+ out.map { |s| s.gsub(/__I18NIZE_PLH_(\d+)__/) { placeholders[$1.to_i] } }
45
47
  end
46
48
  end
47
49
  end
48
-
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nize
4
- VERSION = "0.4.5"
4
+ VERSION = "0.5"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18nize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: '0.5'
5
5
  platform: ruby
6
6
  authors:
7
7
  - nikolas2145