i18n-tasks 0.8.5 → 0.8.6
Sign up to get free protection for your applications and to get access to all the features.
- 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] [![Gitter](https://badges.gitter.im/Join Chat.svg)](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:
|