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
@@ -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
|