i18n-tasks 0.2.20 → 0.2.21
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![i18n-screenshot](https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-usages.png "rake i18n:usages output screenshot")
|
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
|