i18n-youdao-tasks 0.9.37

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +448 -0
  4. data/Rakefile +13 -0
  5. data/bin/i18n-tasks +15 -0
  6. data/bin/i18n-tasks.cmd +2 -0
  7. data/config/locales/en.yml +129 -0
  8. data/config/locales/ru.yml +131 -0
  9. data/i18n-tasks.gemspec +58 -0
  10. data/lib/i18n/tasks/base_task.rb +52 -0
  11. data/lib/i18n/tasks/cli.rb +214 -0
  12. data/lib/i18n/tasks/command/collection.rb +21 -0
  13. data/lib/i18n/tasks/command/commander.rb +38 -0
  14. data/lib/i18n/tasks/command/commands/data.rb +107 -0
  15. data/lib/i18n/tasks/command/commands/eq_base.rb +22 -0
  16. data/lib/i18n/tasks/command/commands/health.rb +30 -0
  17. data/lib/i18n/tasks/command/commands/interpolations.rb +22 -0
  18. data/lib/i18n/tasks/command/commands/meta.rb +37 -0
  19. data/lib/i18n/tasks/command/commands/missing.rb +73 -0
  20. data/lib/i18n/tasks/command/commands/tree.rb +102 -0
  21. data/lib/i18n/tasks/command/commands/usages.rb +81 -0
  22. data/lib/i18n/tasks/command/dsl.rb +56 -0
  23. data/lib/i18n/tasks/command/option_parsers/enum.rb +57 -0
  24. data/lib/i18n/tasks/command/option_parsers/locale.rb +60 -0
  25. data/lib/i18n/tasks/command/options/common.rb +47 -0
  26. data/lib/i18n/tasks/command/options/data.rb +97 -0
  27. data/lib/i18n/tasks/command/options/locales.rb +44 -0
  28. data/lib/i18n/tasks/command_error.rb +15 -0
  29. data/lib/i18n/tasks/commands.rb +29 -0
  30. data/lib/i18n/tasks/concurrent/cache.rb +22 -0
  31. data/lib/i18n/tasks/concurrent/cached_value.rb +61 -0
  32. data/lib/i18n/tasks/configuration.rb +136 -0
  33. data/lib/i18n/tasks/console_context.rb +76 -0
  34. data/lib/i18n/tasks/data/adapter/json_adapter.rb +29 -0
  35. data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +27 -0
  36. data/lib/i18n/tasks/data/file_formats.rb +99 -0
  37. data/lib/i18n/tasks/data/file_system.rb +14 -0
  38. data/lib/i18n/tasks/data/file_system_base.rb +200 -0
  39. data/lib/i18n/tasks/data/router/conservative_router.rb +62 -0
  40. data/lib/i18n/tasks/data/router/pattern_router.rb +62 -0
  41. data/lib/i18n/tasks/data/tree/node.rb +206 -0
  42. data/lib/i18n/tasks/data/tree/nodes.rb +97 -0
  43. data/lib/i18n/tasks/data/tree/siblings.rb +333 -0
  44. data/lib/i18n/tasks/data/tree/traversal.rb +197 -0
  45. data/lib/i18n/tasks/data.rb +87 -0
  46. data/lib/i18n/tasks/html_keys.rb +14 -0
  47. data/lib/i18n/tasks/ignore_keys.rb +31 -0
  48. data/lib/i18n/tasks/interpolations.rb +30 -0
  49. data/lib/i18n/tasks/key_pattern_matching.rb +38 -0
  50. data/lib/i18n/tasks/locale_list.rb +19 -0
  51. data/lib/i18n/tasks/locale_pathname.rb +17 -0
  52. data/lib/i18n/tasks/logging.rb +35 -0
  53. data/lib/i18n/tasks/missing_keys.rb +185 -0
  54. data/lib/i18n/tasks/plural_keys.rb +67 -0
  55. data/lib/i18n/tasks/references.rb +103 -0
  56. data/lib/i18n/tasks/reports/base.rb +75 -0
  57. data/lib/i18n/tasks/reports/terminal.rb +243 -0
  58. data/lib/i18n/tasks/scanners/erb_ast_processor.rb +51 -0
  59. data/lib/i18n/tasks/scanners/erb_ast_scanner.rb +48 -0
  60. data/lib/i18n/tasks/scanners/file_scanner.rb +66 -0
  61. data/lib/i18n/tasks/scanners/files/caching_file_finder.rb +35 -0
  62. data/lib/i18n/tasks/scanners/files/caching_file_finder_provider.rb +31 -0
  63. data/lib/i18n/tasks/scanners/files/caching_file_reader.rb +28 -0
  64. data/lib/i18n/tasks/scanners/files/file_finder.rb +61 -0
  65. data/lib/i18n/tasks/scanners/files/file_reader.rb +19 -0
  66. data/lib/i18n/tasks/scanners/local_ruby_parser.rb +74 -0
  67. data/lib/i18n/tasks/scanners/occurrence_from_position.rb +29 -0
  68. data/lib/i18n/tasks/scanners/pattern_mapper.rb +60 -0
  69. data/lib/i18n/tasks/scanners/pattern_scanner.rb +108 -0
  70. data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +100 -0
  71. data/lib/i18n/tasks/scanners/relative_keys.rb +70 -0
  72. data/lib/i18n/tasks/scanners/results/key_occurrences.rb +54 -0
  73. data/lib/i18n/tasks/scanners/results/occurrence.rb +69 -0
  74. data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +63 -0
  75. data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +234 -0
  76. data/lib/i18n/tasks/scanners/ruby_key_literals.rb +30 -0
  77. data/lib/i18n/tasks/scanners/scanner.rb +17 -0
  78. data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +43 -0
  79. data/lib/i18n/tasks/split_key.rb +72 -0
  80. data/lib/i18n/tasks/stats.rb +24 -0
  81. data/lib/i18n/tasks/string_interpolation.rb +17 -0
  82. data/lib/i18n/tasks/translation.rb +29 -0
  83. data/lib/i18n/tasks/translators/base_translator.rb +156 -0
  84. data/lib/i18n/tasks/translators/deepl_translator.rb +81 -0
  85. data/lib/i18n/tasks/translators/google_translator.rb +69 -0
  86. data/lib/i18n/tasks/translators/yandex_translator.rb +63 -0
  87. data/lib/i18n/tasks/translators/youdao_translator.rb +69 -0
  88. data/lib/i18n/tasks/unused_keys.rb +25 -0
  89. data/lib/i18n/tasks/used_keys.rb +184 -0
  90. data/lib/i18n/tasks/version.rb +7 -0
  91. data/lib/i18n/tasks.rb +69 -0
  92. data/templates/config/i18n-tasks.yml +142 -0
  93. data/templates/minitest/i18n_test.rb +36 -0
  94. data/templates/rspec/i18n_spec.rb +34 -0
  95. metadata +441 -0
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n::Tasks
4
+ module Command
5
+ module Commands
6
+ module Interpolations
7
+ include Command::Collection
8
+
9
+ cmd :check_consistent_interpolations,
10
+ pos: '[locale ...]',
11
+ desc: t('i18n_tasks.cmd.desc.check_consistent_interpolations'),
12
+ args: %i[locales out_format]
13
+
14
+ def check_consistent_interpolations(opt = {})
15
+ forest = i18n.inconsistent_interpolations(**opt.slice(:locales, :base_locale))
16
+ print_forest forest, opt, :inconsistent_interpolations
17
+ :exit1 unless forest.empty?
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n::Tasks
4
+ module Command
5
+ module Commands
6
+ module Meta
7
+ include Command::Collection
8
+
9
+ cmd :config,
10
+ pos: '[section ...]',
11
+ desc: t('i18n_tasks.cmd.desc.config')
12
+
13
+ def config(opts = {})
14
+ cfg = i18n.config_for_inspect
15
+ cfg = cfg.slice(*opts[:arguments]) if opts[:arguments].present?
16
+ cfg = cfg.to_yaml
17
+ cfg.sub!(/\A---\n/, '')
18
+ cfg.gsub!(/^([^\s-].+?:)/, Rainbow('\1').cyan.bright)
19
+ puts cfg
20
+ end
21
+
22
+ cmd :gem_path, desc: t('i18n_tasks.cmd.desc.gem_path')
23
+
24
+ def gem_path
25
+ puts I18n::Tasks.gem_path
26
+ end
27
+
28
+ cmd :irb, desc: t('i18n_tasks.cmd.desc.irb')
29
+
30
+ def irb
31
+ require 'i18n/tasks/console_context'
32
+ ::I18n::Tasks::ConsoleContext.start
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'i18n/tasks/command/collection'
4
+
5
+ module I18n::Tasks
6
+ module Command
7
+ module Commands
8
+ module Missing
9
+ include Command::Collection
10
+
11
+ missing_types = I18n::Tasks::MissingKeys.missing_keys_types
12
+ arg :missing_types,
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
+
25
+ cmd :missing,
26
+ pos: '[locale ...]',
27
+ desc: t('i18n_tasks.cmd.desc.missing'),
28
+ args: %i[locales out_format missing_types]
29
+
30
+ def missing(opt = {})
31
+ forest = i18n.missing_keys(**opt.slice(:locales, :base_locale, :types))
32
+ print_forest forest, opt, :missing_keys
33
+ :exit1 unless forest.empty?
34
+ end
35
+
36
+ cmd :translate_missing,
37
+ pos: '[locale ...]',
38
+ desc: t('i18n_tasks.cmd.desc.translate_missing'),
39
+ args: [:locales, :locale_to_translate_from, arg(:out_format).from(1), :translation_backend]
40
+
41
+ def translate_missing(opt = {})
42
+ missing = i18n.missing_diff_forest opt[:locales], opt[:from]
43
+ translated = i18n.translate_forest missing, from: opt[:from], backend: opt[:backend].to_sym
44
+ i18n.data.merge! translated
45
+ log_stderr t('i18n_tasks.translate_missing.translated', count: translated.leaves.count)
46
+ print_forest translated, opt
47
+ end
48
+
49
+ cmd :add_missing,
50
+ pos: '[locale ...]',
51
+ desc: t('i18n_tasks.cmd.desc.add_missing'),
52
+ args: [:locales, :out_format, arg(:value) + [{ default: '%{value_or_default_or_human_key}' }],
53
+ ['--nil-value', 'Set value to nil. Takes precedence over the value argument.']]
54
+
55
+ # Merge base locale first, as this may affect the value for the other locales
56
+ def add_missing(opt = {})
57
+ [
58
+ [i18n.base_locale] & opt[:locales],
59
+ opt[:locales] - [i18n.base_locale]
60
+ ].reject(&:empty?).each_with_object(i18n.empty_forest) do |locales, added|
61
+ forest = i18n.missing_keys(locales: locales, **opt.slice(:types, :base_locale))
62
+ .set_each_value!(opt[:'nil-value'] ? nil : opt[:value])
63
+ i18n.data.merge! forest
64
+ added.merge! forest
65
+ end.tap do |added|
66
+ log_stderr t('i18n_tasks.add_missing.added', count: added.leaves.count)
67
+ print_forest added, opt
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n::Tasks
4
+ module Command
5
+ module Commands
6
+ module Tree
7
+ include Command::Collection
8
+ include I18n::Tasks::KeyPatternMatching
9
+
10
+ cmd :tree_translate,
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
+
15
+ def tree_translate(opts = {})
16
+ forest = forest_pos_or_stdin!(opts)
17
+ print_forest i18n.translate_forest(forest, from: opts[:from], backend: opts[:backend].to_sym), opts
18
+ end
19
+
20
+ cmd :tree_merge,
21
+ pos: '[[tree] [tree] ... (or stdin)]',
22
+ desc: t('i18n_tasks.cmd.desc.tree_merge'),
23
+ args: %i[data_format nostdin]
24
+
25
+ def tree_merge(opts = {})
26
+ print_forest merge_forests_stdin_and_pos!(opts), opts
27
+ end
28
+
29
+ cmd :tree_filter,
30
+ pos: '[pattern] [tree (or stdin)]',
31
+ desc: t('i18n_tasks.cmd.desc.tree_filter'),
32
+ args: %i[data_format pattern]
33
+
34
+ def tree_filter(opts = {})
35
+ pattern = arg_or_pos! :pattern, opts
36
+ forest = forest_pos_or_stdin! opts
37
+ unless pattern.blank?
38
+ pattern_re = i18n.compile_key_pattern(pattern)
39
+ forest = forest.select_keys { |full_key, _node| full_key =~ pattern_re }
40
+ end
41
+ print_forest forest, opts
42
+ end
43
+
44
+ arg :all_locales,
45
+ '-a',
46
+ '--all-locales',
47
+ t('i18n_tasks.cmd.args.desc.all_locales')
48
+
49
+ cmd :tree_mv,
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
+ def tree_mv(opt = {})
54
+ fail CommandError, 'requires FROM_KEY_PATTERN and TO_KEY_PATTERN' if opt[:arguments].size < 2
55
+
56
+ from_pattern = opt[:arguments].shift
57
+ to_pattern = opt[:arguments].shift
58
+ forest = forest_pos_or_stdin!(opt)
59
+ forest.mv_key!(compile_key_pattern(from_pattern), to_pattern, root: !opt[:'all-locales'])
60
+ print_forest forest, opt
61
+ end
62
+
63
+ cmd :tree_subtract,
64
+ pos: '[[tree] [tree] ... (or stdin)]',
65
+ desc: t('i18n_tasks.cmd.desc.tree_subtract'),
66
+ args: %i[data_format nostdin]
67
+
68
+ def tree_subtract(opt = {})
69
+ forests = forests_stdin_and_pos! opt, 2
70
+ forest = forests.reduce(:subtract_by_key) || empty_forest
71
+ print_forest forest, opt
72
+ end
73
+
74
+ cmd :tree_set_value,
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
+
79
+ def tree_set_value(opt = {})
80
+ value = arg_or_pos! :value, opt
81
+ forest = forest_pos_or_stdin!(opt)
82
+ key_pattern = opt[:pattern]
83
+ fail CommandError, 'pass value (-v, --value)' if value.blank?
84
+
85
+ forest.set_each_value!(value, key_pattern)
86
+ print_forest forest, opt
87
+ end
88
+
89
+ cmd :tree_convert,
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
+
95
+ def tree_convert(opt = {})
96
+ forest = forest_pos_or_stdin! opt.merge(format: opt[:from])
97
+ print_forest forest, opt.merge(format: opt[:to])
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n::Tasks
4
+ module Command
5
+ module Commands
6
+ module Usages
7
+ include Command::Collection
8
+
9
+ arg :strict,
10
+ '--[no-]strict',
11
+ t('i18n_tasks.cmd.args.desc.strict')
12
+
13
+ arg :keep_order,
14
+ '-k',
15
+ '--keep-order',
16
+ t('i18n_tasks.cmd.args.desc.keep_order')
17
+
18
+ cmd :find,
19
+ pos: '[pattern]',
20
+ desc: t('i18n_tasks.cmd.desc.find'),
21
+ args: %i[out_format pattern strict]
22
+
23
+ def find(opt = {})
24
+ opt[:filter] ||= opt.delete(:pattern) || opt[:arguments].try(:first)
25
+ result = i18n.used_tree(strict: opt[:strict], key_filter: opt[:filter].presence, include_raw_references: true)
26
+ print_forest result, opt, :used_keys
27
+ end
28
+
29
+ cmd :unused,
30
+ pos: '[locale ...]',
31
+ desc: t('i18n_tasks.cmd.desc.unused'),
32
+ args: %i[locales out_format strict]
33
+
34
+ def unused(opt = {})
35
+ forest = i18n.unused_keys(**opt.slice(:locales, :strict))
36
+ print_forest forest, opt, :unused_keys
37
+ :exit1 unless forest.empty?
38
+ end
39
+
40
+ cmd :remove_unused,
41
+ pos: '[locale ...]',
42
+ desc: t('i18n_tasks.cmd.desc.remove_unused'),
43
+ args: %i[locales out_format strict keep_order confirm pattern]
44
+
45
+ def remove_unused(opt = {}) # rubocop:disable Metrics/AbcSize
46
+ unused_keys = i18n.unused_keys(**opt.slice(:locales, :strict))
47
+
48
+ if opt[:pattern]
49
+ pattern_re = i18n.compile_key_pattern(opt[:pattern])
50
+ unused_keys = unused_keys.select_keys { |full_key, _node| full_key =~ pattern_re }
51
+ end
52
+
53
+ if unused_keys.present?
54
+ terminal_report.unused_keys(unused_keys)
55
+ confirm_remove_unused!(unused_keys, opt)
56
+ i18n.data.config = i18n.data.config.merge(sort: false) if opt[:'keep-order']
57
+ removed = i18n.data.remove_by_key!(unused_keys)
58
+ log_stderr t('i18n_tasks.remove_unused.removed', count: unused_keys.leaves.count)
59
+ print_forest removed, opt
60
+ else
61
+ log_stderr Rainbow(t('i18n_tasks.remove_unused.noop')).green.bright
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def confirm_remove_unused!(unused_keys, opt)
68
+ return if ENV['CONFIRM'] || opt[:confirm]
69
+
70
+ locales = Rainbow(unused_keys.flat_map { |root| root.key.split('+') }.sort.uniq * ', ').bright
71
+ msg = [
72
+ Rainbow(t('i18n_tasks.remove_unused.confirm', count: unused_keys.leaves.count, locales: locales)).red,
73
+ Rainbow(t('i18n_tasks.common.continue_q')).yellow,
74
+ Rainbow('(yes/no)').yellow
75
+ ].join(' ')
76
+ exit 1 unless agree msg
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n::Tasks
4
+ module Command
5
+ module DSL
6
+ def self.included(base)
7
+ base.module_eval do
8
+ @dsl = Hash.new { |h, k| h[k] = {} }
9
+ extend ClassMethods
10
+ end
11
+ end
12
+
13
+ def t(*args, **kwargs)
14
+ I18n.t(*args, **kwargs)
15
+ end
16
+
17
+ module ClassMethods
18
+ def cmd(name, conf = nil)
19
+ if conf
20
+ conf = conf.dup
21
+ conf[:args] = (conf[:args] || []).map { |arg| arg.is_a?(Symbol) ? arg(arg) : arg }
22
+ dsl(:cmds)[name] = conf
23
+ else
24
+ dsl(:cmds)[name]
25
+ end
26
+ end
27
+
28
+ def arg(ref, *args)
29
+ if args.present?
30
+ dsl(:args)[ref] = args
31
+ else
32
+ dsl(:args)[ref]
33
+ end
34
+ end
35
+
36
+ def cmds
37
+ dsl(:cmds)
38
+ end
39
+
40
+ def dsl(key)
41
+ @dsl[key]
42
+ end
43
+
44
+ # late-bound I18n.t for module bodies
45
+ def t(*args, **kwargs)
46
+ proc { I18n.t(*args, **kwargs) }
47
+ end
48
+
49
+ # if class is a module, merge DSL definitions when it is included
50
+ def included(base)
51
+ base.instance_variable_get(:@dsl).deep_merge!(@dsl)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n::Tasks
4
+ module Command
5
+ module OptionParsers
6
+ module Enum
7
+ class Parser
8
+ DEFAULT_ERROR = proc do |invalid, valid|
9
+ I18n.t('i18n_tasks.cmd.enum_opt.invalid', invalid: invalid, valid: 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
+
20
+ if @valid.include?(value)
21
+ value
22
+ else
23
+ fail CommandError, @error_message.call(value, @valid)
24
+ end
25
+ end
26
+ end
27
+
28
+ class ListParser
29
+ DEFAULT_ERROR = proc do |invalid, valid|
30
+ I18n.t('i18n_tasks.cmd.enum_list_opt.invalid', invalid: invalid * ', ', valid: valid * ', ')
31
+ end
32
+
33
+ def initialize(valid, error_message = DEFAULT_ERROR)
34
+ @valid = valid.map(&:to_s)
35
+ @error_message = error_message
36
+ end
37
+
38
+ def call(values, *)
39
+ values = Array(values)
40
+ return @valid if values == %w[all]
41
+
42
+ invalid = values - @valid
43
+ if invalid.empty?
44
+ if values.empty?
45
+ @valid
46
+ else
47
+ values
48
+ end
49
+ else
50
+ fail CommandError, @error_message.call(invalid, @valid)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n::Tasks
4
+ module Command
5
+ module OptionParsers
6
+ module Locale
7
+ module Validator
8
+ VALID_LOCALE_RE = /\A\w[\w\-.]*\z/i.freeze
9
+
10
+ def validate!(locale)
11
+ if VALID_LOCALE_RE !~ locale
12
+ fail CommandError, I18n.t('i18n_tasks.cmd.errors.invalid_locale', invalid: locale)
13
+ end
14
+
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,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'i18n/tasks/command/dsl'
4
+
5
+ module I18n::Tasks
6
+ module Command
7
+ module Options
8
+ module Common
9
+ include Command::DSL
10
+
11
+ arg :nostdin,
12
+ '-S',
13
+ '--nostdin',
14
+ t('i18n_tasks.cmd.args.desc.nostdin')
15
+
16
+ arg :confirm,
17
+ '-y',
18
+ '--confirm',
19
+ desc: t('i18n_tasks.cmd.args.desc.confirm')
20
+
21
+ arg :pattern,
22
+ '-p',
23
+ '--pattern PATTERN',
24
+ t('i18n_tasks.cmd.args.desc.key_pattern')
25
+
26
+ arg :value,
27
+ '-v',
28
+ '--value VALUE',
29
+ t('i18n_tasks.cmd.args.desc.value')
30
+
31
+ arg :config,
32
+ '-c',
33
+ '--config FILE',
34
+ t('i18n_tasks.cmd.args.desc.config')
35
+
36
+
37
+ def arg_or_pos!(key, opts)
38
+ opts[key] ||= opts[:arguments].try(:shift)
39
+ end
40
+
41
+ def pos_or_stdin!(opts)
42
+ opts[:arguments].try(:shift) || $stdin.read
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'i18n/tasks/command/option_parsers/enum'
4
+
5
+ module I18n::Tasks
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', 'key-values'].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
+ t("i18n_tasks.cmd.args.desc.#{type}", valid_text: values * ', ', default_text: default),
22
+ parser: OptionParsers::Enum::Parser.new(values,
23
+ proc do |value, valid|
24
+ I18n.t('i18n_tasks.cmd.errors.invalid_format',
25
+ invalid: value, valid: valid * ', ')
26
+ end)
27
+ end
28
+
29
+ # i18n-tasks-use t('i18n_tasks.cmd.args.desc.data_format')
30
+ format_arg.call(:data_format, DATA_FORMATS)
31
+
32
+ # i18n-tasks-use t('i18n_tasks.cmd.args.desc.out_format')
33
+ format_arg.call(:out_format, OUT_FORMATS)
34
+
35
+ # @return [I18n::Tasks::Data::Tree::Siblings]
36
+ def forest_pos_or_stdin!(opt, format = opt[:format])
37
+ parse_forest(pos_or_stdin!(opt), format)
38
+ end
39
+
40
+ # @return [Array<I18n::Tasks::Data::Tree::Siblings>] trees read from stdin and positional arguments
41
+ def forests_stdin_and_pos!(opt, num = false, format = opt[:format])
42
+ args = opt[:arguments] || []
43
+ if opt[:nostdin]
44
+ sources = []
45
+ else
46
+ sources = [$stdin.read]
47
+ num -= 1 if num
48
+ end
49
+ if num
50
+ num.times { sources << args.shift }
51
+ else
52
+ sources += args
53
+ args.clear
54
+ end
55
+ sources.map { |src| parse_forest(src, format) }
56
+ end
57
+
58
+ def merge_forests_stdin_and_pos!(opt)
59
+ forests_stdin_and_pos!(opt).inject(i18n.empty_forest) do |result, forest|
60
+ result.merge! forest
61
+ end
62
+ end
63
+
64
+ # @return [I18n::Tasks::Data::Tree::Siblings]
65
+ def parse_forest(src, format)
66
+ fail CommandError, I18n.t('i18n_tasks.cmd.errors.pass_forest') unless src
67
+
68
+ if format == 'keys'
69
+ ::I18n::Tasks::Data::Tree::Siblings.from_key_names parse_keys(src)
70
+ else
71
+ ::I18n::Tasks::Data::Tree::Siblings.from_nested_hash i18n.data.adapter_parse(src, format)
72
+ end
73
+ end
74
+
75
+ def parse_keys(src)
76
+ Array(src).compact.flat_map { |v| v.strip.split(/\s*[,\s\n]\s*/) }.map(&:presence).compact
77
+ end
78
+
79
+ def print_forest(forest, opts, version = :show_tree)
80
+ format = opts[:format].to_s
81
+ case format
82
+ when 'terminal-table'
83
+ terminal_report.send(version, forest)
84
+ when 'inspect'
85
+ puts forest.inspect
86
+ when 'keys'
87
+ puts forest.key_names(root: true)
88
+ when 'key-values'
89
+ puts forest.key_values(root: true).map { |kv| kv.join("\t") }
90
+ when *DATA_FORMATS
91
+ puts i18n.data.adapter_dump forest.to_hash(true), format
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'i18n/tasks/command/option_parsers/locale'
4
+
5
+ module I18n::Tasks
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
+ t('i18n_tasks.cmd.args.desc.locales_filter'),
16
+ parser: OptionParsers::Locale::ListParser,
17
+ default: 'all',
18
+ consume_positional: true
19
+
20
+ arg :locale,
21
+ '-l',
22
+ '--locale en',
23
+ t('i18n_tasks.cmd.args.desc.locale'),
24
+ parser: OptionParsers::Locale::Parser,
25
+ default: 'base'
26
+
27
+ arg :locale_to_translate_from,
28
+ '-f',
29
+ '--from en',
30
+ t('i18n_tasks.cmd.args.desc.locale_to_translate_from'),
31
+ parser: OptionParsers::Locale::Parser,
32
+ default: 'base'
33
+
34
+ TRANSLATION_BACKENDS = %w[google deepl].freeze
35
+ arg :translation_backend,
36
+ '-b',
37
+ '--backend BACKEND',
38
+ t('i18n_tasks.cmd.args.desc.translation_backend'),
39
+ parser: OptionParsers::Locale::Parser,
40
+ default: TRANSLATION_BACKENDS[0]
41
+ end
42
+ end
43
+ end
44
+ end