i18n-tasks 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +2 -1
- data/README.md +80 -78
- data/bin/i18n-tasks +24 -30
- data/config/i18n-tasks.yml +87 -0
- data/config/locales/en.yml +95 -0
- data/i18n-tasks.gemspec +1 -0
- data/lib/i18n/tasks.rb +10 -0
- data/lib/i18n/tasks/base_task.rb +6 -2
- data/lib/i18n/tasks/command/collection.rb +18 -0
- data/lib/i18n/tasks/command/commander.rb +72 -0
- data/lib/i18n/tasks/command/commands/data.rb +73 -0
- data/lib/i18n/tasks/command/commands/eq_base.rb +20 -0
- data/lib/i18n/tasks/command/commands/health.rb +26 -0
- data/lib/i18n/tasks/command/commands/meta.rb +35 -0
- data/lib/i18n/tasks/command/commands/missing.rb +73 -0
- data/lib/i18n/tasks/command/commands/tree.rb +92 -0
- data/lib/i18n/tasks/command/commands/usages.rb +70 -0
- data/lib/i18n/tasks/command/commands/xlsx.rb +27 -0
- data/lib/i18n/tasks/command/dsl.rb +27 -0
- data/lib/i18n/tasks/command/dsl/cmd.rb +19 -0
- data/lib/i18n/tasks/command/dsl/cmd_opt.rb +19 -0
- data/lib/i18n/tasks/command/dsl/enum_opt.rb +26 -0
- data/lib/i18n/tasks/command/options/common.rb +48 -0
- data/lib/i18n/tasks/command/options/enum_opt.rb +44 -0
- data/lib/i18n/tasks/command/options/list_opt.rb +11 -0
- data/lib/i18n/tasks/command/options/locales.rb +47 -0
- data/lib/i18n/tasks/command/options/trees.rb +101 -0
- data/lib/i18n/tasks/command_error.rb +3 -0
- data/lib/i18n/tasks/commands.rb +22 -169
- data/lib/i18n/tasks/configuration.rb +1 -16
- data/lib/i18n/tasks/console_context.rb +1 -1
- data/lib/i18n/tasks/data.rb +13 -8
- data/lib/i18n/tasks/data/file_formats.rb +29 -18
- data/lib/i18n/tasks/data/file_system_base.rb +35 -4
- data/lib/i18n/tasks/data/router/conservative_router.rb +18 -11
- data/lib/i18n/tasks/data/tree/node.rb +5 -15
- data/lib/i18n/tasks/data/tree/nodes.rb +0 -3
- data/lib/i18n/tasks/data/tree/siblings.rb +32 -2
- data/lib/i18n/tasks/data/tree/traversal.rb +117 -96
- data/lib/i18n/tasks/google_translation.rb +25 -25
- data/lib/i18n/tasks/html_keys.rb +10 -0
- data/lib/i18n/tasks/key_pattern_matching.rb +1 -0
- data/lib/i18n/tasks/locale_list.rb +19 -0
- data/lib/i18n/tasks/missing_keys.rb +32 -33
- data/lib/i18n/tasks/plural_keys.rb +1 -1
- data/lib/i18n/tasks/reports/base.rb +4 -9
- data/lib/i18n/tasks/reports/spreadsheet.rb +5 -5
- data/lib/i18n/tasks/reports/terminal.rb +62 -38
- data/lib/i18n/tasks/scanners/base_scanner.rb +5 -4
- data/lib/i18n/tasks/slop_command.rb +27 -0
- data/lib/i18n/tasks/stats.rb +20 -0
- data/lib/i18n/tasks/string_interpolation.rb +14 -0
- data/lib/i18n/tasks/unused_keys.rb +0 -10
- data/lib/i18n/tasks/version.rb +1 -1
- data/spec/commands/data_commands_spec.rb +38 -0
- data/spec/commands/tree_commands_spec.rb +68 -0
- data/spec/fixtures/app/views/index.html.slim +1 -0
- data/spec/google_translate_spec.rb +5 -3
- data/spec/i18n_spec.rb +18 -0
- data/spec/i18n_tasks_spec.rb +8 -8
- data/spec/spec_helper.rb +3 -3
- data/spec/support/test_codebase.rb +4 -1
- data/spec/used_keys_spec.rb +7 -7
- data/templates/config/i18n-tasks.yml +2 -2
- metadata +48 -4
- data/lib/i18n/tasks/commands_base.rb +0 -107
- data/lib/i18n/tasks/fill_tasks.rb +0 -40
@@ -0,0 +1,19 @@
|
|
1
|
+
module I18n::Tasks
|
2
|
+
module LocaleList
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# @return locales converted to strings, with base locale first, the rest sorted alphabetically
|
6
|
+
def normalize_locale_list(locales, base_locale, include_base = false)
|
7
|
+
locales = Array(locales).map(&:to_s).sort
|
8
|
+
if locales.include?(base_locale)
|
9
|
+
[base_locale] + (locales - [base_locale])
|
10
|
+
else
|
11
|
+
if include_base
|
12
|
+
[base_locale] + locales
|
13
|
+
else
|
14
|
+
locales
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,34 +1,40 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
module I18n::Tasks
|
3
3
|
module MissingKeys
|
4
|
+
|
5
|
+
MISSING_TYPES = {
|
6
|
+
used: {glyph: '✗', summary: 'used in code but missing from base locale'},
|
7
|
+
diff: {glyph: '∅', summary: 'translated in one locale but not in the other'}
|
8
|
+
}
|
9
|
+
|
10
|
+
def self.missing_keys_types
|
11
|
+
@missing_keys_types ||= MISSING_TYPES.keys
|
12
|
+
end
|
13
|
+
|
4
14
|
def missing_keys_types
|
5
|
-
|
15
|
+
MissingKeys.missing_keys_types
|
6
16
|
end
|
7
17
|
|
8
|
-
# @param [:missing_used, :missing_diff]
|
18
|
+
# @param [:missing_used, :missing_diff] types (default nil)
|
9
19
|
# @return [Siblings]
|
10
20
|
def missing_keys(opts = {})
|
11
|
-
locales =
|
12
|
-
types =
|
13
|
-
validate_missing_types! types
|
21
|
+
locales = opts[:locales].presence || self.locales
|
22
|
+
types = opts[:types].presence || missing_keys_types
|
14
23
|
base = opts[:base_locale] || base_locale
|
15
|
-
|
16
|
-
|
17
|
-
types.each do |type|
|
18
|
-
tree.merge! send(:"missing_#{type}_forest", locales, base)
|
24
|
+
types.inject(empty_forest) do |f, type|
|
25
|
+
f.merge! send(:"missing_#{type}_forest", locales, base)
|
19
26
|
end
|
20
|
-
tree
|
21
27
|
end
|
22
28
|
|
23
29
|
def eq_base_keys(opts = {})
|
24
30
|
locales = Array(opts[:locales]).presence || self.locales
|
25
|
-
(locales - [base_locale]).inject(
|
31
|
+
(locales - [base_locale]).inject(empty_forest) { |tree, locale|
|
26
32
|
tree.merge! equal_values_tree(locale, base_locale)
|
27
33
|
}
|
28
34
|
end
|
29
35
|
|
30
36
|
def missing_diff_forest(locales, base = base_locale)
|
31
|
-
tree =
|
37
|
+
tree = empty_forest
|
32
38
|
# present in base but not locale
|
33
39
|
(locales - [base]).each { |locale|
|
34
40
|
tree.merge! missing_diff_tree(locale, base)
|
@@ -36,7 +42,7 @@ module I18n::Tasks
|
|
36
42
|
if locales.include?(base)
|
37
43
|
# present in locale but not base
|
38
44
|
(self.locales - [base]).each { |locale|
|
39
|
-
tree.merge! missing_diff_tree(base, locale)
|
45
|
+
tree.merge! missing_diff_tree(base, locale)
|
40
46
|
}
|
41
47
|
end
|
42
48
|
tree
|
@@ -46,30 +52,35 @@ module I18n::Tasks
|
|
46
52
|
if locales.include?(base)
|
47
53
|
missing_used_tree(base)
|
48
54
|
else
|
49
|
-
|
55
|
+
empty_forest
|
50
56
|
end
|
51
57
|
end
|
52
58
|
|
53
|
-
def missing_tree(locale, compared_to
|
59
|
+
def missing_tree(locale, compared_to)
|
54
60
|
if locale == compared_to
|
55
61
|
missing_used_tree locale
|
56
62
|
else
|
57
|
-
missing_diff_tree locale, compared_to
|
63
|
+
missing_diff_tree locale, compared_to
|
58
64
|
end
|
59
65
|
end
|
60
66
|
|
61
67
|
# keys present in compared_to, but not in locale
|
62
|
-
def missing_diff_tree(locale, compared_to = base_locale
|
68
|
+
def missing_diff_tree(locale, compared_to = base_locale)
|
63
69
|
data[compared_to].select_keys { |key, _node|
|
64
|
-
locale_key_missing?
|
65
|
-
}.set_root_key(locale, type: :missing_diff).
|
70
|
+
locale_key_missing? locale, depluralize_key(key, locale)
|
71
|
+
}.set_root_key!(locale, type: :missing_diff).keys { |_key, node|
|
72
|
+
if node.data.key?(:path)
|
73
|
+
# change path and locale to base
|
74
|
+
node.data.update path: LocalePathname.replace_locale(node.data[:path], node.data[:locale], locale), locale: locale
|
75
|
+
end
|
76
|
+
}
|
66
77
|
end
|
67
78
|
|
68
79
|
# keys used in the code missing translations in locale
|
69
80
|
def missing_used_tree(locale)
|
70
81
|
used_tree(strict: true).select_keys { |key, _node|
|
71
82
|
locale_key_missing?(locale, key)
|
72
|
-
}.set_root_key(locale, type: :missing_used)
|
83
|
+
}.set_root_key!(locale, type: :missing_used)
|
73
84
|
end
|
74
85
|
|
75
86
|
def equal_values_tree(locale, compare_to = base_locale)
|
@@ -77,23 +88,11 @@ module I18n::Tasks
|
|
77
88
|
data[locale].select_keys(root: false) { |key, node|
|
78
89
|
other_node = base[key]
|
79
90
|
other_node && node.value == other_node.value && !ignore_key?(key, :eq_base, locale)
|
80
|
-
}.set_root_key(locale, type: :eq_base)
|
91
|
+
}.set_root_key!(locale, type: :eq_base)
|
81
92
|
end
|
82
93
|
|
83
94
|
def locale_key_missing?(locale, key)
|
84
95
|
!key_value?(key, locale) && !ignore_key?(key, :missing)
|
85
96
|
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def validate_missing_types!(types)
|
90
|
-
valid_types = missing_keys_types.map(&:to_s)
|
91
|
-
types = types.map(&:to_s)
|
92
|
-
invalid_types = types - valid_types
|
93
|
-
if invalid_types.present?
|
94
|
-
raise CommandError.new("Unknown types: #{invalid_types * ', '}. Valid types are: #{valid_types * ', '}.")
|
95
|
-
end
|
96
|
-
true
|
97
|
-
end
|
98
97
|
end
|
99
98
|
end
|
@@ -5,7 +5,7 @@ module I18n::Tasks::PluralKeys
|
|
5
5
|
PLURAL_KEY_RE = /\.(?:#{PLURAL_KEY_SUFFIXES.to_a * '|'})$/
|
6
6
|
|
7
7
|
def collapse_plural_nodes!(tree)
|
8
|
-
tree.leaves.map(&:parent).uniq.each do |node|
|
8
|
+
tree.leaves.select(&:parent?).map(&:parent).uniq.each do |node|
|
9
9
|
children = node.children
|
10
10
|
if children.present? && children.all? { |c| PLURAL_KEY_SUFFIXES.include?(c.key) }
|
11
11
|
node.value = children.to_hash
|
@@ -12,13 +12,8 @@ module I18n::Tasks::Reports
|
|
12
12
|
|
13
13
|
protected
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
missing_diff: {glyph: '∅', summary: 'translated in one locale but not in the other'}
|
18
|
-
}
|
19
|
-
|
20
|
-
def missing_types
|
21
|
-
MISSING_TYPES
|
15
|
+
def missing_type_info(type)
|
16
|
+
::I18n::Tasks::MissingKeys::MISSING_TYPES[type.to_s.sub(/\Amissing_/, '').to_sym]
|
22
17
|
end
|
23
18
|
|
24
19
|
def missing_title(forest)
|
@@ -36,13 +31,13 @@ module I18n::Tasks::Reports
|
|
36
31
|
def used_title(used_tree)
|
37
32
|
leaves = used_tree.leaves.to_a
|
38
33
|
filter = used_tree.first.root.data[:key_filter]
|
39
|
-
used_n = leaves.map { |node| node.data[:
|
34
|
+
used_n = leaves.map { |node| node.data[:source_occurrences].size }.reduce(:+).to_i
|
40
35
|
"#{leaves.length} key#{'s' if leaves.size != 1}#{" matching '#{filter}'" if filter}#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n > 0}"
|
41
36
|
end
|
42
37
|
|
43
38
|
# Sort keys by their attributes in order
|
44
39
|
# @param [Hash] order e.g. {locale: :asc, type: :desc, key: :asc}
|
45
|
-
def sort_by_attr!(objects, order)
|
40
|
+
def sort_by_attr!(objects, order = {locale: :asc, key: :asc})
|
46
41
|
order_keys = order.keys
|
47
42
|
objects.sort! { |a, b|
|
48
43
|
by = order_keys.detect { |by| a[by] != b[by] }
|
@@ -5,8 +5,8 @@ require 'fileutils'
|
|
5
5
|
module I18n::Tasks::Reports
|
6
6
|
class Spreadsheet < Base
|
7
7
|
|
8
|
-
def save_report(path
|
9
|
-
path = 'tmp/i18n-report.xlsx'
|
8
|
+
def save_report(path, opts)
|
9
|
+
path = path.presence || 'tmp/i18n-report.xlsx'
|
10
10
|
p = Axlsx::Package.new
|
11
11
|
add_missing_sheet p.workbook
|
12
12
|
add_unused_sheet p.workbook
|
@@ -27,11 +27,11 @@ module I18n::Tasks::Reports
|
|
27
27
|
regular_style = s.add_style
|
28
28
|
wb.add_worksheet(name: missing_title(tree)) { |sheet|
|
29
29
|
sheet.page_setup.fit_to :width => 1
|
30
|
-
sheet.add_row ['
|
30
|
+
sheet.add_row [I18n.t('i18n_tasks.common.type'), I18n.t('i18n_tasks.common.locale'), I18n.t('i18n_tasks.common.key'), I18n.t('i18n_tasks.common.base_value')]
|
31
31
|
style_header sheet
|
32
32
|
tree.keys do |key, node|
|
33
33
|
locale, type = node.root.data[:locale], node.data[:type]
|
34
|
-
sheet.add_row [
|
34
|
+
sheet.add_row [missing_type_info(type)[:summary], locale, key, task.t(key)],
|
35
35
|
styles: [type_cell, locale_cell, regular_style, regular_style]
|
36
36
|
end
|
37
37
|
}
|
@@ -52,7 +52,7 @@ module I18n::Tasks::Reports
|
|
52
52
|
|
53
53
|
def add_locale_key_value_table(wb, keys, worksheet_opts = {})
|
54
54
|
wb.add_worksheet worksheet_opts do |sheet|
|
55
|
-
sheet.add_row ['
|
55
|
+
sheet.add_row [I18n.t('i18n_tasks.common.locale'), I18n.t('i18n_tasks.common.key'), I18n.t('i18n_tasks.common.value')]
|
56
56
|
style_header sheet
|
57
57
|
keys.each do |locale_k_v|
|
58
58
|
sheet.add_row locale_k_v
|
@@ -8,71 +8,50 @@ module I18n
|
|
8
8
|
include Term::ANSIColor
|
9
9
|
|
10
10
|
def missing_keys(forest = task.missing_keys)
|
11
|
-
|
12
|
-
|
11
|
+
forest = task.collapse_plural_nodes!(forest)
|
13
12
|
if forest.present?
|
14
|
-
|
15
|
-
print_table headings: [cyan(bold('
|
16
|
-
t.rows =
|
17
|
-
|
18
|
-
if a[:type] == :missing_used
|
19
|
-
occ = a[:data][:source_locations]
|
20
|
-
first = occ.first
|
21
|
-
info = [green("#{first[:src_path]}:#{first[:line_num]}"),
|
22
|
-
("(#{occ.length - 1} more)" if occ.length > 1)].compact.join(' ')
|
23
|
-
else
|
24
|
-
info = a[:value].to_s.strip
|
25
|
-
end
|
26
|
-
[{value: cyan(locale), alignment: :center},
|
27
|
-
cyan(key),
|
28
|
-
wrap_string(info, 60)]
|
13
|
+
print_title missing_title(forest)
|
14
|
+
print_table headings: [cyan(bold(I18n.t('i18n_tasks.common.locale'))), cyan(bold I18n.t('i18n_tasks.common.key')), I18n.t('i18n_tasks.common.details')] do |t|
|
15
|
+
t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
|
16
|
+
[{value: cyan(a[:locale]), alignment: :center}, cyan(a[:key]), wrap_string(key_info(a), 60)]
|
29
17
|
end
|
30
18
|
end
|
31
19
|
else
|
32
|
-
print_success '
|
20
|
+
print_success I18n.t('i18n_tasks.missing.none')
|
33
21
|
end
|
34
22
|
end
|
35
23
|
|
36
24
|
def icon(type)
|
37
|
-
glyph =
|
25
|
+
glyph = missing_type_info(type)[:glyph]
|
38
26
|
{missing_used: red(glyph), missing_diff: yellow(glyph)}[type]
|
39
27
|
end
|
40
28
|
|
41
|
-
def used_keys(used_tree = task.used_tree(
|
29
|
+
def used_keys(used_tree = task.used_tree(source_occurrences: true))
|
42
30
|
print_title used_title(used_tree)
|
43
31
|
keys_nodes = used_tree.keys.to_a
|
44
32
|
if keys_nodes.present?
|
45
|
-
keys_nodes.sort! { |a, b| a[0] <=> b[0] }
|
46
|
-
|
47
|
-
usages = node.data[:source_locations]
|
48
|
-
puts "#{bold "#{key}"} #{green(usages.size.to_s) if usages.size > 1}"
|
49
|
-
usages.each do |u|
|
50
|
-
line = u[:line].dup.tap { |line|
|
51
|
-
line.strip!
|
52
|
-
line.sub!(/(.*?)(#{key})(.*)$/) { dark($1) + underline($2) + dark($3) }
|
53
|
-
}
|
54
|
-
puts " #{green "#{u[:src_path]}:#{u[:line_num]}"} #{line}"
|
55
|
-
end
|
33
|
+
keys_nodes.sort! { |a, b| a[0] <=> b[0] }.each do |key, node|
|
34
|
+
print_occurrences node, key
|
56
35
|
end
|
57
36
|
else
|
58
|
-
print_error '
|
37
|
+
print_error I18n.t('i18n_tasks.usages.none')
|
59
38
|
end
|
60
39
|
end
|
61
40
|
|
62
41
|
def unused_keys(tree = task.unused_keys)
|
63
42
|
keys = tree.root_key_values(true)
|
64
|
-
print_title unused_title(keys)
|
65
43
|
if keys.present?
|
44
|
+
print_title unused_title(keys)
|
66
45
|
print_locale_key_value_table keys
|
67
46
|
else
|
68
|
-
print_success '
|
47
|
+
print_success I18n.t('i18n_tasks.unused.none')
|
69
48
|
end
|
70
49
|
end
|
71
50
|
|
72
51
|
def eq_base_keys(tree = task.eq_base_keys)
|
73
52
|
keys = tree.root_key_values(true)
|
74
|
-
print_title eq_base_title(keys)
|
75
53
|
if keys.present?
|
54
|
+
print_title eq_base_title(keys)
|
76
55
|
print_locale_key_value_table keys
|
77
56
|
else
|
78
57
|
print_info cyan('No translations are the same as base value')
|
@@ -83,11 +62,29 @@ module I18n
|
|
83
62
|
print_locale_key_value_table tree.root_key_values(true)
|
84
63
|
end
|
85
64
|
|
65
|
+
def forest_stats(forest, stats = task.forest_stats(forest))
|
66
|
+
text = if stats[:locale_count] == 1
|
67
|
+
I18n.t('i18n_tasks.data_stats.text_single_locale', stats)
|
68
|
+
else
|
69
|
+
I18n.t('i18n_tasks.data_stats.text', stats)
|
70
|
+
end
|
71
|
+
title = bold(I18n.t('i18n_tasks.data_stats.title', stats.slice(:locales)))
|
72
|
+
print_info "#{cyan title} #{cyan text}"
|
73
|
+
end
|
74
|
+
|
86
75
|
private
|
87
76
|
|
77
|
+
def print_occurrences(node, full_key = node.full_key)
|
78
|
+
occurrences = node.data[:source_occurrences]
|
79
|
+
puts "#{bold "#{full_key}"} #{green(occurrences.size.to_s) if occurrences.size > 1}"
|
80
|
+
occurrences.each do |occurrence|
|
81
|
+
puts " #{key_occurrence full_key, occurrence}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
88
85
|
def print_locale_key_value_table(locale_key_values)
|
89
86
|
if locale_key_values.present?
|
90
|
-
print_table headings: [bold(cyan('
|
87
|
+
print_table headings: [bold(cyan(I18n.t('i18n_tasks.common.locale'))), bold(cyan(I18n.t('i18n_tasks.common.key'))), I18n.t('i18n_tasks.common.value')] do |t|
|
91
88
|
t.rows = locale_key_values.map { |(locale, k, v)| [{value: cyan(locale), alignment: :center}, cyan(k), v.to_s] }
|
92
89
|
end
|
93
90
|
else
|
@@ -100,7 +97,7 @@ module I18n
|
|
100
97
|
end
|
101
98
|
|
102
99
|
def print_success(message)
|
103
|
-
log_stderr(
|
100
|
+
log_stderr bold(green "✓ #{I18n.t('i18n_tasks.cmd.encourage').sample} #{message}")
|
104
101
|
end
|
105
102
|
|
106
103
|
def print_error(message)
|
@@ -120,9 +117,36 @@ module I18n
|
|
120
117
|
puts ::Terminal::Table.new(opts, &block)
|
121
118
|
end
|
122
119
|
|
120
|
+
def key_occurrence(full_key, info)
|
121
|
+
location = green "#{info[:src_path]}:#{info[:line_num]}"
|
122
|
+
source = highlight_key(full_key, info[:line], info[:line_pos]..-1).strip
|
123
|
+
"#{location} #{source}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def highlight_key(full_key, line, range = (0..-1))
|
127
|
+
result = line.dup
|
128
|
+
result[range] = result[range].sub!(full_key) { |m| underline m }
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
def key_info(leaf)
|
133
|
+
if leaf[:type] == :missing_used
|
134
|
+
first_occurrence leaf
|
135
|
+
else
|
136
|
+
leaf[:value].to_s.strip
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def first_occurrence(leaf)
|
141
|
+
usages = leaf[:data][:source_occurrences]
|
142
|
+
first = usages.first
|
143
|
+
[green("#{first[:src_path]}:#{first[:line_num]}"),
|
144
|
+
("(#{I18n.t 'i18n_tasks.common.n_more', count: usages.length - 1})" if usages.length > 1)].compact.join(' ')
|
145
|
+
end
|
146
|
+
|
123
147
|
def wrap_string(s, max)
|
124
148
|
chars = []
|
125
|
-
dist
|
149
|
+
dist = 0
|
126
150
|
s.chars.each do |c|
|
127
151
|
chars << c
|
128
152
|
dist += 1
|
@@ -36,13 +36,13 @@ module I18n::Tasks::Scanners
|
|
36
36
|
@key_filter_pattern = compile_key_pattern(value) if @key_filter
|
37
37
|
end
|
38
38
|
|
39
|
-
# @return [Array<{key,data:{
|
39
|
+
# @return [Array<{key,data:{source_occurrences:[]}}]
|
40
40
|
def keys(opts = {})
|
41
41
|
keys = traverse_files { |path|
|
42
42
|
scan_file(path, opts)
|
43
43
|
}.reduce(:+) || []
|
44
44
|
keys.group_by(&:first).map { |key, key_loc|
|
45
|
-
[key, data: {
|
45
|
+
[key, data: {source_occurrences: key_loc.map { |(k, attr)| attr[:data] }}]
|
46
46
|
}
|
47
47
|
end
|
48
48
|
|
@@ -115,8 +115,9 @@ module I18n::Tasks::Scanners
|
|
115
115
|
key
|
116
116
|
end
|
117
117
|
|
118
|
-
|
119
|
-
VALID_KEY_RE_STRICT =
|
118
|
+
VALID_KEY_CHARS = /[-\w.?!;:]/
|
119
|
+
VALID_KEY_RE_STRICT = /^#{VALID_KEY_CHARS}+$/
|
120
|
+
VALID_KEY_RE = /^(#{VALID_KEY_CHARS}|[\#{}])+$/
|
120
121
|
|
121
122
|
def valid_key?(key, strict = false)
|
122
123
|
return false if @key_filter && @key_filter_pattern !~ key
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module I18n::Tasks::SlopCommand
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def slop_command(name, attr, &block)
|
5
|
+
proc {
|
6
|
+
command name.tr('_', '-') do
|
7
|
+
opts = attr[:opt]
|
8
|
+
args = attr[:args]
|
9
|
+
banner "Usage: i18n-tasks #{name} [options] #{args}" if args.present?
|
10
|
+
desc = attr[:desc]
|
11
|
+
description desc if desc
|
12
|
+
if opts
|
13
|
+
opts.each do |opt|
|
14
|
+
on *[:short, :long, :desc, :conf].map { |k| opt[k] }.compact
|
15
|
+
end
|
16
|
+
end
|
17
|
+
run { |opts, args| block.call(name, opts, args) }
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_slop_opts_args(opts, args)
|
23
|
+
opts = opts.to_hash(true).reject { |k, v| v.nil? }
|
24
|
+
opts.merge!(arguments: args) unless args.empty?
|
25
|
+
opts
|
26
|
+
end
|
27
|
+
end
|