i18n-tasks 1.0.7 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -4
- data/i18n-tasks.gemspec +7 -4
- data/lib/i18n/tasks/cli.rb +4 -4
- data/lib/i18n/tasks/command/options/common.rb +0 -1
- data/lib/i18n/tasks/command/options/data.rb +1 -1
- data/lib/i18n/tasks/configuration.rb +11 -6
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +2 -2
- data/lib/i18n/tasks/data/file_formats.rb +1 -1
- data/lib/i18n/tasks/data/router/pattern_router.rb +1 -1
- data/lib/i18n/tasks/data/tree/node.rb +1 -1
- data/lib/i18n/tasks/data/tree/nodes.rb +5 -7
- data/lib/i18n/tasks/data/tree/siblings.rb +1 -1
- data/lib/i18n/tasks/html_keys.rb +2 -2
- data/lib/i18n/tasks/references.rb +2 -2
- data/lib/i18n/tasks/reports/base.rb +1 -1
- data/lib/i18n/tasks/reports/terminal.rb +2 -2
- data/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb +7 -3
- data/lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb +69 -0
- data/lib/i18n/tasks/scanners/pattern_mapper.rb +1 -1
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +1 -1
- data/lib/i18n/tasks/scanners/relative_keys.rb +2 -2
- data/lib/i18n/tasks/scanners/results/occurrence.rb +1 -1
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +8 -3
- data/lib/i18n/tasks/scanners/ruby_key_literals.rb +2 -2
- data/lib/i18n/tasks/split_key.rb +1 -1
- data/lib/i18n/tasks/translators/base_translator.rb +1 -1
- data/lib/i18n/tasks/translators/deepl_translator.rb +2 -2
- data/lib/i18n/tasks/used_keys.rb +2 -1
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/i18n/tasks.rb +11 -0
- data/templates/config/i18n-tasks.yml +9 -0
- metadata +16 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1ae937151395c0958e2cab91b89bca5b4b97fb12d307940a9e75bb005f41e2e
|
4
|
+
data.tar.gz: 7dd9cbac5bc49401c15df858adae478c63d49f69e91c3a34b8ee7732793efb4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b918580b7971a2843bf170277195e9bb59019b2040d2a8403f400ef41e718b25d5545b29a2b8b34f050db753d2f9067bc9a8b892c3b2c491403d80ecf0dfab6
|
7
|
+
data.tar.gz: 254c3e547b8d06b684900f1dc3551ed1f52079a6623e5f572fb9bf9ec31c4eaa30b43eb9b2e0aeecd5287b69643a0ed23976bdd8886b42c5b7625bf879fb62ab
|
data/README.md
CHANGED
@@ -24,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
|
|
24
24
|
Add i18n-tasks to the Gemfile:
|
25
25
|
|
26
26
|
```ruby
|
27
|
-
gem 'i18n-tasks', '~> 1.0.
|
27
|
+
gem 'i18n-tasks', '~> 1.0.10'
|
28
28
|
```
|
29
29
|
|
30
30
|
Copy the default [configuration file](#configuration):
|
@@ -355,7 +355,7 @@ If you have implemented a custom adapter please share it on [the wiki][wiki].
|
|
355
355
|
|
356
356
|
### Usage search
|
357
357
|
|
358
|
-
i18n-tasks uses an AST scanner for `.rb` files, and a regexp scanner for all other files.
|
358
|
+
i18n-tasks uses an AST scanner for `.rb` and `.html.erb` files, and a regexp scanner for all other files.
|
359
359
|
New scanners can be added easily: please refer to [this example](https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example).
|
360
360
|
|
361
361
|
See the `search` section in the [config file][config] for all available configuration options.
|
@@ -436,15 +436,28 @@ See [i18n-tasks wiki: CSV import and export tasks](https://github.com/glebm/i18n
|
|
436
436
|
Tasks that come with the gem are defined in [lib/i18n/tasks/command/commands](lib/i18n/tasks/command/commands).
|
437
437
|
Custom tasks can be added easily, see the examples [on the wiki](https://github.com/glebm/i18n-tasks/wiki#custom-tasks).
|
438
438
|
|
439
|
+
# Development
|
440
|
+
|
441
|
+
- Install dependencies using `bundle install`
|
442
|
+
- Run tests using `bundle exec rspec`
|
443
|
+
- Install [Overcommit](overcommit) by running `overcommit --install`
|
444
|
+
|
445
|
+
## Skip Overcommit-hooks
|
446
|
+
|
447
|
+
- `SKIP=RuboCop git commit`
|
448
|
+
- `OVERCOMMIT_DISABLE=1 git commit`
|
449
|
+
|
450
|
+
|
439
451
|
[MIT license]: /LICENSE.txt
|
440
452
|
[ci]: https://github.com/glebm/i18n-tasks/actions/workflows/tests.yml
|
441
453
|
[badge-ci]: https://github.com/glebm/i18n-tasks/actions/workflows/tests.yml/badge.svg
|
442
454
|
[coverage]: https://codeclimate.com/github/glebm/i18n-tasks
|
443
455
|
[badge-coverage]: https://api.codeclimate.com/v1/badges/5d173e90ada8df07cedc/test_coverage
|
444
|
-
[config]: https://github.com/glebm/i18n-tasks/blob/
|
456
|
+
[config]: https://github.com/glebm/i18n-tasks/blob/main/templates/config/i18n-tasks.yml
|
445
457
|
[wiki]: https://github.com/glebm/i18n-tasks/wiki "i18n-tasks wiki"
|
446
458
|
[i18n-gem]: https://github.com/svenfuchs/i18n "svenfuchs/i18n on Github"
|
447
459
|
[screenshot-i18n-tasks]: https://i.imgur.com/XZBd8l7.png "i18n-tasks screenshot"
|
448
460
|
[screenshot-find]: https://i.imgur.com/VxBrSfY.png "i18n-tasks find output screenshot"
|
449
|
-
[adapter-example]: https://github.com/glebm/i18n-tasks/blob/
|
461
|
+
[adapter-example]: https://github.com/glebm/i18n-tasks/blob/main/lib/i18n/tasks/data/file_system_base.rb
|
450
462
|
[custom-scanner-docs]: https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example
|
463
|
+
[overcommit]: https://github.com/sds/overcommit#installation
|
data/i18n-tasks.gemspec
CHANGED
@@ -24,8 +24,11 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength
|
|
24
24
|
cp $(bundle exec i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
|
25
25
|
TEXT
|
26
26
|
s.homepage = 'https://github.com/glebm/i18n-tasks'
|
27
|
-
s.metadata = {
|
28
|
-
|
27
|
+
s.metadata = {
|
28
|
+
'issue_tracker' => 'https://github.com/glebm/i18n-tasks',
|
29
|
+
'rubygems_mfa_required' => 'true'
|
30
|
+
}
|
31
|
+
s.required_ruby_version = '>= 2.6', '< 4.0'
|
29
32
|
|
30
33
|
s.files = `git ls-files`.split($/)
|
31
34
|
s.files -= s.files.grep(%r{^(doc/|\.|spec/)}) + %w[CHANGES.md config/i18n-tasks.yml Gemfile]
|
@@ -43,11 +46,11 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength
|
|
43
46
|
s.add_dependency 'rails-i18n'
|
44
47
|
s.add_dependency 'rainbow', '>= 2.2.2', '< 4.0'
|
45
48
|
s.add_dependency 'terminal-table', '>= 1.5.1'
|
46
|
-
s.add_development_dependency 'axlsx', '~> 2.0'
|
47
49
|
s.add_development_dependency 'bundler', '~> 2.0', '>= 2.0.1'
|
50
|
+
s.add_development_dependency 'overcommit', '~> 0.58.0'
|
48
51
|
s.add_development_dependency 'rake'
|
49
52
|
s.add_development_dependency 'rspec', '~> 3.3'
|
50
|
-
s.add_development_dependency 'rubocop', '~> 1.
|
53
|
+
s.add_development_dependency 'rubocop', '~> 1.27.0'
|
51
54
|
s.add_development_dependency 'simplecov'
|
52
55
|
s.add_development_dependency 'yard'
|
53
56
|
|
data/lib/i18n/tasks/cli.rb
CHANGED
@@ -35,12 +35,12 @@ class I18n::Tasks::CLI
|
|
35
35
|
|
36
36
|
def run(argv)
|
37
37
|
argv.each_with_index do |arg, i|
|
38
|
-
if [
|
39
|
-
if File.exist?(argv[i+1])
|
40
|
-
@config_file = argv[i+1]
|
38
|
+
if ['--config', '-c'].include?(arg)
|
39
|
+
if File.exist?(argv[i + 1])
|
40
|
+
@config_file = argv[i + 1]
|
41
41
|
break
|
42
42
|
else
|
43
|
-
error "Config file doesn't exist: #{argv[i+1]}", 128
|
43
|
+
error "Config file doesn't exist: #{argv[i + 1]}", 128
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -86,7 +86,7 @@ module I18n::Tasks
|
|
86
86
|
when 'keys'
|
87
87
|
puts forest.key_names(root: true)
|
88
88
|
when 'key-values'
|
89
|
-
puts
|
89
|
+
puts(forest.key_values(root: true).map { |kv| kv.join("\t") })
|
90
90
|
when *DATA_FORMATS
|
91
91
|
puts i18n.data.adapter_dump forest.to_hash(true), format
|
92
92
|
end
|
@@ -30,6 +30,13 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
30
30
|
warn_deprecated 'Please move relative_roots under search in config/i18n-tasks.yml.'
|
31
31
|
c[:search][:relative_roots] = c.delete(:relative_roots)
|
32
32
|
end
|
33
|
+
|
34
|
+
if c.dig(:search, :exclude_method_name_paths)
|
35
|
+
warn_deprecated(
|
36
|
+
'Please rename exclude_method_name_paths to relative_exclude_method_name_paths in config/i18n-tasks.yml.'
|
37
|
+
)
|
38
|
+
c[:search][:relative_exclude_method_name_paths] = c[:search].delete(:exclude_method_name_paths)
|
39
|
+
end
|
33
40
|
end
|
34
41
|
else
|
35
42
|
{}.with_indifferent_access
|
@@ -44,12 +51,10 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
44
51
|
# data config
|
45
52
|
# @return [Hash<adapter: String, options: Hash>]
|
46
53
|
def data_config
|
47
|
-
@config_sections[:data] ||=
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
}
|
52
|
-
end
|
54
|
+
@config_sections[:data] ||= {
|
55
|
+
adapter: data.class.name,
|
56
|
+
config: data.config
|
57
|
+
}
|
53
58
|
end
|
54
59
|
|
55
60
|
# translation config
|
@@ -5,7 +5,7 @@ module I18n::Tasks
|
|
5
5
|
module Data
|
6
6
|
module Adapter
|
7
7
|
module YamlAdapter
|
8
|
-
EMOJI_REGEX = /\\u[\da-f]{8}/i
|
8
|
+
EMOJI_REGEX = /\\u[\da-f]{8}/i.freeze
|
9
9
|
|
10
10
|
class << self
|
11
11
|
# @return [Hash] locale tree
|
@@ -25,7 +25,7 @@ module I18n::Tasks
|
|
25
25
|
|
26
26
|
# @return [String]
|
27
27
|
def restore_emojis(yaml)
|
28
|
-
yaml.gsub(EMOJI_REGEX) { |m| [m[-8..].to_i(16)].pack(
|
28
|
+
yaml.gsub(EMOJI_REGEX) { |m| [m[-8..].to_i(16)].pack('U') }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -38,7 +38,7 @@ module I18n::Tasks
|
|
38
38
|
if pattern
|
39
39
|
key_match = $~
|
40
40
|
path = format(path, locale: locale)
|
41
|
-
path.gsub!(/\\\d+/) { |m| key_match[m[1
|
41
|
+
path.gsub!(/\\\d+/) { |m| key_match[m[1..].to_i] }
|
42
42
|
(out[path] ||= Set.new) << "#{locale}.#{key}"
|
43
43
|
else
|
44
44
|
fail CommandError, "Cannot route key #{key}. Routes are #{@routes_config.inspect}"
|
@@ -168,7 +168,7 @@ module I18n::Tasks::Data::Tree
|
|
168
168
|
label = if key.nil?
|
169
169
|
Rainbow('∅').faint
|
170
170
|
else
|
171
|
-
[Rainbow(key).color(1 + level % 15),
|
171
|
+
[Rainbow(key).color(1 + (level % 15)),
|
172
172
|
(": #{format_value_for_inspect(value)}" if leaf?),
|
173
173
|
(" #{data}" if data?)].compact.join
|
174
174
|
end
|
@@ -31,13 +31,11 @@ module I18n::Tasks::Data::Tree
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def to_hash(sort = false)
|
34
|
-
(@hash ||= {})[sort] ||=
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end.map { |node| node.to_hash(sort) }.reduce({}, :deep_merge!)
|
40
|
-
end
|
34
|
+
(@hash ||= {})[sort] ||= if sort
|
35
|
+
sort_by(&:key)
|
36
|
+
else
|
37
|
+
self
|
38
|
+
end.map { |node| node.to_hash(sort) }.reduce({}, :deep_merge!)
|
41
39
|
end
|
42
40
|
|
43
41
|
delegate :to_json, to: :to_hash
|
@@ -50,7 +50,7 @@ module I18n::Tasks::Data::Tree
|
|
50
50
|
next
|
51
51
|
end
|
52
52
|
match = $~
|
53
|
-
new_key = to_pattern.gsub(/\\\d+/) { |m| match[m[1
|
53
|
+
new_key = to_pattern.gsub(/\\\d+/) { |m| match[m[1..].to_i] }
|
54
54
|
old_key_to_new_key[full_key] = new_key
|
55
55
|
moved_forest.merge!(Siblings.new.tap do |forest|
|
56
56
|
forest[[(node.root.try(:key) unless root), new_key].compact.join('.')] =
|
data/lib/i18n/tasks/html_keys.rb
CHANGED
@@ -7,8 +7,8 @@ module I18n::Tasks
|
|
7
7
|
|
8
8
|
def html_key?(full_key, locale)
|
9
9
|
!!(full_key =~ HTML_KEY_PATTERN ||
|
10
|
-
full_key =~ MAYBE_PLURAL_HTML_KEY_PATTERN &&
|
11
|
-
depluralize_key(split_key(full_key, 2)[1], locale) =~ HTML_KEY_PATTERN)
|
10
|
+
(full_key =~ MAYBE_PLURAL_HTML_KEY_PATTERN &&
|
11
|
+
depluralize_key(split_key(full_key, 2)[1], locale) =~ HTML_KEY_PATTERN))
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -91,8 +91,8 @@ module I18n::Tasks
|
|
91
91
|
if node.value != other.value
|
92
92
|
log_warn(
|
93
93
|
'Conflicting references: '\
|
94
|
-
|
95
|
-
|
94
|
+
"#{node.full_key(root: false)} ⮕ #{node.value} in #{node.data[:locale]},"\
|
95
|
+
" but ⮕ #{other.value} in #{other.data[:locale]}"
|
96
96
|
)
|
97
97
|
end
|
98
98
|
end
|
@@ -37,7 +37,7 @@ module I18n::Tasks::Reports
|
|
37
37
|
def used_title(keys_nodes, filter)
|
38
38
|
used_n = keys_nodes.map { |_k, node| node.data[:occurrences].size }.reduce(:+).to_i
|
39
39
|
"#{keys_nodes.size} key#{'s' if keys_nodes.size != 1}#{" matching '#{filter}'" if filter}"\
|
40
|
-
|
40
|
+
"#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n.positive?}"
|
41
41
|
end
|
42
42
|
|
43
43
|
# Sort keys by their attributes in order
|
@@ -121,9 +121,9 @@ module I18n
|
|
121
121
|
if data[:ref_info]
|
122
122
|
from, to = data[:ref_info]
|
123
123
|
resolved = key[0...to.length]
|
124
|
-
after = key[to.length
|
124
|
+
after = key[to.length..]
|
125
125
|
" #{Rainbow(from).yellow}#{Rainbow(after).cyan}\n" \
|
126
|
-
|
126
|
+
"#{Rainbow('⮕').yellow.bright} #{Rainbow(resolved).yellow.bright}"
|
127
127
|
else
|
128
128
|
Rainbow(key).cyan
|
129
129
|
end
|
@@ -6,6 +6,10 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
6
6
|
@scanner = scanner
|
7
7
|
end
|
8
8
|
|
9
|
+
def convert_to_key_occurrences(send_node, _method_name, location: send_node.loc)
|
10
|
+
fail('Not implemented')
|
11
|
+
end
|
12
|
+
|
9
13
|
protected
|
10
14
|
|
11
15
|
# If the node type is of `%i(sym str int false true)`, return the value as a string.
|
@@ -20,7 +24,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
20
24
|
# No effect unless `array_join_with` is set.
|
21
25
|
# @return [String, nil] `nil` is returned only when a dynamic value is encountered in strict mode
|
22
26
|
# or the node type is not supported.
|
23
|
-
def extract_string(node, array_join_with: nil, array_flatten: false, array_reject_blank: false)
|
27
|
+
def extract_string(node, array_join_with: nil, array_flatten: false, array_reject_blank: false) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
24
28
|
return if node.nil?
|
25
29
|
|
26
30
|
if %i[sym str int].include?(node.type)
|
@@ -39,7 +43,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
39
43
|
# `nil` is returned when a dynamic value is encountered in strict mode. Propagate:
|
40
44
|
return nil if str.nil?
|
41
45
|
end
|
42
|
-
elsif !@scanner
|
46
|
+
elsif !@scanner.config[:strict] && %i[dsym dstr].include?(node.type)
|
43
47
|
node.children.map do |child|
|
44
48
|
if %i[sym str].include?(child.type)
|
45
49
|
child.children[0].to_s
|
@@ -77,7 +81,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
77
81
|
extract_string child
|
78
82
|
else
|
79
83
|
# ignore dynamic argument in strict mode
|
80
|
-
return nil if config[:strict]
|
84
|
+
return nil if @scanner.config[:strict]
|
81
85
|
|
82
86
|
if %i[dsym dstr].include?(child.type) || (child.type == :array && array_flatten)
|
83
87
|
extract_string(child, array_join_with: array_join_with)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'i18n/tasks/scanners/results/occurrence'
|
4
|
+
|
5
|
+
module I18n::Tasks::Scanners::AstMatchers
|
6
|
+
class RailsModelMatcher < BaseMatcher
|
7
|
+
def convert_to_key_occurrences(send_node, _method_name, location: send_node.loc)
|
8
|
+
human_attribute_name_to_key_occurences(send_node: send_node, location: location) ||
|
9
|
+
model_name_human_to_key_occurences(send_node: send_node, location: location)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def human_attribute_name_to_key_occurences(send_node:, location:)
|
15
|
+
children = Array(send_node&.children)
|
16
|
+
receiver = children[0]
|
17
|
+
method_name = children[1]
|
18
|
+
|
19
|
+
return unless method_name == :human_attribute_name && receiver.type == :const
|
20
|
+
|
21
|
+
value = children[2]
|
22
|
+
|
23
|
+
model_name = underscore(receiver.to_a.last)
|
24
|
+
attribute = extract_string(value)
|
25
|
+
key = "activerecord.attributes.#{model_name}.#{attribute}"
|
26
|
+
[
|
27
|
+
key,
|
28
|
+
I18n::Tasks::Scanners::Results::Occurrence.from_range(
|
29
|
+
raw_key: key,
|
30
|
+
range: location.expression
|
31
|
+
)
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
# User.model_name.human(count: 2)
|
36
|
+
# s(:send,
|
37
|
+
# s(:send,
|
38
|
+
# s(:const, nil, :User), :model_name), :human,
|
39
|
+
# s(:hash,
|
40
|
+
# s(:pair,
|
41
|
+
# s(:sym, :count),
|
42
|
+
# s(:int, 2))))
|
43
|
+
def model_name_human_to_key_occurences(send_node:, location:)
|
44
|
+
children = Array(send_node&.children)
|
45
|
+
return unless children[1] == :human
|
46
|
+
|
47
|
+
base_children = Array(children[0]&.children)
|
48
|
+
class_node = base_children[0]
|
49
|
+
|
50
|
+
return unless class_node&.type == :const && base_children[1] == :model_name
|
51
|
+
|
52
|
+
model_name = underscore(class_node.to_a.last)
|
53
|
+
key = "activerecord.models.#{model_name}"
|
54
|
+
[
|
55
|
+
key,
|
56
|
+
I18n::Tasks::Scanners::Results::Occurrence.from_range(
|
57
|
+
raw_key: key,
|
58
|
+
range: location.expression
|
59
|
+
)
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
def underscore(value)
|
64
|
+
value = value.dup.to_s
|
65
|
+
value.gsub!(/(.)([A-Z])/, '\1_\2')
|
66
|
+
value.downcase!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -35,7 +35,7 @@ module I18n::Tasks::Scanners
|
|
35
35
|
result = []
|
36
36
|
text.scan(pattern) do |_|
|
37
37
|
match = Regexp.last_match
|
38
|
-
matches =
|
38
|
+
matches = match.names.map(&:to_sym).zip(match.captures).to_h
|
39
39
|
if matches.key?(:key)
|
40
40
|
matches[:key] = strip_literal(matches[:key])
|
41
41
|
next unless valid_key?(matches[:key])
|
@@ -10,7 +10,7 @@ module I18n
|
|
10
10
|
# @param calling_method [#call, Symbol, String, false, nil]
|
11
11
|
# @return [String] absolute version of the key
|
12
12
|
def absolute_key(key, path, roots: config[:relative_roots],
|
13
|
-
exclude_method_name_paths: config[:
|
13
|
+
exclude_method_name_paths: config[:relative_exclude_method_name_paths],
|
14
14
|
calling_method: nil)
|
15
15
|
return key unless key.start_with?(DOT)
|
16
16
|
fail 'roots argument is required' unless roots.present?
|
@@ -18,7 +18,7 @@ module I18n
|
|
18
18
|
normalized_path = File.expand_path(path)
|
19
19
|
(root = path_root(normalized_path, roots)) ||
|
20
20
|
fail(CommandError, "Cannot resolve relative key \"#{key}\".\n" \
|
21
|
-
|
21
|
+
"Set search.relative_roots in config/i18n-tasks.yml (currently #{roots.inspect})")
|
22
22
|
normalized_path.sub!(root, '')
|
23
23
|
|
24
24
|
if (exclude_method_name_paths || []).map { |p| expand_path(p) }.include?(root)
|
@@ -48,7 +48,7 @@ module I18n::Tasks
|
|
48
48
|
# rubocop:enable Metrics/ParameterLists
|
49
49
|
|
50
50
|
def inspect
|
51
|
-
"Occurrence(#{@path}:#{@line_num}, line_pos: #{@line_pos}, pos: #{@pos}, raw_key: #{@raw_key}, default_arg: #{@default_arg}, line: #{@line})"
|
51
|
+
"Occurrence(#{@path}:#{@line_num}, line_pos: #{@line_pos}, pos: #{@pos}, raw_key: #{@raw_key}, default_arg: #{@default_arg}, line: #{@line})" # rubocop:disable Layout/LineLength
|
52
52
|
end
|
53
53
|
|
54
54
|
def ==(other)
|
@@ -68,8 +68,7 @@ module I18n::Tasks::Scanners
|
|
68
68
|
def comments_to_occurences(path, ast, comments)
|
69
69
|
magic_comments = comments.select { |comment| comment.text =~ MAGIC_COMMENT_PREFIX }
|
70
70
|
comment_to_node = Parser::Source::Comment.associate_locations(ast, magic_comments).tap do |h|
|
71
|
-
|
72
|
-
h.each { |k, v| h[k] = v.first }
|
71
|
+
h.transform_values!(&:first)
|
73
72
|
end.invert
|
74
73
|
|
75
74
|
magic_comments.flat_map do |comment|
|
@@ -122,7 +121,7 @@ module I18n::Tasks::Scanners
|
|
122
121
|
)
|
123
122
|
end
|
124
123
|
else
|
125
|
-
%i[t t! translate translate!].map do |message|
|
124
|
+
matchers = %i[t t! translate translate!].map do |message|
|
126
125
|
AstMatchers::MessageReceiversMatcher.new(
|
127
126
|
receivers: [
|
128
127
|
AST::Node.new(:const, [nil, :I18n]),
|
@@ -132,6 +131,12 @@ module I18n::Tasks::Scanners
|
|
132
131
|
scanner: self
|
133
132
|
)
|
134
133
|
end
|
134
|
+
|
135
|
+
Array(config[:ast_matchers]).each do |class_name|
|
136
|
+
matchers << ActiveSupport::Inflector.constantize(class_name).new(scanner: self)
|
137
|
+
end
|
138
|
+
|
139
|
+
matchers
|
135
140
|
end
|
136
141
|
end
|
137
142
|
end
|
@@ -15,12 +15,12 @@ module I18n::Tasks::Scanners
|
|
15
15
|
# @param literal [String] e.g: "key", 'key', or :key.
|
16
16
|
# @return [String] key
|
17
17
|
def strip_literal(literal)
|
18
|
-
literal = literal[1
|
18
|
+
literal = literal[1..] if literal[0] == ':'
|
19
19
|
literal = literal[1..-2] if literal[0] == "'" || literal[0] == '"'
|
20
20
|
literal
|
21
21
|
end
|
22
22
|
|
23
|
-
VALID_KEY_CHARS =
|
23
|
+
VALID_KEY_CHARS = %r{(?:[[:word:]]|[-.?!:;À-ž/])}.freeze
|
24
24
|
VALID_KEY_RE = /^#{VALID_KEY_CHARS}+$/.freeze
|
25
25
|
|
26
26
|
def valid_key?(key)
|
data/lib/i18n/tasks/split_key.rb
CHANGED
@@ -114,7 +114,7 @@ module I18n::Tasks
|
|
114
114
|
|
115
115
|
values = untranslated.scan(INTERPOLATION_KEY_RE)
|
116
116
|
translated.gsub(/#{Regexp.escape(UNTRANSLATABLE_STRING)}\d+/i) do |m|
|
117
|
-
values[m[UNTRANSLATABLE_STRING.length
|
117
|
+
values[m[UNTRANSLATABLE_STRING.length..].to_i]
|
118
118
|
end
|
119
119
|
rescue StandardError => e
|
120
120
|
raise_interpolation_error(untranslated, translated, e)
|
@@ -71,11 +71,11 @@ module I18n::Tasks::Translators
|
|
71
71
|
version = @i18n_tasks.translation_config[:deepl_version]
|
72
72
|
fail ::I18n::Tasks::CommandError, I18n.t('i18n_tasks.deepl_translate.errors.no_api_key') if api_key.blank?
|
73
73
|
|
74
|
-
DeepL.configure
|
74
|
+
DeepL.configure do |config|
|
75
75
|
config.auth_key = api_key
|
76
76
|
config.host = host unless host.blank?
|
77
77
|
config.version = version unless version.blank?
|
78
|
-
|
78
|
+
end
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
data/lib/i18n/tasks/used_keys.rb
CHANGED
@@ -24,6 +24,7 @@ module I18n::Tasks
|
|
24
24
|
['::I18n::Tasks::Scanners::ErbAstScanner', { only: %w[*.erb] }],
|
25
25
|
['::I18n::Tasks::Scanners::PatternWithScopeScanner', { exclude: %w[*.erb *.rb] }]
|
26
26
|
],
|
27
|
+
ast_matchers: [],
|
27
28
|
strict: true
|
28
29
|
}.freeze
|
29
30
|
|
@@ -177,7 +178,7 @@ module I18n::Tasks
|
|
177
178
|
braces << '{'
|
178
179
|
end
|
179
180
|
end
|
180
|
-
result << key[scanner.pos
|
181
|
+
result << key[scanner.pos..] unless scanner.eos?
|
181
182
|
result.join
|
182
183
|
end
|
183
184
|
end
|
data/lib/i18n/tasks/version.rb
CHANGED
data/lib/i18n/tasks.rb
CHANGED
@@ -34,6 +34,17 @@ module I18n
|
|
34
34
|
::I18n::Tasks::Commands.send :include, commands_module
|
35
35
|
self
|
36
36
|
end
|
37
|
+
|
38
|
+
# Add AST-matcher to i18n-tasks
|
39
|
+
#
|
40
|
+
# @param matcher_class_name
|
41
|
+
# @return self
|
42
|
+
def add_ast_matcher(matcher_class_name)
|
43
|
+
matchers = I18n::Tasks::Configuration::DEFAULTS[:search][:ast_matchers]
|
44
|
+
matchers << matcher_class_name
|
45
|
+
matchers.uniq!
|
46
|
+
self
|
47
|
+
end
|
37
48
|
end
|
38
49
|
|
39
50
|
@verbose = !ENV['VERBOSE'].nil?
|
@@ -85,6 +85,15 @@ search:
|
|
85
85
|
## If `strict` is `false`, guess usages such as t("categories.#{category}.title"). The default is `true`.
|
86
86
|
# strict: true
|
87
87
|
|
88
|
+
## Allows adding ast_matchers for finding translations using the AST-scanners
|
89
|
+
## The available matchers are:
|
90
|
+
## - RailsModelMatcher
|
91
|
+
## Matches ActiveRecord translations like
|
92
|
+
## User.human_attribute_name(:email) and User.model_name.human
|
93
|
+
##
|
94
|
+
## To implement your own, please see `I18n::Tasks::Scanners::AstMatchers::BaseMatcher`.
|
95
|
+
<%# I18n::Tasks.add_ast_matcher('I18n::Tasks::Scanners::AstMatchers::RailsModelMatcher') %>
|
96
|
+
|
88
97
|
## Multiple scanners can be used. Their results are merged.
|
89
98
|
## The options specified above are passed down to each scanner. Per-scanner options can be specified as well.
|
90
99
|
## See this example of a custom scanner: https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example
|
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: 1.0.
|
4
|
+
version: 1.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- glebm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -157,12 +157,15 @@ dependencies:
|
|
157
157
|
- !ruby/object:Gem::Version
|
158
158
|
version: 1.5.1
|
159
159
|
- !ruby/object:Gem::Dependency
|
160
|
-
name:
|
160
|
+
name: bundler
|
161
161
|
requirement: !ruby/object:Gem::Requirement
|
162
162
|
requirements:
|
163
163
|
- - "~>"
|
164
164
|
- !ruby/object:Gem::Version
|
165
165
|
version: '2.0'
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: 2.0.1
|
166
169
|
type: :development
|
167
170
|
prerelease: false
|
168
171
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -170,26 +173,23 @@ dependencies:
|
|
170
173
|
- - "~>"
|
171
174
|
- !ruby/object:Gem::Version
|
172
175
|
version: '2.0'
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: 2.0.1
|
173
179
|
- !ruby/object:Gem::Dependency
|
174
|
-
name:
|
180
|
+
name: overcommit
|
175
181
|
requirement: !ruby/object:Gem::Requirement
|
176
182
|
requirements:
|
177
183
|
- - "~>"
|
178
184
|
- !ruby/object:Gem::Version
|
179
|
-
version:
|
180
|
-
- - ">="
|
181
|
-
- !ruby/object:Gem::Version
|
182
|
-
version: 2.0.1
|
185
|
+
version: 0.58.0
|
183
186
|
type: :development
|
184
187
|
prerelease: false
|
185
188
|
version_requirements: !ruby/object:Gem::Requirement
|
186
189
|
requirements:
|
187
190
|
- - "~>"
|
188
191
|
- !ruby/object:Gem::Version
|
189
|
-
version:
|
190
|
-
- - ">="
|
191
|
-
- !ruby/object:Gem::Version
|
192
|
-
version: 2.0.1
|
192
|
+
version: 0.58.0
|
193
193
|
- !ruby/object:Gem::Dependency
|
194
194
|
name: rake
|
195
195
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,14 +224,14 @@ dependencies:
|
|
224
224
|
requirements:
|
225
225
|
- - "~>"
|
226
226
|
- !ruby/object:Gem::Version
|
227
|
-
version: 1.
|
227
|
+
version: 1.27.0
|
228
228
|
type: :development
|
229
229
|
prerelease: false
|
230
230
|
version_requirements: !ruby/object:Gem::Requirement
|
231
231
|
requirements:
|
232
232
|
- - "~>"
|
233
233
|
- !ruby/object:Gem::Version
|
234
|
-
version: 1.
|
234
|
+
version: 1.27.0
|
235
235
|
- !ruby/object:Gem::Dependency
|
236
236
|
name: simplecov
|
237
237
|
requirement: !ruby/object:Gem::Requirement
|
@@ -373,6 +373,7 @@ files:
|
|
373
373
|
- lib/i18n/tasks/reports/terminal.rb
|
374
374
|
- lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb
|
375
375
|
- lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb
|
376
|
+
- lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb
|
376
377
|
- lib/i18n/tasks/scanners/erb_ast_processor.rb
|
377
378
|
- lib/i18n/tasks/scanners/erb_ast_scanner.rb
|
378
379
|
- lib/i18n/tasks/scanners/file_scanner.rb
|
@@ -413,6 +414,7 @@ licenses:
|
|
413
414
|
- MIT
|
414
415
|
metadata:
|
415
416
|
issue_tracker: https://github.com/glebm/i18n-tasks
|
417
|
+
rubygems_mfa_required: 'true'
|
416
418
|
post_install_message: |
|
417
419
|
# Install default configuration:
|
418
420
|
cp $(bundle exec i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
|