i18n-tasks 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGES.md +5 -0
- data/README.md +23 -9
- data/i18n-tasks.gemspec +1 -0
- data/lib/i18n/tasks.rb +1 -0
- data/lib/i18n/tasks/base_task.rb +8 -0
- data/lib/i18n/tasks/data/yaml.rb +1 -1
- data/lib/i18n/tasks/data_traversal.rb +13 -3
- data/lib/i18n/tasks/missing_keys.rb +33 -0
- data/lib/i18n/tasks/output/terminal.rb +6 -6
- data/lib/i18n/tasks/source_keys.rb +4 -4
- data/lib/i18n/tasks/translation.rb +20 -0
- data/lib/i18n/tasks/translation_data.rb +33 -2
- data/lib/i18n/tasks/untranslated_keys.rb +12 -0
- data/lib/i18n/tasks/{unused.rb → unused_keys.rb} +2 -3
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/tasks/i18n-tasks.rake +80 -14
- data/spec/fixtures/app/views/relative/index.html.slim +2 -0
- data/spec/i18n_tasks_spec.rb +25 -6
- data/spec/support/i18n_tasks_output_matcher.rb +12 -6
- data/spec/support/test_codebase.rb +13 -7
- metadata +20 -5
- data/lib/i18n/tasks/missing.rb +0 -54
- data/lib/i18n/tasks/prefill.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4c2dfceb8ce78b38a271f5b2ee5afda329cd15d
|
4
|
+
data.tar.gz: 89936d161a3e2b826164658bb64b7816dbda0a6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed3cec3d3aadfac2f0d30b8b707cb8b5f46dfd1e063c283546be4222644757906c736aeceb4860c5a31319a6a799acf7056d444acda15d99d94ca4389e30c3f0
|
7
|
+
data.tar.gz: 2781e4bf20a94badc077233c21e9d4fa0fe9a2dda74e76ff05429e4833b13bc265868528fb8d9d9c8fce07a32161ca70d7acac5c2b00981ee640dd6e061dbbb8
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -7,13 +7,18 @@ Rails I18n tasks to find missing / unused translations and more. Works with slim
|
|
7
7
|
|
8
8
|
## Usage
|
9
9
|
|
10
|
-
Use `rake -T i18n` to get the list of tasks with descriptions.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
Use `rake -T i18n` to get the list of tasks with descriptions. These are [the tasks](/lib/tasks/i18n-tasks.rake) available:
|
11
|
+
|
12
|
+
Rake Task | Summary
|
13
|
+
:---------------------------------------------- |:------------------------------------------------------------------
|
14
|
+
i18n:missing | Show missing translations.
|
15
|
+
i18n:unused | Show unused translations.
|
16
|
+
i18n:normalize | Normalize translation data: sort by name and file.
|
17
|
+
i18n:fill:add_missing *[value = key.humanize]* | Add missing `key: value` to base locale.
|
18
|
+
i18n:fill:with_base *[locales = all - base]* | Add missing `key: base value` to each non-base locale.
|
19
|
+
i18n:fill:with_google *[locales = all - base]* | Add missing `key: Google Translated value` to each non-base locale.
|
20
|
+
i18n:fill:with_blanks *[locales = all]* | Add `key: ''` for missing keys to each locale.
|
21
|
+
|
17
22
|
The `i18n:unused` task will detect pattern translations and not report them, e.g.:
|
18
23
|
|
19
24
|
```ruby
|
@@ -31,7 +36,7 @@ For more examples see [the tests](/spec/i18n_tasks_spec.rb).
|
|
31
36
|
Simply add to Gemfile:
|
32
37
|
|
33
38
|
```ruby
|
34
|
-
gem 'i18n-tasks', '~> 0.
|
39
|
+
gem 'i18n-tasks', '~> 0.2.0'
|
35
40
|
```
|
36
41
|
|
37
42
|
## Configuration
|
@@ -59,6 +64,15 @@ data:
|
|
59
64
|
- 'config/locales/%{locale}.yml'
|
60
65
|
```
|
61
66
|
|
67
|
+
### Translation
|
68
|
+
|
69
|
+
Set `GOOGLE_TRANSLATE_API_KEY` environment variable, or specify the key in config/i18n-tasks.yml:
|
70
|
+
|
71
|
+
```yaml
|
72
|
+
translation:
|
73
|
+
api_key: THE_KEY
|
74
|
+
```
|
75
|
+
|
62
76
|
### Usage search
|
63
77
|
|
64
78
|
```yaml
|
@@ -77,7 +91,7 @@ search:
|
|
77
91
|
exclude:
|
78
92
|
- '*.js'
|
79
93
|
# you can override the default grep pattern:
|
80
|
-
pattern:
|
94
|
+
pattern: "\\bt[( ]\\s*(.)((?<=\").+?(?=\")|(?<=').+?(?=')|(?<=:)\\w+\\b)"
|
81
95
|
```
|
82
96
|
|
83
97
|
### Fine-tuning
|
data/i18n-tasks.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency 'rake'
|
22
22
|
spec.add_dependency 'activesupport'
|
23
|
+
spec.add_dependency 'easy_translate'
|
23
24
|
spec.add_dependency 'term-ansicolor'
|
24
25
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
25
26
|
spec.add_development_dependency 'rake'
|
data/lib/i18n/tasks.rb
CHANGED
data/lib/i18n/tasks/base_task.rb
CHANGED
@@ -4,7 +4,11 @@ require 'i18n/tasks/relative_keys'
|
|
4
4
|
require 'i18n/tasks/plural_keys'
|
5
5
|
require 'i18n/tasks/source_keys'
|
6
6
|
require 'i18n/tasks/translation_data'
|
7
|
+
require 'i18n/tasks/translation'
|
7
8
|
require 'i18n/tasks/ignore_keys'
|
9
|
+
require 'i18n/tasks/missing_keys'
|
10
|
+
require 'i18n/tasks/untranslated_keys'
|
11
|
+
require 'i18n/tasks/unused_keys'
|
8
12
|
|
9
13
|
module I18n
|
10
14
|
module Tasks
|
@@ -13,7 +17,11 @@ module I18n
|
|
13
17
|
include RelativeKeys
|
14
18
|
include PluralKeys
|
15
19
|
include SourceKeys
|
20
|
+
include MissingKeys
|
21
|
+
include UntranslatedKeys
|
22
|
+
include UnusedKeys
|
16
23
|
include TranslationData
|
24
|
+
include Translation
|
17
25
|
include IgnoreKeys
|
18
26
|
|
19
27
|
# i18n-tasks config (defaults + config/i18n-tasks.yml)
|
data/lib/i18n/tasks/data/yaml.rb
CHANGED
@@ -1,10 +1,20 @@
|
|
1
1
|
module I18n::Tasks::DataTraversal
|
2
2
|
# translation of the key found in the passed hash or nil
|
3
3
|
# @return [String,nil]
|
4
|
-
def t(hash, key)
|
4
|
+
def t(hash = data[base_locale], key)
|
5
5
|
key.split('.').inject(hash) { |r, seg| r[seg] if r }
|
6
6
|
end
|
7
7
|
|
8
|
+
# traverse => flat_map
|
9
|
+
def traverse_flat_map(hash)
|
10
|
+
list = []
|
11
|
+
traverse hash do |k, v|
|
12
|
+
mapped = yield(k, v)
|
13
|
+
list << mapped if mapped
|
14
|
+
end
|
15
|
+
list
|
16
|
+
end
|
17
|
+
|
8
18
|
# traverse hash, yielding with full key and value
|
9
19
|
# @param hash [Hash{String => String,Hash}] translation data to traverse
|
10
20
|
# @yield [full_key, value] yields full key and value for every translation in #hash
|
@@ -25,8 +35,8 @@ module I18n::Tasks::DataTraversal
|
|
25
35
|
list = list.sort
|
26
36
|
tree = {}
|
27
37
|
list.each do |key, value|
|
28
|
-
key_segments
|
29
|
-
node
|
38
|
+
key_segments = key.to_s.split('.')
|
39
|
+
node = key_segments[0..-2].inject(tree) do |r, segment|
|
30
40
|
r[segment] ||= {}
|
31
41
|
end
|
32
42
|
node[key_segments.last] = value
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module I18n::Tasks::MissingKeys
|
2
|
+
# missing keys, i.e. key that are in the code but are not in the base locale data
|
3
|
+
# @return Array{Hash}
|
4
|
+
def keys_missing_base_value
|
5
|
+
find_source_keys.reject { |key|
|
6
|
+
key_value?(key, base_locale) || pattern_key?(key) || ignore_key?(key, :missing)
|
7
|
+
}.map { |key| {locale: base_locale, type: :none, key: key} }
|
8
|
+
end
|
9
|
+
|
10
|
+
# present in base locale, but untranslated in another locale
|
11
|
+
# @return Array{Hash}
|
12
|
+
def keys_missing_translation(locale)
|
13
|
+
trn = data[locale]
|
14
|
+
r = []
|
15
|
+
traverse data[base_locale] do |key, base_value|
|
16
|
+
value_in_locale = t(trn, key)
|
17
|
+
if value_in_locale.blank? && !ignore_key?(key, :missing)
|
18
|
+
r << {locale: locale, key: key, type: :blank, base_value: base_value}
|
19
|
+
elsif value_in_locale == base_value && !ignore_key?(key, :eq_base, locale)
|
20
|
+
r << {locale: locale, key: key, type: :eq_base, base_value: base_value}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
r
|
24
|
+
end
|
25
|
+
|
26
|
+
# sort first by locale, then by type
|
27
|
+
# @return Array{Hash}
|
28
|
+
def sort_keys(keys)
|
29
|
+
keys.sort { |a, b|
|
30
|
+
(l = a[:locale] <=> b[:locale]).zero? ? a[:type] <=> b[:type] : l
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
@@ -38,14 +38,14 @@ module I18n
|
|
38
38
|
$stderr.puts(bold green message)
|
39
39
|
end
|
40
40
|
|
41
|
-
STATUS_TEXTS = {
|
42
|
-
none: red("✗".ljust(6)),
|
43
|
-
blank: yellow(bold '∅'.ljust(6)),
|
44
|
-
eq_base: blue(bold "=".ljust(6))
|
45
|
-
}
|
46
41
|
|
47
42
|
def print_missing_translation(m, opts)
|
48
|
-
|
43
|
+
status_texts = {
|
44
|
+
none: red("✗".ljust(6)),
|
45
|
+
blank: yellow(bold '∅'.ljust(6)),
|
46
|
+
eq_base: blue(bold "=".ljust(6))
|
47
|
+
}
|
48
|
+
locale, key, base_value, status_text = m[:locale], m[:key], m[:base_value].to_s.try(:strip), " #{status_texts[m[:type]]}"
|
49
49
|
|
50
50
|
key = magenta "#{key}".ljust(opts[:key_col_width])
|
51
51
|
s = if m[:type] == :none
|
@@ -69,13 +69,13 @@ module I18n::Tasks::SourceKeys
|
|
69
69
|
line.scan(search_config[:pattern]) do |t, key|
|
70
70
|
if key.start_with? '.'
|
71
71
|
key = absolutize_key(key, path)
|
72
|
-
elsif t == ':'
|
73
|
-
key = absolutize_key(".#{key}", path)
|
74
72
|
end
|
75
|
-
|
73
|
+
if key =~ /^[\w.\#{}]+$/
|
74
|
+
keys << key
|
75
|
+
end
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
79
|
-
keys
|
79
|
+
keys
|
80
80
|
end
|
81
81
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'easy_translate'
|
2
|
+
|
3
|
+
module I18n::Tasks::Translation
|
4
|
+
def google_translate(strings, opts)
|
5
|
+
return [] if strings.empty?
|
6
|
+
opts = opts.dup
|
7
|
+
if (key = translation_config[:api_key]).present?
|
8
|
+
opts[:key] ||= key
|
9
|
+
end
|
10
|
+
EasyTranslate.translate strings, opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def translation_config
|
14
|
+
@translation_config ||= begin
|
15
|
+
conf = (config[:translation] ||= {}).with_indifferent_access
|
16
|
+
conf[:api_key] ||= ENV['GOOGLE_TRANSLATE_API_KEY']
|
17
|
+
conf
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -17,12 +17,43 @@ module I18n::Tasks::TranslationData
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# whether the value for key exists in locale (defaults: base_locale)
|
20
|
-
def
|
20
|
+
def key_value?(key, locale = base_locale)
|
21
21
|
t(data[locale], key).present?
|
22
22
|
end
|
23
23
|
|
24
24
|
# @return [String] default i18n locale
|
25
25
|
def base_locale
|
26
|
-
I18n.default_locale.to_s
|
26
|
+
config[:base_locale] ||= I18n.default_locale.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Array<String>] all available locales
|
30
|
+
def locales
|
31
|
+
config[:locales] ||= I18n.available_locales.map(&:to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
# write to store, normalizing all data
|
35
|
+
def normalize_store!(locales = self.locales)
|
36
|
+
Array(locales).each do |target_locale|
|
37
|
+
# the store itself handles normalization
|
38
|
+
data[target_locale] = data[target_locale]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# fill missing / blank keys with values from passed block
|
43
|
+
def fill_blanks!(locale = base_locale, &fill_with)
|
44
|
+
blank_keys =
|
45
|
+
if locale == base_locale
|
46
|
+
# for base locale "blank" is: present in source but not in the base locale.
|
47
|
+
keys_missing_base_value.map { |e| e[:key] } +
|
48
|
+
traverse_flat_map(data[base_locale]) { |key, value|
|
49
|
+
key if value.to_s.blank? && !ignore_key?(key, :missing) }
|
50
|
+
else
|
51
|
+
# for other locales "blank" is: present in base but not in the locale itself.
|
52
|
+
traverse_flat_map(data[base_locale]) { |key|
|
53
|
+
key if !key_value?(key, locale) && !ignore_key?(key, :missing) }
|
54
|
+
end
|
55
|
+
|
56
|
+
list = blank_keys.uniq.zip fill_with.call(blank_keys)
|
57
|
+
data[locale] = data[locale].deep_merge(list_to_tree(list))
|
27
58
|
end
|
28
59
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module I18n::Tasks::UntranslatedKeys
|
2
|
+
# Get all the missing translations as an array of missing keys as hashes with the following options:
|
3
|
+
# :locale
|
4
|
+
# :key
|
5
|
+
# :type — :blank, :missing, or :eq_base
|
6
|
+
# :base_value — translation value in base locale if one is present
|
7
|
+
# @return [Array<Hash{Symbol => String,Symbol,nil}>]
|
8
|
+
def untranslated_keys
|
9
|
+
other_locales = locales - [base_locale]
|
10
|
+
sort_keys keys_missing_base_value + other_locales.map { |locale| keys_missing_translation(locale) }.flatten(1)
|
11
|
+
end
|
12
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
require 'i18n/tasks/base_task'
|
3
2
|
|
4
3
|
module I18n
|
5
4
|
module Tasks
|
6
|
-
|
5
|
+
module UnusedKeys
|
7
6
|
# @return [Array<[String, String]>] all the unused translations as an array of [key, value] pairs
|
8
|
-
def
|
7
|
+
def unused_keys
|
9
8
|
r = []
|
10
9
|
d = self.data[base_locale]
|
11
10
|
traverse d do |key, value|
|
data/lib/i18n/tasks/version.rb
CHANGED
data/lib/tasks/i18n-tasks.rake
CHANGED
@@ -1,28 +1,94 @@
|
|
1
1
|
require 'set'
|
2
|
-
require '
|
3
|
-
|
4
|
-
require 'i18n/tasks/missing'
|
5
|
-
require 'i18n/tasks/prefill'
|
6
|
-
require 'i18n/tasks/unused'
|
2
|
+
require 'i18n/tasks/base_task'
|
7
3
|
require 'i18n/tasks/output/terminal'
|
8
4
|
|
9
5
|
namespace :i18n do
|
10
|
-
desc '
|
11
|
-
task :
|
12
|
-
|
6
|
+
desc 'show missing translations'
|
7
|
+
task :missing => 'i18n:setup' do
|
8
|
+
term_output.missing i18n_tasks.untranslated_keys
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'show unused translations'
|
12
|
+
task :unused => 'i18n:setup' do
|
13
|
+
term_output.unused i18n_tasks.unused_keys
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'normalize translation data: sort and move to the right files'
|
17
|
+
task :normalize => 'i18n:setup' do
|
18
|
+
normalize_store!
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'fill translations with values'
|
22
|
+
namespace :fill do
|
23
|
+
|
24
|
+
desc 'add <key: placeholder || key.humanize> to the base locale'
|
25
|
+
task :add_missing, [:placeholder] => 'i18n:setup' do |t, args|
|
26
|
+
normalize_store!
|
27
|
+
i18n_tasks.fill_blanks!(base_locale) { |keys|
|
28
|
+
keys.map { |key|
|
29
|
+
args[:placeholder] || key.split('.').last.to_s.humanize
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'add <key: ""> to each locale'
|
35
|
+
task :with_blanks, [:locales] => 'i18n:setup' do |t, args|
|
36
|
+
normalize_store!
|
37
|
+
[base_locale, *non_base_locales].each do |locale|
|
38
|
+
fill_blanks!(locale) { |blank_keys| blank_keys.map { '' } }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'add <key: Google Translated value> to each non-base locale, uses env GOOGLE_TRANSLATE_API_KEY'
|
43
|
+
task :with_google, [:locales] => 'i18n:setup' do |t, args|
|
44
|
+
normalize_store!
|
45
|
+
non_base_locales(args).each do |locale|
|
46
|
+
fill_blanks!(locale) { |blank_keys|
|
47
|
+
i18n_tasks.google_translate blank_keys.map { |k| t(k) }, to: locale, from: base_locale
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'add <key: base value> to each non-base locale'
|
53
|
+
task :with_base, [:locales] => 'i18n:setup' do |t, args|
|
54
|
+
normalize_store!
|
55
|
+
non_base_locales(args).each do |locale|
|
56
|
+
fill_blanks!(locale) { |blank_keys| blank_keys.map { |k| t(k) } }
|
57
|
+
end
|
58
|
+
end
|
13
59
|
end
|
14
60
|
|
15
|
-
|
16
|
-
task :missing => :environment do
|
61
|
+
task 'i18n:setup' => :environment do
|
17
62
|
if File.exists?('.i18nignore')
|
18
63
|
STDERR.puts 'Looks like you are using .i18ignore. It is no longer used in favour of config/i18n-tasks.yml.'
|
19
64
|
STDERR.puts 'See README.md https://github.com/glebm/i18n-tasks'
|
20
65
|
end
|
21
|
-
I18n::Tasks::Output::Terminal.new.missing I18n::Tasks::Missing.new.find_keys
|
22
66
|
end
|
23
67
|
|
24
|
-
|
25
|
-
|
26
|
-
|
68
|
+
module I18n::Tasks::RakeHelpers
|
69
|
+
extend ActiveSupport::Concern
|
70
|
+
|
71
|
+
included do
|
72
|
+
delegate :t, :locales, :base_locale, :normalize_store!, :fill_blanks!, to: :i18n_tasks
|
73
|
+
|
74
|
+
def i18n_tasks
|
75
|
+
@i18n_tasks ||= I18n::Tasks::BaseTask.new
|
76
|
+
end
|
77
|
+
|
78
|
+
def term_output
|
79
|
+
@term_output ||= I18n::Tasks::Output::Terminal.new
|
80
|
+
end
|
81
|
+
|
82
|
+
def non_base_locales(args = nil)
|
83
|
+
locales_or_all(args) - [base_locale]
|
84
|
+
end
|
85
|
+
|
86
|
+
def locales_or_all(args = nil)
|
87
|
+
args[:locales] ? args[:locales].strip.split(/\s*\+\s*/) : locales
|
88
|
+
end
|
89
|
+
end
|
27
90
|
end
|
91
|
+
|
92
|
+
include I18n::Tasks::RakeHelpers
|
93
|
+
include Term::ANSIColor
|
28
94
|
end
|
data/spec/i18n_tasks_spec.rb
CHANGED
@@ -5,8 +5,11 @@ describe 'rake i18n' do
|
|
5
5
|
describe 'missing' do
|
6
6
|
it 'detects missing or identical' do
|
7
7
|
TestCodebase.capture_stderr do
|
8
|
-
TestCodebase.rake_result('i18n:missing').should be_i18n_keys %w(
|
9
|
-
|
8
|
+
TestCodebase.rake_result('i18n:missing').should be_i18n_keys %w(
|
9
|
+
en.used_but_missing.a en.missing_symbol_key en.relative.index.missing
|
10
|
+
es.missing_in_es.a es.blank_in_es.a es.same_in_es.a
|
11
|
+
)
|
12
|
+
end.should =~ /Missing keys and translations \(6\)/
|
10
13
|
end
|
11
14
|
end
|
12
15
|
|
@@ -18,10 +21,20 @@ describe 'rake i18n' do
|
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
|
-
describe '
|
22
|
-
it '
|
24
|
+
describe 'normalize' do
|
25
|
+
it 'moves keys to the corresponding files as per data.write' do
|
26
|
+
TestCodebase.in_test_app_dir {
|
27
|
+
File.exists?('config/locales/devise.en.yml').should be_false
|
28
|
+
TestCodebase.rake_result('i18n:normalize')
|
29
|
+
YAML.load_file('config/locales/devise.en.yml')['en']['devise']['a'].should == 'EN_TEXT'
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'fill:' do
|
35
|
+
it 'with_base' do
|
23
36
|
TestCodebase.in_test_app_dir { YAML.load_file('config/locales/es.yml')['es']['missing_in_es'].should be_nil }
|
24
|
-
TestCodebase.rake_result('i18n:
|
37
|
+
TestCodebase.rake_result('i18n:fill:with_base')
|
25
38
|
TestCodebase.in_test_app_dir {
|
26
39
|
YAML.load_file('config/locales/es.yml')['es']['missing_in_es']['a'].should == 'EN_TEXT'
|
27
40
|
YAML.load_file('config/locales/devise.en.yml')['en']['devise']['a'].should == 'EN_TEXT'
|
@@ -47,7 +60,13 @@ describe 'rake i18n' do
|
|
47
60
|
'ignore_eq_base_all' => {'a' => v},
|
48
61
|
'ignore_eq_base_es' => {'a' => v},
|
49
62
|
'blank_in_es' => {'a' => v},
|
50
|
-
'relative' => {
|
63
|
+
'relative' => {
|
64
|
+
'index' => {
|
65
|
+
'title' => v,
|
66
|
+
'description' => v,
|
67
|
+
'summary' => v,
|
68
|
+
}
|
69
|
+
},
|
51
70
|
'numeric' => {'a' => v_num},
|
52
71
|
'plural' => {'a' => {'one' => v, 'other' => "%{count} #{v}s"}},
|
53
72
|
'devise' => {'a' => v}
|
@@ -5,12 +5,18 @@ RSpec::Matchers.define :be_i18n_keys do |expected|
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def extract_keys(actual)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
actual = actual.split("\n").map(&:presence).compact.map { |row|
|
9
|
+
row.gsub(/\s+/, ' ').strip.split(' ').map(&:presence).compact
|
10
|
+
}
|
11
|
+
if actual[0][1] =~ /[✗∅=]/
|
12
|
+
locale_col = 0
|
13
|
+
key_col = 2
|
14
|
+
else
|
15
|
+
key_col = 0
|
16
|
+
end
|
17
|
+
actual.map { |row|
|
18
|
+
key = row[key_col]
|
19
|
+
key = row[locale_col] + '.' + key if locale_col
|
14
20
|
key = key[0..-2] if key.end_with?(':')
|
15
21
|
key
|
16
22
|
}.compact
|
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'yaml'
|
2
3
|
|
3
4
|
module TestCodebase
|
4
5
|
extend self
|
5
6
|
AT = 'tmp/test_codebase'
|
6
7
|
|
7
8
|
DEFAULTS = {
|
8
|
-
|
9
|
-
|
9
|
+
'config/locales/en.yml' => {'en' => {}}.to_yaml,
|
10
|
+
'config/locales/es.yml' => {'es' => {}}.to_yaml
|
10
11
|
}
|
11
12
|
|
12
13
|
def setup(files)
|
@@ -32,11 +33,16 @@ module TestCodebase
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def in_test_app_dir(&block)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
return block.call if @in_dir
|
37
|
+
begin
|
38
|
+
pwd = Dir.pwd
|
39
|
+
Dir.chdir AT
|
40
|
+
@in_dir = true
|
41
|
+
block.call
|
42
|
+
ensure
|
43
|
+
Dir.chdir pwd
|
44
|
+
@in_dir = false
|
45
|
+
end
|
40
46
|
end
|
41
47
|
|
42
48
|
def capture_stderr
|
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.
|
4
|
+
version: 0.2.0
|
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-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: easy_translate
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: term-ansicolor
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,15 +144,16 @@ files:
|
|
130
144
|
- lib/i18n/tasks/data_traversal.rb
|
131
145
|
- lib/i18n/tasks/ignore_keys.rb
|
132
146
|
- lib/i18n/tasks/key_pattern_matching.rb
|
133
|
-
- lib/i18n/tasks/
|
147
|
+
- lib/i18n/tasks/missing_keys.rb
|
134
148
|
- lib/i18n/tasks/output/terminal.rb
|
135
149
|
- lib/i18n/tasks/plural_keys.rb
|
136
|
-
- lib/i18n/tasks/prefill.rb
|
137
150
|
- lib/i18n/tasks/railtie.rb
|
138
151
|
- lib/i18n/tasks/relative_keys.rb
|
139
152
|
- lib/i18n/tasks/source_keys.rb
|
153
|
+
- lib/i18n/tasks/translation.rb
|
140
154
|
- lib/i18n/tasks/translation_data.rb
|
141
|
-
- lib/i18n/tasks/
|
155
|
+
- lib/i18n/tasks/untranslated_keys.rb
|
156
|
+
- lib/i18n/tasks/unused_keys.rb
|
142
157
|
- lib/i18n/tasks/version.rb
|
143
158
|
- lib/tasks/i18n-tasks.rake
|
144
159
|
- spec/fixtures/app/assets/javascripts/application.js
|
data/lib/i18n/tasks/missing.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
require 'i18n/tasks/base_task'
|
3
|
-
|
4
|
-
module I18n
|
5
|
-
module Tasks
|
6
|
-
class Missing < BaseTask
|
7
|
-
|
8
|
-
# Get all the missing translations as an array of missing keys as hashes with the following options:
|
9
|
-
# :locale
|
10
|
-
# :key
|
11
|
-
# :type — :blank, :missing, or :eq_base
|
12
|
-
# :base_value — translation value in base locale if one is present
|
13
|
-
# @return [Array<Hash{Symbol => String,Symbol,nil}>]
|
14
|
-
def find_keys
|
15
|
-
other_locales = I18n.available_locales.map(&:to_s) - [base_locale]
|
16
|
-
sort_keys keys_missing_base_value + other_locales.map { |locale| keys_missing_translation(locale) }.flatten(1)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
# missing keys, i.e. key that are in the code but are not in the base locale data
|
22
|
-
# @return Array{Hash}
|
23
|
-
def keys_missing_base_value
|
24
|
-
find_source_keys.reject { |key|
|
25
|
-
key_has_value?(key, base_locale) || pattern_key?(key) || ignore_key?(key, :missing)
|
26
|
-
}.map { |key| {locale: base_locale, type: :none, key: key} }
|
27
|
-
end
|
28
|
-
|
29
|
-
# present in base locale, but untranslated in another locale
|
30
|
-
# @return Array{Hash}
|
31
|
-
def keys_missing_translation(locale)
|
32
|
-
trn = data[locale]
|
33
|
-
r = []
|
34
|
-
traverse data[base_locale] do |key, base_value|
|
35
|
-
value_in_locale = t(trn, key)
|
36
|
-
if value_in_locale.blank? && !ignore_key?(key, :missing)
|
37
|
-
r << {locale: locale, key: key, type: :blank, base_value: base_value}
|
38
|
-
elsif value_in_locale == base_value && !ignore_key?(key, :eq_base, locale)
|
39
|
-
r << {locale: locale, key: key, type: :eq_base, base_value: base_value}
|
40
|
-
end
|
41
|
-
end
|
42
|
-
r
|
43
|
-
end
|
44
|
-
|
45
|
-
# sort first by locale, then by type
|
46
|
-
# @return Array{Hash}
|
47
|
-
def sort_keys(keys)
|
48
|
-
keys.sort { |a, b|
|
49
|
-
(l = a[:locale] <=> b[:locale]).zero? ? a[:type] <=> b[:type] : l
|
50
|
-
}
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
data/lib/i18n/tasks/prefill.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'i18n/tasks/base_task'
|
2
|
-
|
3
|
-
module I18n
|
4
|
-
module Tasks
|
5
|
-
# Prefill values from base locale data
|
6
|
-
class Prefill < BaseTask
|
7
|
-
def perform
|
8
|
-
I18n.available_locales.map(&:to_s).each do |target_locale|
|
9
|
-
data[target_locale] = data[base_locale].deep_merge(data[target_locale])
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|