i18n-processes 0.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 +7 -0
- data/Gemfile.lock +102 -0
- data/LICENSE.txt +21 -0
- data/README.md +46 -0
- data/Rakefile +12 -0
- data/bin/i18n-processes +28 -0
- data/bin/i18n-processes.cmd +2 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/zh-CN.yml +2 -0
- data/i18n-processes.gemspec +64 -0
- data/lib/i18n/processes/base_process.rb +47 -0
- data/lib/i18n/processes/cli.rb +208 -0
- data/lib/i18n/processes/command/collection.rb +21 -0
- data/lib/i18n/processes/command/commander.rb +43 -0
- data/lib/i18n/processes/command/commands/data.rb +107 -0
- data/lib/i18n/processes/command/commands/eq_base.rb +21 -0
- data/lib/i18n/processes/command/commands/health.rb +26 -0
- data/lib/i18n/processes/command/commands/meta.rb +38 -0
- data/lib/i18n/processes/command/commands/missing.rb +86 -0
- data/lib/i18n/processes/command/commands/preprocessing.rb +90 -0
- data/lib/i18n/processes/command/commands/tree.rb +119 -0
- data/lib/i18n/processes/command/commands/usages.rb +69 -0
- data/lib/i18n/processes/command/commands/xlsx.rb +29 -0
- data/lib/i18n/processes/command/dsl.rb +56 -0
- data/lib/i18n/processes/command/option_parsers/enum.rb +55 -0
- data/lib/i18n/processes/command/option_parsers/locale.rb +60 -0
- data/lib/i18n/processes/command/options/common.rb +41 -0
- data/lib/i18n/processes/command/options/data.rb +95 -0
- data/lib/i18n/processes/command/options/locales.rb +36 -0
- data/lib/i18n/processes/command_error.rb +13 -0
- data/lib/i18n/processes/commands.rb +31 -0
- data/lib/i18n/processes/configuration.rb +132 -0
- data/lib/i18n/processes/console_context.rb +76 -0
- data/lib/i18n/processes/data/adapter/json_adapter.rb +29 -0
- data/lib/i18n/processes/data/adapter/yaml_adapter.rb +27 -0
- data/lib/i18n/processes/data/file_formats.rb +111 -0
- data/lib/i18n/processes/data/file_system.rb +14 -0
- data/lib/i18n/processes/data/file_system_base.rb +205 -0
- data/lib/i18n/processes/data/router/conservative_router.rb +66 -0
- data/lib/i18n/processes/data/router/pattern_router.rb +60 -0
- data/lib/i18n/processes/data/tree/node.rb +204 -0
- data/lib/i18n/processes/data/tree/nodes.rb +97 -0
- data/lib/i18n/processes/data/tree/siblings.rb +333 -0
- data/lib/i18n/processes/data/tree/traversal.rb +190 -0
- data/lib/i18n/processes/data.rb +87 -0
- data/lib/i18n/processes/google_translation.rb +125 -0
- data/lib/i18n/processes/html_keys.rb +16 -0
- data/lib/i18n/processes/ignore_keys.rb +30 -0
- data/lib/i18n/processes/key_pattern_matching.rb +37 -0
- data/lib/i18n/processes/locale_list.rb +19 -0
- data/lib/i18n/processes/locale_pathname.rb +17 -0
- data/lib/i18n/processes/logging.rb +37 -0
- data/lib/i18n/processes/missing_keys.rb +122 -0
- data/lib/i18n/processes/path.rb +42 -0
- data/lib/i18n/processes/plural_keys.rb +41 -0
- data/lib/i18n/processes/rainbow_utils.rb +13 -0
- data/lib/i18n/processes/references.rb +101 -0
- data/lib/i18n/processes/reports/base.rb +71 -0
- data/lib/i18n/processes/reports/spreadsheet.rb +72 -0
- data/lib/i18n/processes/reports/terminal.rb +252 -0
- data/lib/i18n/processes/scanners/file_scanner.rb +65 -0
- data/lib/i18n/processes/scanners/files/caching_file_finder.rb +34 -0
- data/lib/i18n/processes/scanners/files/caching_file_finder_provider.rb +33 -0
- data/lib/i18n/processes/scanners/files/caching_file_reader.rb +28 -0
- data/lib/i18n/processes/scanners/files/file_finder.rb +60 -0
- data/lib/i18n/processes/scanners/files/file_reader.rb +19 -0
- data/lib/i18n/processes/scanners/occurrence_from_position.rb +27 -0
- data/lib/i18n/processes/scanners/pattern_mapper.rb +60 -0
- data/lib/i18n/processes/scanners/pattern_scanner.rb +103 -0
- data/lib/i18n/processes/scanners/pattern_with_scope_scanner.rb +98 -0
- data/lib/i18n/processes/scanners/relative_keys.rb +53 -0
- data/lib/i18n/processes/scanners/results/key_occurrences.rb +54 -0
- data/lib/i18n/processes/scanners/results/occurrence.rb +69 -0
- data/lib/i18n/processes/scanners/ruby_ast_call_finder.rb +62 -0
- data/lib/i18n/processes/scanners/ruby_ast_scanner.rb +206 -0
- data/lib/i18n/processes/scanners/ruby_key_literals.rb +30 -0
- data/lib/i18n/processes/scanners/scanner.rb +17 -0
- data/lib/i18n/processes/scanners/scanner_multiplexer.rb +41 -0
- data/lib/i18n/processes/split_key.rb +68 -0
- data/lib/i18n/processes/stats.rb +24 -0
- data/lib/i18n/processes/string_interpolation.rb +16 -0
- data/lib/i18n/processes/unused_keys.rb +23 -0
- data/lib/i18n/processes/used_keys.rb +177 -0
- data/lib/i18n/processes/version.rb +7 -0
- data/lib/i18n/processes.rb +69 -0
- data/source/p1/_messages/zh/article.properties +9 -0
- data/source/p1/_messages/zh/company.properties +62 -0
- data/source/p1/_messages/zh/devices.properties +40 -0
- data/source/p1/_messages/zh/meeting-rooms.properties +99 -0
- data/source/p1/_messages/zh/meetingBooking.properties +18 -0
- data/source/p1/_messages/zh/office-areas.properties +64 -0
- data/source/p1/_messages/zh/orders.properties +25 -0
- data/source/p1/_messages/zh/schedulings.properties +7 -0
- data/source/p1/_messages/zh/tag.properties +2 -0
- data/source/p1/_messages/zh/ticket.properties +9 -0
- data/source/p1/_messages/zh/visitor.properties +5 -0
- data/source/p1/messages +586 -0
- data/source/p2/orders.properties +25 -0
- data/source/p2/schedulings.properties +7 -0
- data/source/p2/tag.properties +2 -0
- data/source/p2/ticket.properties +9 -0
- data/source/p2/visitor.properties +5 -0
- data/source/zh.messages.ts +30 -0
- data/translated/en/p1/_messages/zh/article.properties +9 -0
- data/translated/en/p1/_messages/zh/company.properties +62 -0
- data/translated/en/p1/_messages/zh/devices.properties +40 -0
- data/translated/en/p1/_messages/zh/meeting-rooms.properties +99 -0
- data/translated/en/p1/_messages/zh/meetingBooking.properties +18 -0
- data/translated/en/p1/_messages/zh/office-areas.properties +64 -0
- data/translated/en/p1/_messages/zh/orders.properties +25 -0
- data/translated/en/p1/_messages/zh/schedulings.properties +7 -0
- data/translated/en/p1/_messages/zh/tag.properties +2 -0
- data/translated/en/p1/_messages/zh/ticket.properties +9 -0
- data/translated/en/p1/_messages/zh/visitor.properties +5 -0
- data/translated/en/p1/messages +586 -0
- data/translated/en/p2/orders.properties +25 -0
- data/translated/en/p2/schedulings.properties +7 -0
- data/translated/en/p2/tag.properties +2 -0
- data/translated/en/p2/ticket.properties +9 -0
- data/translated/en/p2/visitor.properties +5 -0
- data/translated/en/zh.messages.ts +30 -0
- data/translation/en/article.properties +9 -0
- data/translation/en/company.properties +56 -0
- data/translation/en/meeting-rooms.properties +87 -0
- data/translation/en/meetingBooking.properties +14 -0
- data/translation/en/messages.en +164 -0
- data/translation/en/office-areas.properties +51 -0
- data/translation/en/orders.properties +26 -0
- data/translation/en/tag.properties +2 -0
- data/translation/en/translated +1263 -0
- data/translation/en/visitor.properties +4 -0
- metadata +408 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n::Processes
|
|
4
|
+
module Command
|
|
5
|
+
module OptionParsers
|
|
6
|
+
module Enum
|
|
7
|
+
class Parser
|
|
8
|
+
DEFAULT_ERROR = proc do |invalid, valid|
|
|
9
|
+
"#{invalid} is not one of: #{valid * ', '}."
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(valid, error_message = DEFAULT_ERROR)
|
|
13
|
+
@valid = valid.map(&:to_s)
|
|
14
|
+
@error_message = error_message
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call(value, *)
|
|
18
|
+
return @valid.first unless value.present?
|
|
19
|
+
if @valid.include?(value)
|
|
20
|
+
value
|
|
21
|
+
else
|
|
22
|
+
fail CommandError, @error_message.call(value, @valid)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class ListParser
|
|
28
|
+
DEFAULT_ERROR = proc do |invalid, valid|
|
|
29
|
+
"#{invalid * ', '} is not in: #{valid * ', '}."
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(valid, error_message = DEFAULT_ERROR)
|
|
33
|
+
@valid = valid.map(&:to_s)
|
|
34
|
+
@error_message = error_message
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def call(values, *)
|
|
38
|
+
values = Array(values)
|
|
39
|
+
return @valid if values == %w[all]
|
|
40
|
+
invalid = values - @valid
|
|
41
|
+
if invalid.empty?
|
|
42
|
+
if values.empty?
|
|
43
|
+
@valid
|
|
44
|
+
else
|
|
45
|
+
values
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
fail CommandError, @error_message.call(invalid, @valid)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n::Processes
|
|
4
|
+
module Command
|
|
5
|
+
module OptionParsers
|
|
6
|
+
module Locale
|
|
7
|
+
module Validator
|
|
8
|
+
VALID_LOCALE_RE = /\A\w[\w\-\.]*\z/i
|
|
9
|
+
|
|
10
|
+
def validate!(locale)
|
|
11
|
+
if VALID_LOCALE_RE !~ locale
|
|
12
|
+
fail CommandError, "invalid locale: #{locale}"
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
locale
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module Parser
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
extend Validator
|
|
23
|
+
|
|
24
|
+
# @param [#base_locale, #locales] context
|
|
25
|
+
def call(val, context)
|
|
26
|
+
if val.blank? || val == 'base'
|
|
27
|
+
context.base_locale
|
|
28
|
+
else
|
|
29
|
+
validate! val
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module ListParser
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
extend Validator
|
|
38
|
+
|
|
39
|
+
# @param [#base_locale,#locales] context
|
|
40
|
+
def call(vals, context)
|
|
41
|
+
if vals == %w[all] || vals.blank?
|
|
42
|
+
context.locales
|
|
43
|
+
else
|
|
44
|
+
move_base_locale_to_front!(vals.map { |v| v == 'base' ? context.base_locale : v }, context.base_locale)
|
|
45
|
+
end.tap do |locales|
|
|
46
|
+
locales.each { |locale| validate! locale }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def move_base_locale_to_front!(locales, base_locale)
|
|
51
|
+
if (pos = locales.index(base_locale)) && pos.positive?
|
|
52
|
+
locales[pos], locales[0] = locales[0], locales[pos]
|
|
53
|
+
end
|
|
54
|
+
locales
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/dsl'
|
|
4
|
+
|
|
5
|
+
module I18n::Processes
|
|
6
|
+
module Command
|
|
7
|
+
module Options
|
|
8
|
+
module Common
|
|
9
|
+
include Command::DSL
|
|
10
|
+
|
|
11
|
+
arg :nostdin,
|
|
12
|
+
'-S',
|
|
13
|
+
'--nostdin',
|
|
14
|
+
'Do not read from stdin'
|
|
15
|
+
|
|
16
|
+
arg :confirm,
|
|
17
|
+
'-y',
|
|
18
|
+
'--confirm',
|
|
19
|
+
desc: 'Confirm automatically'
|
|
20
|
+
|
|
21
|
+
arg :pattern,
|
|
22
|
+
'-p',
|
|
23
|
+
'--pattern PATTERN',
|
|
24
|
+
"Filter by key pattern (e.g. 'common.*')"
|
|
25
|
+
|
|
26
|
+
arg :value,
|
|
27
|
+
'-v',
|
|
28
|
+
'--value VALUE',
|
|
29
|
+
'Value. Interpolates: %{value}, %{human_key}, %{key}, %{default}, %{value_or_human_key}, %{value_or_default_or_human_key}'
|
|
30
|
+
|
|
31
|
+
def arg_or_pos!(key, opts)
|
|
32
|
+
opts[key] ||= opts[:arguments].try(:shift)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def pos_or_stdin!(opts)
|
|
36
|
+
opts[:arguments].try(:shift) || $stdin.read
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/option_parsers/enum'
|
|
4
|
+
require 'i18n/processes/command/dsl'
|
|
5
|
+
module I18n::Processes
|
|
6
|
+
module Command
|
|
7
|
+
module Options
|
|
8
|
+
module Data
|
|
9
|
+
include Command::DSL
|
|
10
|
+
|
|
11
|
+
DATA_FORMATS = %w[yaml json keys].freeze
|
|
12
|
+
OUT_FORMATS = ['terminal-table', *DATA_FORMATS, 'inspect'].freeze
|
|
13
|
+
|
|
14
|
+
format_arg = proc do |type, values|
|
|
15
|
+
default = values.first
|
|
16
|
+
arg type,
|
|
17
|
+
'-f',
|
|
18
|
+
'--format FORMAT',
|
|
19
|
+
values,
|
|
20
|
+
{ 'yml' => 'yaml' },
|
|
21
|
+
"#{type}: #{values * ', '}",
|
|
22
|
+
parser: OptionParsers::Enum::Parser.new(values,
|
|
23
|
+
proc do |value, valid|
|
|
24
|
+
"invalid format: #{value}. valid: #{valid * ', '}."
|
|
25
|
+
end)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
format_arg.call(:data_format, DATA_FORMATS)
|
|
29
|
+
|
|
30
|
+
format_arg.call(:out_format, OUT_FORMATS)
|
|
31
|
+
|
|
32
|
+
# @return [I18n::Processes::Data::Tree::Siblings]
|
|
33
|
+
def forest_pos_or_stdin!(opt, format = opt[:format])
|
|
34
|
+
parse_forest(pos_or_stdin!(opt), format)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [Array<I18n::Processes::Data::Tree::Siblings>] trees read from stdin and positional arguments
|
|
38
|
+
def forests_stdin_and_pos!(opt, num = false, format = opt[:format])
|
|
39
|
+
args = opt[:arguments] || []
|
|
40
|
+
if opt[:nostdin]
|
|
41
|
+
sources = []
|
|
42
|
+
else
|
|
43
|
+
sources = [$stdin.read]
|
|
44
|
+
num -= 1 if num
|
|
45
|
+
end
|
|
46
|
+
if num
|
|
47
|
+
num.times { sources << args.shift }
|
|
48
|
+
else
|
|
49
|
+
sources += args
|
|
50
|
+
args.clear
|
|
51
|
+
end
|
|
52
|
+
sources.map { |src| parse_forest(src, format) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def merge_forests_stdin_and_pos!(opt)
|
|
56
|
+
forests_stdin_and_pos!(opt).inject(i18n.empty_forest) do |result, forest|
|
|
57
|
+
result.merge! forest
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [I18n::Processes::Data::Tree::Siblings]
|
|
62
|
+
def parse_forest(src, format)
|
|
63
|
+
fail CommandError, "pass locale forest" unless src
|
|
64
|
+
if format == 'keys'
|
|
65
|
+
::I18n::Processes::Data::Tree::Siblings.from_key_names parse_keys(src)
|
|
66
|
+
else
|
|
67
|
+
::I18n::Processes::Data::Tree::Siblings.from_nested_hash i18n.data.adapter_parse(src, format)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def parse_keys(src)
|
|
72
|
+
Array(src).compact.flat_map { |v| v.strip.split(/\s*[,\s\n]\s*/) }.map(&:presence).compact
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def print_forest(forest, opts, version = :show_tree)
|
|
76
|
+
format = opts[:format].to_s
|
|
77
|
+
case format
|
|
78
|
+
when 'terminal-table'
|
|
79
|
+
terminal_report.send(version, forest)
|
|
80
|
+
when 'inspect'
|
|
81
|
+
puts forest.inspect
|
|
82
|
+
when 'keys'
|
|
83
|
+
puts forest.key_names(root: true)
|
|
84
|
+
when *DATA_FORMATS
|
|
85
|
+
puts i18n.data.adapter_dump forest.to_hash(true), format
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def print_changed_keys(diff, version = :changed_keys)
|
|
90
|
+
terminal_report.send(version, diff)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/option_parsers/locale'
|
|
4
|
+
require 'i18n/processes/command/dsl'
|
|
5
|
+
module I18n::Processes
|
|
6
|
+
module Command
|
|
7
|
+
module Options
|
|
8
|
+
module Locales
|
|
9
|
+
include Command::DSL
|
|
10
|
+
|
|
11
|
+
arg :locales,
|
|
12
|
+
'-l',
|
|
13
|
+
'--locales en,es,ru',
|
|
14
|
+
Array,
|
|
15
|
+
'Locale(s) to process. Special: base',
|
|
16
|
+
parser: OptionParsers::Locale::ListParser,
|
|
17
|
+
default: 'all',
|
|
18
|
+
consume_positional: true
|
|
19
|
+
|
|
20
|
+
arg :locale,
|
|
21
|
+
'-l',
|
|
22
|
+
'--locale en',
|
|
23
|
+
'i18n_processes.common.locale',
|
|
24
|
+
parser: OptionParsers::Locale::Parser,
|
|
25
|
+
default: 'base'
|
|
26
|
+
|
|
27
|
+
arg :locale_to_translate_from,
|
|
28
|
+
'-f',
|
|
29
|
+
'--from en',
|
|
30
|
+
'Locale to translate from',
|
|
31
|
+
parser: OptionParsers::Locale::Parser,
|
|
32
|
+
default: 'base'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n::Processes
|
|
4
|
+
# When this type of error is caught:
|
|
5
|
+
# 1. show error message of the backtrace
|
|
6
|
+
# 2. exit with non-zero exit code
|
|
7
|
+
class CommandError < StandardError
|
|
8
|
+
def initialize(error = nil, message) # rubocop:disable Style/OptionalArguments
|
|
9
|
+
super(message)
|
|
10
|
+
set_backtrace error.backtrace if error
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/dsl'
|
|
4
|
+
require 'i18n/processes/command/collection'
|
|
5
|
+
require 'i18n/processes/command/commands/health'
|
|
6
|
+
require 'i18n/processes/command/commands/missing'
|
|
7
|
+
require 'i18n/processes/command/commands/usages'
|
|
8
|
+
require 'i18n/processes/command/commands/eq_base'
|
|
9
|
+
require 'i18n/processes/command/commands/data'
|
|
10
|
+
require 'i18n/processes/command/commands/tree'
|
|
11
|
+
require 'i18n/processes/command/commands/meta'
|
|
12
|
+
require 'i18n/processes/command/commands/xlsx'
|
|
13
|
+
require 'i18n/processes/command/commands/preprocessing'
|
|
14
|
+
require 'i18n/processes/command/commander'
|
|
15
|
+
|
|
16
|
+
module I18n::Processes
|
|
17
|
+
class Commands < Command::Commander
|
|
18
|
+
include Command::DSL
|
|
19
|
+
include Command::Commands::Health
|
|
20
|
+
include Command::Commands::Missing
|
|
21
|
+
include Command::Commands::Usages
|
|
22
|
+
include Command::Commands::EqBase
|
|
23
|
+
include Command::Commands::Data
|
|
24
|
+
include Command::Commands::Tree
|
|
25
|
+
include Command::Commands::Meta
|
|
26
|
+
include Command::Commands::XLSX
|
|
27
|
+
include Command::Commands::Preprocessing
|
|
28
|
+
|
|
29
|
+
require 'highline/import'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n::Processes::Configuration # rubocop:disable Metrics/ModuleLength
|
|
4
|
+
DEFAULTS = {
|
|
5
|
+
base_locale: 'en',
|
|
6
|
+
internal_locale: 'en',
|
|
7
|
+
search: ::I18n::Processes::UsedKeys::SEARCH_DEFAULTS,
|
|
8
|
+
data: ::I18n::Processes::Data::DATA_DEFAULTS
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
# i18n-processes config (defaults + config/i18n-processes.yml)
|
|
12
|
+
# @return [Hash{String => String,Hash,Array}]
|
|
13
|
+
def config
|
|
14
|
+
@config || (self.config = {})
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
CONFIG_FILES = %w[
|
|
18
|
+
config/i18n-processes.yml config/i18n-processes.yml.erb
|
|
19
|
+
i18n-processes.yml i18n-processes.yml.erb
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
def file_config
|
|
23
|
+
file = CONFIG_FILES.detect { |f| File.exist?(f) }
|
|
24
|
+
# rubocop:disable Security/Eval
|
|
25
|
+
config = file && YAML.load(eval(Erubi::Engine.new(File.read(file, encoding: 'UTF-8')).src))
|
|
26
|
+
# rubocop:enable Security/Eval
|
|
27
|
+
if config.present?
|
|
28
|
+
config.with_indifferent_access.tap do |c|
|
|
29
|
+
if c[:relative_roots]
|
|
30
|
+
warn_deprecated 'Please move relative_roots under search in config/i18n-processes.yml.'
|
|
31
|
+
c[:search][:relative_roots] = c.delete(:relative_roots)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
{}.with_indifferent_access
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def config=(conf)
|
|
40
|
+
@config = file_config.deep_merge(conf)
|
|
41
|
+
@config_sections = {}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# data config
|
|
45
|
+
# @return [Hash<adapter: String, options: Hash>]
|
|
46
|
+
def data_config
|
|
47
|
+
@config_sections[:data] ||= begin
|
|
48
|
+
{
|
|
49
|
+
adapter: data.class.name,
|
|
50
|
+
config: data.config
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# translation config
|
|
56
|
+
# @return [Hash{String => String,Hash,Array}]
|
|
57
|
+
def translation_config
|
|
58
|
+
@config_sections[:translation] ||= begin
|
|
59
|
+
conf = (config[:translation] || {}).with_indifferent_access
|
|
60
|
+
conf[:api_key] ||= ENV['GOOGLE_TRANSLATE_API_KEY'] if ENV.key?('GOOGLE_TRANSLATE_API_KEY')
|
|
61
|
+
conf
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [Array<String>] all available locales, base_locale is always first
|
|
66
|
+
def locales
|
|
67
|
+
@config_sections[:locales] ||= data.locales
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @return [String] default i18n locale
|
|
71
|
+
def base_locale
|
|
72
|
+
@config_sections[:base_locale] ||= (config[:base_locale] || DEFAULTS[:base_locale]).to_s
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def internal_locale
|
|
76
|
+
@config_sections[:internal_locale] ||= begin
|
|
77
|
+
internal_locale = (config[:internal_locale] || DEFAULTS[:internal_locale]).to_s
|
|
78
|
+
valid_locales = Dir[File.join(I18n::Processes.gem_path, 'config', 'locales', '*.yml')]
|
|
79
|
+
.map { |f| File.basename(f, '.yml') }
|
|
80
|
+
unless valid_locales.first.include?(internal_locale)
|
|
81
|
+
log_warn "invalid internal_locale #{internal_locale.inspect}. "\
|
|
82
|
+
"Available internal locales: #{valid_locales * ', '}."
|
|
83
|
+
internal_locale = DEFAULTS[:internal_locale].to_s
|
|
84
|
+
end
|
|
85
|
+
internal_locale
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ignore_config(type = nil)
|
|
90
|
+
key = type ? "ignore_#{type}" : 'ignore'
|
|
91
|
+
@config_sections[key] ||= config[key]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
IGNORE_TYPES = [nil, :missing, :unused, :eq_base].freeze
|
|
95
|
+
# evaluated configuration (as the app sees it)
|
|
96
|
+
def config_sections
|
|
97
|
+
# init all sections
|
|
98
|
+
base_locale
|
|
99
|
+
internal_locale
|
|
100
|
+
locales
|
|
101
|
+
data_config
|
|
102
|
+
@config_sections[:search] ||= search_config
|
|
103
|
+
translation_config
|
|
104
|
+
IGNORE_TYPES.each do |ignore_type|
|
|
105
|
+
ignore_config ignore_type
|
|
106
|
+
end
|
|
107
|
+
@config_sections
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def config_for_inspect
|
|
111
|
+
to_hash_from_indifferent(config_sections.reject { |_k, v| v.blank? }).tap do |sections|
|
|
112
|
+
sections.each_value do |section|
|
|
113
|
+
section.merge! section.delete('config') if section.is_a?(Hash) && section.key?('config')
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def to_hash_from_indifferent(value)
|
|
121
|
+
case value
|
|
122
|
+
when Hash
|
|
123
|
+
value.stringify_keys.to_hash.tap do |h|
|
|
124
|
+
h.each { |k, v| h[k] = to_hash_from_indifferent(v) if v.is_a?(Hash) || v.is_a?(Array) }
|
|
125
|
+
end
|
|
126
|
+
when Array
|
|
127
|
+
value.map { |e| to_hash_from_indifferent e }
|
|
128
|
+
else
|
|
129
|
+
value
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n::Processes
|
|
4
|
+
class ConsoleContext < BaseProcess
|
|
5
|
+
def to_s
|
|
6
|
+
@to_s ||= "i18n-processes-#{I18n::Processes::VERSION}"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def banner
|
|
10
|
+
puts Messages.banner
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def guide
|
|
14
|
+
puts Messages.guide
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def start
|
|
19
|
+
require 'irb'
|
|
20
|
+
IRB.setup nil
|
|
21
|
+
ctx = IRB::Irb.new.context
|
|
22
|
+
IRB.conf[:MAIN_CONTEXT] = ctx
|
|
23
|
+
$stderr.puts Messages.banner
|
|
24
|
+
require 'irb/ext/multi-irb'
|
|
25
|
+
IRB.irb nil, new
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module Messages
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
def banner
|
|
33
|
+
Rainbow("i18n-processes v#{I18n::Processes::VERSION} IRB").bright + "\nType #{Rainbow('guide').green} to learn more"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def guide
|
|
37
|
+
Rainbow('i18n-processes IRB Quick Start guide').green.bright + "\n" + <<-TEXT
|
|
38
|
+
#{Rainbow('Data as trees').yellow}
|
|
39
|
+
tree(locale)
|
|
40
|
+
used_tree(key_filter: nil, strict: nil)
|
|
41
|
+
unused_tree(locale: base_locale, strict: nil)
|
|
42
|
+
build_tree('es' => {'hello' => 'Hola'})
|
|
43
|
+
|
|
44
|
+
#{Rainbow('Traversal').yellow}
|
|
45
|
+
tree = missing_diff_tree('es')
|
|
46
|
+
tree.nodes { |node| }
|
|
47
|
+
tree.nodes.to_a
|
|
48
|
+
tree.leaves { |node| }
|
|
49
|
+
tree.each { |root_node| }
|
|
50
|
+
# also levels, depth_first, and breadth_first
|
|
51
|
+
|
|
52
|
+
#{Rainbow('Select nodes').yellow}
|
|
53
|
+
tree.select_nodes { |node| } # new tree with only selected nodes
|
|
54
|
+
|
|
55
|
+
#{Rainbow('Match by full key').yellow}
|
|
56
|
+
tree.select_keys { |key, leaf| } # new tree with only selected keys
|
|
57
|
+
tree.grep_keys(/hello/) # grep, using ===
|
|
58
|
+
tree.keys { |key, leaf| } # enumerate over [full_key, leaf_node]
|
|
59
|
+
# Pass {root: true} to include root node in full_key (usually locale)
|
|
60
|
+
|
|
61
|
+
#{Rainbow('Nodes').yellow}
|
|
62
|
+
node = node(key, locale)
|
|
63
|
+
node.key # only the part after the last dot
|
|
64
|
+
node.full_key # full key. Includes root key, pass {root: false} to override.
|
|
65
|
+
# also: value, value_or_children_hash, data, walk_to_root, walk_from_root
|
|
66
|
+
Tree::Node.new(key: 'en')
|
|
67
|
+
|
|
68
|
+
#{Rainbow('Keys').yellow}
|
|
69
|
+
t(key, locale)
|
|
70
|
+
key_value?(key, locale)
|
|
71
|
+
depluralize_key(key, locale) # convert 'hat.one' to 'hat'
|
|
72
|
+
TEXT
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module I18n::Processes
|
|
6
|
+
module Data
|
|
7
|
+
module Adapter
|
|
8
|
+
module JsonAdapter
|
|
9
|
+
class << self
|
|
10
|
+
# @return [Hash] locale tree
|
|
11
|
+
def parse(str, opts)
|
|
12
|
+
JSON.parse(str, parse_opts(opts))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [String]
|
|
16
|
+
def dump(tree, opts)
|
|
17
|
+
JSON.generate(tree, parse_opts(opts))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def parse_opts(opts)
|
|
23
|
+
opts.try(:symbolize_keys) || {}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Data
|
|
6
|
+
module Adapter
|
|
7
|
+
module YamlAdapter
|
|
8
|
+
class << self
|
|
9
|
+
# @return [Hash] locale tree
|
|
10
|
+
def parse(str, options)
|
|
11
|
+
if YAML.method(:load).arity.abs == 2
|
|
12
|
+
YAML.load(str, options || {})
|
|
13
|
+
else
|
|
14
|
+
# older jruby and rbx 2.2.7 do not accept options
|
|
15
|
+
YAML.load(str)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [String]
|
|
20
|
+
def dump(tree, options)
|
|
21
|
+
tree.to_yaml(options || {})
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|