i18n-tasks 1.0.15 → 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 +85 -13
- data/Rakefile +4 -4
- data/bin/i18n-tasks +3 -3
- data/config/locales/en.yml +6 -0
- data/config/locales/ru.yml +7 -0
- data/i18n-tasks.gemspec +28 -41
- 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 +25 -25
- 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 +5 -5
- 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 +32 -32
- 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 +22 -21
- 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 +1 -1
- 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 +8 -8
- 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 +8 -1
- data/lib/i18n/tasks/scanners/prism_scanners/nodes.rb +101 -61
- data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +169 -105
- 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 +1 -1
- 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 +5 -5
- data/lib/i18n/tasks/translators/base_translator.rb +40 -14
- data/lib/i18n/tasks/translators/deepl_translator.rb +17 -14
- data/lib/i18n/tasks/translators/google_translator.rb +169 -25
- data/lib/i18n/tasks/translators/openai_translator.rb +34 -23
- data/lib/i18n/tasks/translators/watsonx_translator.rb +16 -16
- data/lib/i18n/tasks/translators/yandex_translator.rb +8 -8
- data/lib/i18n/tasks/unused_keys.rb +1 -1
- data/lib/i18n/tasks/used_keys.rb +32 -33
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/i18n/tasks.rb +17 -17
- data/templates/config/i18n-tasks.yml +12 -0
- data/templates/minitest/i18n_test.rb +3 -3
- data/templates/rspec/i18n_spec.rb +7 -7
- metadata +25 -185
- data/lib/i18n/tasks/scanners/prism_scanner.rb +0 -83
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +0 -145
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n::Tasks
|
|
4
|
+
module Command
|
|
5
|
+
module Commands
|
|
6
|
+
module CheckPrism
|
|
7
|
+
include Command::Collection
|
|
8
|
+
|
|
9
|
+
arg :prism_mode,
|
|
10
|
+
"--prism_mode MODE",
|
|
11
|
+
"Prism parser mode: 'rails' or 'ruby'. Defaults to 'rails' if Rails is available in the project, otherwise 'ruby'."
|
|
12
|
+
|
|
13
|
+
cmd :check_prism,
|
|
14
|
+
desc: t("i18n_tasks.cmd.desc.check_prism"),
|
|
15
|
+
args: %i[prism_mode]
|
|
16
|
+
|
|
17
|
+
# Run both the default (Parser-based) and the Prism-enabled scanning
|
|
18
|
+
# and produce a plain-text report in tmp/i18n_tasks_check_prism.md suitable
|
|
19
|
+
# for pasting into an issue.
|
|
20
|
+
def check_prism(opt = {})
|
|
21
|
+
now = Time.now
|
|
22
|
+
report_path = File.join(Dir.pwd, "tmp", "i18n_tasks_check_prism.md")
|
|
23
|
+
FileUtils.mkdir_p(File.dirname(report_path))
|
|
24
|
+
|
|
25
|
+
# keep original search config and clear caches helper
|
|
26
|
+
orig_search = (i18n.config[:search] || {}).dup
|
|
27
|
+
|
|
28
|
+
results = {}
|
|
29
|
+
|
|
30
|
+
# determine prism mode: use provided opt or default to rails when Rails present
|
|
31
|
+
chosen_prism_mode = (opt[:prism_mode].presence || (rails_available? ? "rails" : "ruby")).to_s
|
|
32
|
+
unless %w[rails ruby].include?(chosen_prism_mode)
|
|
33
|
+
fail CommandError, "--prism-mode must be 'rails' or 'ruby'"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
{default: nil, prism: chosen_prism_mode}.each do |label, prism_mode|
|
|
37
|
+
i18n.config[:search] = orig_search.merge(prism: prism_mode)
|
|
38
|
+
# clear caches used by UsedKeys
|
|
39
|
+
i18n.instance_variable_set(:@scanner, nil)
|
|
40
|
+
i18n.instance_variable_set(:@search_config, nil)
|
|
41
|
+
i18n.instance_variable_set(:@keys_used_in_source_tree, nil)
|
|
42
|
+
|
|
43
|
+
tree = i18n.used_in_source_tree
|
|
44
|
+
keys = tree.nodes.select { |n| n.data[:occurrences].present? }
|
|
45
|
+
results[label] = keys.each_with_object({}) do |node, h|
|
|
46
|
+
full_key = node.full_key(root: false)
|
|
47
|
+
h[full_key] = node.data[:occurrences]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# restore original config and caches
|
|
52
|
+
i18n.config[:search] = orig_search
|
|
53
|
+
i18n.instance_variable_set(:@scanner, nil)
|
|
54
|
+
i18n.instance_variable_set(:@search_config, nil)
|
|
55
|
+
i18n.instance_variable_set(:@keys_used_in_source_tree, nil)
|
|
56
|
+
|
|
57
|
+
default_keys = results[:default].keys
|
|
58
|
+
prism_keys = results[:prism].keys
|
|
59
|
+
|
|
60
|
+
only_default = (default_keys - prism_keys).sort
|
|
61
|
+
only_prism = (prism_keys - default_keys).sort
|
|
62
|
+
both = (default_keys & prism_keys).sort
|
|
63
|
+
|
|
64
|
+
File.open(report_path, "w") do |f|
|
|
65
|
+
f.puts "# i18n-tasks check_prism report"
|
|
66
|
+
f.puts "Generated at: #{now.utc}"
|
|
67
|
+
f.puts "Prism mode: #{chosen_prism_mode}"
|
|
68
|
+
f.puts "Gem version: #{I18n::Tasks::VERSION}"
|
|
69
|
+
f.puts ""
|
|
70
|
+
f.puts "Summary"
|
|
71
|
+
f.puts "- total keys (default parser): #{default_keys.size}"
|
|
72
|
+
f.puts "- total keys (prism): #{prism_keys.size}"
|
|
73
|
+
f.puts "- keys in both: #{both.size}"
|
|
74
|
+
f.puts "- keys only in default parser: #{only_default.size}"
|
|
75
|
+
f.puts "- keys only in prism: #{only_prism.size}"
|
|
76
|
+
f.puts ""
|
|
77
|
+
|
|
78
|
+
unless only_default.empty?
|
|
79
|
+
f.puts "## Keys found by the default parser but NOT by Prism (#{only_default.size})"
|
|
80
|
+
only_default.each do |k|
|
|
81
|
+
f.puts "\n### #{k}"
|
|
82
|
+
results[:default][k].first(5).each do |occ|
|
|
83
|
+
src = (occ.raw_key || k).to_s
|
|
84
|
+
line = (occ.line || "").strip
|
|
85
|
+
highlighted = line.gsub(src) { |m| "`#{m}`" }
|
|
86
|
+
f.puts "- #{occ.path}:#{occ.line_num} `#{src}` — #{highlighted}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
unless only_prism.empty?
|
|
92
|
+
f.puts "## Keys found by Prism but NOT by the default parser (#{only_prism.size})"
|
|
93
|
+
only_prism.each do |k|
|
|
94
|
+
f.puts "\n### #{k}"
|
|
95
|
+
results[:prism][k].each do |occ|
|
|
96
|
+
src = (occ.raw_key || k).to_s
|
|
97
|
+
line = (occ.line || "").strip
|
|
98
|
+
highlighted = line.gsub(src) { |m| "`#{m}`" }
|
|
99
|
+
f.puts "- #{occ.path}:#{occ.line_num} `#{src}` — #{highlighted}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
f.puts "\n## Notes"
|
|
105
|
+
f.puts "- This report compares keys discovered by the project default parser and by Prism (rails mode)."
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
log_stderr "Wrote check_prism report: #{report_path}"
|
|
109
|
+
puts File.read(report_path)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def rails_available?
|
|
115
|
+
return true if defined?(Rails)
|
|
116
|
+
return true if Gem.loaded_specs.key?("rails")
|
|
117
|
+
lock = File.join(Dir.pwd, "Gemfile.lock")
|
|
118
|
+
return false unless File.exist?(lock)
|
|
119
|
+
File.read(lock).lines.any? { |l| l.strip.start_with?("rails ") || l.strip =~ /^rails \(/ }
|
|
120
|
+
rescue
|
|
121
|
+
false
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -7,24 +7,24 @@ module I18n::Tasks
|
|
|
7
7
|
include Command::Collection
|
|
8
8
|
|
|
9
9
|
arg :pattern_router,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
"-p",
|
|
11
|
+
"--pattern_router",
|
|
12
|
+
t("i18n_tasks.cmd.args.desc.pattern_router")
|
|
13
13
|
|
|
14
14
|
cmd :normalize,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
pos: "[locale ...]",
|
|
16
|
+
desc: t("i18n_tasks.cmd.desc.normalize"),
|
|
17
|
+
args: %i[locales pattern_router]
|
|
18
18
|
|
|
19
19
|
def normalize(opt = {})
|
|
20
20
|
i18n.normalize_store! locales: opt[:locales],
|
|
21
|
-
|
|
21
|
+
force_pattern_router: opt[:pattern_router]
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
cmd :check_normalized,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
pos: "[locale ...]",
|
|
26
|
+
desc: t("i18n_tasks.cmd.desc.check_normalized"),
|
|
27
|
+
args: %i[locales]
|
|
28
28
|
|
|
29
29
|
def check_normalized(opt)
|
|
30
30
|
non_normalized = i18n.non_normalized_paths locales: opt[:locales]
|
|
@@ -33,10 +33,10 @@ module I18n::Tasks
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
cmd :mv,
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
pos: "FROM_KEY_PATTERN TO_KEY_PATTERN",
|
|
37
|
+
desc: t("i18n_tasks.cmd.desc.mv")
|
|
38
38
|
def mv(opt = {})
|
|
39
|
-
fail CommandError,
|
|
39
|
+
fail CommandError, "requires FROM_KEY_PATTERN and TO_KEY_PATTERN" if opt[:arguments].size < 2
|
|
40
40
|
|
|
41
41
|
from_pattern = opt[:arguments].shift
|
|
42
42
|
to_pattern = opt[:arguments].shift
|
|
@@ -47,10 +47,10 @@ module I18n::Tasks
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
cmd :cp,
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
pos: "FROM_KEY_PATTERN TO_KEY_PATTERN",
|
|
51
|
+
desc: t("i18n_tasks.cmd.desc.cp")
|
|
52
52
|
def cp(opt = {})
|
|
53
|
-
fail CommandError,
|
|
53
|
+
fail CommandError, "requires FROM_KEY_PATTERN and TO_KEY_PATTERN" if opt[:arguments].size < 2
|
|
54
54
|
|
|
55
55
|
from_pattern = opt[:arguments].shift
|
|
56
56
|
to_pattern = opt[:arguments].shift
|
|
@@ -61,32 +61,32 @@ module I18n::Tasks
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
cmd :rm,
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
pos: "KEY_PATTERN [KEY_PATTERN...]",
|
|
65
|
+
desc: t("i18n_tasks.cmd.desc.rm")
|
|
66
66
|
def rm(opt = {})
|
|
67
|
-
fail CommandError,
|
|
67
|
+
fail CommandError, "requires KEY_PATTERN" if opt[:arguments].empty?
|
|
68
68
|
|
|
69
69
|
forest = i18n.data_forest
|
|
70
70
|
results = opt[:arguments].each_with_object({}) do |key_pattern, h|
|
|
71
|
-
h.merge! forest.mv_key!(compile_key_pattern(key_pattern),
|
|
71
|
+
h.merge! forest.mv_key!(compile_key_pattern(key_pattern), "", root: false)
|
|
72
72
|
end
|
|
73
73
|
i18n.data.write forest
|
|
74
74
|
terminal_report.mv_results results
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
cmd :data,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
pos: "[locale ...]",
|
|
79
|
+
desc: t("i18n_tasks.cmd.desc.data"),
|
|
80
|
+
args: %i[locales out_format]
|
|
81
81
|
|
|
82
82
|
def data(opt = {})
|
|
83
83
|
print_forest i18n.data_forest(opt[:locales]), opt
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
cmd :data_merge,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
pos: "[tree ...]",
|
|
88
|
+
desc: t("i18n_tasks.cmd.desc.data_merge"),
|
|
89
|
+
args: %i[data_format nostdin]
|
|
90
90
|
|
|
91
91
|
def data_merge(opt = {})
|
|
92
92
|
forest = merge_forests_stdin_and_pos!(opt)
|
|
@@ -95,9 +95,9 @@ module I18n::Tasks
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
cmd :data_write,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
pos: "[tree]",
|
|
99
|
+
desc: t("i18n_tasks.cmd.desc.data_write"),
|
|
100
|
+
args: %i[data_format nostdin]
|
|
101
101
|
|
|
102
102
|
def data_write(opt = {})
|
|
103
103
|
forest = forest_pos_or_stdin!(opt)
|
|
@@ -106,13 +106,13 @@ module I18n::Tasks
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
cmd :data_remove,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
pos: "[tree]",
|
|
110
|
+
desc: t("i18n_tasks.cmd.desc.data_remove"),
|
|
111
|
+
args: %i[data_format nostdin]
|
|
112
112
|
|
|
113
113
|
def data_remove(opt = {})
|
|
114
114
|
removed = i18n.data.remove_by_key!(forest_pos_or_stdin!(opt))
|
|
115
|
-
log_stderr
|
|
115
|
+
log_stderr "Removed:"
|
|
116
116
|
print_forest removed, opt
|
|
117
117
|
end
|
|
118
118
|
end
|
|
@@ -7,9 +7,9 @@ module I18n::Tasks
|
|
|
7
7
|
include Command::Collection
|
|
8
8
|
|
|
9
9
|
cmd :eq_base,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
pos: "[locale ...]",
|
|
11
|
+
desc: t("i18n_tasks.cmd.desc.eq_base"),
|
|
12
|
+
args: %i[locales out_format]
|
|
13
13
|
|
|
14
14
|
def eq_base(opt = {})
|
|
15
15
|
forest = i18n.eq_base_keys(opt)
|
|
@@ -7,20 +7,21 @@ module I18n::Tasks
|
|
|
7
7
|
include Command::Collection
|
|
8
8
|
|
|
9
9
|
cmd :health,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
pos: "[locale ...]",
|
|
11
|
+
desc: t("i18n_tasks.cmd.desc.health"),
|
|
12
|
+
args: %i[locales out_format config]
|
|
13
13
|
|
|
14
14
|
def health(opt = {})
|
|
15
15
|
forest = i18n.data_forest(opt[:locales])
|
|
16
|
-
stats
|
|
17
|
-
fail CommandError, t(
|
|
16
|
+
stats = i18n.forest_stats(forest)
|
|
17
|
+
fail CommandError, t("i18n_tasks.health.no_keys_detected") if stats[:key_count].zero?
|
|
18
18
|
|
|
19
19
|
terminal_report.forest_stats forest, stats
|
|
20
20
|
[
|
|
21
21
|
missing(**opt),
|
|
22
22
|
unused(**opt),
|
|
23
23
|
check_consistent_interpolations(**opt),
|
|
24
|
+
check_reserved_interpolations(**opt),
|
|
24
25
|
check_normalized(**opt)
|
|
25
26
|
].detect { |result| result == :exit1 }
|
|
26
27
|
end
|
|
@@ -7,15 +7,26 @@ module I18n::Tasks
|
|
|
7
7
|
include Command::Collection
|
|
8
8
|
|
|
9
9
|
cmd :check_consistent_interpolations,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
pos: "[locale ...]",
|
|
11
|
+
desc: t("i18n_tasks.cmd.desc.check_consistent_interpolations"),
|
|
12
|
+
args: %i[locales out_format]
|
|
13
13
|
|
|
14
14
|
def check_consistent_interpolations(opt = {})
|
|
15
15
|
forest = i18n.inconsistent_interpolations(**opt.slice(:locales, :base_locale))
|
|
16
16
|
print_forest forest, opt, :inconsistent_interpolations
|
|
17
17
|
:exit1 unless forest.empty?
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
cmd :check_reserved_interpolations,
|
|
21
|
+
pos: "[locale]",
|
|
22
|
+
desc: t("i18n_tasks.cmd.desc.check_reserved_interpolations"),
|
|
23
|
+
args: %i[locales out_format]
|
|
24
|
+
|
|
25
|
+
def check_reserved_interpolations(opt = {})
|
|
26
|
+
forest = i18n.reserved_interpolations(**opt.slice(:locale))
|
|
27
|
+
print_forest forest, opt, :reserved_interpolations
|
|
28
|
+
:exit1 unless forest.empty?
|
|
29
|
+
end
|
|
19
30
|
end
|
|
20
31
|
end
|
|
21
32
|
end
|
|
@@ -7,28 +7,28 @@ module I18n::Tasks
|
|
|
7
7
|
include Command::Collection
|
|
8
8
|
|
|
9
9
|
cmd :config,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
pos: "[section ...]",
|
|
11
|
+
desc: t("i18n_tasks.cmd.desc.config")
|
|
12
12
|
|
|
13
13
|
def config(opts = {})
|
|
14
14
|
cfg = i18n.config_for_inspect
|
|
15
15
|
cfg = cfg.slice(*opts[:arguments]) if opts[:arguments].present?
|
|
16
16
|
cfg = cfg.to_yaml
|
|
17
|
-
cfg.sub!(/\A---\n/,
|
|
17
|
+
cfg.sub!(/\A---\n/, "")
|
|
18
18
|
cfg.gsub!(/^([^\s-].+?:)/, Rainbow('\1').cyan.bright)
|
|
19
19
|
puts cfg
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
cmd :gem_path, desc: t(
|
|
22
|
+
cmd :gem_path, desc: t("i18n_tasks.cmd.desc.gem_path")
|
|
23
23
|
|
|
24
24
|
def gem_path
|
|
25
25
|
puts I18n::Tasks.gem_path
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
cmd :irb, desc: t(
|
|
28
|
+
cmd :irb, desc: t("i18n_tasks.cmd.desc.irb")
|
|
29
29
|
|
|
30
30
|
def irb
|
|
31
|
-
require
|
|
31
|
+
require "i18n/tasks/console_context"
|
|
32
32
|
::I18n::Tasks::ConsoleContext.start
|
|
33
33
|
end
|
|
34
34
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "i18n/tasks/command/collection"
|
|
4
4
|
|
|
5
5
|
module I18n::Tasks
|
|
6
6
|
module Command
|
|
@@ -10,22 +10,22 @@ module I18n::Tasks
|
|
|
10
10
|
|
|
11
11
|
missing_types = I18n::Tasks::MissingKeys.missing_keys_types
|
|
12
12
|
arg :missing_types,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
"-t",
|
|
14
|
+
"--types #{missing_types * ","}",
|
|
15
|
+
Array,
|
|
16
|
+
t("i18n_tasks.cmd.args.desc.missing_types", valid: missing_types * ", "),
|
|
17
|
+
parser: OptionParsers::Enum::ListParser.new(
|
|
18
|
+
missing_types,
|
|
19
|
+
proc do |invalid, valid|
|
|
20
|
+
I18n.t("i18n_tasks.cmd.errors.invalid_missing_type",
|
|
21
|
+
invalid: invalid * ", ", valid: valid * ", ", count: invalid.length)
|
|
22
|
+
end
|
|
23
|
+
)
|
|
24
24
|
|
|
25
25
|
cmd :missing,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
pos: "[locale ...]",
|
|
27
|
+
desc: t("i18n_tasks.cmd.desc.missing"),
|
|
28
|
+
args: %i[locales out_format missing_types pattern]
|
|
29
29
|
|
|
30
30
|
def missing(opt = {})
|
|
31
31
|
forest = i18n.missing_keys(**opt.slice(:locales, :base_locale, :types))
|
|
@@ -38,9 +38,9 @@ module I18n::Tasks
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
cmd :translate_missing,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
pos: "[locale ...]",
|
|
42
|
+
desc: t("i18n_tasks.cmd.desc.translate_missing"),
|
|
43
|
+
args: [:locales, :locale_to_translate_from, arg(:out_format).from(1), :translation_backend, :pattern]
|
|
44
44
|
|
|
45
45
|
def translate_missing(opt = {})
|
|
46
46
|
missing = i18n.missing_diff_forest opt[:locales], opt[:from]
|
|
@@ -52,15 +52,15 @@ module I18n::Tasks
|
|
|
52
52
|
backend = opt[:backend].presence || i18n.translation_config[:backend]
|
|
53
53
|
translated = i18n.translate_forest missing, from: opt[:from], backend: backend.to_sym
|
|
54
54
|
i18n.data.merge! translated
|
|
55
|
-
log_stderr t(
|
|
55
|
+
log_stderr t("i18n_tasks.translate_missing.translated", count: translated.leaves.count)
|
|
56
56
|
print_forest translated, opt
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
cmd :add_missing,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
pos: "[locale ...]",
|
|
61
|
+
desc: t("i18n_tasks.cmd.desc.add_missing"),
|
|
62
|
+
args: [:locales, :out_format, :pattern, arg(:value) + [{default: "%{value_or_default_or_human_key}"}],
|
|
63
|
+
["--nil-value", "Set value to nil. Takes precedence over the value argument."]]
|
|
64
64
|
|
|
65
65
|
# Merge base locale first, as this may affect the value for the other locales
|
|
66
66
|
def add_missing(opt = {}) # rubocop:disable Metrics/AbcSize
|
|
@@ -69,7 +69,7 @@ module I18n::Tasks
|
|
|
69
69
|
opt[:locales] - [i18n.base_locale]
|
|
70
70
|
].reject(&:empty?).each_with_object(i18n.empty_forest) do |locales, added|
|
|
71
71
|
forest = i18n.missing_keys(locales: locales, **opt.slice(:types, :base_locale))
|
|
72
|
-
|
|
72
|
+
.set_each_value!(opt[:"nil-value"] ? nil : opt[:value])
|
|
73
73
|
if opt[:pattern]
|
|
74
74
|
pattern_re = i18n.compile_key_pattern(opt[:pattern])
|
|
75
75
|
forest.select_keys! { |full_key, _node| full_key =~ pattern_re }
|
|
@@ -77,7 +77,7 @@ module I18n::Tasks
|
|
|
77
77
|
i18n.data.merge! forest
|
|
78
78
|
added.merge! forest
|
|
79
79
|
end.tap do |added|
|
|
80
|
-
log_stderr t(
|
|
80
|
+
log_stderr t("i18n_tasks.add_missing.added", count: added.leaves.count)
|
|
81
81
|
print_forest added, opt
|
|
82
82
|
end
|
|
83
83
|
end
|
|
@@ -8,9 +8,9 @@ module I18n::Tasks
|
|
|
8
8
|
include I18n::Tasks::KeyPatternMatching
|
|
9
9
|
|
|
10
10
|
cmd :tree_translate,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
pos: "[tree (or stdin)]",
|
|
12
|
+
desc: t("i18n_tasks.cmd.desc.tree_translate"),
|
|
13
|
+
args: [:locale_to_translate_from, arg(:data_format).from(1), :translation_backend]
|
|
14
14
|
|
|
15
15
|
def tree_translate(opts = {})
|
|
16
16
|
forest = forest_pos_or_stdin!(opts)
|
|
@@ -18,79 +18,79 @@ module I18n::Tasks
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
cmd :tree_merge,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
pos: "[[tree] [tree] ... (or stdin)]",
|
|
22
|
+
desc: t("i18n_tasks.cmd.desc.tree_merge"),
|
|
23
|
+
args: %i[data_format nostdin]
|
|
24
24
|
|
|
25
25
|
def tree_merge(opts = {})
|
|
26
26
|
print_forest merge_forests_stdin_and_pos!(opts), opts
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
cmd :tree_filter,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
pos: "[pattern] [tree (or stdin)]",
|
|
31
|
+
desc: t("i18n_tasks.cmd.desc.tree_filter"),
|
|
32
|
+
args: %i[data_format pattern]
|
|
33
33
|
|
|
34
34
|
def tree_filter(opts = {})
|
|
35
35
|
pattern = arg_or_pos! :pattern, opts
|
|
36
|
-
forest
|
|
36
|
+
forest = forest_pos_or_stdin! opts
|
|
37
37
|
unless pattern.blank?
|
|
38
38
|
pattern_re = i18n.compile_key_pattern(pattern)
|
|
39
|
-
forest
|
|
39
|
+
forest = forest.select_keys { |full_key, _node| full_key =~ pattern_re }
|
|
40
40
|
end
|
|
41
41
|
print_forest forest, opts
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
arg :all_locales,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
"-a",
|
|
46
|
+
"--all-locales",
|
|
47
|
+
t("i18n_tasks.cmd.args.desc.all_locales")
|
|
48
48
|
|
|
49
49
|
cmd :tree_mv,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
pos: "FROM_KEY_PATTERN TO_KEY_PATTERN [tree (or stdin)]",
|
|
51
|
+
desc: t("i18n_tasks.cmd.desc.tree_mv_key"),
|
|
52
|
+
args: %i[data_format all_locales]
|
|
53
53
|
def tree_mv(opt = {})
|
|
54
|
-
fail CommandError,
|
|
54
|
+
fail CommandError, "requires FROM_KEY_PATTERN and TO_KEY_PATTERN" if opt[:arguments].size < 2
|
|
55
55
|
|
|
56
56
|
from_pattern = opt[:arguments].shift
|
|
57
57
|
to_pattern = opt[:arguments].shift
|
|
58
58
|
forest = forest_pos_or_stdin!(opt)
|
|
59
|
-
forest.mv_key!(compile_key_pattern(from_pattern), to_pattern, root: !opt[:
|
|
59
|
+
forest.mv_key!(compile_key_pattern(from_pattern), to_pattern, root: !opt[:"all-locales"])
|
|
60
60
|
print_forest forest, opt
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
cmd :tree_subtract,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
pos: "[[tree] [tree] ... (or stdin)]",
|
|
65
|
+
desc: t("i18n_tasks.cmd.desc.tree_subtract"),
|
|
66
|
+
args: %i[data_format nostdin]
|
|
67
67
|
|
|
68
68
|
def tree_subtract(opt = {})
|
|
69
69
|
forests = forests_stdin_and_pos! opt, 2
|
|
70
|
-
forest
|
|
70
|
+
forest = forests.reduce(:subtract_by_key) || empty_forest
|
|
71
71
|
print_forest forest, opt
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
cmd :tree_set_value,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
pos: "[VALUE] [tree (or stdin)]",
|
|
76
|
+
desc: t("i18n_tasks.cmd.desc.tree_set_value"),
|
|
77
|
+
args: %i[value data_format nostdin pattern]
|
|
78
78
|
|
|
79
79
|
def tree_set_value(opt = {})
|
|
80
|
-
value
|
|
81
|
-
forest
|
|
80
|
+
value = arg_or_pos! :value, opt
|
|
81
|
+
forest = forest_pos_or_stdin!(opt)
|
|
82
82
|
key_pattern = opt[:pattern]
|
|
83
|
-
fail CommandError,
|
|
83
|
+
fail CommandError, "pass value (-v, --value)" if value.blank?
|
|
84
84
|
|
|
85
85
|
forest.set_each_value!(value, key_pattern)
|
|
86
86
|
print_forest forest, opt
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
cmd :tree_convert,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
pos: "[tree (or stdin)]",
|
|
91
|
+
desc: t("i18n_tasks.cmd.desc.tree_convert"),
|
|
92
|
+
args: [arg(:data_format).dup.tap { |a| a[0..1] = ["-f", "--from FORMAT"] },
|
|
93
|
+
arg(:out_format).dup.tap { |a| a[0..1] = ["-t", "--to FORMAT"] }]
|
|
94
94
|
|
|
95
95
|
def tree_convert(opt = {})
|
|
96
96
|
forest = forest_pos_or_stdin! opt.merge(format: opt[:from])
|