i18n-tasks 0.8.5 → 0.8.6
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/.gitattributes +2 -0
- data/CHANGES.md +9 -0
- data/README.md +3 -1
- data/bin/i18n-tasks.cmd +2 -0
- data/config/locales/en.yml +2 -2
- data/i18n-tasks.gemspec +1 -1
- data/lib/i18n/tasks/cli.rb +3 -1
- data/lib/i18n/tasks/command/commands/health.rb +1 -2
- data/lib/i18n/tasks/command/commands/missing.rb +3 -6
- data/lib/i18n/tasks/command/commands/usages.rb +4 -2
- data/lib/i18n/tasks/console_context.rb +1 -2
- data/lib/i18n/tasks/data/tree/node.rb +8 -1
- data/lib/i18n/tasks/data/tree/nodes.rb +1 -1
- data/lib/i18n/tasks/data/tree/siblings.rb +29 -11
- data/lib/i18n/tasks/data/tree/traversal.rb +24 -6
- data/lib/i18n/tasks/missing_keys.rb +32 -14
- data/lib/i18n/tasks/reports/base.rb +9 -0
- data/lib/i18n/tasks/reports/spreadsheet.rb +7 -4
- data/lib/i18n/tasks/reports/terminal.rb +2 -1
- data/lib/i18n/tasks/scanners/base_scanner.rb +4 -2
- data/lib/i18n/tasks/version.rb +1 -1
- data/spec/commands/data_commands_spec.rb +1 -1
- data/spec/commands/tree_commands_spec.rb +1 -1
- data/spec/conservative_router_spec.rb +1 -1
- data/spec/file_system_data_spec.rb +1 -1
- data/spec/google_translate_spec.rb +1 -1
- data/spec/i18n_spec.rb +1 -1
- data/spec/i18n_tasks_spec.rb +34 -24
- data/spec/key_pattern_matching_spec.rb +1 -1
- data/spec/locale_pathname_spec.rb +1 -1
- data/spec/locale_tree/siblings_spec.rb +1 -1
- data/spec/pattern_scanner_spec.rb +1 -1
- data/spec/plural_keys_spec.rb +1 -1
- data/spec/readme_spec.rb +1 -1
- data/spec/relative_keys_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/split_key_spec.rb +1 -1
- data/spec/support/i18n_tasks_output_matcher.rb +2 -3
- data/spec/support/test_codebase.rb +8 -0
- data/spec/support/trees.rb +5 -0
- data/spec/used_keys_spec.rb +7 -7
- data/templates/config/i18n-tasks.yml +4 -3
- data/templates/rspec/i18n_spec.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a70e643cadbeb135db42081bdf2b9ea9ed7d5163
|
|
4
|
+
data.tar.gz: 99b5ba4d17e4d93b646895172faa2a74d982d006
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 15800850eefad7aea706c8275ac804976bff7047d84d13c1ee3a1b33b53e91215ac55515e5c74b7d93b02dd9bb764ce34a4076f88668899620dad141e87a8a65
|
|
7
|
+
data.tar.gz: 55820e43acb2cc6e033fee27cc0abc19cfe945c1334ba18aed67c5b03c27a96adba8ca0b6dc89c79ee4e1b5f2da36367a7c0b2914604c6de494a41302dad5633
|
data/.gitattributes
ADDED
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.8.6
|
|
2
|
+
|
|
3
|
+
* Report missing keys found in source in all the locales. [#162](https://github.com/glebm/i18n-tasks/issues/162)
|
|
4
|
+
* Fix `data-remove` task. [#140](https://github.com/glebm/i18n-tasks/issues/140)
|
|
5
|
+
* Non-zero exit code on `health`, `missing`, and `unused` if such keys are present. [#151](https://github.com/glebm/i18n-tasks/issues/151)
|
|
6
|
+
* XLSX report compatibility with the OSX Numbers App. [#159](https://github.com/glebm/i18n-tasks/issues/159)
|
|
7
|
+
* RSpec template compatibility with `config.expose_dsl_globally = false`. [#148](https://github.com/glebm/i18n-tasks/issues/148)
|
|
8
|
+
* `bundle show vagrant` example in the config template is no longer interpolated .[#161](https://github.com/glebm/i18n-tasks/issues/161)
|
|
9
|
+
|
|
1
10
|
## 0.8.5
|
|
2
11
|
|
|
3
12
|
* Fix regression: Plugin support [#153](https://github.com/glebm/i18n-tasks/issues/153).
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# i18n-tasks [![Build Status][badge-travis]][travis] [![Coverage Status][badge-coverage]][coverage] [![Code Climate][badge-code-climate]][code-climate] [![Gemnasium][badge-gemnasium]][gemnasium] [](https://gitter.im/glebm/i18n-tasks?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
2
2
|
|
|
3
|
+
<a href='https://pledgie.com/campaigns/28570'><img alt='Click here to lend your support to: i18n-tasks for Ruby and make a donation at pledgie.com !' src='https://pledgie.com/campaigns/28570.png?skin_name=chrome' border='0' ></a>
|
|
4
|
+
|
|
3
5
|
i18n-tasks helps you find and manage missing and unused translations.
|
|
4
6
|
|
|
5
7
|
<img width="539" height="331" src="https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-tasks.png">
|
|
@@ -22,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
|
|
|
22
24
|
Add i18n-tasks to the Gemfile:
|
|
23
25
|
|
|
24
26
|
```ruby
|
|
25
|
-
gem 'i18n-tasks', '~> 0.8.
|
|
27
|
+
gem 'i18n-tasks', '~> 0.8.6'
|
|
26
28
|
```
|
|
27
29
|
|
|
28
30
|
Copy the default [configuration file](#configuration):
|
data/bin/i18n-tasks.cmd
ADDED
data/config/locales/en.yml
CHANGED
|
@@ -15,7 +15,7 @@ en:
|
|
|
15
15
|
key_pattern_to_rename: Full key (pattern) to rename. Required
|
|
16
16
|
locale: Locale
|
|
17
17
|
locale_to_translate_from: Locale to translate from
|
|
18
|
-
locales_filter:
|
|
18
|
+
locales_filter: 'Locale(s) to process. Special: base'
|
|
19
19
|
missing_types: 'Filter by types: %{valid}'
|
|
20
20
|
new_key_name: New name, interpolates original name as %{key}. Required
|
|
21
21
|
nostdin: Do not read from stdin
|
|
@@ -90,7 +90,7 @@ en:
|
|
|
90
90
|
none: No translations are missing.
|
|
91
91
|
remove_unused:
|
|
92
92
|
confirm:
|
|
93
|
-
one:
|
|
93
|
+
one: "%{count} translation will be removed from %{locales}."
|
|
94
94
|
other: "%{count} translation will be removed from %{locales}."
|
|
95
95
|
noop: No unused keys to remove
|
|
96
96
|
removed: Removed %{count} keys
|
data/i18n-tasks.gemspec
CHANGED
|
@@ -43,6 +43,6 @@ TEXT
|
|
|
43
43
|
s.add_development_dependency 'axlsx', '~> 2.0'
|
|
44
44
|
s.add_development_dependency 'bundler', '~> 1.3'
|
|
45
45
|
s.add_development_dependency 'rake'
|
|
46
|
-
s.add_development_dependency 'rspec', '~> 3.
|
|
46
|
+
s.add_development_dependency 'rspec', '~> 3.3'
|
|
47
47
|
s.add_development_dependency 'yard'
|
|
48
48
|
end
|
data/lib/i18n/tasks/cli.rb
CHANGED
|
@@ -23,7 +23,9 @@ module I18n::Tasks
|
|
|
23
23
|
args: [:locales, :out_format, :missing_types]
|
|
24
24
|
|
|
25
25
|
def missing(opt = {})
|
|
26
|
-
|
|
26
|
+
forest = i18n.missing_keys(opt)
|
|
27
|
+
print_forest forest, opt, :missing_keys
|
|
28
|
+
:exit_1 unless forest.empty?
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
cmd :translate_missing,
|
|
@@ -47,11 +49,6 @@ module I18n::Tasks
|
|
|
47
49
|
def add_missing(opt = {})
|
|
48
50
|
forest = i18n.missing_keys(opt).set_each_value!(opt[:value])
|
|
49
51
|
i18n.data.merge! forest
|
|
50
|
-
# missing keys detected in the source are only returned in the base locale tree
|
|
51
|
-
# merge again in case such keys have been added to add them to other locales
|
|
52
|
-
forest_2 = i18n.missing_keys(opt).set_each_value!(opt[:value])
|
|
53
|
-
i18n.data.merge! forest_2
|
|
54
|
-
forest.merge! forest_2
|
|
55
52
|
log_stderr t('i18n_tasks.add_missing.added', count: forest.leaves.count)
|
|
56
53
|
print_forest forest, opt
|
|
57
54
|
end
|
|
@@ -25,7 +25,9 @@ module I18n::Tasks
|
|
|
25
25
|
args: [:locales, :out_format, :strict]
|
|
26
26
|
|
|
27
27
|
def unused(opt = {})
|
|
28
|
-
|
|
28
|
+
forest = i18n.unused_keys(opt)
|
|
29
|
+
print_forest forest, opt, :unused_keys
|
|
30
|
+
:exit_1 unless forest.empty?
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
cmd :remove_unused,
|
|
@@ -50,7 +52,7 @@ module I18n::Tasks
|
|
|
50
52
|
|
|
51
53
|
def confirm_remove_unused!(unused_keys, opt)
|
|
52
54
|
return if ENV['CONFIRM'] || opt[:confirm]
|
|
53
|
-
locales = bold(
|
|
55
|
+
locales = bold(unused_keys.flat_map { |root| root.key.split('+') }.sort.uniq * ', ')
|
|
54
56
|
msg = [
|
|
55
57
|
red(t('i18n_tasks.remove_unused.confirm', count: unused_keys.leaves.count, locales: locales)),
|
|
56
58
|
yellow(t('i18n_tasks.common.continue_q')),
|
|
@@ -36,13 +36,12 @@ module I18n::Tasks
|
|
|
36
36
|
green(bold "i18n-tasks IRB Quick Start guide") + "\n" + <<-TEXT
|
|
37
37
|
#{yellow 'Data as trees'}
|
|
38
38
|
tree(locale)
|
|
39
|
-
missing_tree(locale, compared_to = base_locale)
|
|
40
39
|
used_tree(source_occurrences: false, key_filter: nil)
|
|
41
40
|
unused_tree(locale)
|
|
42
41
|
build_tree('es' => {'hello' => 'Hola'})
|
|
43
42
|
|
|
44
43
|
#{yellow 'Traversal'}
|
|
45
|
-
tree =
|
|
44
|
+
tree = missing_diff_tree('es')
|
|
46
45
|
tree.nodes { |node| }
|
|
47
46
|
tree.nodes.to_a
|
|
48
47
|
tree.leaves { |node| }
|
|
@@ -63,7 +63,7 @@ module I18n::Tasks::Data::Tree
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def children?
|
|
66
|
-
children && children.
|
|
66
|
+
children && !children.empty?
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def data
|
|
@@ -119,6 +119,13 @@ module I18n::Tasks::Data::Tree
|
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
def set(full_key, node)
|
|
123
|
+
(@children ||= Siblings.new(parent: self)).set(full_key, node)
|
|
124
|
+
dirty!
|
|
125
|
+
node
|
|
126
|
+
end
|
|
127
|
+
alias []= set
|
|
128
|
+
|
|
122
129
|
def to_nodes
|
|
123
130
|
Nodes.new([self])
|
|
124
131
|
end
|
|
@@ -79,7 +79,7 @@ module I18n::Tasks::Data::Tree
|
|
|
79
79
|
alias + merge!
|
|
80
80
|
|
|
81
81
|
def children(&block)
|
|
82
|
-
return to_enum(:children) { map { |c| c.children.size }.reduce(:+) } unless block
|
|
82
|
+
return to_enum(:children) { map { |c| c.children ? c.children.size : 0 }.reduce(:+) } unless block
|
|
83
83
|
each do |node|
|
|
84
84
|
node.children.each(&block) if node.children?
|
|
85
85
|
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
+
require 'set'
|
|
4
|
+
require 'i18n/tasks/split_key'
|
|
3
5
|
require 'i18n/tasks/data/tree/nodes'
|
|
4
6
|
module I18n::Tasks::Data::Tree
|
|
5
7
|
# Siblings represents a subtree sharing a common parent
|
|
6
8
|
# in case of an empty parent (nil) it represents a forest
|
|
7
9
|
# siblings' keys are unique
|
|
8
10
|
class Siblings < Nodes
|
|
11
|
+
include ::I18n::Tasks::SplitKey
|
|
12
|
+
|
|
9
13
|
attr_reader :parent, :key_to_node
|
|
10
14
|
|
|
11
15
|
def initialize(opts = {})
|
|
@@ -44,8 +48,6 @@ module I18n::Tasks::Data::Tree
|
|
|
44
48
|
key_to_node[new_node.key] = new_node
|
|
45
49
|
end
|
|
46
50
|
|
|
47
|
-
include ::I18n::Tasks::SplitKey
|
|
48
|
-
|
|
49
51
|
# @return [Node] by full key
|
|
50
52
|
def get(full_key)
|
|
51
53
|
first_key, rest = split_key(full_key.to_s, 2)
|
|
@@ -121,15 +123,12 @@ module I18n::Tasks::Data::Tree
|
|
|
121
123
|
end
|
|
122
124
|
|
|
123
125
|
def subtract_keys(keys)
|
|
124
|
-
|
|
126
|
+
to_remove = Set.new
|
|
125
127
|
keys.each do |full_key|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
end
|
|
128
|
+
node = get full_key
|
|
129
|
+
to_remove << node if node
|
|
129
130
|
end
|
|
130
|
-
|
|
131
|
-
not exclude[node] || node.children.try(:all?) { |c| exclude[c] }
|
|
132
|
-
}
|
|
131
|
+
remove_nodes_collapsing_emptied_ancestors to_remove
|
|
133
132
|
end
|
|
134
133
|
|
|
135
134
|
def subtract_by_key(other)
|
|
@@ -143,8 +142,6 @@ module I18n::Tasks::Data::Tree
|
|
|
143
142
|
self
|
|
144
143
|
end
|
|
145
144
|
|
|
146
|
-
private
|
|
147
|
-
|
|
148
145
|
def merge_node!(node)
|
|
149
146
|
if key_to_node.key?(node.key)
|
|
150
147
|
our = key_to_node[node.key]
|
|
@@ -164,6 +161,27 @@ module I18n::Tasks::Data::Tree
|
|
|
164
161
|
end
|
|
165
162
|
end
|
|
166
163
|
|
|
164
|
+
# @param [Enumerable] nodes. Modified in-place.
|
|
165
|
+
def remove_nodes_collapsing_emptied_ancestors(nodes)
|
|
166
|
+
add_ancestors_that_only_contain_nodes! nodes
|
|
167
|
+
select_nodes { |node| !nodes.include?(node) }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @param [Enumerable] nodes. Modified in-place.
|
|
171
|
+
def remove_nodes_collapsing_emptied_ancestors!(nodes)
|
|
172
|
+
add_ancestors_that_only_contain_nodes! nodes
|
|
173
|
+
select_nodes! { |node| !nodes.include?(node) }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
# @param [Set] nodes. Modified in-place.
|
|
179
|
+
def add_ancestors_that_only_contain_nodes!(nodes)
|
|
180
|
+
levels.reverse_each do |level_nodes|
|
|
181
|
+
level_nodes.each { |node| nodes << node if node.children? && node.children.all? { |c| nodes.include?(c) } }
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
167
185
|
def warn_add_children_to_leaf(node)
|
|
168
186
|
::I18n::Tasks::Logging.log_warn "'#{node.full_key}' was a leaf, now has children (value <- scope conflict)"
|
|
169
187
|
end
|
|
@@ -18,14 +18,15 @@ module I18n::Tasks
|
|
|
18
18
|
|
|
19
19
|
def levels(&block)
|
|
20
20
|
return to_enum(:levels) unless block
|
|
21
|
-
nodes
|
|
21
|
+
nodes = to_nodes
|
|
22
22
|
unless nodes.empty?
|
|
23
23
|
block.yield nodes
|
|
24
|
-
if nodes.
|
|
25
|
-
first
|
|
24
|
+
if nodes.size == 1
|
|
25
|
+
node = first
|
|
26
|
+
node.children.levels(&block) if node.children?
|
|
26
27
|
else
|
|
27
|
-
Nodes.new(nodes: nodes.children)
|
|
28
|
-
end
|
|
28
|
+
Nodes.new(nodes: nodes.children).levels(&block)
|
|
29
|
+
end
|
|
29
30
|
end
|
|
30
31
|
self
|
|
31
32
|
end
|
|
@@ -76,7 +77,8 @@ module I18n::Tasks
|
|
|
76
77
|
|
|
77
78
|
#-- modify / derive
|
|
78
79
|
|
|
79
|
-
#
|
|
80
|
+
# Select the nodes for which the block returns true. Pre-order traversal.
|
|
81
|
+
# @return [Siblings] a new tree
|
|
80
82
|
def select_nodes(&block)
|
|
81
83
|
tree = Siblings.new
|
|
82
84
|
each do |node|
|
|
@@ -90,6 +92,22 @@ module I18n::Tasks
|
|
|
90
92
|
tree
|
|
91
93
|
end
|
|
92
94
|
|
|
95
|
+
# Select the nodes for which the block returns true. Pre-order traversal.
|
|
96
|
+
# @return [Siblings] self
|
|
97
|
+
def select_nodes!(&block)
|
|
98
|
+
to_remove = []
|
|
99
|
+
each do |node|
|
|
100
|
+
if block.yield(node)
|
|
101
|
+
node.children.select_nodes!(&block) if node.children
|
|
102
|
+
else
|
|
103
|
+
# removing during each is unsafe
|
|
104
|
+
to_remove << node
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
to_remove.each { |node| remove! node }
|
|
108
|
+
self
|
|
109
|
+
end
|
|
110
|
+
|
|
93
111
|
# @return Siblings
|
|
94
112
|
def select_keys(opts = {}, &block)
|
|
95
113
|
root = opts.key?(:root) ? opts[:root] : false
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
+
require 'set'
|
|
2
3
|
module I18n::Tasks
|
|
3
4
|
module MissingKeys
|
|
4
5
|
|
|
@@ -48,20 +49,10 @@ module I18n::Tasks
|
|
|
48
49
|
tree
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
def missing_used_forest(locales,
|
|
52
|
-
|
|
53
|
-
missing_used_tree(
|
|
54
|
-
|
|
55
|
-
empty_forest
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def missing_tree(locale, compared_to)
|
|
60
|
-
if locale == compared_to
|
|
61
|
-
missing_used_tree locale
|
|
62
|
-
else
|
|
63
|
-
missing_diff_tree locale, compared_to
|
|
64
|
-
end
|
|
52
|
+
def missing_used_forest(locales, _base = base_locale)
|
|
53
|
+
locales.inject(empty_forest) { |forest, locale|
|
|
54
|
+
forest.merge! missing_used_tree(locale)
|
|
55
|
+
}
|
|
65
56
|
end
|
|
66
57
|
|
|
67
58
|
# keys present in compared_to, but not in locale
|
|
@@ -96,5 +87,32 @@ module I18n::Tasks
|
|
|
96
87
|
def locale_key_missing?(locale, key)
|
|
97
88
|
!key_value?(key, locale) && !ignore_key?(key, :missing)
|
|
98
89
|
end
|
|
90
|
+
|
|
91
|
+
# @param [::I18n::Tasks::Data::Tree::Siblings] forest
|
|
92
|
+
def collapse_missing_used_locales!(forest)
|
|
93
|
+
locales_and_nodes_by_key = {}
|
|
94
|
+
to_remove = []
|
|
95
|
+
forest.each do |root|
|
|
96
|
+
locale = root.key
|
|
97
|
+
root.leaves { |node|
|
|
98
|
+
if node.data[:type] == :missing_used
|
|
99
|
+
(locales_and_nodes_by_key[node.full_key(root: false)] ||= []) << [locale, node]
|
|
100
|
+
to_remove << node
|
|
101
|
+
end
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
forest.remove_nodes_collapsing_emptied_ancestors! to_remove
|
|
105
|
+
keys_and_nodes_by_locale = {}
|
|
106
|
+
locales_and_nodes_by_key.each { |key, locales_and_nodes|
|
|
107
|
+
locales = locales_and_nodes.map(&:first).sort.join('+')
|
|
108
|
+
(keys_and_nodes_by_locale[locales] ||= []) << [key, locales_and_nodes[0][1]]
|
|
109
|
+
}
|
|
110
|
+
keys_and_nodes_by_locale.map { |locales, keys_nodes|
|
|
111
|
+
keys_nodes.each { |(key, node)|
|
|
112
|
+
forest["#{locales}.#{key}"] = node
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
forest
|
|
116
|
+
end
|
|
99
117
|
end
|
|
100
118
|
end
|
|
@@ -51,5 +51,14 @@ module I18n::Tasks::Reports
|
|
|
51
51
|
{key: key, value: node.value, type: node.data[:type], locale: node.root.key, data: node.data}
|
|
52
52
|
}
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
def format_locale(locale)
|
|
56
|
+
return '' unless locale
|
|
57
|
+
if locale.split('+') == task.locales.sort
|
|
58
|
+
'all'
|
|
59
|
+
else
|
|
60
|
+
locale.tr '+', ' '
|
|
61
|
+
end
|
|
62
|
+
end
|
|
54
63
|
end
|
|
55
64
|
end
|
|
@@ -8,6 +8,7 @@ module I18n::Tasks::Reports
|
|
|
8
8
|
def save_report(path, opts)
|
|
9
9
|
path = path.presence || 'tmp/i18n-report.xlsx'
|
|
10
10
|
p = Axlsx::Package.new
|
|
11
|
+
p.use_shared_strings = true # see #159
|
|
11
12
|
add_missing_sheet p.workbook
|
|
12
13
|
add_unused_sheet p.workbook
|
|
13
14
|
add_eq_base_sheet p.workbook
|
|
@@ -19,17 +20,19 @@ module I18n::Tasks::Reports
|
|
|
19
20
|
private
|
|
20
21
|
|
|
21
22
|
def add_missing_sheet(wb)
|
|
22
|
-
|
|
23
|
+
forest = task.missing_keys
|
|
24
|
+
forest = task.collapse_plural_nodes!(forest)
|
|
25
|
+
forest = task.collapse_missing_used_locales!(forest)
|
|
23
26
|
wb.styles do |s|
|
|
24
27
|
type_cell = s.add_style :alignment => {:horizontal => :center}
|
|
25
28
|
locale_cell = s.add_style :alignment => {:horizontal => :center}
|
|
26
29
|
regular_style = s.add_style
|
|
27
|
-
wb.add_worksheet(name: missing_title(
|
|
30
|
+
wb.add_worksheet(name: missing_title(forest)) { |sheet|
|
|
28
31
|
sheet.page_setup.fit_to :width => 1
|
|
29
32
|
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')]
|
|
30
33
|
style_header sheet
|
|
31
|
-
|
|
32
|
-
locale, type = node.root.data[:locale], node.data[:type]
|
|
34
|
+
forest.keys do |key, node|
|
|
35
|
+
locale, type = format_locale(node.root.data[:locale]), node.data[:type]
|
|
33
36
|
sheet.add_row [missing_type_info(type)[:summary], locale, key, task.t(key)],
|
|
34
37
|
styles: [type_cell, locale_cell, regular_style, regular_style]
|
|
35
38
|
end
|
|
@@ -9,13 +9,14 @@ module I18n
|
|
|
9
9
|
|
|
10
10
|
def missing_keys(forest = task.missing_keys)
|
|
11
11
|
forest = task.collapse_plural_nodes!(forest)
|
|
12
|
+
forest = task.collapse_missing_used_locales!(forest)
|
|
12
13
|
if forest.present?
|
|
13
14
|
print_title missing_title(forest)
|
|
14
15
|
print_table headings: [cyan(bold(I18n.t('i18n_tasks.common.locale'))),
|
|
15
16
|
cyan(bold I18n.t('i18n_tasks.common.key')),
|
|
16
17
|
I18n.t('i18n_tasks.missing.details_title')] do |t|
|
|
17
18
|
t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
|
|
18
|
-
[{value: cyan(a[:locale]), alignment: :center}, cyan(a[:key]), missing_key_info(a)]
|
|
19
|
+
[{value: cyan(format_locale(a[:locale])), alignment: :center}, cyan(a[:key]), missing_key_info(a)]
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
else
|
|
@@ -15,7 +15,9 @@ module I18n::Tasks::Scanners
|
|
|
15
15
|
|
|
16
16
|
def initialize(config = {})
|
|
17
17
|
@config = config.dup.with_indifferent_access.tap do |conf|
|
|
18
|
-
|
|
18
|
+
if conf[:relative_roots].blank?
|
|
19
|
+
conf[:relative_roots] = %w(app/controllers app/helpers app/mailers app/presenters app/views)
|
|
20
|
+
end
|
|
19
21
|
conf[:paths] = %w(app/) if conf[:paths].blank?
|
|
20
22
|
conf[:include] = Array(conf[:include]) if conf[:include].present?
|
|
21
23
|
conf[:exclude] = Array(conf[:exclude]) + ALWAYS_EXCLUDE
|
|
@@ -109,7 +111,7 @@ module I18n::Tasks::Scanners
|
|
|
109
111
|
data = {src_path: path}
|
|
110
112
|
if position
|
|
111
113
|
line_begin = text.rindex(/^/, src_pos - 1)
|
|
112
|
-
line_end = text.index(/.(?=\n|$)/, src_pos)
|
|
114
|
+
line_end = text.index(/.(?=\r?\n|$)/, src_pos)
|
|
113
115
|
data.merge! pos: src_pos,
|
|
114
116
|
line_num: text[0..src_pos].count("\n") + 1,
|
|
115
117
|
line_pos: src_pos - line_begin + 1,
|
data/lib/i18n/tasks/version.rb
CHANGED
data/spec/i18n_spec.rb
CHANGED
data/spec/i18n_tasks_spec.rb
CHANGED
|
@@ -4,8 +4,9 @@ require 'fileutils'
|
|
|
4
4
|
require 'open3'
|
|
5
5
|
|
|
6
6
|
# Integration tests
|
|
7
|
-
describe 'i18n-tasks' do
|
|
8
|
-
delegate :run_cmd, :run_cmd_capture_stderr, :i18n_task, :in_test_app_dir,
|
|
7
|
+
RSpec.describe 'i18n-tasks' do
|
|
8
|
+
delegate :run_cmd, :run_cmd_capture_stdout_and_result, :run_cmd_capture_stderr, :i18n_task, :in_test_app_dir,
|
|
9
|
+
to: :TestCodebase
|
|
9
10
|
|
|
10
11
|
describe 'bin/i18n-tasks' do
|
|
11
12
|
it 'shows help when invoked with no arguments, shows version on --version' do
|
|
@@ -43,29 +44,36 @@ describe 'i18n-tasks' do
|
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
describe 'missing' do
|
|
46
|
-
let (:
|
|
47
|
-
%w(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
let (:expected_missing_keys_in_source) {
|
|
48
|
+
%w(
|
|
49
|
+
used_but_missing.key
|
|
50
|
+
relative.index.missing
|
|
51
|
+
hash.pattern_missing.a
|
|
52
|
+
hash.pattern_missing.b
|
|
53
|
+
missing_symbol_key
|
|
54
|
+
missing_symbol.key_two
|
|
55
|
+
missing_symbol.key_three
|
|
56
|
+
missing-key-with-a-dash.key
|
|
57
|
+
missing-key-question?.key
|
|
58
|
+
fn_comment
|
|
59
|
+
events.show.success
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
let (:expected_missing_keys_diff) {
|
|
63
|
+
%w(
|
|
64
|
+
es.missing_in_es.a
|
|
65
|
+
en.present_in_es_but_not_en.a
|
|
66
|
+
es.missing_in_es_plural_1.a
|
|
67
|
+
es.missing_in_es_plural_2.a
|
|
68
|
+
en.only_in_es
|
|
69
|
+
)
|
|
64
70
|
}
|
|
65
71
|
it 'detects missing' do
|
|
66
|
-
es_keys =
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
es_keys = expected_missing_keys_diff.grep(/^es\./) + expected_missing_keys_in_source.map { |k| "es.#{k}" }
|
|
73
|
+
out, result = run_cmd_capture_stdout_and_result 'missing'
|
|
74
|
+
expect(result).to eq :exit_1
|
|
75
|
+
expect(out).to be_i18n_keys(expected_missing_keys_diff +
|
|
76
|
+
expected_missing_keys_in_source.map { |k| "all.#{k}" })
|
|
69
77
|
expect(run_cmd 'missing', '-les').to be_i18n_keys es_keys
|
|
70
78
|
expect(run_cmd 'missing', 'es').to be_i18n_keys es_keys
|
|
71
79
|
end
|
|
@@ -91,7 +99,9 @@ describe 'i18n-tasks' do
|
|
|
91
99
|
|
|
92
100
|
describe 'unused' do
|
|
93
101
|
it 'detects unused' do
|
|
94
|
-
|
|
102
|
+
out, result = run_cmd_capture_stdout_and_result 'unused'
|
|
103
|
+
expect(result).to eq :exit_1
|
|
104
|
+
expect(out).to be_i18n_keys expected_unused_keys
|
|
95
105
|
end
|
|
96
106
|
|
|
97
107
|
it 'detects unused (--strict)' do
|
data/spec/plural_keys_spec.rb
CHANGED
data/spec/readme_spec.rb
CHANGED
data/spec/relative_keys_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/split_key_spec.rb
CHANGED
|
@@ -8,14 +8,13 @@ RSpec::Matchers.define :be_i18n_keys do |expected|
|
|
|
8
8
|
actual = Term::ANSIColor.uncolor(actual).split("\n").map(&:presence).compact
|
|
9
9
|
actual = actual[3..-2]
|
|
10
10
|
actual = actual.map { |row|
|
|
11
|
-
|
|
12
|
-
row.gsub(/(?:\s|^)\|(?:\s|$)/, ' ').gsub(/\s+/, ' ').strip.split(' ').map(&:presence).compact
|
|
11
|
+
row[1..-1].gsub(/(?:\s+|^)\|(?:\s+|$)/, '|').gsub(/\s+/, ' ').strip.split(/\s*\|\s*/)
|
|
13
12
|
}.compact
|
|
14
13
|
return [] if actual.empty?
|
|
15
14
|
locale_col = 0
|
|
16
15
|
key_col = 1
|
|
17
16
|
actual.map { |row|
|
|
18
|
-
key =
|
|
17
|
+
key = [row[locale_col], row[key_col]].map(&:presence).compact.join('.')
|
|
19
18
|
key = key[0..-2] if key.end_with?(':')
|
|
20
19
|
key
|
|
21
20
|
}.compact
|
|
@@ -22,6 +22,14 @@ module TestCodebase
|
|
|
22
22
|
} } }
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def run_cmd_capture_stdout_and_result(name, *args)
|
|
26
|
+
result = nil
|
|
27
|
+
out = capture_stdout { capture_stderr { in_test_app_dir {
|
|
28
|
+
result = run_cli(name, *args)
|
|
29
|
+
} } }
|
|
30
|
+
[out, result]
|
|
31
|
+
end
|
|
32
|
+
|
|
25
33
|
def run_cmd_capture_stderr(name, *args)
|
|
26
34
|
capture_stderr { capture_stdout { in_test_app_dir {
|
|
27
35
|
run_cli(name, *args)
|
data/spec/support/trees.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
module Trees
|
|
3
3
|
def expect_node_key_data(node, key, data)
|
|
4
4
|
expect(node.full_key(root: false)).to eq key
|
|
5
|
+
if Gem.win_platform?
|
|
6
|
+
# adjust position to account for \r on Windows
|
|
7
|
+
data = data.dup
|
|
8
|
+
data[:source_occurrences].map! { |occ| occ.dup.tap { |o| o[:pos] += o[:line_num] - 1 } }
|
|
9
|
+
end
|
|
5
10
|
expect(node.data).to eq data
|
|
6
11
|
end
|
|
7
12
|
|
data/spec/used_keys_spec.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
require 'spec_helper'
|
|
3
3
|
|
|
4
|
-
describe 'UsedKeys' do
|
|
4
|
+
RSpec.describe 'UsedKeys' do
|
|
5
5
|
let!(:task) { I18n::Tasks::BaseTask.new }
|
|
6
6
|
let(:file_name) { 'a.html.slim' }
|
|
7
7
|
let(:file_content) do
|
|
@@ -27,15 +27,15 @@ h1 = t 'b'
|
|
|
27
27
|
leaves[0],
|
|
28
28
|
'a',
|
|
29
29
|
source_occurrences:
|
|
30
|
-
[{pos: 6, line_num: 1, line_pos: 7, line: "div = t 'a'"
|
|
31
|
-
{pos: 18, line_num: 2, line_pos: 7, line: " p = t 'a'"
|
|
30
|
+
[{src_path: 'a.html.slim', pos: 6, line_num: 1, line_pos: 7, line: "div = t 'a'"},
|
|
31
|
+
{src_path: 'a.html.slim', pos: 18, line_num: 2, line_pos: 7, line: " p = t 'a'"}]
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
expect_node_key_data(
|
|
35
35
|
leaves[1],
|
|
36
36
|
'b',
|
|
37
37
|
source_occurrences:
|
|
38
|
-
[{pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'"
|
|
38
|
+
[{src_path: 'a.html.slim', pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'"}]
|
|
39
39
|
)
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -46,7 +46,7 @@ h1 = t 'b'
|
|
|
46
46
|
used_keys.leaves.first,
|
|
47
47
|
'b',
|
|
48
48
|
source_occurrences:
|
|
49
|
-
[{pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'"
|
|
49
|
+
[{src_path: 'a.html.slim', pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'"}]
|
|
50
50
|
)
|
|
51
51
|
end
|
|
52
52
|
|
|
@@ -67,8 +67,8 @@ h1 = t 'b'
|
|
|
67
67
|
used_keys.leaves.first,
|
|
68
68
|
'a',
|
|
69
69
|
source_occurrences:
|
|
70
|
-
[{pos: 15, line_num: 1, line_pos: 16, line: "#first{ title: t('a') }"
|
|
71
|
-
{pos: 40, line_num: 2, line_pos: 17, line: ".second{ title: t('a') }"
|
|
70
|
+
[{src_path: 'a.html.haml', pos: 15, line_num: 1, line_pos: 16, line: "#first{ title: t('a') }"},
|
|
71
|
+
{src_path: 'a.html.haml', pos: 40, line_num: 2, line_pos: 17, line: ".second{ title: t('a') }"}]
|
|
72
72
|
)
|
|
73
73
|
end
|
|
74
74
|
end
|
|
@@ -19,8 +19,8 @@ data:
|
|
|
19
19
|
# - config/locales/%{locale}.yml
|
|
20
20
|
## More files:
|
|
21
21
|
# - config/locales/**/*.%{locale}.yml
|
|
22
|
-
## Another gem:
|
|
23
|
-
# - "
|
|
22
|
+
## Another gem (replace %#= with %=):
|
|
23
|
+
# - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
|
|
24
24
|
|
|
25
25
|
# Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
|
|
26
26
|
# `i18n-tasks normalize -p` will force move the keys according to these rules
|
|
@@ -54,10 +54,11 @@ search:
|
|
|
54
54
|
|
|
55
55
|
## Root directories for relative keys resolution.
|
|
56
56
|
# relative_roots:
|
|
57
|
-
# - app/views
|
|
58
57
|
# - app/controllers
|
|
59
58
|
# - app/helpers
|
|
59
|
+
# - app/mailers
|
|
60
60
|
# - app/presenters
|
|
61
|
+
# - app/views
|
|
61
62
|
|
|
62
63
|
## Files or `File.fnmatch` patterns to exclude from search. Some files are always excluded regardless of this setting:
|
|
63
64
|
## %w(*.jpg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less *.yml *.json)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: i18n-tasks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- glebm
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-08-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: erubis
|
|
@@ -156,14 +156,14 @@ dependencies:
|
|
|
156
156
|
requirements:
|
|
157
157
|
- - "~>"
|
|
158
158
|
- !ruby/object:Gem::Version
|
|
159
|
-
version: '3.
|
|
159
|
+
version: '3.3'
|
|
160
160
|
type: :development
|
|
161
161
|
prerelease: false
|
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
|
163
163
|
requirements:
|
|
164
164
|
- - "~>"
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
|
-
version: '3.
|
|
166
|
+
version: '3.3'
|
|
167
167
|
- !ruby/object:Gem::Dependency
|
|
168
168
|
name: yard
|
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -187,10 +187,12 @@ email:
|
|
|
187
187
|
- glex.spb@gmail.com
|
|
188
188
|
executables:
|
|
189
189
|
- i18n-tasks
|
|
190
|
+
- i18n-tasks.cmd
|
|
190
191
|
extensions: []
|
|
191
192
|
extra_rdoc_files: []
|
|
192
193
|
files:
|
|
193
194
|
- ".coveralls.yml"
|
|
195
|
+
- ".gitattributes"
|
|
194
196
|
- ".gitignore"
|
|
195
197
|
- ".travis.yml"
|
|
196
198
|
- CHANGES.md
|
|
@@ -199,6 +201,7 @@ files:
|
|
|
199
201
|
- README.md
|
|
200
202
|
- Rakefile
|
|
201
203
|
- bin/i18n-tasks
|
|
204
|
+
- bin/i18n-tasks.cmd
|
|
202
205
|
- config/i18n-tasks.yml
|
|
203
206
|
- config/locales/en.yml
|
|
204
207
|
- config/locales/ru.yml
|
|
@@ -317,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
317
320
|
version: '0'
|
|
318
321
|
requirements: []
|
|
319
322
|
rubyforge_project:
|
|
320
|
-
rubygems_version: 2.4.
|
|
323
|
+
rubygems_version: 2.4.8
|
|
321
324
|
signing_key:
|
|
322
325
|
specification_version: 4
|
|
323
326
|
summary: Manage localization and translation with the awesome power of static analysis
|
|
@@ -352,4 +355,3 @@ test_files:
|
|
|
352
355
|
- spec/support/test_codebase.rb
|
|
353
356
|
- spec/support/trees.rb
|
|
354
357
|
- spec/used_keys_spec.rb
|
|
355
|
-
has_rdoc:
|