i18n-tasks 0.4.5 → 0.5.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 +0 -4
- data/CHANGES.md +7 -0
- data/README.md +10 -14
- data/i18n-tasks.gemspec +1 -1
- data/lib/i18n/tasks.rb +0 -2
- data/lib/i18n/tasks/base_task.rb +4 -2
- data/lib/i18n/tasks/commands.rb +14 -14
- data/lib/i18n/tasks/configuration.rb +10 -2
- data/lib/i18n/tasks/console_context.rb +73 -0
- data/lib/i18n/tasks/data.rb +0 -47
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +6 -1
- data/lib/i18n/tasks/data/file_system_base.rb +1 -1
- data/lib/i18n/tasks/data/router/conservative_router.rb +5 -5
- data/lib/i18n/tasks/data/router/pattern_router.rb +2 -2
- data/lib/i18n/tasks/data/tree/node.rb +47 -36
- data/lib/i18n/tasks/data/tree/nodes.rb +0 -4
- data/lib/i18n/tasks/data/tree/siblings.rb +54 -9
- data/lib/i18n/tasks/data/tree/traversal.rb +62 -23
- data/lib/i18n/tasks/fill_tasks.rb +29 -21
- data/lib/i18n/tasks/ignore_keys.rb +1 -1
- data/lib/i18n/tasks/key_pattern_matching.rb +17 -0
- data/lib/i18n/tasks/missing_keys.rb +39 -44
- data/lib/i18n/tasks/plural_keys.rb +14 -1
- data/lib/i18n/tasks/reports/base.rb +28 -8
- data/lib/i18n/tasks/reports/spreadsheet.rb +9 -8
- data/lib/i18n/tasks/reports/terminal.rb +33 -29
- data/lib/i18n/tasks/scanners/base_scanner.rb +22 -14
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +2 -1
- data/lib/i18n/tasks/unused_keys.rb +13 -13
- data/lib/i18n/tasks/used_keys.rb +39 -38
- data/lib/i18n/tasks/version.rb +1 -1
- data/spec/i18n_tasks_spec.rb +41 -40
- data/spec/locale_tree/siblings_spec.rb +26 -1
- data/spec/support/i18n_tasks_output_matcher.rb +4 -1
- data/spec/support/trees.rb +6 -1
- data/spec/used_keys_spec.rb +23 -15
- metadata +4 -11
- data/lib/i18n/tasks/file_structure.rb +0 -19
- data/lib/i18n/tasks/key.rb +0 -48
- data/lib/i18n/tasks/key/key_group.rb +0 -45
- data/lib/i18n/tasks/key/match_pattern.rb +0 -24
- data/lib/i18n/tasks/key/usages.rb +0 -12
- data/lib/i18n/tasks/key_group.rb +0 -68
- data/spec/key_group_spec.rb +0 -49
@@ -14,7 +14,7 @@ module I18n::Tasks::IgnoreKeys
|
|
14
14
|
@ignore_patterns ||= HashWithIndifferentAccess.new
|
15
15
|
@ignore_patterns[type] ||= {}
|
16
16
|
@ignore_patterns[type][locale] ||= begin
|
17
|
-
global, type_ignore =
|
17
|
+
global, type_ignore = ignore_config.presence || [], ignore_config(type).presence || []
|
18
18
|
if type_ignore.is_a?(Array)
|
19
19
|
patterns = global + type_ignore
|
20
20
|
elsif type_ignore.is_a?(Hash)
|
@@ -29,4 +29,21 @@ module I18n::Tasks::KeyPatternMatching
|
|
29
29
|
gsub(/:/, '(?<=^|\.)[^.]+?(?=\.|$)').
|
30
30
|
gsub(/\{(.*?)}/) { "(#{$1.strip.gsub /\s*,\s*/, '|'})" }
|
31
31
|
end
|
32
|
+
|
33
|
+
def key_match_pattern(k)
|
34
|
+
@key_match_pattern ||= {}
|
35
|
+
@key_match_pattern[k] ||= begin
|
36
|
+
"#{k.gsub(KEY_INTERPOLATION_RE, '*')}#{'*' if k.end_with?('.')}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return true if the key looks like an expression
|
41
|
+
KEY_INTERPOLATION_RE = /(?:\#{.*?}|\*+)/.freeze
|
42
|
+
def key_expression?(k)
|
43
|
+
@key_is_expr ||= {}
|
44
|
+
if @key_is_expr[k].nil?
|
45
|
+
@key_is_expr[k] = (k =~ KEY_INTERPOLATION_RE || k.end_with?('.'))
|
46
|
+
end
|
47
|
+
@key_is_expr[k]
|
48
|
+
end
|
32
49
|
end
|
@@ -1,61 +1,56 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
module I18n::Tasks
|
3
3
|
module MissingKeys
|
4
|
+
def missing_keys_types
|
5
|
+
@missing_keys_types ||= [:missing_from_base, :eq_base, :missing_from_locale]
|
6
|
+
end
|
7
|
+
|
4
8
|
# @param [:missing_from_base, :missing_from_locale, :eq_base] type (default nil)
|
5
|
-
# @return [
|
9
|
+
# @return [Siblings]
|
6
10
|
def missing_keys(opts = {})
|
7
11
|
locales = Array(opts[:locales]).presence || self.locales
|
8
|
-
|
9
|
-
unless type
|
10
|
-
types = opts[:types].presence || missing_keys_types
|
11
|
-
opts = opts.except(:types).merge(locales: locales)
|
12
|
-
return types.map { |t| missing_keys(opts.merge(type: t)) }.reduce(:+)
|
13
|
-
end
|
14
|
-
|
15
|
-
if type.to_s == 'missing_from_base'
|
16
|
-
keys = keys_missing_from_base if locales.include?(base_locale)
|
17
|
-
else
|
18
|
-
keys = non_base_locales(locales).map { |locale|
|
19
|
-
send("keys_#{type}", locale)
|
20
|
-
}.reduce(:+)
|
21
|
-
end
|
22
|
-
keys || KeyGroup.new([])
|
23
|
-
end
|
12
|
+
types = Array(opts[:type] || opts[:types].presence || missing_keys_types)
|
24
13
|
|
25
|
-
|
26
|
-
|
14
|
+
types.map { |type|
|
15
|
+
case type.to_s
|
16
|
+
when 'missing_from_base'
|
17
|
+
missing_tree(base_locale) if locales.include?(base_locale)
|
18
|
+
when 'missing_from_locale'
|
19
|
+
non_base_locales(locales).map { |locale| missing_tree(locale) }.reduce(:merge!)
|
20
|
+
when 'eq_base'
|
21
|
+
non_base_locales(locales).map { |locale| eq_base_tree(locale) }.reduce(:merge!)
|
22
|
+
end
|
23
|
+
}.compact.reduce(:merge!)
|
27
24
|
end
|
28
25
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
key
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
def missing_tree(locale, compared_to = base_locale)
|
27
|
+
if locale == compared_to
|
28
|
+
# keys used, but not present in locale
|
29
|
+
set_locale_tree_type used_tree.select_keys { |key, node|
|
30
|
+
!(key_expression?(key) || key_value?(key, locale) || ignore_key?(key, :missing))
|
31
|
+
}, locale, :missing_from_base
|
32
|
+
else
|
33
|
+
# keys present in compared_to, but not in locale
|
34
|
+
collapse_plural_nodes! set_locale_tree_type data[compared_to].select_keys { |key, node|
|
35
|
+
!key_value?(key, locale) && !ignore_key?(key, :missing)
|
36
|
+
}, locale, :missing_from_locale
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
KeyGroup.new keys, type: :missing_from_locale, locale: locale
|
47
|
-
end
|
40
|
+
def eq_base_tree(locale, compare_to = base_locale)
|
41
|
+
base = data[compare_to].first.children
|
42
|
+
set_locale_tree_type data[locale].select_keys(root: false) { |key, node|
|
43
|
+
other_node = base[key]
|
44
|
+
other_node && node.value == other_node.value && !ignore_key?(key, :eq_base, locale)
|
45
|
+
}, locale, :eq_base
|
48
46
|
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
}.compact
|
57
|
-
KeyGroup.new keys, type: :eq_base, locale: locale
|
58
|
-
end
|
48
|
+
def set_locale_tree_type(tree, locale, type)
|
49
|
+
tree.siblings { |root|
|
50
|
+
root.key = locale
|
51
|
+
}.leaves { |node|
|
52
|
+
node.data[:type] = type
|
53
|
+
}
|
59
54
|
end
|
60
55
|
end
|
61
56
|
end
|
@@ -1,6 +1,19 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
module I18n::Tasks::PluralKeys
|
3
|
-
|
3
|
+
PLURAL_KEY_SUFFIXES = Set.new %w(zero one two few many other)
|
4
|
+
PLURAL_KEY_RE = /\.(?:#{PLURAL_KEY_SUFFIXES.to_a * '|'})$/
|
5
|
+
|
6
|
+
def collapse_plural_nodes!(tree)
|
7
|
+
tree.leaves.map(&:parent).uniq.each do |node|
|
8
|
+
children = node.children
|
9
|
+
if children.present? && children.all? { |c| PLURAL_KEY_SUFFIXES.include?(c.key) }
|
10
|
+
node.value = children.to_hash
|
11
|
+
node.data.merge!(children.first.data)
|
12
|
+
node.children = nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
tree
|
16
|
+
end
|
4
17
|
|
5
18
|
# @param [String] key i18n key
|
6
19
|
# @param [String] locale to pull key data from
|
@@ -10,6 +10,8 @@ module I18n::Tasks::Reports
|
|
10
10
|
attr_reader :task
|
11
11
|
delegate :base_locale, :locales, to: :task
|
12
12
|
|
13
|
+
protected
|
14
|
+
|
13
15
|
MISSING_TYPES = {
|
14
16
|
missing_from_base: {glyph: '✗', summary: 'missing from base locale'},
|
15
17
|
missing_from_locale: {glyph: '∅', summary: 'missing from locale but present in base locale'},
|
@@ -20,18 +22,36 @@ module I18n::Tasks::Reports
|
|
20
22
|
MISSING_TYPES
|
21
23
|
end
|
22
24
|
|
23
|
-
def missing_title(
|
24
|
-
"Missing translations (#{
|
25
|
+
def missing_title(forest)
|
26
|
+
"Missing translations (#{forest.leaves.count || '∅'})"
|
27
|
+
end
|
28
|
+
|
29
|
+
def unused_title(key_values)
|
30
|
+
"Unused keys (#{key_values.count || '∅'})"
|
31
|
+
end
|
32
|
+
|
33
|
+
def used_title(used_tree)
|
34
|
+
leaves = used_tree.leaves.to_a
|
35
|
+
filter = used_tree.first.root.data[:key_filter]
|
36
|
+
used_n = leaves.map { |node| node.data[:source_locations].size }.reduce(:+).to_i
|
37
|
+
"#{leaves.length} key#{'s' if leaves.size != 1}#{" ~ filter: '#{filter}'" if filter}#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n > 0}"
|
25
38
|
end
|
26
39
|
|
27
|
-
|
28
|
-
|
40
|
+
# Sort keys by their attributes in order
|
41
|
+
# @param [Hash] order e.g. {locale: :asc, type: :desc, key: :asc}
|
42
|
+
def sort_by_attr!(objects, order)
|
43
|
+
order_keys = order.keys
|
44
|
+
objects.sort! { |a, b|
|
45
|
+
by = order_keys.detect { |by| a[by] != b[by] }
|
46
|
+
order[by] == :desc ? b[by] <=> a[by] : a[by] <=> b[by]
|
47
|
+
}
|
48
|
+
objects
|
29
49
|
end
|
30
50
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
51
|
+
def forest_to_attr(forest)
|
52
|
+
forest.keys(root: false).map { |key, node|
|
53
|
+
{key: key, type: node.data[:type], locale: node.root.key}
|
54
|
+
}
|
35
55
|
end
|
36
56
|
end
|
37
57
|
end
|
@@ -19,17 +19,18 @@ module I18n::Tasks::Reports
|
|
19
19
|
private
|
20
20
|
|
21
21
|
def add_missing_sheet(wb)
|
22
|
-
|
22
|
+
tree = task.missing_keys
|
23
23
|
wb.styles do |s|
|
24
24
|
type_cell = s.add_style :alignment => {:horizontal => :center}
|
25
25
|
locale_cell = s.add_style :alignment => {:horizontal => :center}
|
26
26
|
regular_style = s.add_style
|
27
|
-
wb.add_worksheet(name: missing_title(
|
27
|
+
wb.add_worksheet(name: missing_title(tree)) { |sheet|
|
28
28
|
sheet.page_setup.fit_to :width => 1
|
29
29
|
sheet.add_row ['Type', 'Locale', 'Key', 'Base Value']
|
30
30
|
style_header sheet
|
31
|
-
keys
|
32
|
-
|
31
|
+
tree.keys do |key, node|
|
32
|
+
locale, type = node.root.data[:locale], node.data[:type]
|
33
|
+
sheet.add_row [missing_types[type][:summary], locale, key, task.t(key)],
|
33
34
|
styles: [type_cell, locale_cell, regular_style, regular_style]
|
34
35
|
end
|
35
36
|
}
|
@@ -37,12 +38,12 @@ module I18n::Tasks::Reports
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def add_unused_sheet(wb)
|
40
|
-
keys = task.unused_keys
|
41
|
+
keys = task.unused_keys.root_key_values.sort { |a, b| a[0] != b[0] ? a[0] <=> b[0] : a[1] <=> b[1] }
|
41
42
|
wb.add_worksheet name: unused_title(keys) do |sheet|
|
42
|
-
sheet.add_row ['Key', '
|
43
|
+
sheet.add_row ['Locale', 'Key', 'Value']
|
43
44
|
style_header sheet
|
44
|
-
keys.each do |
|
45
|
-
sheet.add_row
|
45
|
+
keys.each do |locale_k_v|
|
46
|
+
sheet.add_row locale_k_v
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
@@ -7,45 +7,49 @@ module I18n
|
|
7
7
|
class Terminal < Base
|
8
8
|
include Term::ANSIColor
|
9
9
|
|
10
|
-
def missing_keys(
|
11
|
-
|
12
|
-
print_title missing_title(keys)
|
13
|
-
if keys.present?
|
10
|
+
def missing_keys(forest = task.missing_keys)
|
11
|
+
print_title missing_title(forest)
|
14
12
|
|
13
|
+
if forest.present?
|
15
14
|
print_info "#{bold 'Types:'} #{missing_types.values.map { |t| "#{t[:glyph]} #{t[:summary]}" } * ', '}"
|
16
|
-
|
15
|
+
keys_data = sort_by_attr! forest_to_attr(forest), {locale: :asc, type: :asc, key: :asc}
|
17
16
|
print_table headings: [magenta(bold('Locale')), bold('Type'), magenta(bold 'i18n Key'), bold(cyan "Base value (#{base_locale})")] do |t|
|
18
|
-
t.rows =
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
t.rows = keys_data.map do |d|
|
18
|
+
key = d[:key]
|
19
|
+
type = d[:type]
|
20
|
+
locale = d[:locale]
|
21
|
+
glyph = missing_types[type][:glyph]
|
22
|
+
glyph = {missing_from_base: red(glyph), missing_from_locale: yellow(glyph), eq_base: bold(blue(glyph))}[type]
|
23
|
+
if type == :missing_from_base
|
24
|
+
locale = magenta locale
|
23
25
|
base_value = ''
|
24
26
|
else
|
25
|
-
locale = magenta
|
26
|
-
base_value = task.t(key
|
27
|
+
locale = magenta locale
|
28
|
+
base_value = task.t(key, base_locale).to_s.strip
|
27
29
|
end
|
28
30
|
[{value: locale, alignment: :center},
|
29
31
|
{value: glyph, alignment: :center},
|
30
|
-
magenta(key
|
32
|
+
magenta(key),
|
31
33
|
cyan(base_value)]
|
32
|
-
|
34
|
+
end
|
33
35
|
end
|
34
36
|
else
|
35
|
-
print_success '
|
37
|
+
print_success 'No translations missing!'
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
def used_keys(
|
40
|
-
print_title used_title(
|
41
|
-
keys.
|
42
|
-
if
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
def used_keys(used_tree = task.used_tree(source_locations: true))
|
42
|
+
print_title used_title(used_tree)
|
43
|
+
keys_nodes = used_tree.keys.to_a
|
44
|
+
if keys_nodes.present?
|
45
|
+
keys_nodes.sort! { |a, b| a[0] <=> b[0] }
|
46
|
+
keys_nodes.each do |key, node|
|
47
|
+
usages = node.data[:source_locations]
|
48
|
+
puts "#{bold "#{key}"} #{green(usages.size.to_s) if usages.size > 1}"
|
49
|
+
usages.each do |u|
|
46
50
|
line = u[:line].dup.tap { |line|
|
47
51
|
line.strip!
|
48
|
-
line.sub!(/(.*?)(#{
|
52
|
+
line.sub!(/(.*?)(#{key})(.*)$/) { dark($1) + underline($2) + dark($3) }
|
49
53
|
}
|
50
54
|
puts " #{green "#{u[:path]}:#{u[:line_num]}"} #{line}"
|
51
55
|
end
|
@@ -55,15 +59,15 @@ module I18n
|
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
58
|
-
def unused_keys(
|
62
|
+
def unused_keys(tree = task.unused_keys)
|
63
|
+
keys = tree.root_key_values.sort { |a, b| a[0] != b[0] ? a[0] <=> b[0] : a[1] <=> b[1] }
|
59
64
|
print_title unused_title(keys)
|
60
|
-
keys.sort_by_attr!(key: :asc)
|
61
65
|
if keys.present?
|
62
|
-
print_table headings: [bold(magenta('i18n Key')), bold(cyan("Base value (#{base_locale})"))] do |t|
|
63
|
-
t.rows = keys.map { |k| [magenta(k
|
66
|
+
print_table headings: [bold(magenta('Locale')), bold(magenta('i18n Key')), bold(cyan("Base value (#{base_locale})"))] do |t|
|
67
|
+
t.rows = keys.map { |(locale, k, v)| [magenta(locale), magenta(k), cyan(v.to_s)] }
|
64
68
|
end
|
65
69
|
else
|
66
|
-
print_success '
|
70
|
+
print_success 'Every translation is used!'
|
67
71
|
end
|
68
72
|
end
|
69
73
|
|
@@ -74,7 +78,7 @@ module I18n
|
|
74
78
|
end
|
75
79
|
|
76
80
|
def print_success(message)
|
77
|
-
log_stderr(bold green message)
|
81
|
+
log_stderr(bold green ['Good job!', 'Well done!'].sample + ' ' + message)
|
78
82
|
end
|
79
83
|
|
80
84
|
def print_error(message)
|
@@ -10,7 +10,7 @@ module I18n::Tasks::Scanners
|
|
10
10
|
attr_reader :config, :key_filter, :record_src_loc
|
11
11
|
|
12
12
|
def initialize(config = {})
|
13
|
-
@config
|
13
|
+
@config = config.dup.with_indifferent_access.tap do |conf|
|
14
14
|
conf[:paths] = %w(app/) if conf[:paths].blank?
|
15
15
|
conf[:include] = Array(conf[:include]) if conf[:include].present?
|
16
16
|
if conf.key?(:exclude)
|
@@ -28,22 +28,23 @@ module I18n::Tasks::Scanners
|
|
28
28
|
@key_filter_pattern = compile_key_pattern(value) if @key_filter
|
29
29
|
end
|
30
30
|
|
31
|
-
# @return [Array]
|
31
|
+
# @return [Array] keys used in source, absolutized and unique
|
32
32
|
def keys
|
33
|
-
@keys ||= (traverse_files { |path| scan_file(path) }.reduce(:+) || []).uniq(&:
|
33
|
+
@keys ||= (traverse_files { |path| scan_file(path) }.reduce(:+) || []).uniq(&:first)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
# @return [Array<{key,usages}]
|
37
|
+
def keys_with_source_locations
|
38
|
+
recording_source_locations do
|
38
39
|
keys = traverse_files { |path|
|
39
|
-
|
40
|
+
scan_file(path)
|
40
41
|
}.reduce(:+) || []
|
41
|
-
keys.group_by(&:
|
42
|
-
|
42
|
+
keys.group_by(&:first).map { |key, key_loc|
|
43
|
+
[key, data: {source_locations: key_loc.map { |(k, attr)| attr[:data] }}]
|
43
44
|
}
|
44
45
|
end
|
45
46
|
end
|
46
|
-
|
47
|
+
|
47
48
|
def read_file(path)
|
48
49
|
result = nil
|
49
50
|
File.open(path, 'rb') { |f| result = f.read }
|
@@ -86,7 +87,7 @@ module I18n::Tasks::Scanners
|
|
86
87
|
self.key_filter = filter_was
|
87
88
|
end
|
88
89
|
|
89
|
-
def
|
90
|
+
def recording_source_locations
|
90
91
|
was = @record_src_loc
|
91
92
|
@record_src_loc = true
|
92
93
|
yield
|
@@ -100,16 +101,23 @@ module I18n::Tasks::Scanners
|
|
100
101
|
globs.any? { |glob| File.fnmatch(glob, path) }
|
101
102
|
end
|
102
103
|
|
103
|
-
def src_location(text, src_pos)
|
104
|
-
|
104
|
+
def src_location(path, text, src_pos)
|
105
|
+
src = {path: path}
|
106
|
+
if @record_src_loc
|
107
|
+
src.merge!(src_text_location(text, src_pos))
|
108
|
+
end
|
109
|
+
src
|
110
|
+
end
|
111
|
+
|
112
|
+
def src_text_location(text, src_pos)
|
105
113
|
line_begin = text.rindex(/^/, src_pos - 1)
|
106
114
|
line_end = text.index(/.(?=\n|$)/, src_pos)
|
107
|
-
{
|
115
|
+
{
|
108
116
|
pos: src_pos,
|
109
117
|
line_num: text[0..src_pos].count("\n") + 1,
|
110
118
|
line_pos: src_pos - line_begin + 1,
|
111
119
|
line: text[line_begin..line_end]
|
112
|
-
}
|
120
|
+
}
|
113
121
|
end
|
114
122
|
|
115
123
|
# remove the leading colon and unwrap quotes from the key match
|
@@ -13,7 +13,7 @@ module I18n::Tasks::Scanners
|
|
13
13
|
src_pos = Regexp.last_match.offset(0).first
|
14
14
|
key = match_to_key(match, path)
|
15
15
|
next unless valid_key?(key)
|
16
|
-
keys <<
|
16
|
+
keys << [key, data: src_location(path, text, src_pos)]
|
17
17
|
end
|
18
18
|
keys
|
19
19
|
end
|
@@ -34,6 +34,7 @@ module I18n::Tasks::Scanners
|
|
34
34
|
# @return [String] full absolute key name
|
35
35
|
def match_to_key(match, path)
|
36
36
|
key = strip_literal(match[0])
|
37
|
+
key = key + '*' if key.end_with?('.')
|
37
38
|
key = absolutize_key(key, path) if path && key.start_with?('.')
|
38
39
|
key
|
39
40
|
end
|
@@ -4,24 +4,24 @@ require 'set'
|
|
4
4
|
module I18n
|
5
5
|
module Tasks
|
6
6
|
module UnusedKeys
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
def unused_keys(opts = {})
|
8
|
+
locales = Array(opts[:locales]).presence || self.locales
|
9
|
+
locales.map { |locale| unused_tree locale }.compact.reduce(:merge!)
|
10
|
+
end
|
11
|
+
|
12
|
+
def unused_tree(locale = base_locale)
|
13
|
+
collapse_plural_nodes! data[locale].select_keys { |key, _node|
|
14
|
+
!ignore_key?(key, :unused) &&
|
15
|
+
!used_in_expr?(key) &&
|
16
|
+
!used_key?(depluralize_key(key, locale))
|
17
|
+
}
|
18
18
|
end
|
19
19
|
|
20
20
|
def remove_unused!(locales = nil)
|
21
21
|
locales ||= self.locales
|
22
|
-
unused = unused_keys
|
23
22
|
locales.each do |locale|
|
24
|
-
|
23
|
+
unused = unused_tree(locale).key_names.to_set
|
24
|
+
data[locale] = data[locale].select_keys { |key, value|
|
25
25
|
!unused.include?(depluralize_key(key, locale))
|
26
26
|
}
|
27
27
|
end
|