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
|
@@ -35,7 +35,7 @@ module I18n::Tasks::Concurrent
|
|
|
35
35
|
# Below are the implementations for major ruby engines, based on concurrent-ruby.
|
|
36
36
|
# rubocop:disable Lint/DuplicateMethods,Naming/AccessorMethodName
|
|
37
37
|
case RUBY_ENGINE
|
|
38
|
-
when
|
|
38
|
+
when "rbx"
|
|
39
39
|
def get_result_volatile
|
|
40
40
|
Rubinius.memory_barrier
|
|
41
41
|
@result
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
4
4
|
DEFAULTS = {
|
|
5
|
-
base_locale:
|
|
6
|
-
internal_locale:
|
|
5
|
+
base_locale: "en",
|
|
6
|
+
internal_locale: "en",
|
|
7
7
|
search: ::I18n::Tasks::UsedKeys::SEARCH_DEFAULTS,
|
|
8
|
-
data: ::I18n::Tasks::Data::DATA_DEFAULTS
|
|
8
|
+
data: ::I18n::Tasks::Data::DATA_DEFAULTS,
|
|
9
|
+
translation_backend: :google
|
|
9
10
|
}.freeze
|
|
10
11
|
|
|
11
12
|
# i18n-tasks config (defaults + config/i18n-tasks.yml)
|
|
@@ -20,20 +21,20 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
|
20
21
|
].freeze
|
|
21
22
|
|
|
22
23
|
def file_config
|
|
23
|
-
file
|
|
24
|
+
file = @config_override || CONFIG_FILES.detect { |f| File.exist?(f) }
|
|
24
25
|
# rubocop:disable Security/Eval
|
|
25
|
-
config = file && YAML.load(eval(Erubi::Engine.new(File.read(file, encoding:
|
|
26
|
+
config = file && YAML.load(eval(Erubi::Engine.new(File.read(file, encoding: "UTF-8")).src))
|
|
26
27
|
# rubocop:enable Security/Eval
|
|
27
28
|
if config.present?
|
|
28
29
|
config.with_indifferent_access.tap do |c|
|
|
29
30
|
if c[:relative_roots]
|
|
30
|
-
warn_deprecated
|
|
31
|
+
warn_deprecated "Please move relative_roots under search in config/i18n-tasks.yml."
|
|
31
32
|
c[:search][:relative_roots] = c.delete(:relative_roots)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
if c.dig(:search, :exclude_method_name_paths)
|
|
35
36
|
warn_deprecated(
|
|
36
|
-
|
|
37
|
+
"Please rename exclude_method_name_paths to relative_exclude_method_name_paths in config/i18n-tasks.yml."
|
|
37
38
|
)
|
|
38
39
|
c[:search][:relative_exclude_method_name_paths] = c[:search].delete(:exclude_method_name_paths)
|
|
39
40
|
end
|
|
@@ -59,16 +60,21 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
|
59
60
|
|
|
60
61
|
# translation config
|
|
61
62
|
# @return [Hash{String => String,Hash,Array}]
|
|
62
|
-
def translation_config
|
|
63
|
+
def translation_config # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
63
64
|
@config_sections[:translation] ||= begin
|
|
64
65
|
conf = (config[:translation] || {}).with_indifferent_access
|
|
65
|
-
conf[:
|
|
66
|
-
conf[:
|
|
67
|
-
conf[:
|
|
68
|
-
conf[:
|
|
69
|
-
conf[:
|
|
70
|
-
conf[:
|
|
71
|
-
conf[:
|
|
66
|
+
conf[:backend] ||= DEFAULTS[:translation_backend]
|
|
67
|
+
conf[:google_translate_api_key] = ENV["GOOGLE_TRANSLATE_API_KEY"] if ENV.key?("GOOGLE_TRANSLATE_API_KEY")
|
|
68
|
+
conf[:deepl_api_key] = ENV["DEEPL_AUTH_KEY"] if ENV.key?("DEEPL_AUTH_KEY")
|
|
69
|
+
conf[:deepl_host] = ENV["DEEPL_HOST"] if ENV.key?("DEEPL_HOST")
|
|
70
|
+
conf[:deepl_version] = ENV["DEEPL_VERSION"] if ENV.key?("DEEPL_VERSION")
|
|
71
|
+
conf[:openai_api_key] = ENV["OPENAI_API_KEY"] if ENV.key?("OPENAI_API_KEY")
|
|
72
|
+
conf[:openai_model] = ENV["OPENAI_MODEL"] if ENV.key?("OPENAI_MODEL")
|
|
73
|
+
conf[:openai_locale_prompts] ||= {}
|
|
74
|
+
conf[:watsonx_api_key] = ENV["WATSONX_API_KEY"] if ENV.key?("WATSONX_API_KEY")
|
|
75
|
+
conf[:watsonx_project_id] = ENV["WATSONX_PROJECT_ID"] if ENV.key?("WATSONX_PROJECT_ID")
|
|
76
|
+
conf[:watsonx_model] = ENV["WATSONX_MODEL"] if ENV.key?("WATSONX_MODEL")
|
|
77
|
+
conf[:yandex_api_key] = ENV["YANDEX_API_KEY"] if ENV.key?("YANDEX_API_KEY")
|
|
72
78
|
conf
|
|
73
79
|
end
|
|
74
80
|
end
|
|
@@ -86,11 +92,11 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
|
86
92
|
def internal_locale
|
|
87
93
|
@config_sections[:internal_locale] ||= begin
|
|
88
94
|
internal_locale = (config[:internal_locale] || DEFAULTS[:internal_locale]).to_s
|
|
89
|
-
valid_locales = Dir[File.join(I18n::Tasks.gem_path,
|
|
90
|
-
|
|
95
|
+
valid_locales = Dir[File.join(I18n::Tasks.gem_path, "config", "locales", "*.yml")]
|
|
96
|
+
.map { |f| File.basename(f, ".yml") }
|
|
91
97
|
unless valid_locales.include?(internal_locale)
|
|
92
98
|
log_warn "invalid internal_locale #{internal_locale.inspect}. " \
|
|
93
|
-
"Available internal locales: #{valid_locales *
|
|
99
|
+
"Available internal locales: #{valid_locales * ", "}."
|
|
94
100
|
internal_locale = DEFAULTS[:internal_locale].to_s
|
|
95
101
|
end
|
|
96
102
|
internal_locale
|
|
@@ -98,7 +104,7 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
|
98
104
|
end
|
|
99
105
|
|
|
100
106
|
def ignore_config(type = nil)
|
|
101
|
-
key = type ? "ignore_#{type}" :
|
|
107
|
+
key = type ? "ignore_#{type}" : "ignore"
|
|
102
108
|
@config_sections[key] ||= config[key]
|
|
103
109
|
end
|
|
104
110
|
|
|
@@ -121,7 +127,7 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
|
|
|
121
127
|
def config_for_inspect
|
|
122
128
|
to_hash_from_indifferent(config_sections.reject { |_k, v| v.blank? }).tap do |sections|
|
|
123
129
|
sections.each_value do |section|
|
|
124
|
-
section.merge! section.delete(
|
|
130
|
+
section.merge! section.delete("config") if section.is_a?(Hash) && section.key?("config")
|
|
125
131
|
end
|
|
126
132
|
end
|
|
127
133
|
end
|
|
@@ -16,12 +16,12 @@ module I18n::Tasks
|
|
|
16
16
|
|
|
17
17
|
class << self
|
|
18
18
|
def start
|
|
19
|
-
require
|
|
19
|
+
require "irb"
|
|
20
20
|
IRB.setup nil
|
|
21
21
|
ctx = IRB::Irb.new.context
|
|
22
22
|
IRB.conf[:MAIN_CONTEXT] = ctx
|
|
23
|
-
|
|
24
|
-
require
|
|
23
|
+
warn Messages.banner
|
|
24
|
+
require "irb/ext/multi-irb"
|
|
25
25
|
IRB.irb nil, new
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -30,18 +30,18 @@ module I18n::Tasks
|
|
|
30
30
|
module_function
|
|
31
31
|
|
|
32
32
|
def banner
|
|
33
|
-
Rainbow("i18n-tasks v#{I18n::Tasks::VERSION} IRB").bright + "\nType #{Rainbow(
|
|
33
|
+
Rainbow("i18n-tasks v#{I18n::Tasks::VERSION} IRB").bright + "\nType #{Rainbow("guide").green} to learn more"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def guide
|
|
37
|
-
"#{Rainbow(
|
|
38
|
-
#{Rainbow(
|
|
37
|
+
"#{Rainbow("i18n-tasks IRB Quick Start guide").green.bright}\n#{<<~TEXT}"
|
|
38
|
+
#{Rainbow("Data as trees").yellow}
|
|
39
39
|
tree(locale)
|
|
40
40
|
used_tree(key_filter: nil, strict: nil)
|
|
41
41
|
unused_tree(locale: base_locale, strict: nil)
|
|
42
42
|
build_tree('es' => {'hello' => 'Hola'})
|
|
43
43
|
|
|
44
|
-
#{Rainbow(
|
|
44
|
+
#{Rainbow("Traversal").yellow}
|
|
45
45
|
tree = missing_diff_tree('es')
|
|
46
46
|
tree.nodes { |node| }
|
|
47
47
|
tree.nodes.to_a
|
|
@@ -49,23 +49,23 @@ module I18n::Tasks
|
|
|
49
49
|
tree.each { |root_node| }
|
|
50
50
|
# also levels, depth_first, and breadth_first
|
|
51
51
|
|
|
52
|
-
#{Rainbow(
|
|
52
|
+
#{Rainbow("Select nodes").yellow}
|
|
53
53
|
tree.select_nodes { |node| } # new tree with only selected nodes
|
|
54
54
|
|
|
55
|
-
#{Rainbow(
|
|
55
|
+
#{Rainbow("Match by full key").yellow}
|
|
56
56
|
tree.select_keys { |key, leaf| } # new tree with only selected keys
|
|
57
57
|
tree.grep_keys(/hello/) # grep, using ===
|
|
58
58
|
tree.keys { |key, leaf| } # enumerate over [full_key, leaf_node]
|
|
59
59
|
# Pass {root: true} to include root node in full_key (usually locale)
|
|
60
60
|
|
|
61
|
-
#{Rainbow(
|
|
61
|
+
#{Rainbow("Nodes").yellow}
|
|
62
62
|
node = node(key, locale)
|
|
63
63
|
node.key # only the part after the last dot
|
|
64
64
|
node.full_key # full key. Includes root key, pass {root: false} to override.
|
|
65
65
|
# also: value, value_or_children_hash, data, walk_to_root, walk_from_root
|
|
66
66
|
Tree::Node.new(key: 'en')
|
|
67
67
|
|
|
68
|
-
#{Rainbow(
|
|
68
|
+
#{Rainbow("Keys").yellow}
|
|
69
69
|
t(key, locale)
|
|
70
70
|
key_value?(key, locale)
|
|
71
71
|
depluralize_key(key, locale) # convert 'hat.one' to 'hat'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "yaml"
|
|
4
4
|
module I18n::Tasks
|
|
5
5
|
module Data
|
|
6
6
|
module Adapter
|
|
7
7
|
module YamlAdapter
|
|
8
|
-
EMOJI_REGEX = /\\u[\da-f]{8}/i
|
|
9
|
-
TRAILING_SPACE_REGEX = /
|
|
8
|
+
EMOJI_REGEX = /\\u[\da-f]{8}/i
|
|
9
|
+
TRAILING_SPACE_REGEX = / $/
|
|
10
10
|
|
|
11
11
|
class << self
|
|
12
12
|
# @return [Hash] locale tree
|
|
@@ -26,12 +26,12 @@ module I18n::Tasks
|
|
|
26
26
|
|
|
27
27
|
# @return [String]
|
|
28
28
|
def restore_emojis(yaml)
|
|
29
|
-
yaml.gsub(EMOJI_REGEX) { |m| [m[-8..].to_i(16)].pack(
|
|
29
|
+
yaml.gsub(EMOJI_REGEX) { |m| [m[-8..].to_i(16)].pack("U") }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
# @return [String]
|
|
33
33
|
def strip_trailing_spaces(yaml)
|
|
34
|
-
yaml.gsub(TRAILING_SPACE_REGEX,
|
|
34
|
+
yaml.gsub(TRAILING_SPACE_REGEX, "")
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "fileutils"
|
|
4
4
|
|
|
5
5
|
module I18n
|
|
6
6
|
module Tasks
|
|
@@ -21,7 +21,7 @@ module I18n
|
|
|
21
21
|
|
|
22
22
|
def adapter_op(op, format, tree, config)
|
|
23
23
|
self.class.adapter_by_name(format).send(op, tree, config)
|
|
24
|
-
rescue
|
|
24
|
+
rescue => e
|
|
25
25
|
raise CommandError, "#{format} #{op} error: #{e.message}"
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -44,7 +44,7 @@ module I18n
|
|
|
44
44
|
|
|
45
45
|
# @return [String]
|
|
46
46
|
def read_file(path)
|
|
47
|
-
::File.read(path, encoding:
|
|
47
|
+
::File.read(path, encoding: "UTF-8")
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def write_tree(path, tree, sort = true)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
3
|
+
require "i18n/tasks/data/file_system_base"
|
|
4
|
+
require "i18n/tasks/data/adapter/json_adapter"
|
|
5
|
+
require "i18n/tasks/data/adapter/yaml_adapter"
|
|
6
6
|
|
|
7
7
|
module I18n::Tasks
|
|
8
8
|
module Data
|
|
9
9
|
class FileSystem < FileSystemBase
|
|
10
|
-
register_adapter :yaml,
|
|
11
|
-
register_adapter :json,
|
|
10
|
+
register_adapter :yaml, "*.yml", Adapter::YamlAdapter
|
|
11
|
+
register_adapter :json, "*.json", Adapter::JsonAdapter
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
3
|
+
require "i18n/tasks/data/tree/node"
|
|
4
|
+
require "i18n/tasks/data/router/pattern_router"
|
|
5
|
+
require "i18n/tasks/data/router/conservative_router"
|
|
6
|
+
require "i18n/tasks/data/router/isolating_router"
|
|
7
|
+
require "i18n/tasks/data/file_formats"
|
|
8
|
+
require "i18n/tasks/key_pattern_matching"
|
|
9
9
|
|
|
10
10
|
module I18n::Tasks
|
|
11
11
|
module Data
|
|
@@ -18,8 +18,8 @@ module I18n::Tasks
|
|
|
18
18
|
attr_writer :router
|
|
19
19
|
|
|
20
20
|
DEFAULTS = {
|
|
21
|
-
read: [
|
|
22
|
-
write: [
|
|
21
|
+
read: ["config/locales/%{locale}.yml"],
|
|
22
|
+
write: ["config/locales/%{locale}.yml"]
|
|
23
23
|
}.freeze
|
|
24
24
|
|
|
25
25
|
def initialize(config = {})
|
|
@@ -29,9 +29,9 @@ module I18n::Tasks
|
|
|
29
29
|
locales = config[:locales].presence
|
|
30
30
|
@locales = LocaleList.normalize_locale_list(locales || available_locales, base_locale, true)
|
|
31
31
|
if locales.present?
|
|
32
|
-
log_verbose "locales read from config #{@locales *
|
|
32
|
+
log_verbose "locales read from config #{@locales * ", "}"
|
|
33
33
|
else
|
|
34
|
-
log_verbose "locales inferred from data: #{@locales *
|
|
34
|
+
log_verbose "locales inferred from data: #{@locales * ", "}"
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -43,7 +43,7 @@ module I18n::Tasks
|
|
|
43
43
|
@trees[locale] ||= read_locale(locale)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
alias_method :[], :get
|
|
47
47
|
|
|
48
48
|
# @param [String, Symbol] locale
|
|
49
49
|
# @return [::I18n::Tasks::Data::Siblings]
|
|
@@ -72,14 +72,14 @@ module I18n::Tasks
|
|
|
72
72
|
@available_locales = nil
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
alias_method :[]=, :set
|
|
76
76
|
|
|
77
77
|
# @param [String] locale
|
|
78
78
|
# @return [Array<String>] paths to files that are not normalized
|
|
79
79
|
def non_normalized_paths(locale)
|
|
80
80
|
router.route(locale, get(locale))
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
.reject { |path, tree_slice| normalized?(path, tree_slice) }
|
|
82
|
+
.map(&:first)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def write(forest)
|
|
@@ -107,7 +107,7 @@ module I18n::Tasks
|
|
|
107
107
|
|
|
108
108
|
# @return self
|
|
109
109
|
def reload
|
|
110
|
-
@trees
|
|
110
|
+
@trees = nil
|
|
111
111
|
@available_locales = nil
|
|
112
112
|
self
|
|
113
113
|
end
|
|
@@ -117,10 +117,10 @@ module I18n::Tasks
|
|
|
117
117
|
@available_locales ||= begin
|
|
118
118
|
locales = Set.new
|
|
119
119
|
Array(config[:read]).map do |pattern|
|
|
120
|
-
[pattern, Dir.glob(format(pattern, locale:
|
|
120
|
+
[pattern, Dir.glob(format(pattern, locale: "*"))] if pattern.include?("%{locale}")
|
|
121
121
|
end.compact.each do |pattern, paths|
|
|
122
|
-
p
|
|
123
|
-
p
|
|
122
|
+
p = pattern.gsub("\\", "\\\\").gsub("/", '\/').gsub(".", '\.')
|
|
123
|
+
p = p.gsub(/(\*+)/) { (Regexp.last_match(1) == "**") ? ".*" : "[^/]*?" }.gsub("%{locale}", "([^/.]+)")
|
|
124
124
|
re = /\A#{p}\z/
|
|
125
125
|
paths.each do |path|
|
|
126
126
|
locales << Regexp.last_match(1) if re =~ path
|
|
@@ -143,7 +143,7 @@ module I18n::Tasks
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def with_router(router)
|
|
146
|
-
router_was
|
|
146
|
+
router_was = self.router
|
|
147
147
|
self.router = router
|
|
148
148
|
yield
|
|
149
149
|
ensure
|
|
@@ -151,13 +151,13 @@ module I18n::Tasks
|
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
ROUTER_NAME_ALIASES = {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
"conservative_router" => "I18n::Tasks::Data::Router::ConservativeRouter",
|
|
155
|
+
"isolating_router" => "I18n::Tasks::Data::Router::IsolatingRouter",
|
|
156
|
+
"pattern_router" => "I18n::Tasks::Data::Router::PatternRouter"
|
|
157
157
|
}.freeze
|
|
158
158
|
def router
|
|
159
159
|
@router ||= begin
|
|
160
|
-
name = @config[:router].presence ||
|
|
160
|
+
name = @config[:router].presence || "conservative_router"
|
|
161
161
|
name = ROUTER_NAME_ALIASES[name] || name
|
|
162
162
|
router_class = ActiveSupport::Inflector.constantize(name)
|
|
163
163
|
router_class.new(self, @config.merge(base_locale: base_locale, locales: locales))
|
|
@@ -173,7 +173,7 @@ module I18n::Tasks
|
|
|
173
173
|
[path.freeze, load_file(path) || {}]
|
|
174
174
|
end.map do |path, data|
|
|
175
175
|
if router.is_a?(I18n::Tasks::Data::Router::IsolatingRouter)
|
|
176
|
-
data.transform_values! { |tree| {
|
|
176
|
+
data.transform_values! { |tree| {"<#{router.alternate_path_for(path, base_locale)}>" => tree} }
|
|
177
177
|
end
|
|
178
178
|
filter_nil_keys! path, data
|
|
179
179
|
Data::Tree::Siblings.from_nested_hash(data).tap do |s|
|
|
@@ -188,11 +188,11 @@ module I18n::Tasks
|
|
|
188
188
|
data.delete(key)
|
|
189
189
|
log_warn <<~TEXT
|
|
190
190
|
Skipping a nil key found in #{path.inspect}:
|
|
191
|
-
key: #{suffix.join(
|
|
191
|
+
key: #{suffix.join(".")}.`nil`
|
|
192
192
|
value: #{value.inspect}
|
|
193
193
|
Nil keys are not supported by i18n.
|
|
194
194
|
The following unquoted YAML keys result in a nil key:
|
|
195
|
-
#{%w[null Null NULL ~].join(
|
|
195
|
+
#{%w[null Null NULL ~].join(", ")}
|
|
196
196
|
See http://yaml.org/type/null.html
|
|
197
197
|
TEXT
|
|
198
198
|
elsif value.is_a?(Hash)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n
|
|
4
|
+
module Tasks
|
|
5
|
+
module Data
|
|
6
|
+
module LanguageNames # rubocop:disable Metrics/ModuleLength
|
|
7
|
+
# Data loaded from https://github.com/grosser/i18n_data
|
|
8
|
+
# I18nData.languages.transform_keys!(&:to_sym).transform_values!{|v| v.split(";").first }
|
|
9
|
+
DATA = {
|
|
10
|
+
AA: "Afar",
|
|
11
|
+
AB: "Abkhazian",
|
|
12
|
+
AF: "Afrikaans",
|
|
13
|
+
AK: "Akan",
|
|
14
|
+
AM: "Amharic",
|
|
15
|
+
AR: "Arabic",
|
|
16
|
+
AN: "Aragonese",
|
|
17
|
+
AS: "Assamese",
|
|
18
|
+
AV: "Avaric",
|
|
19
|
+
AE: "Avestan",
|
|
20
|
+
AY: "Aymara",
|
|
21
|
+
AZ: "Azerbaijani",
|
|
22
|
+
BA: "Bashkir",
|
|
23
|
+
BM: "Bambara",
|
|
24
|
+
BE: "Belarusian",
|
|
25
|
+
BN: "Bengali",
|
|
26
|
+
BH: "Bihari languages",
|
|
27
|
+
BI: "Bislama",
|
|
28
|
+
BO: "Tibetan",
|
|
29
|
+
BS: "Bosnian",
|
|
30
|
+
BR: "Breton",
|
|
31
|
+
BG: "Bulgarian",
|
|
32
|
+
CA: "Catalan",
|
|
33
|
+
CS: "Czech",
|
|
34
|
+
CH: "Chamorro",
|
|
35
|
+
CE: "Chechen",
|
|
36
|
+
CU: "Church Slavic",
|
|
37
|
+
CV: "Chuvash",
|
|
38
|
+
KW: "Cornish",
|
|
39
|
+
CO: "Corsican",
|
|
40
|
+
CR: "Cree",
|
|
41
|
+
CY: "Welsh",
|
|
42
|
+
DA: "Danish",
|
|
43
|
+
DE: "German",
|
|
44
|
+
DV: "Divehi",
|
|
45
|
+
DZ: "Dzongkha",
|
|
46
|
+
EL: "Greek, Modern (1453-)",
|
|
47
|
+
EN: "English",
|
|
48
|
+
EO: "Esperanto",
|
|
49
|
+
ET: "Estonian",
|
|
50
|
+
EU: "Basque",
|
|
51
|
+
EE: "Ewe",
|
|
52
|
+
FO: "Faroese",
|
|
53
|
+
FA: "Persian",
|
|
54
|
+
FJ: "Fijian",
|
|
55
|
+
FI: "Finnish",
|
|
56
|
+
FR: "French",
|
|
57
|
+
FY: "Western Frisian",
|
|
58
|
+
FF: "Fulah",
|
|
59
|
+
GD: "Gaelic",
|
|
60
|
+
GA: "Irish",
|
|
61
|
+
GL: "Galician",
|
|
62
|
+
GV: "Manx",
|
|
63
|
+
GN: "Guarani",
|
|
64
|
+
GU: "Gujarati",
|
|
65
|
+
HT: "Haitian",
|
|
66
|
+
HA: "Hausa",
|
|
67
|
+
HE: "Hebrew",
|
|
68
|
+
HZ: "Herero",
|
|
69
|
+
HI: "Hindi",
|
|
70
|
+
HO: "Hiri Motu",
|
|
71
|
+
HR: "Croatian",
|
|
72
|
+
HU: "Hungarian",
|
|
73
|
+
HY: "Armenian",
|
|
74
|
+
IG: "Igbo",
|
|
75
|
+
IO: "Ido",
|
|
76
|
+
II: "Sichuan Yi",
|
|
77
|
+
IU: "Inuktitut",
|
|
78
|
+
IE: "Interlingue",
|
|
79
|
+
IA: "Interlingua (International Auxiliary Language Association)",
|
|
80
|
+
ID: "Indonesian",
|
|
81
|
+
IK: "Inupiaq",
|
|
82
|
+
IS: "Icelandic",
|
|
83
|
+
IT: "Italian",
|
|
84
|
+
JV: "Javanese",
|
|
85
|
+
JA: "Japanese",
|
|
86
|
+
KL: "Kalaallisut",
|
|
87
|
+
KN: "Kannada",
|
|
88
|
+
KS: "Kashmiri",
|
|
89
|
+
KA: "Georgian",
|
|
90
|
+
KR: "Kanuri",
|
|
91
|
+
KK: "Kazakh",
|
|
92
|
+
KM: "Central Khmer",
|
|
93
|
+
KI: "Kikuyu",
|
|
94
|
+
RW: "Kinyarwanda",
|
|
95
|
+
KY: "Kirghiz",
|
|
96
|
+
KV: "Komi",
|
|
97
|
+
KG: "Kongo",
|
|
98
|
+
KO: "Korean",
|
|
99
|
+
KJ: "Kuanyama",
|
|
100
|
+
KU: "Kurdish",
|
|
101
|
+
LO: "Lao",
|
|
102
|
+
LA: "Latin",
|
|
103
|
+
LV: "Latvian",
|
|
104
|
+
LI: "Limburgan",
|
|
105
|
+
LN: "Lingala",
|
|
106
|
+
LT: "Lithuanian",
|
|
107
|
+
LB: "Luxembourgish",
|
|
108
|
+
LU: "Luba-Katanga",
|
|
109
|
+
LG: "Ganda",
|
|
110
|
+
MH: "Marshallese",
|
|
111
|
+
ML: "Malayalam",
|
|
112
|
+
MR: "Marathi",
|
|
113
|
+
MK: "Macedonian",
|
|
114
|
+
MG: "Malagasy",
|
|
115
|
+
MT: "Maltese",
|
|
116
|
+
MN: "Mongolian",
|
|
117
|
+
MI: "Maori",
|
|
118
|
+
MS: "Malay",
|
|
119
|
+
MY: "Burmese",
|
|
120
|
+
NA: "Nauru",
|
|
121
|
+
NV: "Navajo",
|
|
122
|
+
NR: "Ndebele, South",
|
|
123
|
+
ND: "Ndebele, North",
|
|
124
|
+
NG: "Ndonga",
|
|
125
|
+
NE: "Nepali",
|
|
126
|
+
NL: "Dutch",
|
|
127
|
+
NN: "Norwegian Nynorsk",
|
|
128
|
+
NB: "Bokmål, Norwegian",
|
|
129
|
+
NO: "Norwegian",
|
|
130
|
+
NY: "Chichewa",
|
|
131
|
+
OC: "Occitan (post 1500)",
|
|
132
|
+
OJ: "Ojibwa",
|
|
133
|
+
OR: "Oriya",
|
|
134
|
+
OM: "Oromo",
|
|
135
|
+
OS: "Ossetian",
|
|
136
|
+
PA: "Panjabi",
|
|
137
|
+
PI: "Pali",
|
|
138
|
+
PL: "Polish",
|
|
139
|
+
PT: "Portuguese",
|
|
140
|
+
PS: "Pushto",
|
|
141
|
+
QU: "Quechua",
|
|
142
|
+
RM: "Romansh",
|
|
143
|
+
RO: "Romanian",
|
|
144
|
+
RN: "Rundi",
|
|
145
|
+
RU: "Russian",
|
|
146
|
+
SG: "Sango",
|
|
147
|
+
SA: "Sanskrit",
|
|
148
|
+
SI: "Sinhala",
|
|
149
|
+
SK: "Slovak",
|
|
150
|
+
SL: "Slovenian",
|
|
151
|
+
SE: "Northern Sami",
|
|
152
|
+
SM: "Samoan",
|
|
153
|
+
SN: "Shona",
|
|
154
|
+
SD: "Sindhi",
|
|
155
|
+
SO: "Somali",
|
|
156
|
+
ST: "Sotho, Southern",
|
|
157
|
+
ES: "Spanish",
|
|
158
|
+
SQ: "Albanian",
|
|
159
|
+
SC: "Sardinian",
|
|
160
|
+
SR: "Serbian",
|
|
161
|
+
SS: "Swati",
|
|
162
|
+
SU: "Sundanese",
|
|
163
|
+
SW: "Swahili",
|
|
164
|
+
SV: "Swedish",
|
|
165
|
+
TY: "Tahitian",
|
|
166
|
+
TA: "Tamil",
|
|
167
|
+
TT: "Tatar",
|
|
168
|
+
TE: "Telugu",
|
|
169
|
+
TG: "Tajik",
|
|
170
|
+
TL: "Tagalog",
|
|
171
|
+
TH: "Thai",
|
|
172
|
+
TI: "Tigrinya",
|
|
173
|
+
TO: "Tonga (Tonga Islands)",
|
|
174
|
+
TN: "Tswana",
|
|
175
|
+
TS: "Tsonga",
|
|
176
|
+
TK: "Turkmen",
|
|
177
|
+
TR: "Turkish",
|
|
178
|
+
TW: "Twi",
|
|
179
|
+
UG: "Uighur",
|
|
180
|
+
UK: "Ukrainian",
|
|
181
|
+
UR: "Urdu",
|
|
182
|
+
UZ: "Uzbek",
|
|
183
|
+
VE: "Venda",
|
|
184
|
+
VI: "Vietnamese",
|
|
185
|
+
VO: "Volapük",
|
|
186
|
+
WA: "Walloon",
|
|
187
|
+
WO: "Wolof",
|
|
188
|
+
XH: "Xhosa",
|
|
189
|
+
YI: "Yiddish",
|
|
190
|
+
YO: "Yoruba",
|
|
191
|
+
ZA: "Zhuang",
|
|
192
|
+
ZH: "Chinese",
|
|
193
|
+
ZU: "Zulu"
|
|
194
|
+
}.freeze
|
|
195
|
+
|
|
196
|
+
def language_name(locale)
|
|
197
|
+
DATA[locale.upcase.to_sym] || locale
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "i18n/tasks/data/router/pattern_router"
|
|
4
4
|
|
|
5
5
|
module I18n::Tasks
|
|
6
6
|
module Data::Router
|
|
7
7
|
# Keep the path, or infer from base locale
|
|
8
8
|
class ConservativeRouter < PatternRouter
|
|
9
9
|
def initialize(adapter, config)
|
|
10
|
-
@adapter
|
|
10
|
+
@adapter = adapter
|
|
11
11
|
@base_locale = config[:base_locale]
|
|
12
|
-
@locales
|
|
12
|
+
@locales = config[:locales]
|
|
13
13
|
super
|
|
14
14
|
end
|
|
15
15
|
|