i18n-tasks 1.0.14 → 1.1.0
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 +4 -4
- data/README.md +138 -39
- data/Rakefile +4 -4
- data/bin/i18n-tasks +3 -3
- data/config/locales/en.yml +17 -1
- data/config/locales/ru.yml +18 -1
- data/i18n-tasks.gemspec +28 -38
- data/lib/i18n/tasks/base_task.rb +19 -19
- data/lib/i18n/tasks/cli.rb +37 -30
- data/lib/i18n/tasks/command/collection.rb +4 -4
- data/lib/i18n/tasks/command/commander.rb +5 -5
- data/lib/i18n/tasks/command/commands/check_prism.rb +126 -0
- data/lib/i18n/tasks/command/commands/data.rb +33 -33
- data/lib/i18n/tasks/command/commands/eq_base.rb +3 -3
- data/lib/i18n/tasks/command/commands/health.rb +6 -5
- data/lib/i18n/tasks/command/commands/interpolations.rb +14 -3
- data/lib/i18n/tasks/command/commands/meta.rb +6 -6
- data/lib/i18n/tasks/command/commands/missing.rb +28 -26
- data/lib/i18n/tasks/command/commands/tree.rb +33 -33
- data/lib/i18n/tasks/command/commands/usages.rb +24 -24
- data/lib/i18n/tasks/command/dsl.rb +1 -1
- data/lib/i18n/tasks/command/option_parsers/enum.rb +8 -7
- data/lib/i18n/tasks/command/option_parsers/locale.rb +4 -4
- data/lib/i18n/tasks/command/options/common.rb +16 -16
- data/lib/i18n/tasks/command/options/data.rb +18 -18
- data/lib/i18n/tasks/command/options/locales.rb +33 -24
- data/lib/i18n/tasks/commands.rb +14 -12
- data/lib/i18n/tasks/concurrent/cache.rb +1 -1
- data/lib/i18n/tasks/concurrent/cached_value.rb +1 -1
- data/lib/i18n/tasks/configuration.rb +26 -20
- data/lib/i18n/tasks/console_context.rb +11 -11
- data/lib/i18n/tasks/data/adapter/json_adapter.rb +1 -1
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +5 -5
- data/lib/i18n/tasks/data/file_formats.rb +3 -3
- data/lib/i18n/tasks/data/file_system.rb +5 -5
- data/lib/i18n/tasks/data/file_system_base.rb +26 -26
- data/lib/i18n/tasks/data/language_names.rb +202 -0
- data/lib/i18n/tasks/data/router/conservative_router.rb +3 -3
- data/lib/i18n/tasks/data/router/isolating_router.rb +19 -19
- data/lib/i18n/tasks/data/router/pattern_router.rb +5 -5
- data/lib/i18n/tasks/data/tree/node.rb +27 -27
- data/lib/i18n/tasks/data/tree/nodes.rb +10 -10
- data/lib/i18n/tasks/data/tree/siblings.rb +20 -20
- data/lib/i18n/tasks/data/tree/traversal.rb +5 -5
- data/lib/i18n/tasks/data.rb +4 -4
- data/lib/i18n/tasks/html_keys.rb +2 -2
- data/lib/i18n/tasks/ignore_keys.rb +9 -9
- data/lib/i18n/tasks/interpolations.rb +21 -1
- data/lib/i18n/tasks/key_pattern_matching.rb +8 -8
- data/lib/i18n/tasks/logging.rb +2 -1
- data/lib/i18n/tasks/missing_keys.rb +24 -8
- data/lib/i18n/tasks/plural_keys.rb +6 -4
- data/lib/i18n/tasks/references.rb +4 -4
- data/lib/i18n/tasks/reports/base.rb +18 -14
- data/lib/i18n/tasks/reports/terminal.rb +64 -47
- data/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb +3 -3
- data/lib/i18n/tasks/scanners/ast_matchers/default_i18n_subject_matcher.rb +3 -3
- data/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb +10 -10
- data/lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb +2 -2
- data/lib/i18n/tasks/scanners/erb_ast_scanner.rb +69 -10
- data/lib/i18n/tasks/scanners/file_scanner.rb +5 -5
- data/lib/i18n/tasks/scanners/files/caching_file_finder.rb +3 -3
- data/lib/i18n/tasks/scanners/files/caching_file_finder_provider.rb +3 -3
- data/lib/i18n/tasks/scanners/files/caching_file_reader.rb +2 -2
- data/lib/i18n/tasks/scanners/files/file_finder.rb +8 -8
- data/lib/i18n/tasks/scanners/files/file_reader.rb +1 -1
- data/lib/i18n/tasks/scanners/local_ruby_parser.rb +9 -9
- data/lib/i18n/tasks/scanners/occurrence_from_position.rb +1 -1
- data/lib/i18n/tasks/scanners/pattern_mapper.rb +7 -7
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +20 -20
- data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +8 -8
- data/lib/i18n/tasks/scanners/prism_scanners/arguments_visitor.rb +48 -0
- data/lib/i18n/tasks/scanners/prism_scanners/nodes.rb +374 -0
- data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +337 -0
- data/lib/i18n/tasks/scanners/relative_keys.rb +8 -8
- data/lib/i18n/tasks/scanners/results/key_occurrences.rb +3 -3
- data/lib/i18n/tasks/scanners/results/occurrence.rb +14 -10
- data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +1 -1
- data/lib/i18n/tasks/scanners/ruby_key_literals.rb +6 -6
- data/lib/i18n/tasks/scanners/ruby_parser_factory.rb +27 -0
- data/lib/i18n/tasks/scanners/ruby_scanner.rb +225 -0
- data/lib/i18n/tasks/scanners/scanner.rb +2 -2
- data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +1 -1
- data/lib/i18n/tasks/split_key.rb +4 -4
- data/lib/i18n/tasks/stats.rb +3 -3
- data/lib/i18n/tasks/translation.rb +8 -5
- data/lib/i18n/tasks/translators/base_translator.rb +43 -13
- data/lib/i18n/tasks/translators/deepl_translator.rb +22 -14
- data/lib/i18n/tasks/translators/google_translator.rb +178 -26
- data/lib/i18n/tasks/translators/openai_translator.rb +56 -31
- data/lib/i18n/tasks/translators/watsonx_translator.rb +155 -0
- data/lib/i18n/tasks/translators/yandex_translator.rb +13 -9
- data/lib/i18n/tasks/unused_keys.rb +1 -1
- data/lib/i18n/tasks/used_keys.rb +32 -32
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/i18n/tasks.rb +17 -16
- data/templates/config/i18n-tasks.yml +14 -2
- data/templates/minitest/i18n_test.rb +3 -3
- data/templates/rspec/i18n_spec.rb +7 -7
- metadata +38 -172
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +0 -145
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "set"
|
|
4
4
|
module I18n::Tasks
|
|
5
5
|
module MissingKeys # rubocop:disable Metrics/ModuleLength
|
|
6
6
|
MISSING_TYPES = %w[
|
|
@@ -21,7 +21,7 @@ module I18n::Tasks
|
|
|
21
21
|
# @return [Siblings]
|
|
22
22
|
def missing_keys(locales: nil, types: nil, base_locale: nil)
|
|
23
23
|
locales ||= self.locales
|
|
24
|
-
types
|
|
24
|
+
types ||= missing_keys_types
|
|
25
25
|
base = base_locale || self.base_locale
|
|
26
26
|
types.inject(empty_forest) do |f, type|
|
|
27
27
|
f.merge! send(:"missing_#{type}_forest", locales, base)
|
|
@@ -88,7 +88,7 @@ module I18n::Tasks
|
|
|
88
88
|
|
|
89
89
|
# Loads rails-i18n pluralization config for the given locale.
|
|
90
90
|
def load_rails_i18n_pluralization!(locale)
|
|
91
|
-
path = File.join(Gem::Specification.find_by_name(
|
|
91
|
+
path = File.join(Gem::Specification.find_by_name("rails-i18n").gem_dir, "rails", "pluralization", "#{locale}.rb")
|
|
92
92
|
eval(File.read(path), binding, path) # rubocop:disable Security/Eval
|
|
93
93
|
end
|
|
94
94
|
|
|
@@ -98,7 +98,7 @@ module I18n::Tasks
|
|
|
98
98
|
locale_key_missing? locale, depluralize_key(key, compared_to)
|
|
99
99
|
end.set_root_key!(locale, type: :missing_diff).keys do |_key, node|
|
|
100
100
|
# change path and locale to base
|
|
101
|
-
data = {
|
|
101
|
+
data = {locale: locale, missing_diff_locale: node.data[:locale]}
|
|
102
102
|
if node.data.key?(:path)
|
|
103
103
|
data[:path] = LocalePathname.replace_locale(node.data[:path], node.data[:locale], locale)
|
|
104
104
|
end
|
|
@@ -108,8 +108,24 @@ module I18n::Tasks
|
|
|
108
108
|
|
|
109
109
|
# keys used in the code missing translations in locale
|
|
110
110
|
def missing_used_tree(locale)
|
|
111
|
-
used_tree(strict: true).select_keys do |key,
|
|
112
|
-
|
|
111
|
+
used_tree(strict: true).select_keys do |key, node|
|
|
112
|
+
occurrences = node.data[:occurrences] || []
|
|
113
|
+
|
|
114
|
+
# An occurrence may carry candidate keys (for relative lookups). If any
|
|
115
|
+
# candidate key exists in the locale, the usage is considered present.
|
|
116
|
+
occurrences_all_missing = occurrences.all? do |occ|
|
|
117
|
+
candidates = if occ.respond_to?(:candidate_keys) && occ.candidate_keys.present?
|
|
118
|
+
occ.candidate_keys
|
|
119
|
+
else
|
|
120
|
+
# fallback to the scanned key
|
|
121
|
+
[key]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Occurrence is missing iff all its candidates are missing
|
|
125
|
+
candidates.all? { |c| locale_key_missing?(locale, c) }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
occurrences_all_missing
|
|
113
129
|
end.set_root_key!(locale, type: :missing_used)
|
|
114
130
|
end
|
|
115
131
|
|
|
@@ -130,7 +146,7 @@ module I18n::Tasks
|
|
|
130
146
|
# @yieldreturn [Boolean] whether to collapse the node
|
|
131
147
|
def collapse_same_key_in_locales!(forest)
|
|
132
148
|
locales_and_node_by_key = {}
|
|
133
|
-
to_remove
|
|
149
|
+
to_remove = []
|
|
134
150
|
forest.each do |root|
|
|
135
151
|
locale = root.key
|
|
136
152
|
root.keys do |key, node|
|
|
@@ -146,7 +162,7 @@ module I18n::Tasks
|
|
|
146
162
|
end
|
|
147
163
|
forest.remove_nodes_and_emptied_ancestors! to_remove
|
|
148
164
|
locales_and_node_by_key.each_with_object({}) do |(key, (locales, node)), inv|
|
|
149
|
-
(inv[locales.sort.join(
|
|
165
|
+
(inv[locales.sort.join("+")] ||= []) << [key, node]
|
|
150
166
|
end.map do |locales, keys_nodes|
|
|
151
167
|
keys_nodes.each do |(key, node)|
|
|
152
168
|
forest["#{locales}.#{key}"] = node
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "set"
|
|
4
4
|
module I18n::Tasks::PluralKeys
|
|
5
5
|
# Ref: http://cldr.unicode.org/index/cldr-spec/plural-rules
|
|
6
6
|
CLDR_CATEGORY_KEYS = %w[zero one two few many other].freeze
|
|
7
7
|
PLURAL_KEY_SUFFIXES = Set.new CLDR_CATEGORY_KEYS
|
|
8
|
-
PLURAL_KEY_RE = /\.(?:#{CLDR_CATEGORY_KEYS *
|
|
8
|
+
PLURAL_KEY_RE = /\.(?:#{CLDR_CATEGORY_KEYS * "|"})$/
|
|
9
|
+
# Ref: https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules
|
|
10
|
+
EXPLICIT_0_1 = %w[0 1].freeze
|
|
9
11
|
|
|
10
12
|
def collapse_plural_nodes!(tree)
|
|
11
13
|
tree.leaves.map(&:parent).compact.uniq.each do |node|
|
|
12
14
|
children = node.children
|
|
13
15
|
next unless plural_forms?(children)
|
|
14
16
|
|
|
15
|
-
node.value
|
|
17
|
+
node.value = children.to_hash
|
|
16
18
|
node.children = nil
|
|
17
19
|
node.data.merge! children.first.data
|
|
18
20
|
end
|
|
@@ -56,6 +58,6 @@ module I18n::Tasks::PluralKeys
|
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
def plural_suffix?(key)
|
|
59
|
-
PLURAL_KEY_SUFFIXES.include?(key)
|
|
61
|
+
PLURAL_KEY_SUFFIXES.include?(key) || EXPLICIT_0_1.include?(key)
|
|
60
62
|
end
|
|
61
63
|
end
|
|
@@ -7,9 +7,9 @@ module I18n::Tasks
|
|
|
7
7
|
# 2. Resolved references -- all the used references in their fully resolved form.
|
|
8
8
|
# 3. Reference keys -- all the used reference keys.
|
|
9
9
|
def process_references(usages,
|
|
10
|
-
|
|
11
|
-
fail ArgumentError,
|
|
12
|
-
fail ArgumentError,
|
|
10
|
+
data_refs = merge_reference_trees(data_forest.select_keys { |_, node| node.reference? }))
|
|
11
|
+
fail ArgumentError, "usages must be a Data::Tree::Instance" unless usages.is_a?(Data::Tree::Siblings)
|
|
12
|
+
fail ArgumentError, "all_references must be a Data::Tree::Instance" unless data_refs.is_a?(Data::Tree::Siblings)
|
|
13
13
|
|
|
14
14
|
raw_refs = empty_forest
|
|
15
15
|
resolved_refs = empty_forest
|
|
@@ -90,7 +90,7 @@ module I18n::Tasks
|
|
|
90
90
|
on_leaves_merge: lambda do |node, other|
|
|
91
91
|
if node.value != other.value
|
|
92
92
|
log_warn(
|
|
93
|
-
|
|
93
|
+
"Conflicting references: " \
|
|
94
94
|
"#{node.full_key(root: false)} ⮕ #{node.value} in #{node.data[:locale]}, " \
|
|
95
95
|
"but ⮕ #{other.value} in #{other.data[:locale]}"
|
|
96
96
|
)
|
|
@@ -15,55 +15,59 @@ module I18n::Tasks::Reports
|
|
|
15
15
|
protected
|
|
16
16
|
|
|
17
17
|
def missing_type_info(type)
|
|
18
|
-
::I18n::Tasks::MissingKeys::MISSING_TYPES[type.to_s.sub(/\Amissing_/,
|
|
18
|
+
::I18n::Tasks::MissingKeys::MISSING_TYPES[type.to_s.sub(/\Amissing_/, "").to_sym]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def missing_title(forest)
|
|
22
|
-
"Missing translations (#{forest.leaves.count ||
|
|
22
|
+
"Missing translations (#{forest.leaves.count || "∅"})"
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def inconsistent_interpolations_title(forest)
|
|
26
|
-
"Inconsistent interpolations (#{forest.leaves.count ||
|
|
26
|
+
"Inconsistent interpolations (#{forest.leaves.count || "∅"})"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def reserved_interpolations_title(forest)
|
|
30
|
+
"Reserved interpolations (#{forest.leaves.count || "∅"})"
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def unused_title(key_values)
|
|
30
|
-
"Unused keys (#{key_values.count ||
|
|
34
|
+
"Unused keys (#{key_values.count || "∅"})"
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
def eq_base_title(key_values, locale = base_locale)
|
|
34
|
-
"Same value as #{locale} (#{key_values.count ||
|
|
38
|
+
"Same value as #{locale} (#{key_values.count || "∅"})"
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
def used_title(keys_nodes, filter)
|
|
38
42
|
used_n = keys_nodes.map { |_k, node| node.data[:occurrences].size }.reduce(:+).to_i
|
|
39
|
-
"#{keys_nodes.size} key#{
|
|
40
|
-
"#{" (#{used_n} usage#{
|
|
43
|
+
"#{keys_nodes.size} key#{"s" if keys_nodes.size != 1}#{" matching '#{filter}'" if filter}" \
|
|
44
|
+
"#{" (#{used_n} usage#{"s" if used_n != 1})" if used_n.positive?}"
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
# Sort keys by their attributes in order
|
|
44
48
|
# @param [Hash] order e.g. {locale: :asc, type: :desc, key: :asc}
|
|
45
|
-
def sort_by_attr!(objects, order = {
|
|
49
|
+
def sort_by_attr!(objects, order = {locale: :asc, key: :asc})
|
|
46
50
|
order_keys = order.keys
|
|
47
51
|
objects.sort! do |a, b|
|
|
48
52
|
by = order_keys.detect { |k| a[k] != b[k] }
|
|
49
|
-
order[by] == :desc ? b[by] <=> a[by] : a[by] <=> b[by]
|
|
53
|
+
(order[by] == :desc) ? b[by] <=> a[by] : a[by] <=> b[by]
|
|
50
54
|
end
|
|
51
55
|
objects
|
|
52
56
|
end
|
|
53
57
|
|
|
54
58
|
def forest_to_attr(forest)
|
|
55
59
|
forest.keys(root: false).map do |key, node|
|
|
56
|
-
{
|
|
60
|
+
{key: key, value: node.value, type: node.data[:type], locale: node.root.key, data: node.data}
|
|
57
61
|
end
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
def format_locale(locale)
|
|
61
|
-
return
|
|
65
|
+
return "" unless locale
|
|
62
66
|
|
|
63
|
-
if locale.split(
|
|
64
|
-
|
|
67
|
+
if locale.split("+") == task.locales.sort
|
|
68
|
+
"all"
|
|
65
69
|
else
|
|
66
|
-
locale.tr
|
|
70
|
+
locale.tr "+", " "
|
|
67
71
|
end
|
|
68
72
|
end
|
|
69
73
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "i18n/tasks/reports/base"
|
|
4
|
+
require "terminal-table"
|
|
5
5
|
module I18n
|
|
6
6
|
module Tasks
|
|
7
7
|
module Reports
|
|
@@ -10,17 +10,17 @@ module I18n
|
|
|
10
10
|
forest = collapse_missing_tree! forest
|
|
11
11
|
if forest.present?
|
|
12
12
|
print_title missing_title(forest)
|
|
13
|
-
print_table headings: [Rainbow(I18n.t(
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
print_table headings: [Rainbow(I18n.t("i18n_tasks.common.locale")).cyan.bright,
|
|
14
|
+
Rainbow(I18n.t("i18n_tasks.common.key")).cyan.bright,
|
|
15
|
+
I18n.t("i18n_tasks.missing.details_title")] do |t|
|
|
16
16
|
t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
|
|
17
|
-
[{
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
[{value: Rainbow(format_locale(a[:locale])).cyan, alignment: :center},
|
|
18
|
+
format_key(a[:key], a[:data]),
|
|
19
|
+
missing_key_info(a)]
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
else
|
|
23
|
-
print_success I18n.t(
|
|
23
|
+
print_success I18n.t("i18n_tasks.missing.none")
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -29,7 +29,24 @@ module I18n
|
|
|
29
29
|
print_title inconsistent_interpolations_title(forest)
|
|
30
30
|
show_tree(forest)
|
|
31
31
|
else
|
|
32
|
-
print_success I18n.t(
|
|
32
|
+
print_success I18n.t("i18n_tasks.inconsistent_interpolations.none")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reserved_interpolations(forest = task.reserved_interpolations)
|
|
37
|
+
if forest.present?
|
|
38
|
+
print_title reserved_interpolations_title(forest)
|
|
39
|
+
print_table headings: [Rainbow(I18n.t("i18n_tasks.common.locale")).cyan.bright,
|
|
40
|
+
Rainbow(I18n.t("i18n_tasks.common.key")).cyan.bright,
|
|
41
|
+
I18n.t("i18n_tasks.reserved_interpolations.details_title")] do |t|
|
|
42
|
+
t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
|
|
43
|
+
[{value: Rainbow(format_locale(a[:locale])).cyan, alignment: :center},
|
|
44
|
+
format_key(a[:key], a[:data]),
|
|
45
|
+
a[:value].join(", ")]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
print_success I18n.t("i18n_tasks.reserved_interpolations.none")
|
|
33
50
|
end
|
|
34
51
|
end
|
|
35
52
|
|
|
@@ -45,7 +62,7 @@ module I18n
|
|
|
45
62
|
print_occurrences node, key
|
|
46
63
|
end
|
|
47
64
|
else
|
|
48
|
-
print_error I18n.t(
|
|
65
|
+
print_error I18n.t("i18n_tasks.usages.none")
|
|
49
66
|
end
|
|
50
67
|
end
|
|
51
68
|
|
|
@@ -55,7 +72,7 @@ module I18n
|
|
|
55
72
|
print_title unused_title(keys)
|
|
56
73
|
print_locale_key_value_data_table keys
|
|
57
74
|
else
|
|
58
|
-
print_success I18n.t(
|
|
75
|
+
print_success I18n.t("i18n_tasks.unused.none")
|
|
59
76
|
end
|
|
60
77
|
end
|
|
61
78
|
|
|
@@ -65,7 +82,7 @@ module I18n
|
|
|
65
82
|
print_title eq_base_title(keys)
|
|
66
83
|
print_locale_key_value_data_table keys
|
|
67
84
|
else
|
|
68
|
-
print_info Rainbow(
|
|
85
|
+
print_info Rainbow("No translations are the same as base value").cyan
|
|
69
86
|
end
|
|
70
87
|
end
|
|
71
88
|
|
|
@@ -74,39 +91,39 @@ module I18n
|
|
|
74
91
|
end
|
|
75
92
|
|
|
76
93
|
def forest_stats(forest, stats = task.forest_stats(forest))
|
|
77
|
-
text
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
title = Rainbow(I18n.t(
|
|
94
|
+
text = if stats[:locale_count] == 1
|
|
95
|
+
I18n.t("i18n_tasks.data_stats.text_single_locale", **stats)
|
|
96
|
+
else
|
|
97
|
+
I18n.t("i18n_tasks.data_stats.text", **stats)
|
|
98
|
+
end
|
|
99
|
+
title = Rainbow(I18n.t("i18n_tasks.data_stats.title", **stats.slice(:locales))).bright
|
|
83
100
|
print_info "#{Rainbow(title).cyan} #{Rainbow(text).cyan}"
|
|
84
101
|
end
|
|
85
102
|
|
|
86
103
|
def mv_results(results)
|
|
87
104
|
results.each do |(from, to)|
|
|
88
105
|
if to
|
|
89
|
-
print_info "#{Rainbow(from).cyan} #{Rainbow(
|
|
106
|
+
print_info "#{Rainbow(from).cyan} #{Rainbow("⮕").yellow.bright} #{Rainbow(to).cyan}"
|
|
90
107
|
else
|
|
91
|
-
print_info "#{Rainbow(from).red}#{Rainbow(
|
|
108
|
+
print_info "#{Rainbow(from).red}#{Rainbow(" 🗑").red.bright}"
|
|
92
109
|
end
|
|
93
110
|
end
|
|
94
111
|
end
|
|
95
112
|
|
|
96
113
|
def cp_results(results)
|
|
97
114
|
results.each do |(from, to)|
|
|
98
|
-
print_info "#{Rainbow(from).cyan} #{Rainbow(
|
|
115
|
+
print_info "#{Rainbow(from).cyan} #{Rainbow("+").yellow.bright} #{Rainbow(to).green}"
|
|
99
116
|
end
|
|
100
117
|
end
|
|
101
118
|
|
|
102
119
|
def check_normalized_results(non_normalized)
|
|
103
120
|
if non_normalized.empty?
|
|
104
|
-
print_success
|
|
121
|
+
print_success "All data is normalized"
|
|
105
122
|
return
|
|
106
123
|
end
|
|
107
|
-
log_stderr Rainbow(
|
|
124
|
+
log_stderr Rainbow("The following data requires normalization:").yellow
|
|
108
125
|
puts non_normalized
|
|
109
|
-
log_stderr Rainbow(
|
|
126
|
+
log_stderr Rainbow("Run `i18n-tasks normalize` to fix").yellow
|
|
110
127
|
end
|
|
111
128
|
|
|
112
129
|
private
|
|
@@ -116,7 +133,7 @@ module I18n
|
|
|
116
133
|
when :missing_used
|
|
117
134
|
first_occurrence leaf
|
|
118
135
|
when :missing_plural
|
|
119
|
-
leaf[:data][:missing_keys].join(
|
|
136
|
+
leaf[:data][:missing_keys].join(", ")
|
|
120
137
|
else
|
|
121
138
|
"#{Rainbow(leaf[:data][:missing_diff_locale]).cyan} " \
|
|
122
139
|
"#{format_value(leaf[:value].is_a?(String) ? leaf[:value].strip : leaf[:value])}"
|
|
@@ -127,16 +144,16 @@ module I18n
|
|
|
127
144
|
if data[:ref_info]
|
|
128
145
|
from, to = data[:ref_info]
|
|
129
146
|
resolved = key[0...to.length]
|
|
130
|
-
after
|
|
147
|
+
after = key[to.length..]
|
|
131
148
|
" #{Rainbow(from).yellow}#{Rainbow(after).cyan}\n" \
|
|
132
|
-
"#{Rainbow(
|
|
149
|
+
"#{Rainbow("⮕").yellow.bright} #{Rainbow(resolved).yellow.bright}"
|
|
133
150
|
else
|
|
134
151
|
Rainbow(key).cyan
|
|
135
152
|
end
|
|
136
153
|
end
|
|
137
154
|
|
|
138
155
|
def format_value(val)
|
|
139
|
-
val.is_a?(Symbol) ? "#{Rainbow(
|
|
156
|
+
val.is_a?(Symbol) ? "#{Rainbow("⮕ ").yellow.bright}#{Rainbow(val).yellow}" : val.to_s.strip
|
|
140
157
|
end
|
|
141
158
|
|
|
142
159
|
def format_reference_desc(node_data)
|
|
@@ -144,19 +161,19 @@ module I18n
|
|
|
144
161
|
|
|
145
162
|
case node_data[:ref_type]
|
|
146
163
|
when :reference_usage
|
|
147
|
-
Rainbow(
|
|
164
|
+
Rainbow("(ref)").yellow.bright
|
|
148
165
|
when :reference_usage_resolved
|
|
149
|
-
Rainbow(
|
|
166
|
+
Rainbow("(resolved ref)").yellow.bright
|
|
150
167
|
when :reference_usage_key
|
|
151
|
-
Rainbow(
|
|
168
|
+
Rainbow("(ref key)").yellow.bright
|
|
152
169
|
end
|
|
153
170
|
end
|
|
154
171
|
|
|
155
172
|
def print_occurrences(node, full_key = node.full_key)
|
|
156
173
|
occurrences = node.data[:occurrences]
|
|
157
174
|
puts [Rainbow(full_key).bright,
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
format_reference_desc(node.data),
|
|
176
|
+
(Rainbow(occurrences.size).green if occurrences.size > 1)].compact.join " "
|
|
160
177
|
occurrences.each do |occurrence|
|
|
161
178
|
puts " #{key_occurrence full_key, occurrence}"
|
|
162
179
|
end
|
|
@@ -164,25 +181,25 @@ module I18n
|
|
|
164
181
|
|
|
165
182
|
def print_locale_key_value_data_table(locale_key_value_datas)
|
|
166
183
|
if locale_key_value_datas.present?
|
|
167
|
-
print_table headings: [Rainbow(I18n.t(
|
|
168
|
-
|
|
169
|
-
|
|
184
|
+
print_table headings: [Rainbow(I18n.t("i18n_tasks.common.locale")).cyan.bright,
|
|
185
|
+
Rainbow(I18n.t("i18n_tasks.common.key")).cyan.bright,
|
|
186
|
+
I18n.t("i18n_tasks.common.value")] do |t|
|
|
170
187
|
t.rows = locale_key_value_datas.map do |(locale, k, v, data)|
|
|
171
|
-
[{
|
|
188
|
+
[{value: Rainbow(locale).cyan, alignment: :center}, format_key(k, data), format_value(v)]
|
|
172
189
|
end
|
|
173
190
|
end
|
|
174
191
|
else
|
|
175
|
-
puts
|
|
192
|
+
puts "ø"
|
|
176
193
|
end
|
|
177
194
|
end
|
|
178
195
|
|
|
179
196
|
def print_title(title)
|
|
180
|
-
log_stderr "#{Rainbow(title.strip).bright} #{Rainbow(
|
|
197
|
+
log_stderr "#{Rainbow(title.strip).bright} #{Rainbow("|").faint} " \
|
|
181
198
|
"#{"i18n-tasks v#{I18n::Tasks::VERSION}"}"
|
|
182
199
|
end
|
|
183
200
|
|
|
184
201
|
def print_success(message)
|
|
185
|
-
log_stderr Rainbow("✓ #{I18n.t(
|
|
202
|
+
log_stderr Rainbow("✓ #{I18n.t("i18n_tasks.cmd.encourage").sample} #{message}").green.bright
|
|
186
203
|
end
|
|
187
204
|
|
|
188
205
|
def print_error(message)
|
|
@@ -194,16 +211,16 @@ module I18n
|
|
|
194
211
|
end
|
|
195
212
|
|
|
196
213
|
def indent(txt, n = 2)
|
|
197
|
-
txt.gsub(/^/,
|
|
214
|
+
txt.gsub(/^/, " " * n)
|
|
198
215
|
end
|
|
199
216
|
|
|
200
|
-
def print_table(opts, &
|
|
201
|
-
puts ::Terminal::Table.new(opts, &
|
|
217
|
+
def print_table(opts, &)
|
|
218
|
+
puts ::Terminal::Table.new(opts, &)
|
|
202
219
|
end
|
|
203
220
|
|
|
204
221
|
def key_occurrence(full_key, occurrence)
|
|
205
222
|
location = Rainbow("#{occurrence.path}:#{occurrence.line_num}").green
|
|
206
|
-
source
|
|
223
|
+
source = highlight_key(occurrence.raw_key || full_key, occurrence.line, occurrence.line_pos..-1).strip
|
|
207
224
|
"#{location} #{source}"
|
|
208
225
|
end
|
|
209
226
|
|
|
@@ -214,8 +231,8 @@ module I18n
|
|
|
214
231
|
first = occurrences.first
|
|
215
232
|
[
|
|
216
233
|
Rainbow("#{first.path}:#{first.line_num}").green,
|
|
217
|
-
("(#{I18n.t
|
|
218
|
-
].compact.join(
|
|
234
|
+
("(#{I18n.t "i18n_tasks.common.n_more", count: occurrences.length - 1})" if occurrences.length > 1)
|
|
235
|
+
].compact.join(" ")
|
|
219
236
|
end
|
|
220
237
|
|
|
221
238
|
def highlight_key(full_key, line, range = (0..-1))
|
|
@@ -7,7 +7,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def convert_to_key_occurrences(send_node, _method_name, location: send_node.loc)
|
|
10
|
-
fail(
|
|
10
|
+
fail("Not implemented")
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
protected
|
|
@@ -32,7 +32,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
|
32
32
|
elsif %i[true false].include?(node.type)
|
|
33
33
|
node.type.to_s
|
|
34
34
|
elsif node.type == :nil
|
|
35
|
-
|
|
35
|
+
""
|
|
36
36
|
elsif node.type == :array && array_join_with
|
|
37
37
|
extract_array_as_string(
|
|
38
38
|
node,
|
|
@@ -109,7 +109,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
|
109
109
|
if array_reject_blank
|
|
110
110
|
children_strings.reject! do |x|
|
|
111
111
|
# empty strings and nils in the scope argument are ignored by i18n
|
|
112
|
-
x ==
|
|
112
|
+
x == ""
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
children_strings.join(array_join_with)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "i18n/tasks/scanners/ast_matchers/base_matcher"
|
|
4
|
+
require "i18n/tasks/scanners/results/occurrence"
|
|
5
5
|
|
|
6
6
|
module I18n::Tasks::Scanners::AstMatchers
|
|
7
7
|
class DefaultI18nSubjectMatcher < BaseMatcher
|
|
@@ -10,7 +10,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
|
10
10
|
return unless children[1] == :default_i18n_subject
|
|
11
11
|
|
|
12
12
|
key = @scanner.absolute_key(
|
|
13
|
-
|
|
13
|
+
".subject",
|
|
14
14
|
location.expression.source_buffer.name,
|
|
15
15
|
calling_method: method_name
|
|
16
16
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "i18n/tasks/scanners/ast_matchers/base_matcher"
|
|
4
|
+
require "i18n/tasks/scanners/results/occurrence"
|
|
5
5
|
|
|
6
6
|
module I18n::Tasks::Scanners::AstMatchers
|
|
7
7
|
class MessageReceiversMatcher < BaseMatcher
|
|
@@ -64,25 +64,25 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
|
64
64
|
def process_options(node:, key:)
|
|
65
65
|
return [key, nil] if node&.type != :hash
|
|
66
66
|
|
|
67
|
-
scope_node = extract_hash_pair(node,
|
|
67
|
+
scope_node = extract_hash_pair(node, "scope")
|
|
68
68
|
|
|
69
69
|
if scope_node
|
|
70
70
|
scope = extract_string(
|
|
71
71
|
scope_node.children[1],
|
|
72
|
-
array_join_with:
|
|
72
|
+
array_join_with: ".",
|
|
73
73
|
array_flatten: true,
|
|
74
74
|
array_reject_blank: true
|
|
75
75
|
)
|
|
76
76
|
return nil if scope.nil? && scope_node.type != :nil
|
|
77
77
|
|
|
78
|
-
key = [scope, key].join(
|
|
78
|
+
key = [scope, key].join(".") unless scope == ""
|
|
79
79
|
end
|
|
80
|
-
if default_arg_node = extract_hash_pair(node,
|
|
80
|
+
if (default_arg_node = extract_hash_pair(node, "default"))
|
|
81
81
|
default_arg = if default_arg_node.children[1]&.type == :hash
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
extract_hash(default_arg_node.children[1])
|
|
83
|
+
else
|
|
84
|
+
extract_string(default_arg_node.children[1])
|
|
85
|
+
end
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
[key, default_arg]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "i18n/tasks/scanners/results/occurrence"
|
|
4
4
|
|
|
5
5
|
module I18n::Tasks::Scanners::AstMatchers
|
|
6
6
|
class RailsModelMatcher < BaseMatcher
|
|
@@ -16,7 +16,7 @@ module I18n::Tasks::Scanners::AstMatchers
|
|
|
16
16
|
receiver = children[0]
|
|
17
17
|
method_name = children[1]
|
|
18
18
|
|
|
19
|
-
return unless method_name == :human_attribute_name && receiver
|
|
19
|
+
return unless method_name == :human_attribute_name && receiver&.type == :const
|
|
20
20
|
|
|
21
21
|
value = children[2]
|
|
22
22
|
|