i18n-tasks 0.2.20 → 0.2.21
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/CHANGES.md +5 -0
- data/README.md +5 -1
- data/doc/img/i18n-usages.png +0 -0
- data/lib/i18n/tasks/key.rb +1 -1
- data/lib/i18n/tasks/key_group.rb +1 -1
- data/lib/i18n/tasks/reports/base.rb +3 -1
- data/lib/i18n/tasks/reports/terminal.rb +14 -10
- data/lib/i18n/tasks/scanners/base_scanner.rb +53 -27
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +5 -4
- data/lib/i18n/tasks/used_keys.rb +6 -2
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/tasks/i18n-tasks.rake +8 -3
- data/spec/i18n_tasks_spec.rb +1 -1
- data/spec/used_keys_spec.rb +28 -10
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d02f77ecd3ceb644a2a5a86d1b5dc622880e170
|
4
|
+
data.tar.gz: e859f73a4891034d5e1e95b7b5938aa7d3dace73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d821eab3b6e9f54870ccaa4c76869cea3ca79dc7eebdd62566c2464997bcd08485826b98a2d3d83f27d4ee8c609f502db9f1f5c528f1f1db133894e48f7835d
|
7
|
+
data.tar.gz: 44be3b83606e217455633dbb806e972bac6071a238c5f9f74b499a3fc883199d2edb22804dc160e8c5f3708e0f22c122680a6ea2bbe007d15f15f72873f1986f
|
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Tasks to manage translations in ruby applications using I18n.
|
|
10
10
|
Simply add to Gemfile:
|
11
11
|
|
12
12
|
```ruby
|
13
|
-
gem 'i18n-tasks', '~> 0.2.
|
13
|
+
gem 'i18n-tasks', '~> 0.2.21'
|
14
14
|
```
|
15
15
|
|
16
16
|
If not using Rails, require the tasks in Rakefile:
|
@@ -144,6 +144,10 @@ Inspect all the usages with:
|
|
144
144
|
|
145
145
|
```bash
|
146
146
|
rake i18n:usages
|
147
|
+
# Filter by a key pattern
|
148
|
+
rake i18n:usages[auth.*]
|
149
|
+
# Because commas are not allowed inside rake arguments, + is used here instead
|
150
|
+
rake i18n:usages['{number+currency}.format.*']
|
147
151
|
```
|
148
152
|
|
149
153
|

