i18n-tasks 0.9.31 → 0.9.34

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -5
  3. data/i18n-tasks.gemspec +4 -4
  4. data/lib/i18n/tasks/cli.rb +12 -14
  5. data/lib/i18n/tasks/command/commands/data.rb +8 -6
  6. data/lib/i18n/tasks/command/commands/eq_base.rb +2 -2
  7. data/lib/i18n/tasks/command/commands/health.rb +3 -2
  8. data/lib/i18n/tasks/command/commands/interpolations.rb +1 -1
  9. data/lib/i18n/tasks/command/commands/meta.rb +1 -1
  10. data/lib/i18n/tasks/command/commands/missing.rb +7 -6
  11. data/lib/i18n/tasks/command/commands/tree.rb +8 -6
  12. data/lib/i18n/tasks/command/commands/usages.rb +5 -4
  13. data/lib/i18n/tasks/command/dsl.rb +4 -4
  14. data/lib/i18n/tasks/command/option_parsers/enum.rb +2 -0
  15. data/lib/i18n/tasks/command/option_parsers/locale.rb +2 -1
  16. data/lib/i18n/tasks/command/options/data.rb +1 -0
  17. data/lib/i18n/tasks/command/options/locales.rb +5 -5
  18. data/lib/i18n/tasks/concurrent/cached_value.rb +2 -0
  19. data/lib/i18n/tasks/configuration.rb +4 -4
  20. data/lib/i18n/tasks/console_context.rb +1 -1
  21. data/lib/i18n/tasks/data/file_formats.rb +2 -0
  22. data/lib/i18n/tasks/data/file_system_base.rb +7 -6
  23. data/lib/i18n/tasks/data/router/conservative_router.rb +2 -1
  24. data/lib/i18n/tasks/data/router/pattern_router.rb +2 -0
  25. data/lib/i18n/tasks/data/tree/node.rb +5 -2
  26. data/lib/i18n/tasks/data/tree/nodes.rb +1 -0
  27. data/lib/i18n/tasks/data/tree/siblings.rb +9 -2
  28. data/lib/i18n/tasks/data/tree/traversal.rb +11 -2
  29. data/lib/i18n/tasks/html_keys.rb +2 -4
  30. data/lib/i18n/tasks/ignore_keys.rb +4 -3
  31. data/lib/i18n/tasks/interpolations.rb +4 -2
  32. data/lib/i18n/tasks/key_pattern_matching.rb +3 -2
  33. data/lib/i18n/tasks/missing_keys.rb +33 -7
  34. data/lib/i18n/tasks/plural_keys.rb +6 -1
  35. data/lib/i18n/tasks/references.rb +2 -0
  36. data/lib/i18n/tasks/reports/base.rb +3 -2
  37. data/lib/i18n/tasks/reports/terminal.rb +6 -4
  38. data/lib/i18n/tasks/scanners/file_scanner.rb +4 -3
  39. data/lib/i18n/tasks/scanners/files/caching_file_finder.rb +0 -3
  40. data/lib/i18n/tasks/scanners/files/file_finder.rb +3 -2
  41. data/lib/i18n/tasks/scanners/occurrence_from_position.rb +3 -3
  42. data/lib/i18n/tasks/scanners/pattern_scanner.rb +7 -4
  43. data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +4 -2
  44. data/lib/i18n/tasks/scanners/relative_keys.rb +1 -0
  45. data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +16 -13
  46. data/lib/i18n/tasks/scanners/ruby_key_literals.rb +3 -3
  47. data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +2 -0
  48. data/lib/i18n/tasks/split_key.rb +2 -0
  49. data/lib/i18n/tasks/string_interpolation.rb +1 -0
  50. data/lib/i18n/tasks/translation.rb +3 -3
  51. data/lib/i18n/tasks/translators/base_translator.rb +3 -1
  52. data/lib/i18n/tasks/translators/deepl_translator.rb +9 -2
  53. data/lib/i18n/tasks/translators/google_translator.rb +2 -0
  54. data/lib/i18n/tasks/translators/yandex_translator.rb +2 -0
  55. data/lib/i18n/tasks/used_keys.rb +15 -11
  56. data/lib/i18n/tasks/version.rb +1 -1
  57. data/templates/config/i18n-tasks.yml +1 -1
  58. metadata +10 -7
@@ -9,6 +9,7 @@ module I18n::Tasks::Scanners
9
9
  class ScannerMultiplexer < Scanner
