i18n-tasks 0.3.9 → 0.3.10
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/Gemfile +1 -0
- data/README.md +23 -16
- data/bin/i18n-tasks +21 -14
- data/lib/i18n/tasks/commands.rb +17 -8
- data/lib/i18n/tasks/data/adapter/json_adapter.rb +4 -4
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +4 -4
- data/lib/i18n/tasks/data/file_formats.rb +11 -9
- data/lib/i18n/tasks/data/file_system.rb +2 -2
- data/lib/i18n/tasks/version.rb +1 -1
- data/spec/fixtures/app/views/index.html.slim +1 -1
- data/spec/fixtures/config/i18n-tasks.yml +3 -0
- data/spec/i18n_tasks_spec.rb +23 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 601f3e2a8ac21ec9d8f83aa71f5723623b109f69
|
4
|
+
data.tar.gz: 5a9396e63a232357fd63d1d260a80ddfd274dea4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80187dee2d375fbe01e79313c6d7bd24927c91da51c52b102661f7b13378547c16743b571c0ebdb7fd3d29d35777db48900ce66f5d23108069d6f06f0510ac7b
|
7
|
+
data.tar.gz: fe57c3b82c6d24c3d43c3203a184b21d99a04ec639d142aee38b1f336587d74308937ea9a7a33ac8a8a9b1d16296e83bf58e46749bbbbfb453a715cf0087924e
|
data/CHANGES.md
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
* Fix regression: Remove ActiveSupport::HashWithIndifferentAccess from locale data output
|
4
4
|
|
5
|
+
## 0.3.10
|
6
|
+
|
7
|
+
* New (de)serialization options in config
|
8
|
+
* `add-missing` placeholder argument can now use %{base_value}.
|
9
|
+
|
5
10
|
## 0.3.8
|
6
11
|
|
7
12
|
* Fix activesupport ~3.x compatibility issue (#45).
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# i18n-tasks [![Build Status][badge-travis]][travis] [![Coverage Status][badge-coveralls]][coveralls] [![Code Climate][badge-code-climate]][code-climate] [![
|
1
|
+
# i18n-tasks [![Build Status][badge-travis]][travis] [![Coverage Status][badge-coveralls]][coveralls] [![Code Climate][badge-code-climate]][code-climate] [![Gemnasium][badge-gemnasium]][gemnasium]
|
2
2
|
|
3
3
|
i18n-tasks finds and manages missing and unused translations in your application.
|
4
4
|
|
@@ -7,7 +7,7 @@ If you use a key that does not exist, this will only blow up at runtime. Keys le
|
|
7
7
|
in the resource files and introduce unnecessary overhead on the translators. Translation files can quickly turn to disarray.
|
8
8
|
|
9
9
|
i18n-tasks improves this by using static analysis. It scans calls such as `I18n.t('some.key')` and provides reports on key usage, missing, and unused keys.
|
10
|
-
It can also
|
10
|
+
It can also pre-fill missing keys, including from Google Translate, and it can remove unused keys as well.
|
11
11
|
|
12
12
|
i18n-tasks can be used with any project using [i18n][i18n-gem] (default in Rails), or similar, even if it isn't ruby.
|
13
13
|
|
@@ -18,7 +18,7 @@ i18n-tasks can be used with any project using [i18n][i18n-gem] (default in Rails
|
|
18
18
|
Add to Gemfile:
|
19
19
|
|
20
20
|
```ruby
|
21
|
-
gem 'i18n-tasks', '~> 0.3.
|
21
|
+
gem 'i18n-tasks', '~> 0.3.10'
|
22
22
|
```
|
23
23
|
|
24
24
|
i18n-tasks does not load or execute any of the application's code but performs static-only analysic.
|
@@ -55,6 +55,8 @@ To add missing values to the base locale only:
|
|
55
55
|
```bash
|
56
56
|
# most task accept locales as first argument. `base` and `all` are special
|
57
57
|
i18n-tasks add-missing base
|
58
|
+
# add-missing accepts a placeholder argument, with optional base_value interpolation
|
59
|
+
i18n-tasks add-missing -p 'PLEASE-TRANSLATE %{base_value}' fr
|
58
60
|
```
|
59
61
|
|
60
62
|
Translate missing values with Google Translate ([more below on the API key](#translation-config)).
|
@@ -142,6 +144,11 @@ data:
|
|
142
144
|
- ['devise.*', 'config/locales/devise.%{locale}.yml']
|
143
145
|
# default catch-all:
|
144
146
|
- 'config/locales/%{locale}.yml' # path is short for ['*', path]
|
147
|
+
# configure YAML / JSON serializer options (when using the default adapter)
|
148
|
+
yaml:
|
149
|
+
write:
|
150
|
+
# do not wrap lines at 80 characters (default)
|
151
|
+
line_width: -1
|
145
152
|
```
|
146
153
|
|
147
154
|
#### Key pattern syntax
|
@@ -254,23 +261,23 @@ You might want to test for missing and unused translations as part of your test
|
|
254
261
|
This is how you can do it with rspec:
|
255
262
|
|
256
263
|
```ruby
|
257
|
-
# spec/
|
264
|
+
# spec/i18n_spec.rb:
|
258
265
|
require 'spec_helper'
|
259
|
-
|
260
266
|
require 'i18n/tasks'
|
261
267
|
|
262
|
-
describe '
|
268
|
+
describe 'I18n' do
|
263
269
|
let(:i18n) { I18n::Tasks::BaseTask.new }
|
264
270
|
|
265
|
-
it '
|
266
|
-
|
271
|
+
it 'does not have missing keys' do
|
272
|
+
count = i18n.missing_keys.count
|
273
|
+
fail "There are #{count} missing i18n keys! Run 'i18n-tasks missing' for more details." unless count.zero?
|
267
274
|
end
|
268
275
|
|
269
|
-
it '
|
270
|
-
|
276
|
+
it 'does not have unused keys' do
|
277
|
+
count = i18n.unused_keys.count
|
278
|
+
fail "There are #{count} unused i18n keys! Run 'i18n-tasks unused' for more details." unless count.zero?
|
271
279
|
end
|
272
280
|
end
|
273
|
-
|
274
281
|
```
|
275
282
|
|
276
283
|
## XLSX
|
@@ -292,12 +299,12 @@ This was originally developed for [Zuigo](http://zuigo.com/), a platform to orga
|
|
292
299
|
|
293
300
|
[MIT license]: /LICENSE.txt
|
294
301
|
[travis]: https://travis-ci.org/glebm/i18n-tasks
|
295
|
-
[badge-travis]:
|
302
|
+
[badge-travis]: http://img.shields.io/travis/glebm/i18n-tasks.svg
|
296
303
|
[coveralls]: https://coveralls.io/r/glebm/i18n-tasks?branch=master
|
297
|
-
[badge-coveralls]:
|
304
|
+
[badge-coveralls]: http://img.shields.io/coveralls/glebm/i18n-tasks.svg
|
305
|
+
[gemnasium]: https://gemnasium.com/glebm/i18n-tasks
|
306
|
+
[badge-gemnasium]: https://gemnasium.com/glebm/i18n-tasks.svg
|
298
307
|
[code-climate]: https://codeclimate.com/github/glebm/i18n-tasks
|
299
|
-
[badge-code-climate]:
|
300
|
-
[badge-flattr]: https://api.flattr.com/button/flattr-badge-large.png
|
301
|
-
[flattr]: https://flattr.com/submit/auto?user_id=glebm&url=https%3A%2F%2Fgithub.com%2Fglebm%2Fi18n-tasks
|
308
|
+
[badge-code-climate]: http://img.shields.io/codeclimate/github/glebm/i18n-tasks.svg
|
302
309
|
[i18n-gem]: https://github.com/svenfuchs/i18n "svenfuchs/i18n on Github"
|
303
310
|
[screenshot-find]: https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-usages.png "i18n-tasks find output screenshot"
|
data/bin/i18n-tasks
CHANGED
@@ -4,21 +4,30 @@ require 'i18n/tasks'
|
|
4
4
|
require 'i18n/tasks/commands'
|
5
5
|
require 'slop'
|
6
6
|
|
7
|
+
err = proc { |message, exit_code|
|
8
|
+
STDERR.puts Term::ANSIColor.yellow('i18n-tasks: ' + message)
|
9
|
+
exit exit_code
|
10
|
+
}
|
11
|
+
|
7
12
|
command = nil
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
begin
|
14
|
+
slop = Slop.parse(help: true) do
|
15
|
+
on('-v', '--version', 'Print the version') {
|
16
|
+
puts I18n::Tasks::VERSION
|
17
|
+
exit
|
18
|
+
}
|
19
|
+
::I18n::Tasks::Commands.cmds.each do |name, attr|
|
20
|
+
command name.tr('_', '-') do
|
21
|
+
description attr.desc if attr.desc
|
22
|
+
instance_exec(&attr.opts) if attr.opts
|
23
|
+
run do |opts, args|
|
24
|
+
command = [name, opts, args]
|
25
|
+
end
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
29
|
+
rescue Slop::Error => e
|
30
|
+
err.call(e.message, 64)
|
22
31
|
end
|
23
32
|
|
24
33
|
if command
|
@@ -40,7 +49,5 @@ if command
|
|
40
49
|
# i18n-tasks missing | head
|
41
50
|
end
|
42
51
|
else
|
43
|
-
|
44
|
-
puts slop.help
|
45
|
-
exit 64
|
52
|
+
err.call("Command unknown: #{ARGV[0]}", 64) if ARGV[0]
|
46
53
|
end
|
data/lib/i18n/tasks/commands.rb
CHANGED
@@ -11,7 +11,7 @@ module I18n::Tasks
|
|
11
11
|
desc 'show missing translations'
|
12
12
|
opts do
|
13
13
|
on '-l', :locales=, 'Filter by locale (default: all)', on_locale_opt
|
14
|
-
on '-t', :types
|
14
|
+
on '-t', :types=, 'Filter by type (types: missing_from_base, eq_base, missing_from_locale)', as: Array, delimiter: /[+:,]/
|
15
15
|
end
|
16
16
|
cmd :missing do |opt = {}|
|
17
17
|
parse_locales! opt
|
@@ -25,8 +25,8 @@ module I18n::Tasks
|
|
25
25
|
|
26
26
|
desc 'translate missing keys with Google Translate'
|
27
27
|
opts do
|
28
|
-
on '-l', :locales
|
29
|
-
on '-f', :from
|
28
|
+
on '-l', :locales=, 'Locales to translate (default: all)', on_locale_opt
|
29
|
+
on '-f', :from=, 'Locale to translate from (default: base)', default: 'base', argument: true, optional: false
|
30
30
|
end
|
31
31
|
cmd :translate_missing do |opt = {}|
|
32
32
|
opt[:from] = base_locale if opt[:from].blank? || opt[:from] == 'base'
|
@@ -36,21 +36,30 @@ module I18n::Tasks
|
|
36
36
|
|
37
37
|
desc 'add missing keys to the locales'
|
38
38
|
opts do
|
39
|
-
on '-l', :locales
|
40
|
-
on '-p', :placeholder
|
39
|
+
on '-l', :locales=, 'Locales to add keys into (default: all)', on_locale_opt
|
40
|
+
on '-p', :placeholder=, 'Value for empty keys (default: base value or key.humanize)', argument: true, optional: false
|
41
41
|
end
|
42
42
|
cmd :add_missing do |opt = {}|
|
43
43
|
parse_locales! opt
|
44
44
|
opt[:value] ||= opt.delete(:placeholder) || proc { |key, locale|
|
45
45
|
# default to base value or key.humanize
|
46
|
-
locale
|
46
|
+
locale != base_locale && t(key, base_locale) || key.split('.').last.to_s.humanize
|
47
47
|
}
|
48
|
+
|
49
|
+
v = opt[:value]
|
50
|
+
if v.is_a?(String) && v.include?('%{base_value}')
|
51
|
+
opt[:value] = proc { |key, locale|
|
52
|
+
base_value = t(key, base_locale) || ''
|
53
|
+
v % {base_value: base_value}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
48
57
|
i18n_task.fill_missing_value opt
|
49
58
|
end
|
50
59
|
|
51
60
|
desc 'show where the keys are used in the code'
|
52
61
|
opts do
|
53
|
-
on '-p', :pattern
|
62
|
+
on '-p', :pattern=, 'Show only keys matching pattern', argument: true, optional: false
|
54
63
|
end
|
55
64
|
cmd :find do |opt = {}|
|
56
65
|
opt[:filter] ||= opt.delete(:pattern) || opt[:arguments].try(:first)
|
@@ -95,7 +104,7 @@ module I18n::Tasks
|
|
95
104
|
|
96
105
|
desc 'save missing and unused translations to an Excel file'
|
97
106
|
opts do
|
98
|
-
on :path
|
107
|
+
on :path=, 'Destination path', default: 'tmp/i18n-report.xlsx'
|
99
108
|
end
|
100
109
|
cmd :xlsx_report do |opt = {}|
|
101
110
|
begin
|
@@ -7,13 +7,13 @@ module I18n::Tasks
|
|
7
7
|
extend self
|
8
8
|
|
9
9
|
# @return [Hash] locale tree
|
10
|
-
def parse(str)
|
11
|
-
JSON.parse(str)
|
10
|
+
def parse(str, opts)
|
11
|
+
JSON.parse(str, opts || {})
|
12
12
|
end
|
13
13
|
|
14
14
|
# @return [String]
|
15
|
-
def dump(tree)
|
16
|
-
JSON.generate(tree)
|
15
|
+
def dump(tree, opts)
|
16
|
+
JSON.generate(tree, opts || {})
|
17
17
|
end
|
18
18
|
|
19
19
|
end
|
@@ -6,13 +6,13 @@ module I18n::Tasks
|
|
6
6
|
extend self
|
7
7
|
|
8
8
|
# @return [Hash] locale tree
|
9
|
-
def parse(str)
|
10
|
-
YAML.load(str)
|
9
|
+
def parse(str, options)
|
10
|
+
YAML.load(str, options || {})
|
11
11
|
end
|
12
12
|
|
13
13
|
# @return [String]
|
14
|
-
def dump(tree)
|
15
|
-
tree.to_yaml
|
14
|
+
def dump(tree, options)
|
15
|
+
tree.to_yaml(options || {})
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
@@ -12,29 +12,31 @@ module I18n
|
|
12
12
|
|
13
13
|
protected
|
14
14
|
|
15
|
-
def load_file(
|
16
|
-
adapter_for(
|
17
|
-
|
18
|
-
)
|
15
|
+
def load_file(path)
|
16
|
+
adapter_name, adapter_pattern, adapter = adapter_for(path)
|
17
|
+
adapter_options = (config[adapter_name] || {})[:read]
|
18
|
+
adapter.parse(::File.read(path), adapter_options)
|
19
19
|
end
|
20
20
|
|
21
21
|
def write_tree(path, tree)
|
22
22
|
::File.open(path, 'w') { |f|
|
23
|
-
|
23
|
+
adapter_name, adapter_pattern, adapter = adapter_for(path)
|
24
|
+
adapter_options = (config[adapter_name] || {})[:write]
|
25
|
+
f.write(adapter.dump(tree.to_hash, adapter_options))
|
24
26
|
}
|
25
27
|
end
|
26
28
|
|
27
29
|
module ClassMethods
|
28
30
|
# @param pattern [String] File.fnmatch pattern
|
29
31
|
# @param adapter [responds to parse(string)->hash and dump(hash)->string]
|
30
|
-
def register_adapter(pattern, adapter)
|
31
|
-
(@fn_patterns ||=
|
32
|
+
def register_adapter(name, pattern, adapter)
|
33
|
+
(@fn_patterns ||= []) << [name, pattern, adapter]
|
32
34
|
end
|
33
35
|
|
34
36
|
def adapter_for(path)
|
35
|
-
@fn_patterns.detect { |pattern, adapter|
|
37
|
+
@fn_patterns.detect { |(name, pattern, adapter)|
|
36
38
|
::File.fnmatch(pattern, path)
|
37
|
-
}
|
39
|
+
} or raise "Adapter not found for #{path}. Registered adapters: #{@fn_patterns.inspect}"
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
@@ -5,8 +5,8 @@ require 'i18n/tasks/data/adapter/yaml_adapter'
|
|
5
5
|
module I18n::Tasks
|
6
6
|
module Data
|
7
7
|
class FileSystem < FileSystemBase
|
8
|
-
register_adapter '*.yml', Adapter::YamlAdapter
|
9
|
-
register_adapter '*.json', Adapter::JsonAdapter
|
8
|
+
register_adapter :yaml, '*.yml', Adapter::YamlAdapter
|
9
|
+
register_adapter :json, '*.json', Adapter::JsonAdapter
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/i18n/tasks/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
p #{t('ca.a')} #{t 'ca.b'} #{t "ca.c"}
|
2
2
|
p #{t 'ca.d'} #{t 'ca.f', i: 'world'} #{t 'ca.e', i: 'world'}
|
3
3
|
p #{t 'missing_in_es.a'} #{t 'same_in_es.a'} #{t 'blank_in_es.a'}
|
4
|
-
p = t 'used_but_missing.
|
4
|
+
p = t 'used_but_missing.key'
|
5
5
|
p = t 'x', scope: 'scoped'
|
6
6
|
p = t 'x', scope: [:very, :scoped]
|
7
7
|
p = t 'x', scope: [:scoped, code]
|
data/spec/i18n_tasks_spec.rb
CHANGED
@@ -8,7 +8,7 @@ describe 'i18n-tasks' do
|
|
8
8
|
|
9
9
|
describe 'missing' do
|
10
10
|
let (:expected_missing_keys) {
|
11
|
-
%w( en.used_but_missing.
|
11
|
+
%w( en.used_but_missing.key en.relative.index.missing
|
12
12
|
es.missing_in_es.a es.blank_in_es.a es.same_in_es.a
|
13
13
|
en.hash.pattern_missing.a en.hash.pattern_missing.b
|
14
14
|
en.missing_symbol_key en.missing_symbol.key_two en.missing_symbol.key_three )
|
@@ -76,13 +76,23 @@ describe 'i18n-tasks' do
|
|
76
76
|
end
|
77
77
|
|
78
78
|
describe 'add_missing' do
|
79
|
-
it 'default placeholder' do
|
79
|
+
it 'default placeholder: key.humanize for base_locale' do
|
80
80
|
in_test_app_dir {
|
81
81
|
expect(YAML.load_file('config/locales/en.yml')['en']['used_but_missing']).to be_nil
|
82
82
|
}
|
83
83
|
run_cmd :add_missing, locales: 'base'
|
84
84
|
in_test_app_dir {
|
85
|
-
expect(YAML.load_file('config/locales/en.yml')['en']['used_but_missing']['
|
85
|
+
expect(YAML.load_file('config/locales/en.yml')['en']['used_but_missing']['key']).to eq 'Key'
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'default placeholder: base_value for non-base locale' do
|
90
|
+
in_test_app_dir {
|
91
|
+
expect(YAML.load_file('config/locales/es.yml')['es']['missing_in_es']).to be_nil
|
92
|
+
}
|
93
|
+
run_cmd :add_missing, locales: 'es'
|
94
|
+
in_test_app_dir {
|
95
|
+
expect(YAML.load_file('config/locales/es.yml')['es']['missing_in_es']['a']).to eq 'EN_TEXT'
|
86
96
|
}
|
87
97
|
end
|
88
98
|
|
@@ -97,6 +107,16 @@ describe 'i18n-tasks' do
|
|
97
107
|
expect(YAML.load_file('config/locales/devise.es.yml')['es']['devise']['a']).to eq 'ES_TEXT'
|
98
108
|
}
|
99
109
|
end
|
110
|
+
|
111
|
+
it 'placeholder: value with base_value' do
|
112
|
+
in_test_app_dir {
|
113
|
+
expect(YAML.load_file('config/locales/es.yml')['es']['missing_in_es']).to be_nil
|
114
|
+
}
|
115
|
+
run_cmd :add_missing, locales: 'all', placeholder: 'TRME %{base_value}'
|
116
|
+
in_test_app_dir {
|
117
|
+
expect(YAML.load_file('config/locales/es.yml')['es']['missing_in_es']['a']).to eq 'TRME EN_TEXT'
|
118
|
+
}
|
119
|
+
end
|
100
120
|
end
|
101
121
|
|
102
122
|
describe 'config' do
|
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.3.
|
4
|
+
version: 0.3.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- glebm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: erubis
|