i18n-tasks 0.1.6 → 0.1.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/CHANGES.md +5 -0
- data/README.md +53 -27
- data/lib/i18n/tasks.rb +7 -5
- data/lib/i18n/tasks/base_task.rb +11 -7
- data/lib/i18n/tasks/data/yaml.rb +48 -8
- data/lib/i18n/tasks/data_traversal.rb +40 -0
- data/lib/i18n/tasks/key_pattern_matching.rb +1 -1
- data/lib/i18n/tasks/missing.rb +2 -2
- data/lib/i18n/tasks/output/terminal.rb +14 -5
- data/lib/i18n/tasks/prefill.rb +1 -9
- data/lib/i18n/tasks/{usage_search.rb → source_keys.rb} +34 -15
- data/lib/i18n/tasks/translation_data.rb +16 -46
- data/lib/i18n/tasks/unused.rb +4 -4
- data/lib/i18n/tasks/version.rb +1 -1
- data/spec/fixtures/app/views/index.html.slim +1 -0
- data/spec/fixtures/config/i18n-tasks.yml +35 -28
- data/spec/i18n_tasks_spec.rb +8 -3
- metadata +4 -4
- data/lib/i18n/tasks/fuzzy_source_keys.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fba31809b28d7db5c2f2febad005236e8e4b7e1b
|
4
|
+
data.tar.gz: 0b3f032893745205af5368c017fe092fea521b26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca884fd7cd3f02830003fb4fe8e13586fa162c22b328a17c39f0655e08005e03073ac193b6d57c50e3edb619b93bfc316eef873a761fb4bbdaac2d16d05c6375
|
7
|
+
data.tar.gz: 3ebcfa3023b2b5c6cb83181382a51253ffca5ce2ed476d9167495da31d19358ed8db3b8bd2401b522295f64312bdad9f51f5884a1de17145474eb3a45458d538
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## v0.1.7
|
2
|
+
|
3
|
+
* ability to route prefill output via data.write config (@glebm)
|
4
|
+
* multiple configuration variables renamed (still understands old syntax with deprecation warnings) (@glebm)
|
5
|
+
|
1
6
|
## v0.1.6
|
2
7
|
|
3
8
|
* New key pattern syntax for i18n-tasks.yml a la globbing (@glebm)
|
data/README.md
CHANGED
@@ -8,8 +8,9 @@ Rails I18n tasks to find missing / unused translations and more. Works with slim
|
|
8
8
|
Use `rake -T i18n` to get the list of tasks with descriptions. There are 3 tasks available at the moment:
|
9
9
|
|
10
10
|
* `i18n:missing` task shows all the keys that have not been translated yet *([source](/lib/i18n/tasks/missing.rb))*
|
11
|
-
* `i18n:prefill` task normalizes locale files, and adds missing keys from base locale to others *([source](/lib/i18n/tasks/prefill.rb))*
|
12
11
|
* `i18n:unused` task shows potentially unused translations *([source](/lib/i18n/tasks/unused.rb))*
|
12
|
+
* `i18n:prefill` task normalizes locale files, and adds missing keys from base locale to others *([source](/lib/i18n/tasks/prefill.rb))*
|
13
|
+
|
13
14
|
|
14
15
|
The `i18n:unused` task will detect pattern translations and not report them, e.g.:
|
15
16
|
|
@@ -18,7 +19,7 @@ t 'category.' + category.key # 'category.arts_and_crafts' considered used
|
|
18
19
|
t "category.#{category.key}" # also works
|
19
20
|
```
|
20
21
|
|
21
|
-
Relative keys (`t '.title'`)
|
22
|
+
Relative keys (`t '.title'`) and plural keys (key.one/many/other/etc) are fully supported.
|
22
23
|
|
23
24
|
For more examples see [the tests](/spec/i18n_tasks_spec.rb).
|
24
25
|
|
@@ -36,9 +37,58 @@ gem 'i18n-tasks', '~> 0.1.0'
|
|
36
37
|
|
37
38
|
## Configuration
|
38
39
|
|
39
|
-
|
40
|
+
Configuration is read from `i18n-tasks/config.yml`.
|
41
|
+
|
42
|
+
### Storage
|
43
|
+
|
44
|
+
```yaml
|
45
|
+
# i18n data storage
|
46
|
+
data:
|
47
|
+
# The default YAML adapter supports reading from and writing to YAML files
|
48
|
+
adapter: yaml
|
49
|
+
# yaml adapter read option is a list of glob patterns of files to read from per-locale
|
50
|
+
read:
|
51
|
+
# this one is default:
|
52
|
+
- 'config/locales/%{locale}.yml'
|
53
|
+
# this one would add some more files:
|
54
|
+
- 'config/locales/*.%{locale}.yml'
|
55
|
+
# yaml adapter write option a list of key pattern => output filename "routes" per-locale
|
56
|
+
write:
|
57
|
+
# keys matched top to bottom
|
58
|
+
- ['devise.*', 'config/locales/devise.%{locale}.yml']
|
59
|
+
# default catch-all (same as ['*', 'config/locales/%{locale}.yml'])
|
60
|
+
- 'config/locales/%{locale}.yml'
|
61
|
+
```
|
62
|
+
|
63
|
+
### Usage search
|
40
64
|
|
41
65
|
```yaml
|
66
|
+
# i18n usage search in source
|
67
|
+
search:
|
68
|
+
# search these directories (relative to your Rails.root directory, default: 'app/')
|
69
|
+
paths:
|
70
|
+
- 'app/'
|
71
|
+
- 'vendor/'
|
72
|
+
# include only files matching this glob pattern (default: blank = include all files)
|
73
|
+
include:
|
74
|
+
- '*.rb'
|
75
|
+
- '*.html.*'
|
76
|
+
- '*.text.*'
|
77
|
+
# explicitly exclude files (default: blank = exclude no files)
|
78
|
+
exclude: '*.js'
|
79
|
+
# search uses grep under the hood
|
80
|
+
```
|
81
|
+
|
82
|
+
### Fine-tuning
|
83
|
+
|
84
|
+
Tasks may incorrectly report framework i18n keys as missing, also some patterns may not be detected.
|
85
|
+
When all else fails, use the options below.
|
86
|
+
|
87
|
+
```yaml
|
88
|
+
# do not report these keys as unused
|
89
|
+
ignore_unused:
|
90
|
+
- category.*.db_name
|
91
|
+
|
42
92
|
# do not report these keys as missing (both on blank value and no key)
|
43
93
|
ignore_missing:
|
44
94
|
- devise.errors.unauthorized # ignore this key
|
@@ -51,33 +101,9 @@ ignore_eq_base:
|
|
51
101
|
es,fr:
|
52
102
|
- common.brand
|
53
103
|
|
54
|
-
# do not report these keys as unused
|
55
|
-
ignore_unused:
|
56
|
-
- category.*
|
57
|
-
|
58
104
|
# do not report these keys ever
|
59
105
|
ignore:
|
60
106
|
- kaminari.*
|
61
|
-
|
62
|
-
# where to read locale data from
|
63
|
-
data:
|
64
|
-
# read paths for a given %{locale} (supports globs)
|
65
|
-
paths:
|
66
|
-
- 'config/locales/%{locale}.yml'
|
67
|
-
# you can also implement a custom storage layer, see the yaml one below
|
68
|
-
class: I18n::Tasks::Data::Yaml
|
69
|
-
|
70
|
-
# search configuration (grep arguments)
|
71
|
-
grep:
|
72
|
-
# search these directories (relative to your Rails.root directory, default: 'app/')
|
73
|
-
paths:
|
74
|
-
- 'app/'
|
75
|
-
# include only files matching this glob pattern (default: blank = include all files)
|
76
|
-
include:
|
77
|
-
- '*.rb'
|
78
|
-
- '*.html*'
|
79
|
-
# explicitly exclude files (default: blank = exclude no files)
|
80
|
-
exclude: '*.js'
|
81
107
|
```
|
82
108
|
|
83
109
|
## HTML report
|
data/lib/i18n/tasks.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'i18n/tasks/version'
|
2
2
|
require 'i18n/tasks/railtie'
|
3
|
-
require '
|
4
|
-
|
5
|
-
require '
|
6
|
-
require 'active_support/hash_with_indifferent_access'
|
7
|
-
require 'active_support/core_ext/string/access'
|
3
|
+
require 'active_support/core_ext/hash'
|
4
|
+
require 'active_support/core_ext/string'
|
5
|
+
require 'term/ansicolor'
|
8
6
|
|
9
7
|
module I18n
|
10
8
|
module Tasks
|
@@ -17,6 +15,10 @@ module I18n
|
|
17
15
|
HashWithIndifferentAccess.new.merge(file.presence || {})
|
18
16
|
end
|
19
17
|
end
|
18
|
+
|
19
|
+
def warn_deprecated(message)
|
20
|
+
STDERR.puts Term::ANSIColor.yellow Term::ANSIColor.bold "i18n-tasks: [DEPRECATED] #{message}"
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
data/lib/i18n/tasks/base_task.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
require '
|
3
|
-
require 'i18n/tasks/usage_search'
|
4
|
-
require 'i18n/tasks/fuzzy_source_keys'
|
5
|
-
require 'i18n/tasks/plural_keys'
|
2
|
+
require 'i18n/tasks/key_pattern_matching'
|
6
3
|
require 'i18n/tasks/relative_keys'
|
4
|
+
require 'i18n/tasks/plural_keys'
|
5
|
+
require 'i18n/tasks/source_keys'
|
7
6
|
require 'i18n/tasks/translation_data'
|
8
7
|
require 'i18n/tasks/ignore_keys'
|
9
8
|
|
10
9
|
module I18n
|
11
10
|
module Tasks
|
12
11
|
class BaseTask
|
13
|
-
include
|
14
|
-
include PluralKeys
|
12
|
+
include KeyPatternMatching
|
15
13
|
include RelativeKeys
|
16
|
-
include
|
14
|
+
include PluralKeys
|
15
|
+
include SourceKeys
|
17
16
|
include TranslationData
|
18
17
|
include IgnoreKeys
|
19
18
|
|
@@ -22,6 +21,11 @@ module I18n
|
|
22
21
|
def config
|
23
22
|
I18n::Tasks.config
|
24
23
|
end
|
24
|
+
|
25
|
+
def warn_deprecated(message)
|
26
|
+
I18n::Tasks.warn_deprecated(message)
|
27
|
+
end
|
28
|
+
|
25
29
|
end
|
26
30
|
end
|
27
31
|
end
|
data/lib/i18n/tasks/data/yaml.rb
CHANGED
@@ -1,22 +1,62 @@
|
|
1
|
+
require 'i18n/tasks/data_traversal'
|
2
|
+
require 'i18n/tasks/key_pattern_matching'
|
1
3
|
module I18n::Tasks
|
2
4
|
module Data
|
3
5
|
class Yaml
|
6
|
+
include ::I18n::Tasks::DataTraversal
|
7
|
+
include ::I18n::Tasks::KeyPatternMatching
|
4
8
|
attr_reader :options
|
5
9
|
|
10
|
+
DEFAULTS = {
|
11
|
+
read: ['config/locales/%{locale}.yml'],
|
12
|
+
write: ['config/locales/%{locale}.yml']
|
13
|
+
}.with_indifferent_access
|
14
|
+
|
6
15
|
def initialize(options)
|
7
|
-
|
16
|
+
opt = (options || {}).with_indifferent_access
|
17
|
+
if opt.key?(:paths)
|
18
|
+
opt[:read] ||= opt.delete(:paths)
|
19
|
+
::I18n::Tasks.warn_deprecated 'please rename "data.paths" key to "data.read" in config/i18n-tasks.yml'
|
20
|
+
end
|
21
|
+
opt = DEFAULTS.deep_merge(opt)
|
22
|
+
@read = opt[:read]
|
23
|
+
@write = opt[:write].map { |x| x.is_a?(String) ? ['*', x] : x }.map { |x|
|
24
|
+
[key_pattern_to_re(x[0]), x[1]]
|
25
|
+
}
|
8
26
|
end
|
9
27
|
|
28
|
+
# get locale tree
|
10
29
|
def get(locale)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
hash
|
30
|
+
locale = locale.to_s
|
31
|
+
(@locale_data ||= {})[locale] ||= begin
|
32
|
+
@read.map do |path|
|
33
|
+
Dir.glob path % {locale: locale}
|
34
|
+
end.flatten.map do |locale_file|
|
35
|
+
YAML.load_file locale_file
|
36
|
+
end.inject({}) do |hash, locale_data|
|
37
|
+
hash.deep_merge! locale_data || {}
|
38
|
+
hash
|
39
|
+
end[locale.to_s]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias [] get
|
44
|
+
|
45
|
+
# set locale tree
|
46
|
+
def set(locale, value_tree)
|
47
|
+
out = {}
|
48
|
+
traverse value_tree do |key, value|
|
49
|
+
route = @write.detect { |route| route[0] =~ key }
|
50
|
+
(out[route[1] % {locale: locale}] ||= []) << [key, value]
|
51
|
+
end
|
52
|
+
out.each do |path, data|
|
53
|
+
File.open(path, 'w') { |f|
|
54
|
+
f.write({locale.to_s => list_to_tree(data)}.to_yaml)
|
55
|
+
}
|
18
56
|
end
|
19
57
|
end
|
58
|
+
|
59
|
+
alias []= set
|
20
60
|
end
|
21
61
|
end
|
22
62
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module I18n::Tasks::DataTraversal
|
2
|
+
# translation of the key found in the passed hash or nil
|
3
|
+
# @return [String,nil]
|
4
|
+
def t(hash, key)
|
5
|
+
key.split('.').inject(hash) { |r, seg| r[seg] if r }
|
6
|
+
end
|
7
|
+
|
8
|
+
# traverse hash, yielding with full key and value
|
9
|
+
# @param hash [Hash{String => String,Hash}] translation data to traverse
|
10
|
+
# @yield [full_key, value] yields full key and value for every translation in #hash
|
11
|
+
# @return [nil]
|
12
|
+
def traverse(path = '', hash)
|
13
|
+
q = [[path, hash]]
|
14
|
+
until q.empty?
|
15
|
+
path, value = q.pop
|
16
|
+
if value.is_a?(Hash)
|
17
|
+
value.each { |k, v| q << ["#{path}.#{k}", v] }
|
18
|
+
else
|
19
|
+
yield path[1..-1], value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def list_to_tree(list)
|
25
|
+
list = list.sort
|
26
|
+
tree = {}
|
27
|
+
list.each do |key, value|
|
28
|
+
key_segments = key.to_s.split('.')
|
29
|
+
node = key_segments[0..-2].inject(tree) do |r, segment|
|
30
|
+
if r.key?(segment)
|
31
|
+
r[segment]
|
32
|
+
else
|
33
|
+
r[segment] = {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
node[key_segments.last] = value
|
37
|
+
end
|
38
|
+
tree
|
39
|
+
end
|
40
|
+
end
|
@@ -14,7 +14,7 @@ module I18n::Tasks::KeyPatternMatching
|
|
14
14
|
# convert key.* to key\..*
|
15
15
|
def key_pattern_to_re(key_pattern)
|
16
16
|
if key_pattern.end_with? '.'
|
17
|
-
|
17
|
+
warn_deprecated %Q(please change pattern "#{key_pattern}" to "#{key_pattern += '*'}" in config/i18n-tasks.yml)
|
18
18
|
end
|
19
19
|
/^#{key_pattern.
|
20
20
|
gsub(/\./, '\.').
|
data/lib/i18n/tasks/missing.rb
CHANGED
@@ -29,9 +29,9 @@ module I18n
|
|
29
29
|
# present in base locale, but untranslated in another locale
|
30
30
|
# @return Array{Hash}
|
31
31
|
def keys_missing_translation(locale)
|
32
|
-
trn =
|
32
|
+
trn = data[locale]
|
33
33
|
r = []
|
34
|
-
traverse
|
34
|
+
traverse data[base_locale] do |key, base_value|
|
35
35
|
value_in_locale = t(trn, key)
|
36
36
|
if value_in_locale.blank? && !ignore_key?(key, :missing)
|
37
37
|
r << {locale: locale, key: key, type: :blank, base_value: base_value}
|
@@ -6,29 +6,38 @@ module I18n
|
|
6
6
|
include Term::ANSIColor
|
7
7
|
|
8
8
|
def missing(missing)
|
9
|
-
|
9
|
+
print_title "Missing keys and translations (#{missing.length})"
|
10
10
|
if missing.present?
|
11
|
-
$stderr.puts "#{bold 'Legend:'} #{
|
11
|
+
$stderr.puts "#{bold 'Legend:'} #{on_red '✗'} key missing, #{yellow bold '∅'} translation blank, #{blue bold '='} value equal to base locale, #{cyan 'value in base locale'}"
|
12
12
|
key_col_width = missing.map { |x| x[:key] }.max_by(&:length).length + 2
|
13
13
|
missing.each { |m| print_missing_translation m, key_col_width: key_col_width }
|
14
14
|
else
|
15
|
-
|
15
|
+
print_success 'Good job! No translations missing!'
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
def unused(unused)
|
20
|
-
|
20
|
+
print_title "Unused i18n keys (#{unused.length})"
|
21
21
|
if unused.present?
|
22
22
|
key_col_width = unused.max_by { |x| x[0].length }[0].length + 2
|
23
23
|
unused.each { |(key, value)| puts "#{magenta key.ljust(key_col_width)}#{cyan value.to_s.strip}" }
|
24
24
|
else
|
25
|
-
|
25
|
+
print_success 'Good job! Every translation is used!'
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
31
|
extend Term::ANSIColor
|
32
|
+
|
33
|
+
def print_title(title)
|
34
|
+
$stderr.puts "#{bold cyan title.strip} #{dark "|"} #{bold "i18n-tasks v#{I18n::Tasks::VERSION}"}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def print_success(message)
|
38
|
+
$stderr.puts(bold green message)
|
39
|
+
end
|
40
|
+
|
32
41
|
STATUS_TEXTS = {
|
33
42
|
none: red("✗".ljust(6)),
|
34
43
|
blank: yellow(bold '∅'.ljust(6)),
|
data/lib/i18n/tasks/prefill.rb
CHANGED
@@ -7,17 +7,9 @@ module I18n
|
|
7
7
|
def perform
|
8
8
|
# Will also rewrite en, good for ordering
|
9
9
|
I18n.available_locales.map(&:to_s).each do |target_locale|
|
10
|
-
|
11
|
-
prefilled = { target_locale => base_locale_data }.deep_merge(trn)
|
12
|
-
File.open(locale_file_path(target_locale), 'w'){ |f| f.write prefilled.to_yaml }
|
10
|
+
data[target_locale] = data[base_locale].deep_merge(data[target_locale])
|
13
11
|
end
|
14
12
|
end
|
15
|
-
|
16
|
-
# main locale file path (for writing to)
|
17
|
-
# @return [String]
|
18
|
-
def locale_file_path(locale)
|
19
|
-
"config/locales/#{locale}.yml"
|
20
|
-
end
|
21
13
|
end
|
22
14
|
end
|
23
15
|
end
|
@@ -1,19 +1,5 @@
|
|
1
1
|
require 'open3'
|
2
|
-
module I18n::Tasks::
|
3
|
-
# grep config, also from config/i18n-tasks.yml
|
4
|
-
# @return [Hash{String => String,Hash,Array}]
|
5
|
-
def grep_config
|
6
|
-
@grep_config ||= (config[:grep] || {}).with_indifferent_access.tap do |conf|
|
7
|
-
conf[:paths] = ['app/'] if conf[:paths].blank?
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
# whether the key is used in the source
|
12
|
-
def used_key?(key)
|
13
|
-
@used_keys ||= find_source_keys.to_set
|
14
|
-
@used_keys.include?(key)
|
15
|
-
end
|
16
|
-
|
2
|
+
module I18n::Tasks::SourceKeys
|
17
3
|
# find all keys in the source (relative keys are returned in absolutized)
|
18
4
|
# @return [Array<String>]
|
19
5
|
def find_source_keys
|
@@ -33,8 +19,41 @@ module I18n::Tasks::UsageSearch
|
|
33
19
|
end
|
34
20
|
end
|
35
21
|
|
22
|
+
# whether the key is used in the source
|
23
|
+
def used_key?(key)
|
24
|
+
@used_keys ||= find_source_keys.to_set
|
25
|
+
@used_keys.include?(key)
|
26
|
+
end
|
27
|
+
|
28
|
+
# dynamically generated keys in the source, e.g t("category.#{category_key}")
|
29
|
+
def pattern_key?(key)
|
30
|
+
@pattern_keys_re ||= compile_start_with_re(pattern_key_prefixes)
|
31
|
+
!!(key =~ @pattern_keys_re)
|
32
|
+
end
|
33
|
+
|
34
|
+
# keys in the source that end with a ., e.g. t("category.#{cat.i18n_key}") or t("category." + category.key)
|
35
|
+
def pattern_key_prefixes
|
36
|
+
@pattern_keys_prefixes ||=
|
37
|
+
find_source_keys.select { |k| k =~ /\#{.*?}/ || k.ends_with?('.') }.map { |k| k.split(/\.?#/)[0].presence }.compact
|
38
|
+
end
|
39
|
+
|
36
40
|
protected
|
37
41
|
|
42
|
+
# grep config, also from config/i18n-tasks.yml
|
43
|
+
# @return [Hash{String => String,Hash,Array}]
|
44
|
+
def grep_config
|
45
|
+
@grep_config ||= begin
|
46
|
+
if config.key?(:grep)
|
47
|
+
config[:search] ||= config.delete(:grep)
|
48
|
+
warn_deprecated 'please rename "grep" key to "search" in config/i18n-tasks.yml'
|
49
|
+
end
|
50
|
+
search_config = (config[:search] || {}).with_indifferent_access
|
51
|
+
search_config.tap do |conf|
|
52
|
+
conf[:paths] = %w(app/) if conf[:paths].blank?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
38
57
|
# Run grep searching for source keys and return grep output
|
39
58
|
# @return [String] output of the grep command
|
40
59
|
def run_grep
|
@@ -1,58 +1,28 @@
|
|
1
|
+
require 'i18n/tasks/data_traversal'
|
2
|
+
require 'i18n/tasks/data/yaml'
|
3
|
+
|
1
4
|
module I18n::Tasks::TranslationData
|
2
|
-
|
3
|
-
# @return [Hash{String => String,Hash}] locale data in nested hash format
|
4
|
-
def locale_data(locale)
|
5
|
-
locale = locale.to_s
|
6
|
-
(@locale_data ||= {})[locale] ||= data_source.get(locale) || {}
|
7
|
-
end
|
5
|
+
include I18n::Tasks::DataTraversal
|
8
6
|
|
9
7
|
# I18n data provider
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# translation of the key found in the passed hash or nil
|
23
|
-
# @return [String,nil]
|
24
|
-
def t(hash, key)
|
25
|
-
key.split('.').inject(hash) { |r, seg| r[seg] if r }
|
8
|
+
# @see I18n::Tasks::Data::Yaml
|
9
|
+
def data
|
10
|
+
return @data if @data
|
11
|
+
conf = (config[:data] || {}).with_indifferent_access
|
12
|
+
adapter = (conf[:adapter].presence || conf[:class].presence || :yaml).to_s
|
13
|
+
if adapter !~ /[A-Z]/
|
14
|
+
adapter = "I18n::Tasks::Data::#{adapter.camelize}"
|
15
|
+
end
|
16
|
+
@data = adapter.constantize.new(conf.except(:adapter, :class))
|
26
17
|
end
|
27
18
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
# @return [nil]
|
32
|
-
def traverse(path = '', hash)
|
33
|
-
q = [[path, hash]]
|
34
|
-
until q.empty?
|
35
|
-
path, value = q.pop
|
36
|
-
if value.is_a?(Hash)
|
37
|
-
value.each { |k, v| q << ["#{path}.#{k}", v] }
|
38
|
-
else
|
39
|
-
yield path[1..-1], value
|
40
|
-
end
|
41
|
-
end
|
19
|
+
# whether the value for key exists in locale (defaults: base_locale)
|
20
|
+
def key_has_value?(key, locale = base_locale)
|
21
|
+
t(data[locale], key).present?
|
42
22
|
end
|
43
23
|
|
44
24
|
# @return [String] default i18n locale
|
45
25
|
def base_locale
|
46
26
|
I18n.default_locale.to_s
|
47
27
|
end
|
48
|
-
|
49
|
-
# @return [Hash{String => String,Hash}] default i18n locale data
|
50
|
-
def base_locale_data
|
51
|
-
locale_data(base_locale)[base_locale]
|
52
|
-
end
|
53
|
-
|
54
|
-
# whether the value for key exists in locale (defaults: base_locale)
|
55
|
-
def key_has_value?(key, locale = base_locale)
|
56
|
-
t(locale_data(locale)[locale], key).present?
|
57
|
-
end
|
58
28
|
end
|
data/lib/i18n/tasks/unused.rb
CHANGED
@@ -6,11 +6,11 @@ module I18n
|
|
6
6
|
class Unused < BaseTask
|
7
7
|
# @return [Array<[String, String]>] all the unused translations as an array of [key, value] pairs
|
8
8
|
def find_keys
|
9
|
-
r
|
10
|
-
|
11
|
-
traverse
|
9
|
+
r = []
|
10
|
+
d = self.data[base_locale]
|
11
|
+
traverse d do |key, value|
|
12
12
|
next if pattern_key?(key) || ignore_key?(key, :unused)
|
13
|
-
key = depluralize_key(key,
|
13
|
+
key = depluralize_key(key, d)
|
14
14
|
r << [key, value] unless used_key?(key)
|
15
15
|
end
|
16
16
|
r.uniq
|
data/lib/i18n/tasks/version.rb
CHANGED
@@ -1,25 +1,19 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
-
|
12
|
-
|
13
|
-
|
14
|
-
ignore_unused:
|
15
|
-
- ignore_unused.a
|
16
|
-
|
17
|
-
# do not report these keys ever
|
18
|
-
ignore:
|
19
|
-
- ignore.a
|
1
|
+
# i18n data storage
|
2
|
+
data:
|
3
|
+
# The default YAML adapter supports reading from and writing to YAML files
|
4
|
+
adapter: yaml
|
5
|
+
# adapter options
|
6
|
+
read:
|
7
|
+
- 'config/locales/%{locale}.yml'
|
8
|
+
- 'config/locales/*.%{locale}.yml'
|
9
|
+
write:
|
10
|
+
# keys matched top to bottom
|
11
|
+
- ['devise.*', 'config/locales/devise.%{locale}.yml']
|
12
|
+
# default catch-all (same as ['*', 'config/locales/%{locale}.yml'])
|
13
|
+
- 'config/locales/%{locale}.yml'
|
20
14
|
|
21
|
-
#
|
22
|
-
|
15
|
+
# i18n usage search in source
|
16
|
+
search:
|
23
17
|
# search these directories (relative to your Rails.root directory, default: 'app/')
|
24
18
|
paths:
|
25
19
|
- 'app/'
|
@@ -31,11 +25,24 @@ grep:
|
|
31
25
|
- '*.file'
|
32
26
|
# explicitly exclude files (default: blank = exclude no files)
|
33
27
|
exclude: '*.js'
|
28
|
+
# search uses grep under the hood
|
34
29
|
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
# do not report these keys ever
|
31
|
+
ignore:
|
32
|
+
- ignore.a
|
33
|
+
|
34
|
+
# do not report these keys as unused
|
35
|
+
ignore_unused:
|
36
|
+
- ignore_unused.a
|
37
|
+
|
38
|
+
# do not report these keys as missing:
|
39
|
+
ignore_missing:
|
40
|
+
- ignored_missing_key.a # one key to ignore
|
41
|
+
- ignored_pattern.* # ignore the whole pattern
|
42
|
+
|
43
|
+
# do not report these keys when they have the same value as the base locale version
|
44
|
+
ignore_eq_base:
|
45
|
+
all:
|
46
|
+
- ignore_eq_base_all.a
|
47
|
+
es:
|
48
|
+
- ignore_eq_base_es.a
|
data/spec/i18n_tasks_spec.rb
CHANGED
@@ -19,10 +19,14 @@ describe 'rake i18n' do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
describe 'prefill' do
|
22
|
-
it '
|
22
|
+
it 'prefills from en' do
|
23
23
|
TestCodebase.in_test_app_dir { YAML.load_file('config/locales/es.yml')['es']['missing_in_es'].should be_nil }
|
24
24
|
TestCodebase.rake_result('i18n:prefill')
|
25
|
-
TestCodebase.in_test_app_dir {
|
25
|
+
TestCodebase.in_test_app_dir {
|
26
|
+
YAML.load_file('config/locales/es.yml')['es']['missing_in_es']['a'].should == 'EN_TEXT'
|
27
|
+
YAML.load_file('config/locales/devise.en.yml')['en']['devise']['a'].should == 'EN_TEXT'
|
28
|
+
YAML.load_file('config/locales/devise.es.yml')['es']['devise']['a'].should == 'ES_TEXT'
|
29
|
+
}
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
@@ -45,7 +49,8 @@ describe 'rake i18n' do
|
|
45
49
|
'blank_in_es' => {'a' => v},
|
46
50
|
'relative' => {'index' => {'title' => v}},
|
47
51
|
'numeric' => {'a' => v_num},
|
48
|
-
'plural' => {'a' => {'one' => v, 'other' => "%{count} #{v}s"}}
|
52
|
+
'plural' => {'a' => {'one' => v, 'other' => "%{count} #{v}s"}},
|
53
|
+
'devise' => {'a' => v}
|
49
54
|
}.tap { |r|
|
50
55
|
gen = r["bench"] = {}
|
51
56
|
BENCH_KEYS.times { |i| gen["key#{i}"] = v }
|
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.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- glebm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -128,7 +128,7 @@ files:
|
|
128
128
|
- lib/i18n/tasks.rb
|
129
129
|
- lib/i18n/tasks/base_task.rb
|
130
130
|
- lib/i18n/tasks/data/yaml.rb
|
131
|
-
- lib/i18n/tasks/
|
131
|
+
- lib/i18n/tasks/data_traversal.rb
|
132
132
|
- lib/i18n/tasks/ignore_keys.rb
|
133
133
|
- lib/i18n/tasks/key_pattern_matching.rb
|
134
134
|
- lib/i18n/tasks/missing.rb
|
@@ -137,9 +137,9 @@ files:
|
|
137
137
|
- lib/i18n/tasks/prefill.rb
|
138
138
|
- lib/i18n/tasks/railtie.rb
|
139
139
|
- lib/i18n/tasks/relative_keys.rb
|
140
|
+
- lib/i18n/tasks/source_keys.rb
|
140
141
|
- lib/i18n/tasks/translation_data.rb
|
141
142
|
- lib/i18n/tasks/unused.rb
|
142
|
-
- lib/i18n/tasks/usage_search.rb
|
143
143
|
- lib/i18n/tasks/version.rb
|
144
144
|
- lib/tasks/i18n-tasks.rake
|
145
145
|
- spec/fixtures/app/assets/javascripts/application.js
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'i18n/tasks/key_pattern_matching'
|
2
|
-
|
3
|
-
# e.g t("category.#{category_key}")
|
4
|
-
module I18n::Tasks
|
5
|
-
module FuzzySourceKeys
|
6
|
-
include KeyPatternMatching
|
7
|
-
|
8
|
-
# dynamically generated keys in the source, e.g t("category.#{category_key}")
|
9
|
-
def pattern_key?(key)
|
10
|
-
@pattern_keys_re ||= compile_start_with_re(pattern_key_prefixes)
|
11
|
-
!!(key =~ @pattern_keys_re)
|
12
|
-
end
|
13
|
-
|
14
|
-
# keys in the source that end with a ., e.g. t("category.#{cat.i18n_key}") or t("category." + category.key)
|
15
|
-
def pattern_key_prefixes
|
16
|
-
@pattern_keys_prefixes ||=
|
17
|
-
find_source_keys.select { |k| k =~ /\#{.*?}/ || k.ends_with?('.') }.map { |k| k.split(/\.?#/)[0].presence }.compact
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|