10
10
  # @param scanners [Array<Scanner>]
11
11
  def initialize(scanners:)
12
+ super()
12
13
  @scanners = scanners
13
14
  end
14
15
 
@@ -25,6 +26,7 @@ module I18n::Tasks::Scanners
25
26
  # @return [Array<Array<Results::KeyOccurrences>>]
26
27
  def collect_results
27
28
  return [@scanners[0].keys] if @scanners.length == 1
29
+
28
30
  Array.new(@scanners.length).tap do |results|
29
31
  results_mutex = Mutex.new
30
32
  @scanners.map.with_index do |scanner, i|
@@ -15,6 +15,7 @@ module I18n
15
15
  parts = []
16
16
  pos = 0
17
17
  return [key] if max == 1
18
+
18
19
  key_parts(key) do |part|
19
20
  parts << part
20
21
  pos += part.length + 1
@@ -36,6 +37,7 @@ module I18n
36
37
  # dots inside braces or parenthesis are not split on
37
38
  def key_parts(key, &block)
38
39
  return enum_for(:key_parts, key) unless block
40
+
39
41
  nesting = PARENS
40
42
  counts = PARENS_ZEROS # dup'd later if key contains parenthesis
41
43
  delim = '.'
@@ -6,6 +6,7 @@ module I18n::Tasks
6
6
 
7
7
  def interpolate_soft(s, t = {})
8
8
  return s unless s
9
+
9
10
  t.each do |k, v|
10
11
  pat = "%{#{k}}"
11
12
  s = s.gsub pat, v.to_s if s.include?(pat)
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/translators/deepl_translator.rb'
4
- require 'i18n/tasks/translators/google_translator.rb'
5
- require 'i18n/tasks/translators/yandex_translator.rb'
3
+ require 'i18n/tasks/translators/deepl_translator'
4
+ require 'i18n/tasks/translators/google_translator'
5
+ require 'i18n/tasks/translators/yandex_translator'
6
6
 
7
7
  module I18n::Tasks
8
8
  module Translation
@@ -24,6 +24,7 @@ module I18n::Tasks
24
24
  # @return [Array<[String, Object]>] translated list
25
25
  def translate_pairs(list, opts)
26
26
  return [] if list.empty?
27
+
27
28
  opts = opts.dup
28
29
  key_pos = list.each_with_index.inject({}) { |idx, ((k, _v), i)| idx.update(k => i) }
29
30
  # copy reference keys as is, instead of translating
@@ -92,7 +93,7 @@ module I18n::Tasks
92
93
  end
93
94
  end
94
95
 
95
- INTERPOLATION_KEY_RE = /%\{[^}]+}/
96
+ INTERPOLATION_KEY_RE = /%\{[^}]+}/.freeze
96
97
  UNTRANSLATABLE_STRING = 'zxzxzx'
97
98
 
98
99
  # @param [String] value
@@ -110,6 +111,7 @@ module I18n::Tasks
110
111
  # @return [String] 'hello, <round-trippable string>' => 'hello, %{name}'
111
112
  def restore_interpolations(untranslated, translated)
112
113
  return translated if untranslated !~ INTERPOLATION_KEY_RE
114
+
113
115
  values = untranslated.scan(INTERPOLATION_KEY_RE)
