i18n-tasks 0.9.6 → 0.9.7
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/README.md +1 -1
- data/Rakefile +2 -1
- data/bin/i18n-tasks +1 -0
- data/config/locales/en.yml +15 -9
- data/config/locales/ru.yml +80 -74
- data/i18n-tasks.gemspec +9 -8
- data/lib/i18n/tasks.rb +2 -5
- data/lib/i18n/tasks/cli.rb +14 -18
- data/lib/i18n/tasks/command/commander.rb +2 -3
- data/lib/i18n/tasks/command/commands/health.rb +1 -1
- data/lib/i18n/tasks/command/commands/meta.rb +2 -2
- data/lib/i18n/tasks/command/commands/missing.rb +17 -19
- data/lib/i18n/tasks/command/commands/tree.rb +3 -3
- data/lib/i18n/tasks/command/commands/usages.rb +6 -5
- data/lib/i18n/tasks/command/commands/xlsx.rb +1 -1
- data/lib/i18n/tasks/command/dsl.rb +1 -2
- data/lib/i18n/tasks/command/option_parsers/enum.rb +6 -6
- data/lib/i18n/tasks/command/option_parsers/locale.rb +3 -1
- data/lib/i18n/tasks/command/options/data.rb +22 -21
- data/lib/i18n/tasks/command_error.rb +1 -3
- data/lib/i18n/tasks/configuration.rb +37 -27
- data/lib/i18n/tasks/console_context.rb +3 -2
- data/lib/i18n/tasks/data.rb +5 -4
- data/lib/i18n/tasks/data/adapter/json_adapter.rb +13 -12
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +13 -14
- data/lib/i18n/tasks/data/file_formats.rb +15 -10
- data/lib/i18n/tasks/data/file_system_base.rb +18 -22
- data/lib/i18n/tasks/data/router/conservative_router.rb +3 -6
- data/lib/i18n/tasks/data/router/pattern_router.rb +4 -5
- data/lib/i18n/tasks/data/tree/node.rb +20 -19
- data/lib/i18n/tasks/data/tree/nodes.rb +4 -3
- data/lib/i18n/tasks/data/tree/siblings.rb +36 -29
- data/lib/i18n/tasks/data/tree/traversal.rb +32 -35
- data/lib/i18n/tasks/google_translation.rb +22 -25
- data/lib/i18n/tasks/html_keys.rb +2 -0
- data/lib/i18n/tasks/ignore_keys.rb +3 -2
- data/lib/i18n/tasks/key_pattern_matching.rb +9 -8
- data/lib/i18n/tasks/locale_list.rb +4 -6
- data/lib/i18n/tasks/locale_pathname.rb +8 -8
- data/lib/i18n/tasks/logging.rb +3 -3
- data/lib/i18n/tasks/missing_keys.rb +29 -31
- data/lib/i18n/tasks/plural_keys.rb +6 -7
- data/lib/i18n/tasks/references.rb +72 -41
- data/lib/i18n/tasks/reports/base.rb +8 -7
- data/lib/i18n/tasks/reports/spreadsheet.rb +15 -16
- data/lib/i18n/tasks/reports/terminal.rb +32 -32
- data/lib/i18n/tasks/scanners/file_scanner.rb +6 -5
- data/lib/i18n/tasks/scanners/files/caching_file_finder_provider.rb +1 -2
- data/lib/i18n/tasks/scanners/files/caching_file_reader.rb +0 -1
- data/lib/i18n/tasks/scanners/files/file_finder.rb +12 -15
- data/lib/i18n/tasks/scanners/files/file_reader.rb +0 -1
- data/lib/i18n/tasks/scanners/occurrence_from_position.rb +8 -7
- data/lib/i18n/tasks/scanners/pattern_mapper.rb +2 -2
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +9 -7
- data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +1 -2
- data/lib/i18n/tasks/scanners/relative_keys.rb +11 -11
- data/lib/i18n/tasks/scanners/results/key_occurrences.rb +3 -4
- data/lib/i18n/tasks/scanners/results/occurrence.rb +8 -7
- data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +2 -2
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +39 -31
- data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +9 -3
- data/lib/i18n/tasks/split_key.rb +5 -6
- data/lib/i18n/tasks/stats.rb +10 -8
- data/lib/i18n/tasks/string_interpolation.rb +1 -1
- data/lib/i18n/tasks/unused_keys.rb +2 -2
- data/lib/i18n/tasks/used_keys.rb +47 -43
- data/lib/i18n/tasks/version.rb +1 -1
- data/templates/rspec/i18n_spec.rb +2 -2
- metadata +30 -3
@@ -25,9 +25,15 @@ module I18n::Tasks::Scanners
|
|
25
25
|
def collect_results
|
26
26
|
return [@scanners[0].keys] if @scanners.length == 1
|
27
27
|
Array.new(@scanners.length).tap do |results|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
results_mutex = Mutex.new
|
29
|
+
@scanners.map.with_index do |scanner, i|
|
30
|
+
Thread.start do
|
31
|
+
scanner_results = scanner.keys
|
32
|
+
results_mutex.synchronize do
|
33
|
+
results[i] = scanner_results
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end.each(&:join)
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
data/lib/i18n/tasks/split_key.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
module I18n
|
3
3
|
module Tasks
|
4
4
|
module SplitKey
|
5
|
-
|
5
|
+
module_function
|
6
6
|
|
7
7
|
# split a key by dots (.)
|
8
8
|
# dots inside braces or parenthesis are not split on
|
@@ -37,7 +37,7 @@ module I18n
|
|
37
37
|
return enum_for(:key_parts, key) unless block
|
38
38
|
nesting = PARENS
|
39
39
|
counts = PARENS_ZEROS # dup'd later if key contains parenthesis
|
40
|
-
delim = '.'
|
40
|
+
delim = '.'
|
41
41
|
from = to = 0
|
42
42
|
key.each_char do |char|
|
43
43
|
if char == delim && PARENS_ZEROS == counts
|
@@ -46,7 +46,7 @@ module I18n
|
|
46
46
|
else
|
47
47
|
nest_i, nest_inc = nesting[char]
|
48
48
|
if nest_i
|
49
|
-
counts
|
49
|
+
counts = counts.dup if counts.frozen?
|
50
50
|
counts[nest_i] += nest_inc
|
51
51
|
end
|
52
52
|
to += 1
|
@@ -56,12 +56,11 @@ module I18n
|
|
56
56
|
true
|
57
57
|
end
|
58
58
|
|
59
|
-
PARENS
|
59
|
+
PARENS = %w({} [] ()).each_with_object({}) do |s, h|
|
60
60
|
i = h.size / 2
|
61
61
|
h[s[0].freeze] = [i, 1].freeze
|
62
62
|
h[s[1].freeze] = [i, -1].freeze
|
63
|
-
|
64
|
-
}.freeze
|
63
|
+
end.freeze
|
65
64
|
PARENS_ZEROS = Array.new(PARENS.size, 0).freeze
|
66
65
|
private_constant :PARENS
|
67
66
|
private_constant :PARENS_ZEROS
|
data/lib/i18n/tasks/stats.rb
CHANGED
@@ -2,18 +2,20 @@
|
|
2
2
|
module I18n::Tasks
|
3
3
|
module Stats
|
4
4
|
def forest_stats(forest)
|
5
|
-
key_count
|
5
|
+
key_count = forest.leaves.count
|
6
6
|
locale_count = forest.count
|
7
7
|
if key_count.zero?
|
8
|
-
{key_count: 0}
|
8
|
+
{ key_count: 0 }
|
9
9
|
else
|
10
10
|
{
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
locales: forest.map(&:key).join(', '),
|
12
|
+
key_count: key_count,
|
13
|
+
locale_count: locale_count,
|
14
|
+
per_locale_avg: forest.inject(0) { |sum, f| sum + f.leaves.count } / locale_count,
|
15
|
+
key_segments_avg: format(
|
16
|
+
'%.1f', forest.leaves.inject(0) { |sum, node| sum + node.walk_to_root.count - 1 } / key_count.to_f
|
17
|
+
),
|
18
|
+
value_chars_avg: forest.leaves.inject(0) { |sum, node| sum + node.value.to_s.length } / key_count
|
17
19
|
}
|
18
20
|
end
|
19
21
|
end
|
@@ -15,8 +15,8 @@ module I18n
|
|
15
15
|
used_key_names = used_tree(strict: true).key_names
|
16
16
|
collapse_plural_nodes! data[locale].select_keys { |key, _node|
|
17
17
|
!ignore_key?(key, :unused) &&
|
18
|
-
|
19
|
-
|
18
|
+
(strict || !used_in_expr?(key)) &&
|
19
|
+
!used_key_names.include?(depluralize_key(key, locale))
|
20
20
|
}
|
21
21
|
end
|
22
22
|
end
|
data/lib/i18n/tasks/used_keys.rb
CHANGED
@@ -6,25 +6,30 @@ require 'i18n/tasks/scanners/scanner_multiplexer'
|
|
6
6
|
require 'i18n/tasks/scanners/files/caching_file_finder_provider'
|
7
7
|
require 'i18n/tasks/scanners/files/caching_file_reader'
|
8
8
|
|
9
|
+
# Require the pattern mapper even though it's not used by i18n-tasks directly.
|
10
|
+
# This allows the user to use it in config/i18n-tasks.yml without having to require it.
|
11
|
+
# See https://github.com/glebm/i18n-tasks/issues/204.
|
12
|
+
require 'i18n/tasks/scanners/pattern_mapper'
|
13
|
+
|
9
14
|
module I18n::Tasks
|
10
|
-
module UsedKeys
|
15
|
+
module UsedKeys # rubocop:disable Metrics/ModuleLength
|
11
16
|
SEARCH_DEFAULTS = {
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
}.tap
|
17
|
+
paths: %w(app/).freeze,
|
18
|
+
relative_roots: %w(app/controllers app/helpers app/mailers app/presenters app/views).freeze,
|
19
|
+
scanners: [['::I18n::Tasks::Scanners::RubyAstScanner', only: %w(*.rb)]],
|
20
|
+
strict: true
|
21
|
+
}.tap do |defaults|
|
17
22
|
defaults[:scanners] << ['::I18n::Tasks::Scanners::PatternWithScopeScanner',
|
18
23
|
exclude: defaults[:scanners].map { |(_, opts)| opts[:only] }.reduce(:+).freeze,
|
19
|
-
ignore_lines: {'opal'
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
ignore_lines: { 'opal' => %q(^\s*#(?!\si18n-tasks-use)),
|
25
|
+
'haml' => %q(^\s*-\s*#(?!\si18n-tasks-use)),
|
26
|
+
'slim' => %q(^\s*(?:-#|/)(?!\si18n-tasks-use)),
|
27
|
+
'coffee' => %q(^\s*#(?!\si18n-tasks-use)),
|
28
|
+
'erb' => %q(^\s*<%\s*#(?!\si18n-tasks-use)) }.freeze]
|
29
|
+
end
|
25
30
|
|
26
31
|
ALWAYS_EXCLUDE = %w(*.jpg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less
|
27
|
-
*.yml *.json *.zip *.tar.gz)
|
32
|
+
*.yml *.json *.zip *.tar.gz).freeze
|
28
33
|
|
29
34
|
# Find all keys in the source and return a forest with the keys in absolute form and their occurrences.
|
30
35
|
#
|
@@ -41,28 +46,25 @@ module I18n::Tasks
|
|
41
46
|
src_tree.tap do |result|
|
42
47
|
tree = result['used'].children
|
43
48
|
tree.subtract_by_key!(raw_refs)
|
44
|
-
if include_raw_references
|
45
|
-
tree.merge!(raw_refs)
|
46
|
-
end
|
49
|
+
tree.merge!(raw_refs) if include_raw_references
|
47
50
|
tree.merge!(used_refs).merge!(resolved_refs)
|
48
51
|
end
|
49
52
|
end
|
50
53
|
|
51
54
|
def used_in_source_tree(key_filter: nil, strict: nil)
|
52
55
|
keys = ((@keys_used_in_source_tree ||= {})[strict?(strict)] ||=
|
53
|
-
|
56
|
+
scanner(strict: strict).keys.freeze)
|
54
57
|
if key_filter
|
55
58
|
key_filter_re = compile_key_pattern(key_filter)
|
56
59
|
keys = keys.reject { |k| k.key !~ key_filter_re }
|
57
60
|
end
|
58
61
|
Data::Tree::Node.new(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
+
key: 'used',
|
63
|
+
data: { key_filter: key_filter },
|
64
|
+
children: Data::Tree::Siblings.from_key_occurrences(keys)
|
62
65
|
).to_siblings
|
63
66
|
end
|
64
67
|
|
65
|
-
|
66
68
|
def scanner(strict: nil)
|
67
69
|
(@scanner ||= {})[strict?(strict)] ||= begin
|
68
70
|
shared_options = search_config.dup
|
@@ -70,16 +72,18 @@ module I18n::Tasks
|
|
70
72
|
shared_options[:strict] = strict unless strict.nil?
|
71
73
|
log_verbose 'Scanners: '
|
72
74
|
Scanners::ScannerMultiplexer.new(
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
scanners: search_config[:scanners].map do |(class_name, args)|
|
76
|
+
if args && args[:strict]
|
77
|
+
fail CommandError, 'the strict option is global and cannot be applied on the scanner level'
|
78
|
+
end
|
79
|
+
|
80
|
+
ActiveSupport::Inflector.constantize(class_name).new(
|
81
|
+
config: merge_scanner_configs(shared_options, args || {}),
|
82
|
+
file_finder_provider: caching_file_finder_provider,
|
83
|
+
file_reader: caching_file_reader
|
84
|
+
)
|
85
|
+
end.tap { |scanners| log_verbose { scanners.map { |s| " #{s.class.name} #{s.config.inspect}" } * "\n" } }
|
86
|
+
)
|
83
87
|
end
|
84
88
|
end
|
85
89
|
|
@@ -122,9 +126,9 @@ module I18n::Tasks
|
|
122
126
|
@caching_file_reader ||= Scanners::Files::CachingFileReader.new
|
123
127
|
end
|
124
128
|
|
125
|
-
# @return whether the key is potentially used in a code expression such as `t("category.#{
|
129
|
+
# @return [Boolean] whether the key is potentially used in a code expression such as `t("category.#{category_key}")`
|
126
130
|
def used_in_expr?(key)
|
127
|
-
!!(key =~ expr_key_re)
|
131
|
+
!!(key =~ expr_key_re) # rubocop:disable Style/DoubleNegation
|
128
132
|
end
|
129
133
|
|
130
134
|
private
|
@@ -137,17 +141,17 @@ module I18n::Tasks
|
|
137
141
|
|
138
142
|
# keys in the source that end with a ., e.g. t("category.#{ cat.i18n_key }") or t("category." + category.key)
|
139
143
|
# @param [String] replacement for interpolated values.
|
140
|
-
def expr_key_re(replacement: ':'
|
144
|
+
def expr_key_re(replacement: ':')
|
141
145
|
@expr_key_re ||= begin
|
142
146
|
# disallow patterns with no keys
|
143
147
|
ignore_pattern_re = /\A[\.#{replacement}]*\z/
|
144
|
-
patterns = used_in_source_tree(strict: false).key_names.select
|
145
|
-
k.end_with?('.'
|
146
|
-
|
147
|
-
pattern = "#{replace_key_exp(k, replacement)}#{replacement if k.end_with?('.'
|
148
|
+
patterns = used_in_source_tree(strict: false).key_names.select do |k|
|
149
|
+
k.end_with?('.') || k =~ /\#{/
|
150
|
+
end.map do |k|
|
151
|
+
pattern = "#{replace_key_exp(k, replacement)}#{replacement if k.end_with?('.')}"
|
148
152
|
next if pattern =~ ignore_pattern_re
|
149
153
|
pattern
|
150
|
-
|
154
|
+
end.compact
|
151
155
|
compile_key_pattern "{#{patterns * ','}}"
|
152
156
|
end
|
153
157
|
end
|
@@ -160,15 +164,15 @@ module I18n::Tasks
|
|
160
164
|
scanner = StringScanner.new(key)
|
161
165
|
braces = []
|
162
166
|
result = []
|
163
|
-
while (match_until = scanner.scan_until(/(?:#?\{|})
|
164
|
-
if scanner.matched == '#{'
|
167
|
+
while (match_until = scanner.scan_until(/(?:#?\{|})/))
|
168
|
+
if scanner.matched == '#{'
|
165
169
|
braces << scanner.matched
|
166
170
|
result << match_until[0..-3] if braces.length == 1
|
167
171
|
elsif scanner.matched == '}'
|
168
172
|
prev_brace = braces.pop
|
169
|
-
result << replacement if braces.empty? && prev_brace == '#{'
|
173
|
+
result << replacement if braces.empty? && prev_brace == '#{'
|
170
174
|
else
|
171
|
-
braces << '{'
|
175
|
+
braces << '{'
|
172
176
|
end
|
173
177
|
end
|
174
178
|
result << key[scanner.pos..-1] unless scanner.eos?
|
data/lib/i18n/tasks/version.rb
CHANGED
@@ -8,11 +8,11 @@ RSpec.describe 'I18n' do
|
|
8
8
|
|
9
9
|
it 'does not have missing keys' do
|
10
10
|
expect(missing_keys).to be_empty,
|
11
|
-
|
11
|
+
"Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'does not have unused keys' do
|
15
15
|
expect(unused_keys).to be_empty,
|
16
|
-
|
16
|
+
"#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
|
17
17
|
end
|
18
18
|
end
|
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.9.
|
4
|
+
version: 0.9.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- glebm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -192,6 +192,34 @@ dependencies:
|
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: '3.3'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rubocop
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: simplecov
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
195
223
|
- !ruby/object:Gem::Dependency
|
196
224
|
name: yard
|
197
225
|
requirement: !ruby/object:Gem::Requirement
|
@@ -330,4 +358,3 @@ signing_key:
|
|
330
358
|
specification_version: 4
|
331
359
|
summary: Manage localization and translation with the awesome power of static analysis
|
332
360
|
test_files: []
|
333
|
-
has_rdoc:
|