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,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Command
|
|
6
|
+
module Commands
|
|
7
|
+
module Data
|
|
8
|
+
include Command::Collection
|
|
9
|
+
|
|
10
|
+
arg :pattern_router,
|
|
11
|
+
'-p',
|
|
12
|
+
'--pattern_router',
|
|
13
|
+
'Use pattern router: keys moved per config data.write'
|
|
14
|
+
|
|
15
|
+
cmd :normalize,
|
|
16
|
+
pos: '[locale ...]',
|
|
17
|
+
desc: 'normalize translation data: sort and move to the right files',
|
|
18
|
+
args: %i[locales pattern_router]
|
|
19
|
+
|
|
20
|
+
def normalize(opt = {})
|
|
21
|
+
i18n.normalize_store! locales: opt[:locales],
|
|
22
|
+
force_pattern_router: opt[:pattern_router]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
cmd :check_normalized,
|
|
26
|
+
pos: '[locale ...]',
|
|
27
|
+
desc: 'verify that all translation data is normalized',
|
|
28
|
+
args: %i[locales]
|
|
29
|
+
|
|
30
|
+
def check_normalized(opt)
|
|
31
|
+
non_normalized = i18n.non_normalized_paths locales: opt[:locales]
|
|
32
|
+
terminal_report.check_normalized_results(non_normalized)
|
|
33
|
+
:exit_1 unless non_normalized.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
cmd :mv,
|
|
37
|
+
pos: 'FROM_KEY_PATTERN TO_KEY_PATTERN',
|
|
38
|
+
desc: 'rename/merge the keys in locale data that match the given pattern'
|
|
39
|
+
def mv(opt = {})
|
|
40
|
+
fail CommandError, 'requires FROM_KEY_PATTERN and TO_KEY_PATTERN' if opt[:arguments].size < 2
|
|
41
|
+
from_pattern = opt[:arguments].shift
|
|
42
|
+
to_pattern = opt[:arguments].shift
|
|
43
|
+
forest = i18n.data_forest
|
|
44
|
+
results = forest.mv_key!(compile_key_pattern(from_pattern), to_pattern, root: false)
|
|
45
|
+
i18n.data.write forest
|
|
46
|
+
terminal_report.mv_results results
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
cmd :rm,
|
|
50
|
+
pos: 'KEY_PATTERN [KEY_PATTERN...]',
|
|
51
|
+
desc: 'remove the keys in locale data that match the given pattern'
|
|
52
|
+
def rm(opt = {})
|
|
53
|
+
fail CommandError, 'requires KEY_PATTERN' if opt[:arguments].empty?
|
|
54
|
+
forest = i18n.data_forest
|
|
55
|
+
results = opt[:arguments].each_with_object({}) do |key_pattern, h|
|
|
56
|
+
h.merge! forest.mv_key!(compile_key_pattern(key_pattern), '', root: false)
|
|
57
|
+
end
|
|
58
|
+
i18n.data.write forest
|
|
59
|
+
terminal_report.mv_results results
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
cmd :data,
|
|
63
|
+
pos: '[locale ...]',
|
|
64
|
+
desc: 'show locale data',
|
|
65
|
+
args: %i[locales out_format]
|
|
66
|
+
|
|
67
|
+
def data(opt = {})
|
|
68
|
+
$stderr.puts Rainbow("data from here?!")
|
|
69
|
+
print_forest i18n.data_forest(opt[:locales]), opt
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
cmd :data_merge,
|
|
73
|
+
pos: '[tree ...]',
|
|
74
|
+
desc: 'merge locale data with trees',
|
|
75
|
+
args: %i[data_format nostdin]
|
|
76
|
+
|
|
77
|
+
def data_merge(opt = {})
|
|
78
|
+
forest = merge_forests_stdin_and_pos!(opt)
|
|
79
|
+
merged = i18n.data.merge!(forest)
|
|
80
|
+
print_forest merged, opt
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
cmd :data_write,
|
|
84
|
+
pos: '[tree]',
|
|
85
|
+
desc: 'replace locale data with tree',
|
|
86
|
+
args: %i[data_format nostdin]
|
|
87
|
+
|
|
88
|
+
def data_write(opt = {})
|
|
89
|
+
forest = forest_pos_or_stdin!(opt)
|
|
90
|
+
i18n.data.write forest
|
|
91
|
+
print_forest forest, opt
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
cmd :data_remove,
|
|
95
|
+
pos: '[tree]',
|
|
96
|
+
desc: 'remove keys present in tree from data',
|
|
97
|
+
args: %i[data_format nostdin]
|
|
98
|
+
|
|
99
|
+
def data_remove(opt = {})
|
|
100
|
+
removed = i18n.data.remove_by_key!(forest_pos_or_stdin!(opt))
|
|
101
|
+
log_stderr 'Removed:'
|
|
102
|
+
print_forest removed, opt
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Command
|
|
6
|
+
module Commands
|
|
7
|
+
module EqBase
|
|
8
|
+
include Command::Collection
|
|
9
|
+
|
|
10
|
+
cmd :eq_base,
|
|
11
|
+
pos: '[locale ...]',
|
|
12
|
+
desc: 'show translations equal to base value',
|
|
13
|
+
args: %i[locales out_format]
|
|
14
|
+
|
|
15
|
+
def eq_base(opt = {})
|
|
16
|
+
print_forest i18n.eq_base_keys(opt), opt, :eq_base_keys
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Command
|
|
6
|
+
module Commands
|
|
7
|
+
module Health
|
|
8
|
+
include Command::Collection
|
|
9
|
+
|
|
10
|
+
cmd :health,
|
|
11
|
+
pos: '[locale ...]',
|
|
12
|
+
desc: 'is everything OK?',
|
|
13
|
+
args: %i[locales out_format]
|
|
14
|
+
|
|
15
|
+
def health(opt = {})
|
|
16
|
+
forest = i18n.data_forest(opt[:locales])
|
|
17
|
+
stats = i18n.forest_stats(forest)
|
|
18
|
+
fail CommandError, 'No keys detected. Check data.read in config/i18n-processes.yml.' if stats[:key_count].zero?
|
|
19
|
+
terminal_report.forest_stats forest, stats
|
|
20
|
+
$stderr.puts Rainbow("#{opt}").green
|
|
21
|
+
[missing(opt), unused(opt), check_normalized(opt)].detect { |result| result == :exit_1 }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Command
|
|
6
|
+
module Commands
|
|
7
|
+
module Meta
|
|
8
|
+
include Command::Collection
|
|
9
|
+
|
|
10
|
+
cmd :config,
|
|
11
|
+
pos: '[section ...]',
|
|
12
|
+
desc: 'display i18n-processes configuration'
|
|
13
|
+
|
|
14
|
+
def config(opts = {})
|
|
15
|
+
cfg = i18n.config_for_inspect
|
|
16
|
+
cfg = cfg.slice(*opts[:arguments]) if opts[:arguments].present?
|
|
17
|
+
cfg = cfg.to_yaml
|
|
18
|
+
cfg.sub!(/\A---\n/, '')
|
|
19
|
+
cfg.gsub!(/^([^\s-].+?:)/, Rainbow('\1').cyan.bright)
|
|
20
|
+
puts cfg
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
cmd :gem_path, desc: 'show path to the gem'
|
|
24
|
+
|
|
25
|
+
def gem_path
|
|
26
|
+
puts I18n::Processes.gem_path
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
cmd :irb, desc: 'start REPL session within i18n-processes context'
|
|
30
|
+
|
|
31
|
+
def irb
|
|
32
|
+
require 'i18n/processes/console_context'
|
|
33
|
+
::I18n::Processes::ConsoleContext.start
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
require 'i18n/processes/command/commands/preprocessing'
|
|
5
|
+
module I18n::Processes
|
|
6
|
+
module Command
|
|
7
|
+
module Commands
|
|
8
|
+
module Missing
|
|
9
|
+
include Command::Collection
|
|
10
|
+
include Command::Commands::Preprocessing
|
|
11
|
+
|
|
12
|
+
missing_types = I18n::Processes::MissingKeys.missing_keys_types
|
|
13
|
+
arg :missing_types,
|
|
14
|
+
'-t',
|
|
15
|
+
"--types #{missing_types * ','}",
|
|
16
|
+
Array,
|
|
17
|
+
"Filter by types: #{missing_types * ', '}",
|
|
18
|
+
parser: OptionParsers::Enum::ListParser.new(
|
|
19
|
+
missing_types,
|
|
20
|
+
proc do |invalid, valid|
|
|
21
|
+
"{:one =>'invalid type: #{invalid}. valid: #{valid}.', :other =>'unknown types: #{invalid}. valid: #{valid}.'}"
|
|
22
|
+
end
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
cmd :missing,
|
|
26
|
+
pos: '[locale ...]',
|
|
27
|
+
desc: 'show missing translations',
|
|
28
|
+
args: [:locales, :out_format, :missing_types, ['-p', '--path PATH', 'Destination path', default: 'tmp/missing_keys']]
|
|
29
|
+
|
|
30
|
+
def missing(opt = {})
|
|
31
|
+
translated_locales = opt[:locales].reject{|x| x == base_locale}
|
|
32
|
+
translated_locales.each do |locale|
|
|
33
|
+
$stderr.puts Rainbow("#{base_locale} to #{locale}\n").green
|
|
34
|
+
preprocessing({:locales => [locale] })
|
|
35
|
+
missing_keys = spreadsheet_report.find_missing(locale)
|
|
36
|
+
missing_count = missing_keys.count
|
|
37
|
+
if missing_count.zero?
|
|
38
|
+
spreadsheet_report.translated_files(locale)
|
|
39
|
+
else
|
|
40
|
+
$stderr.puts Rainbow("#{missing_count} keys need to be translated to #{locale}").green
|
|
41
|
+
spreadsheet_report.missing_report(locale)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
cmd :translate_missing,
|
|
48
|
+
pos: '[locale ...]',
|
|
49
|
+
desc: 'translate missing keys with Google Translate',
|
|
50
|
+
args: [:locales, :locale_to_translate_from, arg(:out_format).from(1)]
|
|
51
|
+
|
|
52
|
+
def translate_missing(opt = {})
|
|
53
|
+
missing = i18n.missing_diff_forest opt[:locales], opt[:from]
|
|
54
|
+
translated = i18n.google_translate_forest missing, opt[:from]
|
|
55
|
+
i18n.data.merge! translated
|
|
56
|
+
log_stderr "Translated #{translated.leaves.count} keys"
|
|
57
|
+
print_forest translated, opt
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# cmd :add_missing,
|
|
61
|
+
# pos: '[locale ...]',
|
|
62
|
+
# desc: 'add missing keys to locale data',
|
|
63
|
+
# args: [:locales, :out_format, arg(:value) + [{ default: '%{value_or_default_or_human_key}' }],
|
|
64
|
+
# ['--nil-value', 'Set value to nil. Takes precedence over the value argument.']]
|
|
65
|
+
|
|
66
|
+
# def add_missing(opt = {}) # rubocop:disable Metrics/AbcSize
|
|
67
|
+
# [ # Merge base locale first, as this may affect the value for the other locales
|
|
68
|
+
# [i18n.base_locale] & opt[:locales],
|
|
69
|
+
# opt[:locales] - [i18n.base_locale]
|
|
70
|
+
# ].reject(&:empty?).each_with_object(i18n.empty_forest) do |locales, added|
|
|
71
|
+
# forest = i18n.missing_keys(locales: locales, **opt.slice(:types, :base_locale))
|
|
72
|
+
# .set_each_value!(opt[:'nil-value'] ? nil : opt[:value])
|
|
73
|
+
#
|
|
74
|
+
# # call method I18n::Processes::Data::FileSystemBase merge! to generate file
|
|
75
|
+
# i18n.data.merge! forest
|
|
76
|
+
# # call method I18n::Processes::Data::Tree::Nodes::Siblings merge! to generate file
|
|
77
|
+
# added.merge! forest
|
|
78
|
+
# end.tap do |added|
|
|
79
|
+
# log_stderr "{:one => 'Added #{added.leaves.count} key', :other => 'Added #{added.leaves.count} keys'}"
|
|
80
|
+
# print_forest added, opt
|
|
81
|
+
# end
|
|
82
|
+
# end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
require 'i18n/processes/path'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module I18n::Processes
|
|
8
|
+
module Command
|
|
9
|
+
module Commands
|
|
10
|
+
module Preprocessing
|
|
11
|
+
include Command::Collection
|
|
12
|
+
include Path
|
|
13
|
+
|
|
14
|
+
cmd :preprocessing,
|
|
15
|
+
pos: '[locale...]',
|
|
16
|
+
desc: 'preprocess origin data files into primitive files with key=value format',
|
|
17
|
+
args: %i[locales out_format]
|
|
18
|
+
|
|
19
|
+
def preprocessing(opt = {})
|
|
20
|
+
locale = opt[:locales].include?(base_locale) ? 'zh-CN' : opt[:locales].first
|
|
21
|
+
dic = {}
|
|
22
|
+
origin_files(locale).flatten.each do |file|
|
|
23
|
+
# dic.merge!(origin_file_read(file)) { |key, v1, v2| fail "conflict: #{key}: #{v1}, #{v2} in #{file}" unless v1 == v2 }
|
|
24
|
+
dic.merge!(origin_file_read(file))
|
|
25
|
+
end
|
|
26
|
+
## 对比文件默认存储路径
|
|
27
|
+
path = 'tmp/'
|
|
28
|
+
keys_source(dic, path, locale)
|
|
29
|
+
$stderr.puts Rainbow('preprocess origin data files into primitive files successfully').green if locale == base_locale
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def origin_file_read(file)
|
|
34
|
+
{}.tap do |a|
|
|
35
|
+
File.open(file).read.each_line do |line|
|
|
36
|
+
next if line =~ /^#/ || line == "\n" || !line.include?('.')
|
|
37
|
+
if line.include?('\':')
|
|
38
|
+
line.gsub!(/'|,/, '')
|
|
39
|
+
key = line.split(': ').first.delete(' ')
|
|
40
|
+
value = line.split(': ').last
|
|
41
|
+
else
|
|
42
|
+
key = line.split('=').first.delete(' ')
|
|
43
|
+
value = line.split('=').last
|
|
44
|
+
end
|
|
45
|
+
a[key] = value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def keys_source(dic, path, locale)
|
|
51
|
+
filename = path + locale
|
|
52
|
+
previous_file = "#{path}pre_#{locale}"
|
|
53
|
+
File.delete(previous_file) if File.exist?(previous_file)
|
|
54
|
+
File.rename(filename, previous_file) if File.exist?(filename)
|
|
55
|
+
local_file = File.new(filename, 'w')
|
|
56
|
+
dic.map do |key, value|
|
|
57
|
+
value.include?("\n") ? local_file.write("#{key}=#{value}") : local_file.write("#{key}=#{value}\n")
|
|
58
|
+
end
|
|
59
|
+
local_file.close
|
|
60
|
+
changed_keys(previous_file, filename) if File.exist?(previous_file) && locale == base_locale
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def changed_keys(previous, current)
|
|
64
|
+
previous_dic = get_dic(previous)
|
|
65
|
+
current_dic = get_dic(current)
|
|
66
|
+
diff = current_dic.merge(previous_dic){|k, v1, v2| {:current => v1, :previous => v2} unless v1 == v2 }
|
|
67
|
+
diff.select!{ |k, v| v.is_a?(Hash)}
|
|
68
|
+
print_changed_keys(diff) unless diff == {}
|
|
69
|
+
changed_keys_save(diff) unless diff == {}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def changed_keys_save(diff)
|
|
73
|
+
file = 'tmp/changed_keys/changed_keys'
|
|
74
|
+
FileUtils::mkdir_p File.dirname(file) unless Dir.exist?File.dirname(file)
|
|
75
|
+
changed_keys = File.new(file, 'w')
|
|
76
|
+
diff.each do |k, v|
|
|
77
|
+
changed_keys.write "key: #{k}\n"
|
|
78
|
+
changed_keys.write "current: #{v[:current]}"
|
|
79
|
+
changed_keys.write "previous: #{v[:previous]}"
|
|
80
|
+
changed_keys.write "\n"
|
|
81
|
+
end
|
|
82
|
+
changed_keys.close
|
|
83
|
+
log_warn("need to update #{diff.count} keys' translation")
|
|
84
|
+
log_warn("changed_keys save to #{file}")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Command
|
|
6
|
+
module Commands
|
|
7
|
+
module Tree
|
|
8
|
+
include Command::Collection
|
|
9
|
+
include I18n::Processes::KeyPatternMatching
|
|
10
|
+
|
|
11
|
+
cmd :tree_translate,
|
|
12
|
+
pos: '[tree (or stdin)]',
|
|
13
|
+
desc: 'Google Translate a tree to root locales',
|
|
14
|
+
args: [:locale_to_translate_from, arg(:data_format).from(1)]
|
|
15
|
+
|
|
16
|
+
def tree_translate(opts = {})
|
|
17
|
+
forest = forest_pos_or_stdin!(opts)
|
|
18
|
+
print_forest i18n.google_translate_forest(forest, opts[:from]), opts
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
cmd :tree_merge,
|
|
22
|
+
pos: '[[tree] [tree] ... (or stdin)]',
|
|
23
|
+
desc: 'merge trees',
|
|
24
|
+
args: %i[data_format nostdin]
|
|
25
|
+
|
|
26
|
+
def tree_merge(opts = {})
|
|
27
|
+
print_forest merge_forests_stdin_and_pos!(opts), opts
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
cmd :tree_filter,
|
|
31
|
+
pos: '[pattern] [tree (or stdin)]',
|
|
32
|
+
desc: 'filter tree by key pattern',
|
|
33
|
+
args: %i[data_format pattern]
|
|
34
|
+
|
|
35
|
+
def tree_filter(opts = {})
|
|
36
|
+
pattern = arg_or_pos! :pattern, opts
|
|
37
|
+
forest = forest_pos_or_stdin! opts
|
|
38
|
+
unless pattern.blank?
|
|
39
|
+
pattern_re = i18n.compile_key_pattern(pattern)
|
|
40
|
+
forest = forest.select_keys { |full_key, _node| full_key =~ pattern_re }
|
|
41
|
+
end
|
|
42
|
+
print_forest forest, opts
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
cmd :tree_rename_key,
|
|
46
|
+
pos: 'KEY_PATTERN NAME [tree (or stdin)]',
|
|
47
|
+
desc: 'rename tree node',
|
|
48
|
+
args: [['-k', '--key KEY_PATTERN', 'Full key (pattern) to rename. Required'],
|
|
49
|
+
['-n', '--name NAME', 'New name, interpolates original name as %{key}. Required'],
|
|
50
|
+
:data_format]
|
|
51
|
+
|
|
52
|
+
def tree_rename_key(opt = {})
|
|
53
|
+
warn_deprecated 'Use tree-mv instead.'
|
|
54
|
+
key = arg_or_pos! :key, opt
|
|
55
|
+
name = arg_or_pos! :name, opt
|
|
56
|
+
forest = forest_pos_or_stdin! opt
|
|
57
|
+
fail CommandError, 'pass full key to rename (-k, --key)' if key.blank?
|
|
58
|
+
fail CommandError, 'pass new name (-n, --name)' if name.blank?
|
|
59
|
+
forest.rename_each_key!(key, name)
|
|
60
|
+
print_forest forest, opt
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
arg :all_locales,
|
|
64
|
+
'-a',
|
|
65
|
+
'--all-locales',
|
|
66
|
+
'Do not expect key patterns to start with a locale, instead apply them to all locales implicitly.'
|
|
67
|
+
|
|
68
|
+
cmd :tree_mv,
|
|
69
|
+
pos: 'FROM_KEY_PATTERN TO_KEY_PATTERN [tree (or stdin)]',
|
|
70
|
+
desc: 'rename/merge/remove the keys matching the given pattern',
|
|
71
|
+
args: %i[data_format all_locales]
|
|
72
|
+
def tree_mv(opt = {})
|
|
73
|
+
fail CommandError, 'requires FROM_KEY_PATTERN and TO_KEY_PATTERN' if opt[:arguments].size < 2
|
|
74
|
+
from_pattern = opt[:arguments].shift
|
|
75
|
+
to_pattern = opt[:arguments].shift
|
|
76
|
+
forest = forest_pos_or_stdin!(opt)
|
|
77
|
+
forest.mv_key!(compile_key_pattern(from_pattern), to_pattern, root: !opt[:'all-locales'])
|
|
78
|
+
print_forest forest, opt
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
cmd :tree_subtract,
|
|
82
|
+
pos: '[[tree] [tree] ... (or stdin)]',
|
|
83
|
+
desc: 'tree A minus the keys in tree B',
|
|
84
|
+
args: %i[data_format nostdin]
|
|
85
|
+
|
|
86
|
+
def tree_subtract(opt = {})
|
|
87
|
+
forests = forests_stdin_and_pos! opt, 2
|
|
88
|
+
forest = forests.reduce(:subtract_by_key) || empty_forest
|
|
89
|
+
print_forest forest, opt
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
cmd :tree_set_value,
|
|
93
|
+
pos: '[VALUE] [tree (or stdin)]',
|
|
94
|
+
desc: 'set values of keys, optionally match a pattern',
|
|
95
|
+
args: %i[value data_format nostdin pattern]
|
|
96
|
+
|
|
97
|
+
def tree_set_value(opt = {})
|
|
98
|
+
value = arg_or_pos! :value, opt
|
|
99
|
+
forest = forest_pos_or_stdin!(opt)
|
|
100
|
+
key_pattern = opt[:pattern]
|
|
101
|
+
fail CommandError, 'pass value (-v, --value)' if value.blank?
|
|
102
|
+
forest.set_each_value!(value, key_pattern)
|
|
103
|
+
print_forest forest, opt
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
cmd :tree_convert,
|
|
107
|
+
pos: '[tree (or stdin)]',
|
|
108
|
+
desc: 'convert tree between formats',
|
|
109
|
+
args: [arg(:data_format).dup.tap { |a| a[0..1] = ['-f', '--from FORMAT'] },
|
|
110
|
+
arg(:out_format).dup.tap { |a| a[0..1] = ['-t', '--to FORMAT'] }]
|
|
111
|
+
|
|
112
|
+
def tree_convert(opt = {})
|
|
113
|
+
forest = forest_pos_or_stdin! opt.merge(format: opt[:from])
|
|
114
|
+
print_forest forest, opt.merge(format: opt[:to])
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Command
|
|
6
|
+
module Commands
|
|
7
|
+
module Usages
|
|
8
|
+
include Command::Collection
|
|
9
|
+
|
|
10
|
+
arg :strict,
|
|
11
|
+
'--[no-]strict',
|
|
12
|
+
'Avoid inferring dynamic key usages such as t("cats.#{cat}.name"). Takes precedence over the config setting if set.'
|
|
13
|
+
|
|
14
|
+
cmd :find,
|
|
15
|
+
pos: '[pattern]',
|
|
16
|
+
desc: 'show where keys are used in the code',
|
|
17
|
+
args: %i[out_format pattern strict]
|
|
18
|
+
|
|
19
|
+
def find(opt = {})
|
|
20
|
+
opt[:filter] ||= opt.delete(:pattern) || opt[:arguments].try(:first)
|
|
21
|
+
result = i18n.used_tree(strict: opt[:strict], key_filter: opt[:filter].presence, include_raw_references: true)
|
|
22
|
+
print_forest result, opt, :used_keys
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
cmd :unused,
|
|
26
|
+
pos: '[locale ...]',
|
|
27
|
+
desc: 'show unused translations',
|
|
28
|
+
args: %i[locales out_format strict]
|
|
29
|
+
|
|
30
|
+
def unused(opt = {})
|
|
31
|
+
forest = i18n.unused_keys(opt.slice(:locales, :strict))
|
|
32
|
+
print_forest forest, opt, :unused_keys
|
|
33
|
+
:exit_1 unless forest.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
cmd :remove_unused,
|
|
37
|
+
pos: '[locale ...]',
|
|
38
|
+
desc: 'remove unused keys',
|
|
39
|
+
args: %i[locales out_format strict confirm]
|
|
40
|
+
|
|
41
|
+
def remove_unused(opt = {})
|
|
42
|
+
unused_keys = i18n.unused_keys(opt.slice(:locales, :strict))
|
|
43
|
+
if unused_keys.present?
|
|
44
|
+
terminal_report.unused_keys(unused_keys)
|
|
45
|
+
confirm_remove_unused!(unused_keys, opt)
|
|
46
|
+
removed = i18n.data.remove_by_key!(unused_keys)
|
|
47
|
+
log_stderr "Removed #{unused_keys.leaves.count} keys"
|
|
48
|
+
print_forest removed, opt
|
|
49
|
+
else
|
|
50
|
+
log_stderr Rainbow("No unused keys to remove").green.bright
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def confirm_remove_unused!(unused_keys, opt)
|
|
57
|
+
return if ENV['CONFIRM'] || opt[:confirm]
|
|
58
|
+
locales = Rainbow(unused_keys.flat_map { |root| root.key.split('+') }.sort.uniq * ', ').bright
|
|
59
|
+
msg = [
|
|
60
|
+
Rainbow("{:one =>'#{unused_keys.leaves.count} translation will be removed from #{locales}.', :other =>'#{unused_keys.leaves.count} translation will be removed from #{locales}.'}").red,
|
|
61
|
+
Rainbow("Continue?").yellow,
|
|
62
|
+
Rainbow('(yes/no)').yellow
|
|
63
|
+
].join(' ')
|
|
64
|
+
exit 1 unless agree msg
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/processes/command/collection'
|
|
4
|
+
module I18n::Processes
|
|
5
|
+
module Command
|
|
6
|
+
module Commands
|
|
7
|
+
module XLSX
|
|
8
|
+
include Command::Collection
|
|
9
|
+
|
|
10
|
+
cmd :xlsx_report,
|
|
11
|
+
pos: '[locale...]',
|
|
12
|
+
desc: 'save missing and unused translations to an Excel file',
|
|
13
|
+
args: [:locales,
|
|
14
|
+
['-p', '--path PATH', 'Destination path', default: 'tmp/i18n-report.xlsx']]
|
|
15
|
+
|
|
16
|
+
def xlsx_report(opt = {})
|
|
17
|
+
begin
|
|
18
|
+
require 'axlsx'
|
|
19
|
+
rescue LoadError
|
|
20
|
+
message = %(For spreadsheet report please add axlsx gem to Gemfile:\ngem 'axlsx', '~> 2.0')
|
|
21
|
+
log_stderr Rainbow(message).red.bright
|
|
22
|
+
exit 1
|
|
23
|
+
end
|
|
24
|
+
spreadsheet_report.save_report opt[:path], opt.except(:path)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module I18n::Processes
|
|
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)
|
|
14
|
+
I18n.t(*args)
|
|
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)
|
|
46
|
+
proc { I18n.t(*args) }
|
|
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
|