114
116
  translated.gsub(/#{Regexp.escape(UNTRANSLATABLE_STRING)}\d+/i) do |m|
115
117
  values[m[UNTRANSLATABLE_STRING.length..-1].to_i]
@@ -17,7 +17,12 @@ module I18n::Tasks::Translators
17
17
  protected
18
18
 
19
19
  def translate_values(list, from:, to:, **options)
20
- DeepL.translate(list, to_deepl_compatible_locale(from), to_deepl_compatible_locale(to), options).map(&:text)
20
+ result = DeepL.translate(list, to_deepl_compatible_locale(from), to_deepl_compatible_locale(to), options)
21
+ if result.is_a?(DeepL::Resources::Text)
22
+ [result.text]
23
+ else
24
+ result.map(&:text)
25
+ end
21
26
  end
22
27
 
23
28
  def options_for_translate_values(**options)
@@ -43,7 +48,8 @@ module I18n::Tasks::Translators
43
48
  # @return [String] 'hello, <i18n>%{name}</i18n>' => 'hello, %{name}'
44
49
  def restore_interpolations(untranslated, translated)
45
50
  return translated if untranslated !~ INTERPOLATION_KEY_RE
46
- translated.gsub(%r{<\/?i18n>}, '')
51
+
52
+ translated.gsub(%r{</?i18n>}, '')
47
53
  rescue StandardError => e
48
54
  raise_interpolation_error(untranslated, translated, e)
49
55
  end
@@ -62,6 +68,7 @@ module I18n::Tasks::Translators
62
68
  def configure_api_key!
63
69
  api_key = @i18n_tasks.translation_config[:deepl_api_key]
64
70
  fail ::I18n::Tasks::CommandError, I18n.t('i18n_tasks.deepl_translate.errors.no_api_key') if api_key.blank?
71
+
65
72
  DeepL.configure { |config| config.auth_key = api_key }
66
73
  end
67
74
  end
@@ -46,6 +46,7 @@ module I18n::Tasks::Translators
46
46
  # Convert 'es-ES' to 'es'
47
47
  def to_google_translate_compatible_locale(locale)
48
48
  return locale unless locale.include?('-') && !SUPPORTED_LOCALES_WITH_REGION.include?(locale)
49
+
49
50
  locale.split('-', 2).first
50
51
  end
51
52
 
@@ -60,6 +61,7 @@ module I18n::Tasks::Translators
60
61
  key ||= translation_config[:api_key]
61
62
  end
62
63
  fail ::I18n::Tasks::CommandError, I18n.t('i18n_tasks.google_translate.errors.no_api_key') if key.blank?
64
+
63
65
  key
64
66
  end
65
67
  end
@@ -43,6 +43,7 @@ module I18n::Tasks::Translators
43
43
  # Convert 'es-ES' to 'es'
44
44
  def to_yandex_compatible_locale(locale)
45
45
  return locale unless locale.include?('-')
46
+
46
47
  locale.split('-', 2).first
47
48
  end
48
49
 
@@ -54,6 +55,7 @@ module I18n::Tasks::Translators
54
55
  @api_key ||= begin
55
56
  key = @i18n_tasks.translation_config[:yandex_api_key]
56
57
  fail ::I18n::Tasks::CommandError, I18n.t('i18n_tasks.yandex_translate.errors.no_api_key') if key.blank?
58
+
57
59
  key
58
60
  end
59
61
  end
@@ -18,14 +18,15 @@ module I18n::Tasks
18
18
  paths: %w[app/].freeze,
19
19
  relative_roots: %w[app/controllers app/helpers app/mailers app/presenters app/views].freeze,
20
20
  scanners: [
21
- ['::I18n::Tasks::Scanners::RubyAstScanner', only: %w[*.rb]],
22
- ['::I18n::Tasks::Scanners::PatternWithScopeScanner', exclude: %w[*.rb]]
21
+ ['::I18n::Tasks::Scanners::RubyAstScanner', { only: %w[*.rb] }],
22
+ ['::I18n::Tasks::Scanners::PatternWithScopeScanner', { exclude: %w[*.rb] }]
23
23
  ],
24
24
  strict: true
25
25
  }.freeze
26
26
 
27
27
  ALWAYS_EXCLUDE = %w[*.jpg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less
28
- *.yml *.json *.zip *.tar.gz *.swf *.flv *.mp3 *.wav *.flac *.webm *.mp4 *.ogg *.opus].freeze
28
+ *.yml *.json *.zip *.tar.gz *.swf *.flv *.mp3 *.wav *.flac *.webm *.mp4 *.ogg *.opus
29
+ *.webp].freeze
29
30
 
30
31
  # Find all keys in the source and return a forest with the keys in absolute form and their occurrences.
31
32
  #
@@ -55,8 +56,8 @@ module I18n::Tasks
55
56
  keys = keys.select { |k| k.key =~ key_filter_re }
56
57
  end
57
58
  Data::Tree::Node.new(
58
- key: 'used',
59
- data: { key_filter: key_filter },
59
+ key: 'used',
60
+ data: { key_filter: key_filter },
60
61
  children: Data::Tree::Siblings.from_key_occurrences(keys)
61
62
  ).to_siblings
62
63
  end
@@ -72,10 +73,11 @@ module I18n::Tasks
72
73
  if args && args[:strict]
73
74
  fail CommandError, 'the strict option is global and cannot be applied on the scanner level'
74
75
  end
76
+
75
77
  ActiveSupport::Inflector.constantize(class_name).new(
76
- config: merge_scanner_configs(shared_options, args || {}),
78
+ config: merge_scanner_configs(shared_options, args || {}),
77
79
  file_finder_provider: caching_file_finder_provider,
78
- file_reader: caching_file_reader
80
+ file_reader: caching_file_reader
79
81
  )
