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
@@ -20,17 +20,28 @@ module I18n::Tasks::Data::Tree
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def derive(new_attr = {})
|
23
|
-
self.class.new(attributes.merge(new_attr))
|
23
|
+
node = self.class.new(attributes.merge(new_attr).except(:children))
|
24
|
+
node.children = new_attr[:children] || @children.try(:derive, parent: node)
|
25
|
+
node
|
24
26
|
end
|
25
27
|
|
26
28
|
def key=(value)
|
27
|
-
|
28
|
-
@key
|
29
|
+
value = value.try(:to_s)
|
30
|
+
if @key != value
|
31
|
+
dirty!
|
32
|
+
parent.try(:children).try(:key_renamed, value, @key)
|
33
|
+
@key = value
|
34
|
+
end
|
29
35
|
end
|
30
36
|
|
31
|
-
def children=(
|
37
|
+
def children=(children)
|
32
38
|
dirty!
|
33
|
-
|
39
|
+
if Siblings === children || children.nil?
|
40
|
+
@children = children
|
41
|
+
@children.parent = self if @children
|
42
|
+
else
|
43
|
+
@children = Siblings.new(nodes: children, parent: self)
|
44
|
+
end
|
34
45
|
end
|
35
46
|
|
36
47
|
def each(&block)
|
@@ -47,12 +58,12 @@ module I18n::Tasks::Data::Tree
|
|
47
58
|
key.nil?
|
48
59
|
end
|
49
60
|
|
50
|
-
def
|
61
|
+
def value_or_children_hash
|
51
62
|
leaf? ? value : children.try(:to_hash)
|
52
63
|
end
|
53
64
|
|
54
65
|
def leaf?
|
55
|
-
!children
|
66
|
+
!children
|
56
67
|
end
|
57
68
|
|
58
69
|
attr_writer :leaf
|
@@ -81,11 +92,9 @@ module I18n::Tasks::Data::Tree
|
|
81
92
|
# do not use directly. use parent.append(node) instead
|
82
93
|
def parent=(value)
|
83
94
|
return if @parent == value
|
84
|
-
if value
|
85
|
-
|
86
|
-
|
87
|
-
dirty!
|
88
|
-
end
|
95
|
+
@parent.children.remove!(self) if @parent.try(:children) && @parent.children != value.children
|
96
|
+
@parent = value
|
97
|
+
dirty!
|
89
98
|
@parent
|
90
99
|
end
|
91
100
|
|
@@ -101,12 +110,10 @@ module I18n::Tasks::Data::Tree
|
|
101
110
|
|
102
111
|
# append and reparent nodes
|
103
112
|
def append!(nodes)
|
104
|
-
if
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
@children = Siblings.new(nodes: nodes, parent: self)
|
109
|
-
end
|
113
|
+
if @children
|
114
|
+
@children.merge!(nodes)
|
115
|
+
else
|
116
|
+
@children = Siblings.new(nodes: nodes, parent: self)
|
110
117
|
end
|
111
118
|
self
|
112
119
|
end
|
@@ -116,7 +123,7 @@ module I18n::Tasks::Data::Tree
|
|
116
123
|
end
|
117
124
|
|
118
125
|
def full_key(opts = {})
|
119
|
-
root
|
126
|
+
root = opts.key?(:root) ? opts[:root] : true
|
120
127
|
@full_key ||= {}
|
121
128
|
@full_key[root] ||= "#{"#{parent.full_key(root: root)}." if parent? && (root || parent.parent?)}#{key}"
|
122
129
|
end
|
@@ -127,6 +134,12 @@ module I18n::Tasks::Data::Tree
|
|
127
134
|
parent.walk_to_root &visitor if parent?
|
128
135
|
end
|
129
136
|
|
137
|
+
def root
|
138
|
+
p = nil
|
139
|
+
walk_to_root { |node| p = node }
|
140
|
+
p
|
141
|
+
end
|
142
|
+
|
130
143
|
def walk_from_root(&visitor)
|
131
144
|
return to_enum(:walk_from_root) unless visitor
|
132
145
|
walk_to_root.reverse_each do |node|
|
@@ -138,6 +151,14 @@ module I18n::Tasks::Data::Tree
|
|
138
151
|
Nodes.new([self])
|
139
152
|
end
|
140
153
|
|
154
|
+
def to_siblings
|
155
|
+
if parent
|
156
|
+
parent.children
|
157
|
+
else
|
158
|
+
Siblings.new(nodes: [self])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
141
162
|
def to_hash
|
142
163
|
@hash ||= begin
|
143
164
|
children_hash = (children || {}).map(&:to_hash).reduce(:deep_merge) || {}
|
@@ -155,24 +176,14 @@ module I18n::Tasks::Data::Tree
|
|
155
176
|
delegate :to_yaml, to: :to_hash
|
156
177
|
|
157
178
|
def inspect(level = 0)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
value = Term::ANSIColor.cyan(self.value.to_s)
|
165
|
-
"#{key}: #{value}"
|
166
|
-
else
|
167
|
-
"#{key}"
|
168
|
-
end + (self.data? ? " #{self.data}" : '')
|
169
|
-
end
|
170
|
-
|
171
|
-
r = "#{' ' * level}#{label}"
|
172
|
-
if children?
|
173
|
-
r += "\n" + children.map { |c| c.inspect(level + 1) }.join("\n") if children?
|
179
|
+
if null?
|
180
|
+
label = Term::ANSIColor.dark '∅'
|
181
|
+
else
|
182
|
+
label = [Term::ANSIColor.color(1 + level % 15, self.key),
|
183
|
+
(": #{Term::ANSIColor.cyan(self.value.to_s)}" if leaf?),
|
184
|
+
(" #{self.data}" if data?)].compact.join
|
174
185
|
end
|
175
|
-
|
186
|
+
[' ' * level, label, ("\n" + children.map { |c| c.inspect(level + 1) }.join("\n") if children?)].compact.join
|
176
187
|
end
|
177
188
|
|
178
189
|
protected
|
@@ -12,8 +12,8 @@ module I18n::Tasks::Data::Tree
|
|
12
12
|
def initialize(opts = {})
|
13
13
|
super(nodes: opts[:nodes])
|
14
14
|
@key_to_node = siblings.inject({}) { |h, node| h[node.key] = node; h }
|
15
|
-
@parent = first.try(:parent)
|
16
|
-
self.parent = opts[:parent]
|
15
|
+
@parent = first.try(:parent)
|
16
|
+
self.parent = opts[:parent] || @parent || Node.null
|
17
17
|
end
|
18
18
|
|
19
19
|
def attributes
|
@@ -22,16 +22,19 @@ module I18n::Tasks::Data::Tree
|
|
22
22
|
|
23
23
|
def parent=(node)
|
24
24
|
return if @parent == node
|
25
|
-
each { |
|
25
|
+
each { |root| root.parent = node }
|
26
26
|
@parent = node
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
def siblings(&block)
|
30
|
+
each(&block)
|
31
|
+
self
|
32
|
+
end
|
30
33
|
|
31
34
|
# @return [Node] by full key
|
32
35
|
def get(full_key)
|
33
36
|
first_key, rest = full_key.to_s.split('.', 2)
|
34
|
-
node
|
37
|
+
node = key_to_node[first_key]
|
35
38
|
if rest && node
|
36
39
|
node = node.children.try(:get, rest)
|
37
40
|
end
|
@@ -43,7 +46,7 @@ module I18n::Tasks::Data::Tree
|
|
43
46
|
# add or replace node by full key
|
44
47
|
def set(full_key, node)
|
45
48
|
key_part, rest = full_key.split('.', 2)
|
46
|
-
child
|
49
|
+
child = key_to_node[key_part]
|
47
50
|
if rest
|
48
51
|
unless child
|
49
52
|
child = Node.new(key: key_part)
|
@@ -106,17 +109,51 @@ module I18n::Tasks::Data::Tree
|
|
106
109
|
derive.merge!(nodes)
|
107
110
|
end
|
108
111
|
|
112
|
+
def key_renamed(new_name, old_name)
|
113
|
+
node = key_to_node.delete old_name
|
114
|
+
key_to_node[new_name] = node
|
115
|
+
end
|
116
|
+
|
109
117
|
class << self
|
110
118
|
def null
|
111
119
|
new
|
112
120
|
end
|
113
121
|
|
122
|
+
def build_forest(opts = {}, &block)
|
123
|
+
opts[:nodes] ||= []
|
124
|
+
parse_parent_opt!(opts)
|
125
|
+
forest = Siblings.new(opts)
|
126
|
+
block.call(forest) if block
|
127
|
+
forest.parent.children = forest
|
128
|
+
end
|
129
|
+
|
130
|
+
def from_key_attr(key_attrs, opts = {}, &block)
|
131
|
+
build_forest(opts) { |forest|
|
132
|
+
key_attrs.each { |(full_key, attr)|
|
133
|
+
raise "Invalid key #{full_key.inspect}" if full_key.end_with?('.')
|
134
|
+
node = Node.new(attr.merge(key: full_key.split('.').last))
|
135
|
+
block.call(full_key, node) if block
|
136
|
+
forest[full_key] = node
|
137
|
+
}
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
def from_key_names(keys, opts = {}, &block)
|
142
|
+
build_forest(opts) { |forest|
|
143
|
+
keys.each { |full_key|
|
144
|
+
node = Node.new(key: full_key.split('.').last)
|
145
|
+
block.call(full_key, node) if block
|
146
|
+
forest[full_key] = node
|
147
|
+
}
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
114
151
|
# build forest from nested hash, e.g. {'es' => { 'common' => { name => 'Nombre', 'age' => 'Edad' } } }
|
115
152
|
# this is the native i18n gem format
|
116
153
|
def from_nested_hash(hash, opts = {})
|
117
|
-
opts
|
118
|
-
|
119
|
-
|
154
|
+
parse_parent_opt!(opts)
|
155
|
+
opts[:nodes] = hash.map { |key, value| Node.from_key_value key, value }
|
156
|
+
Siblings.new(opts)
|
120
157
|
end
|
121
158
|
|
122
159
|
alias [] from_nested_hash
|
@@ -129,6 +166,14 @@ module I18n::Tasks::Data::Tree
|
|
129
166
|
}
|
130
167
|
end
|
131
168
|
end
|
169
|
+
|
170
|
+
private
|
171
|
+
def parse_parent_opt!(opts)
|
172
|
+
opts[:parent] = Node.new(key: opts[:parent_key]) if opts[:parent_key]
|
173
|
+
opts[:parent] = Node.new(opts[:parent_attr]) if opts[:parent_attr]
|
174
|
+
opts[:parent] = Node.new(key: opts[:parent_locale], data: {locale: opts[:parent_locale]}) if opts[:parent_locale]
|
175
|
+
opts[:parent] ||= Node.null
|
176
|
+
end
|
132
177
|
end
|
133
178
|
end
|
134
179
|
end
|
@@ -4,7 +4,7 @@ module I18n::Tasks::Data::Tree
|
|
4
4
|
module Traversal
|
5
5
|
|
6
6
|
def nodes(&block)
|
7
|
-
|
7
|
+
depth_first(&block)
|
8
8
|
end
|
9
9
|
|
10
10
|
def leaves(&visitor)
|
@@ -12,13 +12,7 @@ module I18n::Tasks::Data::Tree
|
|
12
12
|
nodes do |node|
|
13
13
|
visitor.yield(node) if node.leaf?
|
14
14
|
end
|
15
|
-
|
16
|
-
|
17
|
-
# @param root include root in full key
|
18
|
-
def keys(opts = {}, &visitor)
|
19
|
-
root = opts.key?(:root) ? opts[:root] : true
|
20
|
-
return to_enum(:keys, root: root) unless visitor
|
21
|
-
leaves { |node| visitor.yield(node.full_key(root: root), node) }
|
15
|
+
self
|
22
16
|
end
|
23
17
|
|
24
18
|
def levels(&block)
|
@@ -29,47 +23,71 @@ module I18n::Tasks::Data::Tree
|
|
29
23
|
block.yield non_null
|
30
24
|
Nodes.new(nodes.children).levels(&block)
|
31
25
|
end
|
26
|
+
self
|
32
27
|
end
|
33
28
|
|
34
|
-
def
|
35
|
-
return to_enum(:
|
29
|
+
def breadth_first(&visitor)
|
30
|
+
return to_enum(:breadth_first) unless visitor
|
36
31
|
levels do |nodes|
|
37
32
|
nodes.each { |node| visitor.yield(node) unless node.null? }
|
38
33
|
end
|
34
|
+
self
|
39
35
|
end
|
40
36
|
|
41
|
-
def
|
42
|
-
return to_enum(:
|
37
|
+
def depth_first(&visitor)
|
38
|
+
return to_enum(:depth_first) unless visitor
|
43
39
|
each { |node|
|
44
40
|
visitor.yield node unless node.null?
|
45
41
|
node.children.each do |child|
|
46
|
-
child.
|
42
|
+
child.depth_first(&visitor)
|
47
43
|
end if node.children?
|
48
44
|
}
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# @option root include root in full key
|
49
|
+
def keys(key_opts = {}, &visitor)
|
50
|
+
key_opts[:root] = false unless key_opts.key?(:root)
|
51
|
+
return to_enum(:keys, key_opts) unless visitor
|
52
|
+
leaves { |node| visitor.yield(node.full_key(key_opts), node) }
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def key_names(opts = {})
|
58
|
+
opts[:root] = false unless opts.key?(:root)
|
59
|
+
keys(opts).map { |key, _node| key }
|
60
|
+
end
|
61
|
+
|
62
|
+
def key_values(opts = {})
|
63
|
+
opts[:root] = false unless opts.key?(:root)
|
64
|
+
keys(opts).map { |key, node| [key, node.value] }
|
65
|
+
end
|
66
|
+
|
67
|
+
def root_key_values
|
68
|
+
keys(root: false).map { |key, node| [node.root.key, key, node.value]}
|
49
69
|
end
|
50
70
|
|
51
71
|
#-- modify / derive
|
52
72
|
|
53
73
|
# @return Siblings
|
54
74
|
def select_nodes(&block)
|
55
|
-
|
75
|
+
tree = Siblings.new
|
56
76
|
each do |node|
|
57
77
|
if block.yield(node)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
children: (node.children.select_nodes(&block) if node.children)
|
62
|
-
)
|
78
|
+
tree.append! node.derive(
|
79
|
+
parent: tree.parent,
|
80
|
+
children: (node.children.select_nodes(&block).to_a if node.children)
|
63
81
|
)
|
64
82
|
end
|
65
83
|
end
|
66
|
-
|
84
|
+
tree
|
67
85
|
end
|
68
86
|
|
69
87
|
# @return Siblings
|
70
88
|
def select_keys(opts = {}, &block)
|
71
|
-
root = opts.key?(:root) ? opts[:root] :
|
72
|
-
ok
|
89
|
+
root = opts.key?(:root) ? opts[:root] : false
|
90
|
+
ok = {}
|
73
91
|
keys(root: root) do |full_key, node|
|
74
92
|
if block.yield(full_key, node)
|
75
93
|
node.walk_to_root { |p|
|
@@ -78,7 +96,28 @@ module I18n::Tasks::Data::Tree
|
|
78
96
|
}
|
79
97
|
end
|
80
98
|
end
|
81
|
-
select_nodes { |node|
|
99
|
+
select_nodes { |node|
|
100
|
+
ok[node]
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# @return Siblings
|
106
|
+
def intersect_keys(other_tree, key_opts = {}, &block)
|
107
|
+
if block
|
108
|
+
select_keys(key_opts) { |key, node|
|
109
|
+
other_node = other_tree[key]
|
110
|
+
other_node && block.call(key, node, other_node)
|
111
|
+
}
|
112
|
+
else
|
113
|
+
select_keys(key_opts) { |key, node| other_tree[key] }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def grep_keys(match, opts = {})
|
118
|
+
select_keys(opts) do |full_key, _node|
|
119
|
+
match === full_key
|
120
|
+
end
|
82
121
|
end
|
83
122
|
end
|
84
123
|
end
|
@@ -1,26 +1,34 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
module I18n::Tasks
|
3
|
-
|
4
|
-
opts =
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
def fill_missing_google_translate(opts = {})
|
12
|
-
from = opts[:from] || base_locale
|
13
|
-
opts = opts.merge(
|
14
|
-
locales: Array(opts[:locales]) - Array(from),
|
15
|
-
keys: proc { |locale| missing_tree(locale, from).key_names.map(&:to_s) },
|
16
|
-
values: proc { |keys, locale|
|
17
|
-
google_translate(keys.zip(keys.map(&t_proc(from))), to: locale, from: from).map(&:last)
|
2
|
+
module I18n::Tasks
|
3
|
+
module FillTasks
|
4
|
+
def fill_missing_value(opts = {})
|
5
|
+
value = opts[:value] || ''
|
6
|
+
locales_for_update(opts).each do |locale|
|
7
|
+
data[locale] = data[locale].merge! missing_tree(locale).keys { |key, node|
|
8
|
+
node.value = value.respond_to?(:call) ? value.call(key, locale, node) : value
|
18
9
|
}
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def fill_missing_google_translate(opts = {})
|
14
|
+
from = opts[:from] || base_locale
|
15
|
+
locales = (Array(opts[:locales]).presence || self.locales) - [from]
|
16
|
+
locales.each do |locale|
|
17
|
+
keys = missing_tree(locale, from).key_names.map(&:to_s)
|
18
|
+
values = google_translate(keys.zip(keys.map(&t_proc(from))), to: locale, from: from).map(&:last)
|
19
|
+
|
20
|
+
data[locale] = Data::Tree::Node.new(
|
21
|
+
key: locale,
|
22
|
+
children: Data::Tree::Siblings.from_flat_pairs(keys.zip(values))
|
23
|
+
).to_siblings
|
24
|
+
end
|
25
|
+
end
|
22
26
|
|
23
|
-
|
24
|
-
|
27
|
+
def locales_for_update(opts)
|
28
|
+
locales = (Array(opts[:locales] || opts[:locale]).presence || self.locales).map(&:to_s)
|
29
|
+
# make sure base_locale always comes first if present
|
30
|
+
locales = [base_locale] + (locales - [base_locale]) if locales.include?(base_locale)
|
31
|
+
locales
|
32
|
+
end
|
25
33
|
end
|
26
34
|
end
|