i18n-tasks 0.7.13 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +11 -0
- data/Gemfile +5 -3
- data/README.md +27 -126
- data/bin/i18n-tasks +2 -45
- data/config/locales/en.yml +11 -14
- data/config/locales/ru.yml +0 -3
- data/i18n-tasks.gemspec +0 -1
- data/lib/i18n/tasks.rb +14 -2
- data/lib/i18n/tasks/cli.rb +195 -0
- data/lib/i18n/tasks/command/collection.rb +2 -2
- data/lib/i18n/tasks/command/commander.rb +4 -24
- data/lib/i18n/tasks/command/commands/data.rb +17 -19
- data/lib/i18n/tasks/command/commands/eq_base.rb +2 -2
- data/lib/i18n/tasks/command/commands/health.rb +2 -2
- data/lib/i18n/tasks/command/commands/meta.rb +2 -2
- data/lib/i18n/tasks/command/commands/missing.rb +22 -15
- data/lib/i18n/tasks/command/commands/tree.rb +31 -35
- data/lib/i18n/tasks/command/commands/usages.rb +10 -11
- data/lib/i18n/tasks/command/commands/xlsx.rb +3 -3
- data/lib/i18n/tasks/command/dsl.rb +22 -7
- data/lib/i18n/tasks/command/option_parsers/enum.rb +53 -0
- data/lib/i18n/tasks/command/option_parsers/locale.rb +48 -0
- data/lib/i18n/tasks/command/options/common.rb +21 -31
- data/lib/i18n/tasks/command/options/data.rb +91 -0
- data/lib/i18n/tasks/command/options/locales.rb +22 -42
- data/lib/i18n/tasks/data/file_system_base.rb +2 -2
- data/lib/i18n/tasks/data/tree/node.rb +3 -3
- data/lib/i18n/tasks/data/tree/nodes.rb +3 -2
- data/lib/i18n/tasks/data/tree/siblings.rb +0 -1
- data/lib/i18n/tasks/logging.rb +9 -5
- data/lib/i18n/tasks/scanners/base_scanner.rb +6 -7
- data/lib/i18n/tasks/scanners/relative_keys.rb +1 -1
- data/lib/i18n/tasks/version.rb +1 -1
- data/spec/commands/data_commands_spec.rb +4 -4
- data/spec/commands/tree_commands_spec.rb +5 -5
- data/spec/google_translate_spec.rb +2 -3
- data/spec/i18n_spec.rb +0 -1
- data/spec/i18n_tasks_spec.rb +44 -23
- data/spec/readme_spec.rb +1 -1
- data/spec/support/test_codebase.rb +21 -10
- data/templates/config/i18n-tasks.yml +51 -39
- data/templates/rspec/i18n_spec.rb +0 -1
- metadata +6 -23
- data/lib/i18n/tasks/command/dsl/cmd.rb +0 -19
- data/lib/i18n/tasks/command/dsl/cmd_opt.rb +0 -19
- data/lib/i18n/tasks/command/dsl/enum_opt.rb +0 -45
- data/lib/i18n/tasks/command/options/enum_opt.rb +0 -50
- data/lib/i18n/tasks/command/options/list_opt.rb +0 -11
- data/lib/i18n/tasks/command/options/trees.rb +0 -81
- data/lib/i18n/tasks/slop_command.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ffa593b45f4a5d40746da85f93879fffce47423
|
4
|
+
data.tar.gz: 689a92c8047df2b9b56638e2a9f3b9c46c43e115
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d988d11c0b8690cb5596845a61b818eb9aa0702c6b84075c65b99a9fe0670c2435dfdc746d1cb5b92fb1f6929e9fc73b9bf7e8e8c76ab199a7c2517bfda782d
|
7
|
+
data.tar.gz: b071f96e5d5b912c95848ea6b363185b26a216fc968ae9657e1d28950b753f0933b9fc9c8f29e105a1ff2e8a4a324fc706ed33e53128c9074411980830628d2f
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 0.8.0
|
2
|
+
|
3
|
+
* Parse command line arguments with `optparse`. Remove dependency on Slop.
|
4
|
+
Simplified commands DSL: options are mostly passed directly to optparse.
|
5
|
+
* `search.relative_roots` default changed from from `%w(app/views)` to
|
6
|
+
`%w(app/views app/controllers app/helpers app/presenters)`.
|
7
|
+
* `add-missing` now adds keys detected in source to all locales (previously just base) [#134](https://github.com/glebm/i18n-tasks/issues/134).
|
8
|
+
* The default spec template no long requires `spec_helper` by default [Daniel Levenson](https://github.com/dleve123) [#135](https://github.com/glebm/i18n-tasks/pull/135).
|
9
|
+
* `search.exclude` now appends to and not overrides the default exclude list. More extensions excluded by default:
|
10
|
+
*.css, *.sass, *.scss, *.less, *.yml, and *.json. [#137](https://github.com/glebm/i18n-tasks/issues/137).
|
11
|
+
|
1
12
|
## 0.7.13
|
2
13
|
|
3
14
|
* Fix relative keys when controller name consists of more than one word by [Yuji Nakayama](https://github.com/yujinakayama) [#132](https://github.com/glebm/i18n-tasks/pull/132).
|
data/Gemfile
CHANGED
@@ -3,9 +3,11 @@ source 'https://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in i18n-tasks.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
unless ENV['TRAVIS']
|
7
|
+
group :development do
|
8
|
+
gem 'byebug', platforms: [:mri_21, :mri_22], require: false
|
9
|
+
gem 'rubinius-debugger', platform: :rbx, require: false
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
13
|
gem 'codeclimate-test-reporter', group: :test, require: nil
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
|
|
22
22
|
Add it to the Gemfile:
|
23
23
|
|
24
24
|
```ruby
|
25
|
-
gem 'i18n-tasks', '~> 0.
|
25
|
+
gem 'i18n-tasks', '~> 0.8.0'
|
26
26
|
```
|
27
27
|
|
28
28
|
Copy default [configuration file](#configuration) (optional):
|
@@ -177,7 +177,7 @@ For now, you can disable dynamic key inference by passing `-s` or `--strict` to
|
|
177
177
|
Configuration is read from `config/i18n-tasks.yml` or `config/i18n-tasks.yml.erb`.
|
178
178
|
Inspect configuration with `i18n-tasks config`.
|
179
179
|
|
180
|
-
Install the default config file with:
|
180
|
+
Install the [default config file][config] with:
|
181
181
|
|
182
182
|
```console
|
183
183
|
$ cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
|
@@ -188,60 +188,16 @@ Settings are compatible with Rails by default.
|
|
188
188
|
### Locales
|
189
189
|
|
190
190
|
By default, `base_locale` is set to `en` and `locales` are inferred from the paths to data files.
|
191
|
-
You can override these in the config
|
192
|
-
|
193
|
-
```yaml
|
194
|
-
# config/i18n-tasks.yml
|
195
|
-
base_locale: en
|
196
|
-
locales: [es, fr] # This includes base_locale by default
|
197
|
-
```
|
198
|
-
|
199
|
-
`internal_locale` controls the language i18n-tasks reports in. Locales available are `en` and `ru` (pull request to add more!).
|
200
|
-
|
201
|
-
```yaml
|
202
|
-
internal_locale: en
|
203
|
-
```
|
191
|
+
You can override these in the [config][config].
|
204
192
|
|
205
193
|
### Storage
|
206
194
|
|
207
195
|
The default data adapter supports YAML and JSON files.
|
208
196
|
|
209
|
-
```yaml
|
210
|
-
# config/i18n-tasks.yml
|
211
|
-
data:
|
212
|
-
# configure YAML / JSON serializer options
|
213
|
-
# passed directly to load / dump / parse / serialize.
|
214
|
-
yaml:
|
215
|
-
write:
|
216
|
-
# do not wrap lines at 80 characters (override default)
|
217
|
-
line_width: -1
|
218
|
-
```
|
219
|
-
|
220
197
|
#### Multiple locale files
|
221
198
|
|
222
|
-
|
223
|
-
|
224
|
-
`data.read` accepts a list of file globs to read from per-locale:
|
225
|
-
|
226
|
-
```
|
227
|
-
# config/i18n-tasks.yml
|
228
|
-
data:
|
229
|
-
read:
|
230
|
-
# read from namespaced files, e.g. simple_form.en.yml
|
231
|
-
- 'config/locales/*.%{locale}.yml'
|
232
|
-
# read from a gem (config is parsed with ERB first, then YAML)
|
233
|
-
- "<%= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
|
234
|
-
# default
|
235
|
-
- 'config/locales/%{locale}.yml'
|
236
|
-
```
|
237
|
-
|
238
|
-
#### Key pattern syntax
|
239
|
-
|
240
|
-
| syntax | description |
|
241
|
-
|:------------:|:----------------------------------------------------------|
|
242
|
-
| `*` | matches everything |
|
243
|
-
| `:` | matches a single key |
|
244
|
-
| `{a, b.c}` | match any in set, can use `:` and `*`, match is captured |
|
199
|
+
i18n-tasks can manage multiple translation files and read translations from other gems.
|
200
|
+
To find out more the `data` options in the [config][config].
|
245
201
|
|
246
202
|
For writing to locale files i18n-tasks provides 2 options.
|
247
203
|
|
@@ -266,7 +222,7 @@ data:
|
|
266
222
|
|
267
223
|
Conservative router keeps the keys where they are found, or infers the path from base locale.
|
268
224
|
If the key is completely new, conservative router will fall back to pattern router behaviour.
|
269
|
-
Conservative router is the default router.
|
225
|
+
Conservative router is the **default** router.
|
270
226
|
|
271
227
|
```
|
272
228
|
data:
|
@@ -276,55 +232,26 @@ data:
|
|
276
232
|
- 'config/locales/%{locale}.yml'
|
277
233
|
```
|
278
234
|
|
279
|
-
|
280
|
-
|
281
|
-
If you store data somewhere but in the filesystem, e.g. in the database or mongodb, you can implement a custom adapter.
|
282
|
-
Implement [a handful of methods][adapter-example], then set `data.adapter` to the class name; the rest of the `data` config is passed to the initializer.
|
235
|
+
##### Key pattern syntax
|
283
236
|
|
284
|
-
|
285
|
-
# config/i18n-tasks.yml
|
286
|
-
data:
|
287
|
-
# file_system is the default adapter, you can provide a custom class name here:
|
288
|
-
adapter: file_system
|
289
|
-
```
|
237
|
+
A special syntax similar to file glob patterns is used throughout i18n-tasks to match translation keys:
|
290
238
|
|
291
|
-
|
239
|
+
| syntax | description |
|
240
|
+
|:------------:|:----------------------------------------------------------|
|
241
|
+
| `*` | matches everything |
|
242
|
+
| `:` | matches a single key |
|
243
|
+
| `{a, b.c}` | match any in set, can use `:` and `*`, match is captured |
|
292
244
|
|
293
245
|
|
294
|
-
|
246
|
+
#### Custom adapters
|
295
247
|
|
296
|
-
|
297
|
-
|
298
|
-
# i18n usage search in source
|
299
|
-
search:
|
300
|
-
# search these directories (relative to your Rails.root directory, default: 'app/')
|
301
|
-
paths:
|
302
|
-
- 'app/'
|
303
|
-
- 'vendor/'
|
304
|
-
# paths for relative key resolution:
|
305
|
-
relative_roots:
|
306
|
-
# default:
|
307
|
-
- app/views
|
308
|
-
# add a custom one:
|
309
|
-
- app/views-mobile
|
310
|
-
# include only files matching this glob pattern (default: blank = include all files)
|
311
|
-
include:
|
312
|
-
- '*.rb'
|
313
|
-
- '*.html.*'
|
314
|
-
- '*.text.*'
|
315
|
-
# explicitly exclude files (default: exclude common binary files)
|
316
|
-
exclude:
|
317
|
-
- '*.js'
|
318
|
-
# you can override the default key regex pattern:
|
319
|
-
pattern: "\\bt[( ]\\s*(:?\".+?\"|:?'.+?'|:\\w+)"
|
320
|
-
# comments are ignored by default
|
321
|
-
ignore_lines:
|
322
|
-
- "^\\s*[#/](?!\\si18n-tasks-use)"
|
323
|
-
```
|
248
|
+
If you store data somewhere but in the filesystem, e.g. in the database or mongodb, you can implement a custom adapter.
|
249
|
+
If you have implemented a custom adapter please share it on [the wiki][wiki].
|
324
250
|
|
325
|
-
|
326
|
-
See this basic [pattern scanner](/lib/i18n/tasks/scanners/pattern_scanner.rb) for reference.
|
251
|
+
### Usage search
|
327
252
|
|
253
|
+
See the `search` section in the [config file][config] for all available configuration options.
|
254
|
+
An example of a custom scanner can be found here: https://github.com/glebm/i18n-tasks/issues/138#issuecomment-87255708.
|
328
255
|
|
329
256
|
### Fine-tuning
|
330
257
|
|
@@ -335,33 +262,8 @@ Add hints to static analysis with magic comment hints (lines starting with `(#|/
|
|
335
262
|
User.model_name.human
|
336
263
|
```
|
337
264
|
|
338
|
-
You can also explicitly ignore keys appearing in locale files
|
339
|
-
|
340
|
-
```yaml
|
341
|
-
# config/i18n-tasks.yml
|
342
|
-
# do not report these keys as unused
|
343
|
-
ignore_unused:
|
344
|
-
- category.*.db_name
|
345
|
-
|
346
|
-
# do not report these keys as missing (both on blank value and no key)
|
347
|
-
ignore_missing:
|
348
|
-
- devise.errors.unauthorized # ignore this key
|
349
|
-
- pagination.views.* # ignore the whole pattern
|
350
|
-
# E.g to ignore all Rails number / currency keys:
|
351
|
-
- 'number.{format, percentage.format, precision.format, human.format, currency.format}.{strip_insignificant_zeros,significant,delimiter}'
|
352
|
-
- 'time.{pm,am}'
|
353
|
-
|
354
|
-
# do not report these keys when they have the same value as the base locale version
|
355
|
-
ignore_eq_base:
|
356
|
-
all:
|
357
|
-
- common.ok
|
358
|
-
es,fr:
|
359
|
-
- common.brand
|
360
|
-
|
361
|
-
# do not report these keys ever
|
362
|
-
ignore:
|
363
|
-
- kaminari.*
|
364
|
-
```
|
265
|
+
You can also explicitly ignore keys appearing in locale files via `ignore*` settings.
|
266
|
+
See the [config file][config] to find out more.
|
365
267
|
|
366
268
|
<a name="translation-config"></a>
|
367
269
|
### Google Translate
|
@@ -383,7 +285,7 @@ translation:
|
|
383
285
|
api_key: <Google Translate API key>
|
384
286
|
```
|
385
287
|
|
386
|
-
## Interactive
|
288
|
+
## Interactive console
|
387
289
|
|
388
290
|
`i18n-tasks irb` starts an IRB session in i18n-tasks context. Type `guide` for more information.
|
389
291
|
|
@@ -395,12 +297,7 @@ Export missing and unused data to XLSX:
|
|
395
297
|
$ i18n-tasks xlsx-report
|
396
298
|
```
|
397
299
|
|
398
|
-
|
399
|
-
|
400
|
-
While i18n-tasks does not provide an HTML version of the report, you can add [one like this](https://gist.github.com/glebm/bdd3ab6d12d915f0c81b).
|
401
|
-
|
402
|
-
|
403
|
-
## Add New Tasks
|
300
|
+
## Add new tasks
|
404
301
|
|
405
302
|
Tasks that come with the gem are defined in [lib/i18n/tasks/command/commands](lib/i18n/tasks/command/commands).
|
406
303
|
|
@@ -430,6 +327,8 @@ Run with:
|
|
430
327
|
$ i18n-tasks my-task
|
431
328
|
```
|
432
329
|
|
330
|
+
See more examples of custom tasks [on the wiki](https://github.com/glebm/i18n-tasks/wiki#custom-tasks).
|
331
|
+
|
433
332
|
[MIT license]: /LICENSE.txt
|
434
333
|
[travis]: https://travis-ci.org/glebm/i18n-tasks
|
435
334
|
[badge-travis]: http://img.shields.io/travis/glebm/i18n-tasks.svg
|
@@ -439,6 +338,8 @@ $ i18n-tasks my-task
|
|
439
338
|
[badge-gemnasium]: https://gemnasium.com/glebm/i18n-tasks.svg
|
440
339
|
[code-climate]: https://codeclimate.com/github/glebm/i18n-tasks
|
441
340
|
[badge-code-climate]: http://img.shields.io/codeclimate/github/glebm/i18n-tasks.svg
|
341
|
+
[config]: https://github.com/glebm/i18n-tasks/blob/master/templates/config/i18n-tasks.yml
|
342
|
+
[wiki]: https://github.com/glebm/i18n-tasks/wiki "i18n-tasks wiki"
|
442
343
|
[i18n-gem]: https://github.com/svenfuchs/i18n "svenfuchs/i18n on Github"
|
443
344
|
[screenshot-find]: https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-usages.png "i18n-tasks find output screenshot"
|
444
345
|
[adapter-example]: https://github.com/glebm/i18n-tasks/blob/master/lib/i18n/tasks/data/file_system_base.rb
|
data/bin/i18n-tasks
CHANGED
@@ -8,49 +8,6 @@ if i18n_gem_config.respond_to?(:enforce_available_locales=) && i18n_gem_config.e
|
|
8
8
|
i18n_gem_config.enforce_available_locales = true
|
9
9
|
end
|
10
10
|
|
11
|
-
require 'i18n/tasks'
|
12
|
-
require 'i18n/tasks/commands'
|
13
|
-
require 'slop'
|
11
|
+
require 'i18n/tasks/cli'
|
14
12
|
|
15
|
-
|
16
|
-
if $stderr.isatty
|
17
|
-
$stderr.puts Term::ANSIColor.yellow('i18n-tasks: ' + message)
|
18
|
-
else
|
19
|
-
$stderr.puts message
|
20
|
-
end
|
21
|
-
exit exit_code
|
22
|
-
}
|
23
|
-
|
24
|
-
begin
|
25
|
-
ran = false
|
26
|
-
commander = ::I18n::Tasks::Commands
|
27
|
-
instance = commander.new
|
28
|
-
instance.set_internal_locale!
|
29
|
-
slop_adapter = ::I18n::Tasks::SlopCommand
|
30
|
-
args = ARGV.dup
|
31
|
-
args = ['--help'] if args.empty?
|
32
|
-
Slop.parse(args, help: true) do
|
33
|
-
on('-v', '--version', 'Print the version') {
|
34
|
-
puts I18n::Tasks::VERSION
|
35
|
-
exit
|
36
|
-
}
|
37
|
-
commander.cmds.each do |name, attr|
|
38
|
-
slop_dsl = slop_adapter.slop_command(name, attr) { |_name, opts|
|
39
|
-
begin
|
40
|
-
ran = true
|
41
|
-
instance.safe_run name, opts
|
42
|
-
rescue Errno::EPIPE
|
43
|
-
# ignore Errno::EPIPE which is throw when pipe breaks, e.g.:
|
44
|
-
# i18n-tasks missing | head
|
45
|
-
exit 1
|
46
|
-
end
|
47
|
-
}
|
48
|
-
instance_exec(&slop_dsl)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
rescue Slop::Error => e
|
52
|
-
err.call(e.message, 64)
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
err.call("Command unknown: #{args[0]}", 64) if !ran && args[0]
|
13
|
+
I18n::Tasks::CLI.start(ARGV)
|
data/config/locales/en.yml
CHANGED
@@ -7,20 +7,19 @@ en:
|
|
7
7
|
other: Added %{count} keys
|
8
8
|
cmd:
|
9
9
|
args:
|
10
|
-
default_all: 'Default: all'
|
11
10
|
default_text: 'Default: %{value}'
|
12
11
|
desc:
|
13
12
|
confirm: Confirm automatically
|
14
|
-
data_format: 'Data format: %{valid_text}.
|
13
|
+
data_format: 'Data format: %{valid_text}.'
|
15
14
|
key_pattern: Filter by key pattern (e.g. 'common.*')
|
16
15
|
key_pattern_to_rename: Full key (pattern) to rename. Required
|
17
|
-
locale:
|
18
|
-
locale_to_translate_from:
|
19
|
-
locales_filter:
|
20
|
-
missing_types: 'Filter by types: %{valid}
|
16
|
+
locale: Locale
|
17
|
+
locale_to_translate_from: Locale to translate from
|
18
|
+
locales_filter: "Locale(s) to process. Special: base"
|
19
|
+
missing_types: 'Filter by types: %{valid}'
|
21
20
|
new_key_name: New name, interpolates original name as %{key}. Required
|
22
21
|
nostdin: Do not read from stdin
|
23
|
-
out_format: 'Output format: %{valid_text}
|
22
|
+
out_format: 'Output format: %{valid_text}'
|
24
23
|
pattern_router: 'Use pattern router: keys moved per config data.write'
|
25
24
|
strict: Do not infer dynamic key usage such as `t("category.\#{category.name}")`
|
26
25
|
value: 'Value. Interpolates: %{value}, %{human_key}, %{value_or_human_key}'
|
@@ -54,18 +53,16 @@ en:
|
|
54
53
|
- Well done!
|
55
54
|
- Perfect!
|
56
55
|
enum_list_opt:
|
57
|
-
desc: 'Comma-separated list of: %{valid_text}. %{default_text}'
|
58
56
|
invalid: "%{invalid} is not in: %{valid}."
|
59
57
|
enum_opt:
|
60
|
-
desc: "%{valid_text}. %{default_text}"
|
61
58
|
invalid: "%{invalid} is not one of: %{valid}."
|
62
59
|
errors:
|
63
|
-
invalid_format: '
|
64
|
-
invalid_locale:
|
60
|
+
invalid_format: 'invalid format: %{invalid}. valid: %{valid}.'
|
61
|
+
invalid_locale: 'invalid locale: %{invalid}'
|
65
62
|
invalid_missing_type:
|
66
|
-
one: '
|
67
|
-
other: '
|
68
|
-
pass_forest:
|
63
|
+
one: 'invalid type: %{invalid}. valid: %{valid}.'
|
64
|
+
other: 'unknown types: %{invalid}. valid: %{valid}.'
|
65
|
+
pass_forest: pass locale forest
|
69
66
|
common:
|
70
67
|
base_value: Base Value
|
71
68
|
continue_q: Continue?
|
data/config/locales/ru.yml
CHANGED
@@ -5,7 +5,6 @@ ru:
|
|
5
5
|
added: "Добавлены ключи (%{count})"
|
6
6
|
cmd:
|
7
7
|
args:
|
8
|
-
default_all: "По умолчанию: все"
|
9
8
|
default_text: "По умолчанию: %{value}"
|
10
9
|
desc:
|
11
10
|
confirm: "Подтвердить автоматом"
|
@@ -53,10 +52,8 @@ ru:
|
|
53
52
|
- "Отлично!"
|
54
53
|
- "Прекрасно!"
|
55
54
|
enum_list_opt:
|
56
|
-
desc: "Разделенных запятыми список: %{valid_text}. %{default_text}"
|
57
55
|
invalid: "%{invalid} не в: %{valid}."
|
58
56
|
enum_opt:
|
59
|
-
desc: "%{valid_text}. %{default_text}"
|
60
57
|
invalid: "%{invalid} не является одним из: %{valid}."
|
61
58
|
errors:
|
62
59
|
invalid_format: "Неизвестный формат %{invalid}. Форматы: %{valid}."
|
data/i18n-tasks.gemspec
CHANGED
@@ -39,7 +39,6 @@ TEXT
|
|
39
39
|
s.add_dependency 'term-ansicolor'
|
40
40
|
s.add_dependency 'terminal-table'
|
41
41
|
s.add_dependency 'highline'
|
42
|
-
s.add_dependency 'slop', '~> 3.5'
|
43
42
|
s.add_dependency 'i18n'
|
44
43
|
s.add_development_dependency 'axlsx', '~> 2.0'
|
45
44
|
s.add_development_dependency 'bundler', '~> 1.3'
|
data/lib/i18n/tasks.rb
CHANGED
@@ -2,11 +2,22 @@
|
|
2
2
|
# define all the modules to be able to use ::
|
3
3
|
module I18n
|
4
4
|
module Tasks
|
5
|
+
class << self
|
6
|
+
def gem_path
|
7
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
8
|
+
end
|
5
9
|
|
6
|
-
|
7
|
-
|
10
|
+
def verbose?
|
11
|
+
@verbose
|
12
|
+
end
|
13
|
+
|
14
|
+
def verbose=(value)
|
15
|
+
@verbose = value
|
16
|
+
end
|
8
17
|
end
|
9
18
|
|
19
|
+
@verbose = !!ENV['VERBOSE']
|
20
|
+
|
10
21
|
module Data
|
11
22
|
end
|
12
23
|
end
|
@@ -15,6 +26,7 @@ end
|
|
15
26
|
|
16
27
|
require 'active_support/core_ext/hash'
|
17
28
|
require 'active_support/core_ext/string'
|
29
|
+
require 'active_support/core_ext/array/access'
|
18
30
|
require 'active_support/core_ext/module/delegation'
|
19
31
|
require 'active_support/core_ext/object/try'
|
20
32
|
require 'active_support/core_ext/object/blank'
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'i18n/tasks'
|
2
|
+
require 'i18n/tasks/commands'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
class I18n::Tasks::CLI
|
6
|
+
include ::I18n::Tasks::Logging
|
7
|
+
|
8
|
+
def self.start(argv)
|
9
|
+
new.start(argv)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
end
|
14
|
+
|
15
|
+
def start(argv)
|
16
|
+
auto_output_coloring do
|
17
|
+
begin
|
18
|
+
run(argv)
|
19
|
+
rescue OptionParser::ParseError => e
|
20
|
+
error e.message, 64
|
21
|
+
rescue I18n::Tasks::CommandError => e
|
22
|
+
begin
|
23
|
+
error e.message, 78
|
24
|
+
ensure
|
25
|
+
log_verbose e.backtrace * "\n"
|
26
|
+
end
|
27
|
+
rescue Errno::EPIPE
|
28
|
+
# ignore Errno::EPIPE which is throw when pipe breaks, e.g.:
|
29
|
+
# i18n-tasks missing | head
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
rescue ExecutionError => e
|
34
|
+
exit e.exit_code
|
35
|
+
end
|
36
|
+
|
37
|
+
def run(argv)
|
38
|
+
name, *options = parse!(argv.dup)
|
39
|
+
context.run(name, *options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def context
|
43
|
+
@context ||= ::I18n::Tasks::Commands.new.tap(&:set_internal_locale!)
|
44
|
+
end
|
45
|
+
|
46
|
+
def commands
|
47
|
+
@commands ||= ::I18n::Tasks::Commands.cmds.transform_keys { |k| k.to_s.tr('_', '-') }
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def parse!(argv)
|
53
|
+
command = parse_command! argv
|
54
|
+
options = optparse! command, argv
|
55
|
+
parse_options! options, command, argv
|
56
|
+
[command.tr('-', '_'), options.update(arguments: argv)]
|
57
|
+
end
|
58
|
+
|
59
|
+
def optparse!(command, argv)
|
60
|
+
if command
|
61
|
+
optparse_command!(command, argv)
|
62
|
+
else
|
63
|
+
optparse_no_command!(argv)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def optparse_command!(command, argv)
|
68
|
+
cmd_conf = commands[command]
|
69
|
+
flags = cmd_conf[:args].dup
|
70
|
+
options = {}
|
71
|
+
OptionParser.new("Usage: #{program_name} #{command} [options] #{cmd_conf[:pos]}".strip) do |op|
|
72
|
+
flags.each do |flag|
|
73
|
+
op.on(*optparse_args(flag)) { |v| options[option_name(flag)] = v }
|
74
|
+
end
|
75
|
+
verbose_option op
|
76
|
+
help_option op
|
77
|
+
end.parse!(argv)
|
78
|
+
options
|
79
|
+
end
|
80
|
+
|
81
|
+
def optparse_no_command!(argv)
|
82
|
+
argv << '--help' if argv.empty?
|
83
|
+
OptionParser.new("Usage: #{program_name} [command] [options]") do |op|
|
84
|
+
op.on('-v', '--version', 'Print the version') do
|
85
|
+
puts I18n::Tasks::VERSION
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
help_option op
|
89
|
+
commands_summary op
|
90
|
+
end.parse!(argv)
|
91
|
+
end
|
92
|
+
|
93
|
+
def allow_help_arg_first!(argv)
|
94
|
+
# allow `i18n-tasks --help command` in addition to `i18n-tasks command --help`
|
95
|
+
argv[0], argv[1] = argv[1], argv[0] if %w(-h --help).include?(argv[0]) && argv[1] && !argv[1].start_with?('-')
|
96
|
+
end
|
97
|
+
|
98
|
+
def parse_command!(argv)
|
99
|
+
allow_help_arg_first! argv
|
100
|
+
if argv[0] && !argv[0].start_with?('-')
|
101
|
+
if commands.keys.include?(argv[0])
|
102
|
+
argv.shift
|
103
|
+
else
|
104
|
+
error "unknown command: #{argv[0]}", 64
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def verbose_option(op)
|
110
|
+
op.on('--verbose', 'Verbose output') {
|
111
|
+
::I18n::Tasks.verbose = true
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def help_option(op)
|
116
|
+
op.on('-h', '--help', 'Show this message') do
|
117
|
+
$stderr.puts op
|
118
|
+
exit
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# @param [OptionParser] op
|
123
|
+
def commands_summary(op)
|
124
|
+
op.separator ''
|
125
|
+
op.separator 'Available commands:'
|
126
|
+
op.separator ''
|
127
|
+
commands.each do |cmd, cmd_conf|
|
128
|
+
op.separator " #{cmd.ljust(op.summary_width + 1, ' ')}#{try_call cmd_conf[:desc]}"
|
129
|
+
end
|
130
|
+
op.separator ''
|
131
|
+
op.separator 'See `i18n-tasks <command> --help` for more information on a specific command.'
|
132
|
+
end
|
133
|
+
|
134
|
+
def optparse_args(flag)
|
135
|
+
args = flag.dup
|
136
|
+
args.map! { |v| try_call v }
|
137
|
+
conf = args.extract_options!
|
138
|
+
if conf.key?(:default)
|
139
|
+
args[-1] = "#{args[-1]}. #{I18n.t('i18n_tasks.cmd.args.default_text', value: conf[:default])}"
|
140
|
+
end
|
141
|
+
args
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_options!(options, command, argv)
|
145
|
+
commands[command][:args].each do |flag|
|
146
|
+
name = option_name flag
|
147
|
+
options[name] = parse_option flag, options[name], argv, self.context
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def parse_option(flag, val, argv, context)
|
152
|
+
conf = flag.last.is_a?(Hash) ? flag.last : {}
|
153
|
+
if conf[:consume_positional]
|
154
|
+
val = Array(val) + Array(flag.include?(Array) ? argv.flat_map { |x| x.split(',') } : argv)
|
155
|
+
end
|
156
|
+
val = conf[:default] if val.nil? && conf.key?(:default)
|
157
|
+
val = conf[:parser].call(val, context) if conf.key?(:parser)
|
158
|
+
val
|
159
|
+
end
|
160
|
+
|
161
|
+
def option_name(flag)
|
162
|
+
flag.detect { |f| f.start_with?('--') }.sub(/\A--/, '').sub(/[^\-\w].*\z/, '').to_sym
|
163
|
+
end
|
164
|
+
|
165
|
+
def try_call(v)
|
166
|
+
if v.respond_to? :call
|
167
|
+
v.call
|
168
|
+
else
|
169
|
+
v
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def error(message, exit_code)
|
174
|
+
log_error message
|
175
|
+
fail ExecutionError.new(message, exit_code)
|
176
|
+
end
|
177
|
+
|
178
|
+
class ExecutionError < Exception
|
179
|
+
attr_reader :exit_code
|
180
|
+
|
181
|
+
def initialize(message, exit_code)
|
182
|
+
super(message)
|
183
|
+
@exit_code = exit_code
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def auto_output_coloring(coloring = ENV['I18N_TASKS_COLOR'] || STDOUT.isatty)
|
188
|
+
coloring_was = Term::ANSIColor.coloring?
|
189
|
+
Term::ANSIColor.coloring = coloring
|
190
|
+
yield
|
191
|
+
ensure
|
192
|
+
Term::ANSIColor.coloring = coloring_was
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|