i18n-tasks 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|