80
82
  end.tap { |scanners| log_verbose { scanners.map { |s| " #{s.class.name} #{s.config.inspect}" } * "\n" } }
81
83
  )
@@ -123,7 +125,7 @@ module I18n::Tasks
123
125
 
124
126
  # @return [Boolean] whether the key is potentially used in a code expression such as `t("category.#{category_key}")`
125
127
  def used_in_expr?(key)
126
- !!(key =~ expr_key_re) # rubocop:disable Style/DoubleNegation
128
+ !!(key =~ expr_key_re)
127
129
  end
128
130
 
129
131
  private
@@ -139,12 +141,13 @@ module I18n::Tasks
139
141
  def expr_key_re(replacement: ':')
140
142
  @expr_key_re ||= begin
141
143
  # disallow patterns with no keys
142
- ignore_pattern_re = /\A[\.#{replacement}]*\z/
144
+ ignore_pattern_re = /\A[.#{replacement}]*\z/
143
145
  patterns = used_in_source_tree(strict: false).key_names.select do |k|
144
146
  k.end_with?('.') || k =~ /\#{/
145
147
  end.map do |k|
146
148
  pattern = "#{replace_key_exp(k, replacement)}#{replacement if k.end_with?('.')}"
147
149
  next if pattern =~ ignore_pattern_re
150
+
148
151
  pattern
149
152
  end.compact
150
153
  compile_key_pattern "{#{patterns * ','}}"
@@ -160,10 +163,11 @@ module I18n::Tasks
160
163
  braces = []
161
164
  result = []
162
165
  while (match_until = scanner.scan_until(/(?:#?\{|})/))
163
- if scanner.matched == '#{'
166
+ case scanner.matched
167
+ when '#{'
164
168
  braces << scanner.matched
165
169
  result << match_until[0..-3] if braces.length == 1
166
- elsif scanner.matched == '}'
170
+ when '}'
167
171
  prev_brace = braces.pop
168
172
  result << replacement if braces.empty? && prev_brace == '#{'
169
173
  else
@@ -2,6 +2,6 @@
2
2
 
3
3
  module I18n
4
4
  module Tasks
5
- VERSION = '0.9.31'
5
+ VERSION = '0.9.34'
6
6
  end
7
7
  end
@@ -32,7 +32,7 @@ data:
32
32
  # This data is not considered unused and is never written to.
33
33
  external:
34
34
  ## Example (replace %#= with %=):
35
- # - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
35
+ # - "<%#= %x[bundle info vagrant --path].chomp %>/templates/locales/%{locale}.yml"
36
36
 
37
37
  ## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, or a custom class.
38
38
  # router: conservative_router
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.31
4
+ version: 0.9.34
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-10 00:00:00.000000000 Z
11
+ date: 2021-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -210,14 +210,14 @@ dependencies:
210
210
  requirements:
211
211
  - - "~>"
212
212
  - !ruby/object:Gem::Version
213
- version: 0.53.0
213
+ version: 1.6.1
214
214
  type: :development
215
215
  prerelease: false
216
216
  version_requirements: !ruby/object:Gem::Requirement
217
217
  requirements:
218
218
  - - "~>"
219
219
  - !ruby/object:Gem::Version
220
- version: 0.53.0
220
+ version: 1.6.1
221
221
  - !ruby/object:Gem::Dependency
222
222
  name: simplecov
223
223
  requirement: !ruby/object:Gem::Requirement
@@ -404,16 +404,19 @@ require_paths:
404
404
  - lib
405
405
  required_ruby_version: !ruby/object:Gem::Requirement
406
406
  requirements:
407
- - - "~>"
407
+ - - ">="
408
+ - !ruby/object:Gem::Version
409
+ version: '2.5'
410
+ - - "<"
408
411
  - !ruby/object:Gem::Version
409
- version: '2.3'
412
+ version: '4.0'
410
413
  required_rubygems_version: !ruby/object:Gem::Requirement
411
414
  requirements:
412
415
  - - ">="
413
416
  - !ruby/object:Gem::Version
414
417
  version: '0'
415
418
  requirements: []
416
- rubygems_version: 3.0.3
419
+ rubygems_version: 3.1.2
417
420
  signing_key:
418
421
  specification_version: 4
419
422
  summary: Manage localization and translation with the awesome power of static analysis