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.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile.lock +102 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +46 -0
  5. data/Rakefile +12 -0
  6. data/bin/i18n-processes +28 -0
  7. data/bin/i18n-processes.cmd +2 -0
  8. data/config/locales/en.yml +2 -0
  9. data/config/locales/zh-CN.yml +2 -0
  10. data/i18n-processes.gemspec +64 -0
  11. data/lib/i18n/processes/base_process.rb +47 -0
  12. data/lib/i18n/processes/cli.rb +208 -0
  13. data/lib/i18n/processes/command/collection.rb +21 -0
  14. data/lib/i18n/processes/command/commander.rb +43 -0
  15. data/lib/i18n/processes/command/commands/data.rb +107 -0
  16. data/lib/i18n/processes/command/commands/eq_base.rb +21 -0
  17. data/lib/i18n/processes/command/commands/health.rb +26 -0
  18. data/lib/i18n/processes/command/commands/meta.rb +38 -0
  19. data/lib/i18n/processes/command/commands/missing.rb +86 -0
  20. data/lib/i18n/processes/command/commands/preprocessing.rb +90 -0
  21. data/lib/i18n/processes/command/commands/tree.rb +119 -0
  22. data/lib/i18n/processes/command/commands/usages.rb +69 -0
  23. data/lib/i18n/processes/command/commands/xlsx.rb +29 -0
  24. data/lib/i18n/processes/command/dsl.rb +56 -0
  25. data/lib/i18n/processes/command/option_parsers/enum.rb +55 -0
  26. data/lib/i18n/processes/command/option_parsers/locale.rb +60 -0
  27. data/lib/i18n/processes/command/options/common.rb +41 -0
  28. data/lib/i18n/processes/command/options/data.rb +95 -0
  29. data/lib/i18n/processes/command/options/locales.rb +36 -0
  30. data/lib/i18n/processes/command_error.rb +13 -0
  31. data/lib/i18n/processes/commands.rb +31 -0
  32. data/lib/i18n/processes/configuration.rb +132 -0
  33. data/lib/i18n/processes/console_context.rb +76 -0
  34. data/lib/i18n/processes/data/adapter/json_adapter.rb +29 -0
  35. data/lib/i18n/processes/data/adapter/yaml_adapter.rb +27 -0
  36. data/lib/i18n/processes/data/file_formats.rb +111 -0
  37. data/lib/i18n/processes/data/file_system.rb +14 -0
  38. data/lib/i18n/processes/data/file_system_base.rb +205 -0
  39. data/lib/i18n/processes/data/router/conservative_router.rb +66 -0
  40. data/lib/i18n/processes/data/router/pattern_router.rb +60 -0
  41. data/lib/i18n/processes/data/tree/node.rb +204 -0
  42. data/lib/i18n/processes/data/tree/nodes.rb +97 -0
  43. data/lib/i18n/processes/data/tree/siblings.rb +333 -0
  44. data/lib/i18n/processes/data/tree/traversal.rb +190 -0
  45. data/lib/i18n/processes/data.rb +87 -0
  46. data/lib/i18n/processes/google_translation.rb +125 -0
  47. data/lib/i18n/processes/html_keys.rb +16 -0
  48. data/lib/i18n/processes/ignore_keys.rb +30 -0
  49. data/lib/i18n/processes/key_pattern_matching.rb +37 -0
  50. data/lib/i18n/processes/locale_list.rb +19 -0
  51. data/lib/i18n/processes/locale_pathname.rb +17 -0
  52. data/lib/i18n/processes/logging.rb +37 -0
  53. data/lib/i18n/processes/missing_keys.rb +122 -0
  54. data/lib/i18n/processes/path.rb +42 -0
  55. data/lib/i18n/processes/plural_keys.rb +41 -0
  56. data/lib/i18n/processes/rainbow_utils.rb +13 -0
  57. data/lib/i18n/processes/references.rb +101 -0
  58. data/lib/i18n/processes/reports/base.rb +71 -0
  59. data/lib/i18n/processes/reports/spreadsheet.rb +72 -0
  60. data/lib/i18n/processes/reports/terminal.rb +252 -0
  61. data/lib/i18n/processes/scanners/file_scanner.rb +65 -0
  62. data/lib/i18n/processes/scanners/files/caching_file_finder.rb +34 -0
  63. data/lib/i18n/processes/scanners/files/caching_file_finder_provider.rb +33 -0
  64. data/lib/i18n/processes/scanners/files/caching_file_reader.rb +28 -0
  65. data/lib/i18n/processes/scanners/files/file_finder.rb +60 -0
  66. data/lib/i18n/processes/scanners/files/file_reader.rb +19 -0
  67. data/lib/i18n/processes/scanners/occurrence_from_position.rb +27 -0
  68. data/lib/i18n/processes/scanners/pattern_mapper.rb +60 -0
  69. data/lib/i18n/processes/scanners/pattern_scanner.rb +103 -0
  70. data/lib/i18n/processes/scanners/pattern_with_scope_scanner.rb +98 -0
  71. data/lib/i18n/processes/scanners/relative_keys.rb +53 -0
  72. data/lib/i18n/processes/scanners/results/key_occurrences.rb +54 -0
  73. data/lib/i18n/processes/scanners/results/occurrence.rb +69 -0
  74. data/lib/i18n/processes/scanners/ruby_ast_call_finder.rb +62 -0
  75. data/lib/i18n/processes/scanners/ruby_ast_scanner.rb +206 -0
  76. data/lib/i18n/processes/scanners/ruby_key_literals.rb +30 -0
  77. data/lib/i18n/processes/scanners/scanner.rb +17 -0
  78. data/lib/i18n/processes/scanners/scanner_multiplexer.rb +41 -0
  79. data/lib/i18n/processes/split_key.rb +68 -0
  80. data/lib/i18n/processes/stats.rb +24 -0
  81. data/lib/i18n/processes/string_interpolation.rb +16 -0
  82. data/lib/i18n/processes/unused_keys.rb +23 -0
  83. data/lib/i18n/processes/used_keys.rb +177 -0
  84. data/lib/i18n/processes/version.rb +7 -0
  85. data/lib/i18n/processes.rb +69 -0
  86. data/source/p1/_messages/zh/article.properties +9 -0
  87. data/source/p1/_messages/zh/company.properties +62 -0
  88. data/source/p1/_messages/zh/devices.properties +40 -0
  89. data/source/p1/_messages/zh/meeting-rooms.properties +99 -0
  90. data/source/p1/_messages/zh/meetingBooking.properties +18 -0
  91. data/source/p1/_messages/zh/office-areas.properties +64 -0
  92. data/source/p1/_messages/zh/orders.properties +25 -0
  93. data/source/p1/_messages/zh/schedulings.properties +7 -0
  94. data/source/p1/_messages/zh/tag.properties +2 -0
  95. data/source/p1/_messages/zh/ticket.properties +9 -0
  96. data/source/p1/_messages/zh/visitor.properties +5 -0
  97. data/source/p1/messages +586 -0
  98. data/source/p2/orders.properties +25 -0
  99. data/source/p2/schedulings.properties +7 -0
  100. data/source/p2/tag.properties +2 -0
  101. data/source/p2/ticket.properties +9 -0
  102. data/source/p2/visitor.properties +5 -0
  103. data/source/zh.messages.ts +30 -0
  104. data/translated/en/p1/_messages/zh/article.properties +9 -0
  105. data/translated/en/p1/_messages/zh/company.properties +62 -0
  106. data/translated/en/p1/_messages/zh/devices.properties +40 -0
  107. data/translated/en/p1/_messages/zh/meeting-rooms.properties +99 -0
  108. data/translated/en/p1/_messages/zh/meetingBooking.properties +18 -0
  109. data/translated/en/p1/_messages/zh/office-areas.properties +64 -0
  110. data/translated/en/p1/_messages/zh/orders.properties +25 -0
  111. data/translated/en/p1/_messages/zh/schedulings.properties +7 -0
  112. data/translated/en/p1/_messages/zh/tag.properties +2 -0
  113. data/translated/en/p1/_messages/zh/ticket.properties +9 -0
  114. data/translated/en/p1/_messages/zh/visitor.properties +5 -0
  115. data/translated/en/p1/messages +586 -0
  116. data/translated/en/p2/orders.properties +25 -0
  117. data/translated/en/p2/schedulings.properties +7 -0
  118. data/translated/en/p2/tag.properties +2 -0
  119. data/translated/en/p2/ticket.properties +9 -0
  120. data/translated/en/p2/visitor.properties +5 -0
  121. data/translated/en/zh.messages.ts +30 -0
  122. data/translation/en/article.properties +9 -0
  123. data/translation/en/company.properties +56 -0
  124. data/translation/en/meeting-rooms.properties +87 -0
  125. data/translation/en/meetingBooking.properties +14 -0
  126. data/translation/en/messages.en +164 -0
  127. data/translation/en/office-areas.properties +51 -0
  128. data/translation/en/orders.properties +26 -0
  129. data/translation/en/tag.properties +2 -0
  130. data/translation/en/translated +1263 -0
  131. data/translation/en/visitor.properties +4 -0
  132. 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