|
data/doc/img/i18n-usages.png
CHANGED
Binary file
|
data/lib/i18n/tasks/key.rb
CHANGED
data/lib/i18n/tasks/key_group.rb
CHANGED
@@ -27,7 +27,9 @@ module I18n::Tasks::Reports
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def used_title(keys)
|
30
|
-
|
30
|
+
filter = keys.attr[:key_filter]
|
31
|
+
used_n = keys.map { |k| k[:usages].size }.reduce(:+).to_i
|
32
|
+
"#{keys.length} key#{'s' if keys.size != 1}#{" ~ '#{filter}'" if filter}#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n > 0}"
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
@@ -12,7 +12,7 @@ module I18n
|
|
12
12
|
print_title missing_title(keys)
|
13
13
|
if keys.present?
|
14
14
|
|
15
|
-
|
15
|
+
print_info "#{bold 'Types:'} #{missing_types.values.map { |t| "#{t[:glyph]} #{t[:summary]}" } * ', '}"
|
16
16
|
|
17
17
|
print_table headings: [magenta(bold('Locale')), bold('Type'), magenta('i18n Key'), bold(cyan "Base value (#{base_locale})")] do |t|
|
18
18
|
t.rows = keys.map { |key|
|
@@ -36,22 +36,22 @@ module I18n
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def used_keys(keys = task.used_keys)
|
39
|
+
def used_keys(keys = task.used_keys(true))
|
40
40
|
print_title used_title(keys)
|
41
41
|
keys.sort_by_attr!(key: :asc)
|
42
42
|
if keys.present?
|
43
43
|
keys.each do |k|
|
44
|
-
puts "#{bold k.key}
|
44
|
+
puts "#{bold "#{k.key}"} #{k[:usages].size if k[:usages].size > 1}"
|
45
45
|
k[:usages].each do |u|
|
46
|
-
line =
|
47
|
-
line.sub!(k[:key], underline(k[:key]))
|
46
|
+
line = u[:line].dup.tap { |line|
|
48
47
|
line.strip!
|
48
|
+
line.sub!(/(.*?)(#{k[:key]})(.*)$/) { dark($1) + underline($2) + dark($3)}
|
49
49
|
}
|
50
|
-
puts " #{u[:path]}:#{u[:line_num]}
|
50
|
+
puts " #{green "#{u[:path]}:#{u[:line_num]}"} #{line}"
|
51
51
|
end
|
52
52
|
end
|
53
53
|
else
|
54
|
-
print_error 'No key usages found
|
54
|
+
print_error 'No key usages found'
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -69,15 +69,19 @@ module I18n
|
|
69
69
|
private
|
70
70
|
|
71
71
|
def print_title(title)
|
72
|
-
|
72
|
+
print_info "#{bold cyan title.strip} #{dark "|"} #{bold "i18n-tasks v#{I18n::Tasks::VERSION}"}"
|
73
73
|
end
|
74
74
|
|
75
75
|
def print_success(message)
|
76
|
-
|
76
|
+
print_info(bold green message)
|
77
77
|
end
|
78
78
|
|
79
79
|
def print_error(message)
|
80
|
-
|
80
|
+
print_info(bold red message)
|
81
|
+
end
|
82
|
+
|
83
|
+
def print_info(*args)
|
84
|
+
$stderr.puts(*args)
|
81
85
|
end
|
82
86
|
|
83
87
|
def indent(txt, n = 2)
|
@@ -2,38 +2,40 @@ require 'i18n/tasks/relative_keys'
|
|
2
2
|
module I18n::Tasks::Scanners
|
3
3
|
class BaseScanner
|
4
4
|
include ::I18n::Tasks::RelativeKeys
|
5
|
-
|
5
|
+
include ::I18n::Tasks::KeyPatternMatching
|
6
|
+
attr_reader :config, :key_filter, :record_usages
|
6
7
|
|
7
8
|
def initialize(config)
|
8
|
-
@config
|
9
|
+
@config = config.dup.with_indifferent_access.tap do |conf|
|
9
10
|
conf[:paths] = %w(app/) if conf[:paths].blank?
|
10
11
|
conf[:include] = Array(conf[:include]) if conf[:include].present?
|
11
12
|
conf[:exclude] = Array(conf[:exclude])
|
12
13
|
end
|
14
|
+
@record_usages = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def key_filter=(value)
|
18
|
+
@key_filter = value
|
19
|
+
@key_filter_pattern = compile_key_pattern(value) if @key_filter
|
13
20
|
end
|
14
21
|
|
15
22
|
# @return [Array] found key usages, absolutized and unique
|
16
23
|
def keys
|
17
|
-
@
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
::I18n::Tasks::KeyGroup.new(scan_file(path), src_path: path) }.map(&:keys).flatten
|
22
|
-
usages.group_by(&:key).each do |key, instances|
|
23
|
-
key = {
|
24
|
-
key: key,
|
25
|
-
usages: instances.map { |inst| inst[:src].merge(path: inst[:src_path]) }
|
26
|
-
}
|
27
|
-
@keys_by_name[key.to_s] = key
|
28
|
-
keys << key
|
29
|
-
end
|
30
|
-
keys
|
24
|
+
if @record_usages
|
25
|
+
keys_with_usages
|
26
|
+
else
|
27
|
+
@keys ||= traverse_files { |path| scan_file(path, read_file(path)).map(&:key) }.reduce(:+).uniq
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
31
|
+
def keys_with_usages
|
32
|
+
with_usages do
|
33
|
+
traverse_files { |path|
|
34
|
+
::I18n::Tasks::KeyGroup.new(scan_file(path, read_file(path)), src_path: path)
|
35
|
+
}.map(&:keys).reduce(:+).group_by(&:key).map { |key, key_usages|
|
36
|
+
{key: key, usages: key_usages.map { |usage| usage[:src].merge(path: usage[:src_path]) }}
|
37
|
+
}
|
38
|
+
end
|
37
39
|
end
|
38
40
|
|
39
41
|
def read_file(path)
|
@@ -52,23 +54,47 @@ module I18n::Tasks::Scanners
|
|
52
54
|
def traverse_files
|
53
55
|
result = []
|
54
56
|
Find.find(*config[:paths]) do |path|
|
55
|
-
next if File.directory?(path)
|
56
|
-
|
57
|
-
|
57
|
+
next if File.directory?(path) ||
|
58
|
+
config[:include] && !path_fnmatch_any?(path, config[:include]) ||
|
59
|
+
path_fnmatch_any?(path, config[:exclude])
|
58
60
|
result << yield(path)
|
59
61
|
end
|
60
62
|
result
|
61
63
|
end
|
62
64
|
|
65
|
+
def path_fnmatch_any?(path, globs)
|
66
|
+
globs.any? { |glob| File.fnmatch(glob, path) }
|
67
|
+
end
|
68
|
+
protected :path_fnmatch_any?
|
69
|
+
|
70
|
+
def with_key_filter(key_filter = nil)
|
71
|
+
filter_was = @key_filter
|
72
|
+
self.key_filter = key_filter
|
73
|
+
result = yield
|
74
|
+
self.key_filter = filter_was
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
def with_usages
|
79
|
+
was = @record_usages
|
80
|
+
@record_usages = true
|
81
|
+
result = yield
|
82
|
+
@record_usages = was
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
63
86
|
protected
|
64
87
|
|
65
88
|
def usage_context(text, src_pos)
|
89
|
+
return nil unless @record_usages
|
66
90
|
line_begin = text.rindex(/^/, src_pos - 1)
|
67
91
|
line_end = text.index(/.(?=\n|$)/, src_pos)
|
68
|
-
{
|
69
|
-
|
70
|
-
|
71
|
-
|
92
|
+
{src: {
|
93
|
+
pos: src_pos,
|
94
|
+
line_num: text[0..src_pos].count("\n") + 1,
|
95
|
+
line_pos: src_pos - line_begin + 1,
|
96
|
+
line: text[line_begin..line_end]
|
97
|
+
}}
|
72
98
|
end
|
73
99
|
|
74
100
|
def extract_key_from_match(match, path)
|
@@ -88,7 +114,7 @@ module I18n::Tasks::Scanners
|
|
88
114
|
VALID_KEY_RE = /^[\w.\#{}]+$/
|
89
115
|
|
90
116
|
def valid_key?(key)
|
91
|
-
key =~ VALID_KEY_RE
|
117
|
+
key =~ VALID_KEY_RE && !(@key_filter && @key_filter_pattern !~ key)
|
92
118
|
end
|
93
119
|
|
94
120
|
def relative_roots
|
@@ -1,11 +1,13 @@
|
|
1
|
-
# Scans for I18n.t usages
|
2
1
|
require 'i18n/tasks/scanners/base_scanner'
|
2
|
+
|
3
3
|
module I18n::Tasks::Scanners
|
4
|
+
# Scans for I18n.t usages
|
5
|
+
#
|
4
6
|
class PatternScanner < BaseScanner
|
5
7
|
LITERAL_RE = /:?".+?"|:?'.+?'|:\w+/
|
6
8
|
DEFAULT_PATTERN = /\bt(?:ranslate)?[( ]\s*(#{LITERAL_RE})/
|
7
9
|
|
8
|
-
# Extract i18n keys from file based on the pattern
|
10
|
+
# Extract i18n keys from file based on the pattern which must capture the key literal.
|
9
11
|
# @return [String] keys found in file
|
10
12
|
def scan_file(path, text = read_file(path))
|
11
13
|
keys = []
|
@@ -13,14 +15,13 @@ module I18n::Tasks::Scanners
|
|
13
15
|
src_pos = Regexp.last_match.offset(0).first
|
14
16
|
key = extract_key_from_match(match, path)
|
15
17
|
next unless valid_key?(key)
|
16
|
-
keys << ::I18n::Tasks::Key.new(key,
|
18
|
+
keys << ::I18n::Tasks::Key.new(key, usage_context(text, src_pos))
|
17
19
|
end
|
18
20
|
keys
|
19
21
|
end
|
20
22
|
|
21
23
|
protected
|
22
24
|
|
23
|
-
|
24
25
|
def pattern
|
25
26
|
@pattern ||= config[:pattern].present? ? Regexp.new(config[:pattern]) : DEFAULT_PATTERN
|
26
27
|
end
|
data/lib/i18n/tasks/used_keys.rb
CHANGED
@@ -4,8 +4,12 @@ require 'i18n/tasks/scanners/pattern_scanner'
|
|
4
4
|
module I18n::Tasks::UsedKeys
|
5
5
|
# find all keys in the source (relative keys are absolutized)
|
6
6
|
# @return [Array<String>]
|
7
|
-
def used_keys
|
8
|
-
|
7
|
+
def used_keys(with_usages = false)
|
8
|
+
if with_usages
|
9
|
+
I18n::Tasks::KeyGroup.new(scanner.keys_with_usages, type: :used, key_filter: scanner.key_filter)
|
10
|
+
else
|
11
|
+
@used_keys ||= I18n::Tasks::KeyGroup.new(scanner.keys, type: :used, key_filter: scanner.key_filter)
|
12
|
+
end
|
9
13
|
end
|
10
14
|
|
11
15
|
def scanner
|
data/lib/i18n/tasks/version.rb
CHANGED
data/lib/tasks/i18n-tasks.rake
CHANGED
@@ -57,9 +57,14 @@ namespace :i18n do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
desc 'show usages'
|
61
|
-
task :usages => 'i18n:setup' do
|
62
|
-
|
60
|
+
desc 'show usages of the keys in the codebase'
|
61
|
+
task :usages, [:filter] => 'i18n:setup' do |t, args|
|
62
|
+
filter = args[:filter] ? args[:filter].tr('+', ',') : nil
|
63
|
+
i18n_report.used_keys(
|
64
|
+
i18n_task.scanner.with_key_filter(filter) {
|
65
|
+
i18n_task.used_keys(true)
|
66
|
+
}
|
67
|
+
)
|
63
68
|
end
|
64
69
|
|
65
70
|
desc 'normalize translation data: sort and move to the right files'
|
data/spec/i18n_tasks_spec.rb
CHANGED
data/spec/used_keys_spec.rb
CHANGED
@@ -1,22 +1,40 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'UsedKeys' do
|
4
|
-
let(:task) { I18n::Tasks::BaseTask.new }
|
4
|
+
let!(:task) { I18n::Tasks::BaseTask.new }
|
5
5
|
|
6
|
-
|
6
|
+
around do |ex|
|
7
7
|
task.config[:search] = {paths: ['a.html.slim']}
|
8
8
|
TestCodebase.setup('a.html.slim' => <<-SLIM)
|
9
9
|
div = t 'a'
|
10
10
|
p = t 'a'
|
11
|
+
h1 = t 'b'
|
11
12
|
SLIM
|
12
|
-
TestCodebase.in_test_app_dir {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
TestCodebase.in_test_app_dir { ex.run }
|
14
|
+
end
|
15
|
+
|
16
|
+
it '#used_keys(true) finds usages' do
|
17
|
+
used_keys = task.used_keys(true)
|
18
|
+
expect(used_keys.size).to eq 2
|
19
|
+
expect(used_keys[0].own_attr).to(
|
20
|
+
eq(key: 'a',
|
21
|
+
usages: [{pos: 6, line_num: 1, line_pos: 7, line: "div = t 'a'", path: 'a.html.slim'},
|
22
|
+
{pos: 18, line_num: 2, line_pos: 7, line: " p = t 'a'", path: 'a.html.slim'}])
|
23
|
+
)
|
24
|
+
expect(used_keys[1].own_attr).to(
|
25
|
+
eq(key: 'b',
|
26
|
+
usages: [{pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'", path: 'a.html.slim'}])
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
it '#used_keys(true) finds usages with filter' do
|
31
|
+
used_keys = task.scanner.with_key_filter('b*') {
|
32
|
+
task.used_keys(true)
|
20
33
|
}
|
34
|
+
expect(used_keys.size).to eq 1
|
35
|
+
expect(used_keys[0].own_attr).to(
|
36
|
+
eq(key: 'b',
|
37
|
+
usages: [{pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'", path: 'a.html.slim'}])
|
38
|
+
)
|
21
39
|
end
|
22
40
|
end
|