i18n-tasks 0.7.13 → 0.8.0
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 +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
@@ -0,0 +1,53 @@
|
|
1
|
+
module I18n::Tasks
|
2
|
+
module Command
|
3
|
+
module OptionParsers
|
4
|
+
module Enum
|
5
|
+
class Parser
|
6
|
+
DEFAULT_ERROR = proc { |invalid, valid|
|
7
|
+
I18n.t('i18n_tasks.cmd.enum_opt.invalid', invalid: invalid, valid: valid * ', ')
|
8
|
+
}
|
9
|
+
|
10
|
+
def initialize(valid, error_message = DEFAULT_ERROR)
|
11
|
+
@valid = valid.map(&:to_s)
|
12
|
+
@error_message = error_message
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(value, *)
|
16
|
+
return @valid.first unless value.present?
|
17
|
+
if @valid.include?(value)
|
18
|
+
value
|
19
|
+
else
|
20
|
+
raise CommandError.new @error_message.call(value, @valid)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class ListParser
|
26
|
+
DEFAULT_ERROR = proc { |invalid, valid|
|
27
|
+
I18n.t('i18n_tasks.cmd.enum_list_opt.invalid', invalid: invalid * ', ', valid: valid * ', ')
|
28
|
+
}
|
29
|
+
|
30
|
+
def initialize(valid, error_message = DEFAULT_ERROR)
|
31
|
+
@valid = valid.map(&:to_s)
|
32
|
+
@error_message = error_message
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(values, *)
|
36
|
+
values = Array(values)
|
37
|
+
return @valid if values == %w(all)
|
38
|
+
invalid = values - @valid
|
39
|
+
if invalid.empty?
|
40
|
+
if values.empty?
|
41
|
+
@valid
|
42
|
+
else
|
43
|
+
values
|
44
|
+
end
|
45
|
+
else
|
46
|
+
raise CommandError.new @error_message.call(invalid, @valid)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module I18n::Tasks
|
2
|
+
module Command
|
3
|
+
module OptionParsers
|
4
|
+
module Locale
|
5
|
+
module Validator
|
6
|
+
VALID_LOCALE_RE = /\A\w[\w\-\.]*\z/i
|
7
|
+
|
8
|
+
def validate!(locale)
|
9
|
+
if VALID_LOCALE_RE !~ locale
|
10
|
+
raise CommandError.new(I18n.t('i18n_tasks.cmd.errors.invalid_locale', invalid: locale))
|
11
|
+
end
|
12
|
+
locale
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Parser
|
17
|
+
module_function
|
18
|
+
extend Validator
|
19
|
+
|
20
|
+
# @param [#base_locale, #locales] context
|
21
|
+
def call(val, context)
|
22
|
+
if val.blank? || val == 'base'
|
23
|
+
context.base_locale
|
24
|
+
else
|
25
|
+
validate! val
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module ListParser
|
31
|
+
module_function
|
32
|
+
extend Validator
|
33
|
+
|
34
|
+
# @param [#base_locale,#locales] context
|
35
|
+
def call(vals, context)
|
36
|
+
if vals == %w(all) || vals.blank?
|
37
|
+
context.locales
|
38
|
+
else
|
39
|
+
vals.map { |v| v == 'base' ? context.base_locale : v }
|
40
|
+
end.tap do |locales|
|
41
|
+
locales.each { |locale| validate! locale }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,46 +1,36 @@
|
|
1
|
-
require 'i18n/tasks/command/options/enum_opt'
|
2
|
-
require 'i18n/tasks/command/options/list_opt'
|
3
1
|
|
4
2
|
module I18n::Tasks
|
5
3
|
module Command
|
6
4
|
module Options
|
7
5
|
module Common
|
8
6
|
include Command::DSL
|
9
|
-
include Options::EnumOpt
|
10
|
-
include Options::ListOpt
|
11
7
|
|
12
|
-
|
8
|
+
arg :nostdin,
|
9
|
+
'-S',
|
10
|
+
'--nostdin',
|
11
|
+
t('i18n_tasks.cmd.args.desc.nostdin')
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
desc:
|
18
|
-
conf: {default: false}
|
19
|
-
}
|
13
|
+
arg :confirm,
|
14
|
+
'-y',
|
15
|
+
'--confirm',
|
16
|
+
desc: t('i18n_tasks.cmd.args.desc.confirm')
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
conf: {default: false}
|
26
|
-
}
|
18
|
+
arg :pattern,
|
19
|
+
'-p',
|
20
|
+
'--pattern PATTERN',
|
21
|
+
t('i18n_tasks.cmd.args.desc.key_pattern')
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
conf: {argument: true, optional: false}
|
33
|
-
}
|
23
|
+
arg :value,
|
24
|
+
'-v',
|
25
|
+
'--value VALUE',
|
26
|
+
t('i18n_tasks.cmd.args.desc.value')
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
desc: t('i18n_tasks.cmd.args.desc.value'),
|
39
|
-
conf: {argument: true, optional: false}
|
40
|
-
}
|
28
|
+
def arg_or_pos!(key, opts)
|
29
|
+
opts[key] ||= opts[:arguments].try(:shift)
|
30
|
+
end
|
41
31
|
|
42
|
-
def
|
43
|
-
|
32
|
+
def pos_or_stdin!(opts)
|
33
|
+
opts[:arguments].try(:shift) || $stdin.read
|
44
34
|
end
|
45
35
|
end
|
46
36
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'i18n/tasks/command/option_parsers/enum'
|
2
|
+
|
3
|
+
module I18n::Tasks
|
4
|
+
module Command
|
5
|
+
module Options
|
6
|
+
module Data
|
7
|
+
include Command::DSL
|
8
|
+
|
9
|
+
DATA_FORMATS = %w(yaml json keys)
|
10
|
+
OUT_FORMATS = ['terminal-table', *DATA_FORMATS, 'inspect']
|
11
|
+
|
12
|
+
format_arg = proc { |type, values|
|
13
|
+
default = values.first
|
14
|
+
arg type,
|
15
|
+
'-f',
|
16
|
+
'--format FORMAT',
|
17
|
+
values,
|
18
|
+
{'yml' => 'yaml'},
|
19
|
+
t("i18n_tasks.cmd.args.desc.#{type}", valid_text: values * ', ', default_text: default),
|
20
|
+
parser: OptionParsers::Enum::Parser.new(values,
|
21
|
+
proc { |value, valid|
|
22
|
+
I18n.t('i18n_tasks.cmd.errors.invalid_format',
|
23
|
+
invalid: value, valid: valid * ', ') })
|
24
|
+
}
|
25
|
+
|
26
|
+
# i18n-tasks-use t('i18n_tasks.cmd.args.desc.data_format')
|
27
|
+
format_arg.call(:data_format, DATA_FORMATS)
|
28
|
+
|
29
|
+
# i18n-tasks-use t('i18n_tasks.cmd.args.desc.out_format')
|
30
|
+
format_arg.call(:out_format, OUT_FORMATS)
|
31
|
+
|
32
|
+
def forest_pos_or_stdin!(opt, format = opt[:format])
|
33
|
+
parse_forest(pos_or_stdin!(opt), format)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Array<I18n::Tasks::Data::Tree::Siblings>] trees read from stdin and positional arguments
|
37
|
+
def forests_stdin_and_pos!(opt, num = false, format = opt[:format])
|
38
|
+
args = opt[:arguments] || []
|
39
|
+
if opt[:nostdin]
|
40
|
+
sources = []
|
41
|
+
else
|
42
|
+
sources = [$stdin.read]
|
43
|
+
num -= 1 if num
|
44
|
+
end
|
45
|
+
if num
|
46
|
+
num.times { sources << args.shift }
|
47
|
+
else
|
48
|
+
sources += args
|
49
|
+
args.clear
|
50
|
+
end
|
51
|
+
sources.map { |src| parse_forest(src, format) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def merge_forests_stdin_and_pos!(opt)
|
55
|
+
forests_stdin_and_pos!(opt).inject(i18n.empty_forest) { |result, forest|
|
56
|
+
result.merge! forest
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_forest(src, format)
|
61
|
+
if !src
|
62
|
+
raise CommandError.new(I18n.t('i18n_tasks.cmd.errors.pass_forest'))
|
63
|
+
end
|
64
|
+
if format == 'keys'
|
65
|
+
::I18n::Tasks::Data::Tree::Siblings.from_key_names parse_keys(src)
|
66
|
+
else
|
67
|
+
::I18n::Tasks::Data::Tree::Siblings.from_nested_hash i18n.data.adapter_parse(src, format)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_keys(src)
|
72
|
+
Array(src).compact.flat_map { |v| v.strip.split(/\s*[,\s\n]\s*/) }.map(&:presence).compact
|
73
|
+
end
|
74
|
+
|
75
|
+
def print_forest(forest, opts, version = :show_tree)
|
76
|
+
format = opts[:format].to_s
|
77
|
+
case format
|
78
|
+
when 'terminal-table'
|
79
|
+
terminal_report.send(version, forest)
|
80
|
+
when 'inspect'
|
81
|
+
puts forest.inspect
|
82
|
+
when 'keys'
|
83
|
+
puts forest.key_names(root: true)
|
84
|
+
when *DATA_FORMATS
|
85
|
+
puts i18n.data.adapter_dump forest.to_hash(true), format
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -1,53 +1,33 @@
|
|
1
|
+
require 'i18n/tasks/command/option_parsers/locale'
|
2
|
+
|
1
3
|
module I18n::Tasks
|
2
4
|
module Command
|
3
5
|
module Options
|
4
6
|
module Locales
|
5
7
|
include Command::DSL
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
cmd_opt :locale, {
|
16
|
-
short: :l,
|
17
|
-
long: :locale=,
|
18
|
-
desc: t('i18n_tasks.cmd.args.desc.locale'),
|
19
|
-
conf: {default: 'base', argument: true, optional: false},
|
20
|
-
parse: :parse_locale
|
21
|
-
}
|
22
|
-
|
23
|
-
cmd_opt :locale_to_translate_from, cmd_opt(:locale).merge(
|
24
|
-
short: :f,
|
25
|
-
long: :from=,
|
26
|
-
desc: t('i18n_tasks.cmd.args.desc.locale_to_translate_from'))
|
27
|
-
|
28
|
-
def parse_locales(opt, key = :locales)
|
29
|
-
argv = Array(opt[:arguments]) + Array(opt[key])
|
30
|
-
locales = if argv == ['all'] || argv == 'all' || argv.blank?
|
31
|
-
i18n.locales
|
32
|
-
else
|
33
|
-
explode_list_opt(argv).map { |v| v == 'base' ? base_locale : v }
|
34
|
-
end
|
35
|
-
locales.each { |locale| validate_locale!(locale) }
|
36
|
-
log_verbose "locales for the command are #{locales.inspect}"
|
37
|
-
opt[key] = locales
|
38
|
-
end
|
9
|
+
arg :locales,
|
10
|
+
'-l',
|
11
|
+
'--locales en,es,ru',
|
12
|
+
Array,
|
13
|
+
t('i18n_tasks.cmd.args.desc.locales_filter'),
|
14
|
+
parser: OptionParsers::Locale::ListParser,
|
15
|
+
default: 'all',
|
16
|
+
consume_positional: true
|
39
17
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
18
|
+
arg :locale,
|
19
|
+
'-l',
|
20
|
+
'--locale en',
|
21
|
+
t('i18n_tasks.cmd.args.desc.locale'),
|
22
|
+
parser: OptionParsers::Locale::Parser,
|
23
|
+
default: 'base'
|
45
24
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
25
|
+
arg :locale_to_translate_from,
|
26
|
+
'-f',
|
27
|
+
'--from en',
|
28
|
+
t('i18n_tasks.cmd.args.desc.locale_to_translate_from'),
|
29
|
+
parser: OptionParsers::Locale::Parser,
|
30
|
+
default: 'base'
|
51
31
|
end
|
52
32
|
end
|
53
33
|
end
|
@@ -25,9 +25,9 @@ module I18n::Tasks
|
|
25
25
|
locales = config[:locales].presence
|
26
26
|
@locales = LocaleList.normalize_locale_list(locales || available_locales, base_locale, true)
|
27
27
|
if locales.present?
|
28
|
-
log_verbose "
|
28
|
+
log_verbose "locales read from config #{@locales * ', '}"
|
29
29
|
else
|
30
|
-
log_verbose "
|
30
|
+
log_verbose "locales inferred from data: #{@locales * ', '}"
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -4,6 +4,9 @@ require 'i18n/tasks/data/tree/traversal'
|
|
4
4
|
require 'i18n/tasks/data/tree/siblings'
|
5
5
|
module I18n::Tasks::Data::Tree
|
6
6
|
class Node
|
7
|
+
include Enumerable
|
8
|
+
include Traversal
|
9
|
+
|
7
10
|
attr_accessor :value
|
8
11
|
attr_reader :key, :children, :parent
|
9
12
|
|
@@ -42,9 +45,6 @@ module I18n::Tasks::Data::Tree
|
|
42
45
|
self
|
43
46
|
end
|
44
47
|
|
45
|
-
include Enumerable
|
46
|
-
include Traversal
|
47
|
-
|
48
48
|
def value_or_children_hash
|
49
49
|
leaf? ? value : children.try(:to_hash)
|
50
50
|
end
|
@@ -4,6 +4,9 @@ require 'i18n/tasks/data/tree/traversal'
|
|
4
4
|
module I18n::Tasks::Data::Tree
|
5
5
|
# A list of nodes
|
6
6
|
class Nodes
|
7
|
+
include Enumerable
|
8
|
+
include Traversal
|
9
|
+
|
7
10
|
attr_reader :list
|
8
11
|
|
9
12
|
def initialize(opts = {})
|
@@ -11,8 +14,6 @@ module I18n::Tasks::Data::Tree
|
|
11
14
|
end
|
12
15
|
|
13
16
|
delegate :each, :present?, :empty?, :blank?, :size, :to_a, to: :@list
|
14
|
-
include Enumerable
|
15
|
-
include Traversal
|
16
17
|
|
17
18
|
def to_nodes
|
18
19
|
self
|
data/lib/i18n/tasks/logging.rb
CHANGED
@@ -3,24 +3,28 @@ module I18n::Tasks::Logging
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
def warn_deprecated(message)
|
6
|
-
log_stderr Term::ANSIColor.yellow Term::ANSIColor.bold "
|
6
|
+
log_stderr Term::ANSIColor.yellow Term::ANSIColor.bold "#{program_name}: [DEPRECATED] #{message}"
|
7
7
|
end
|
8
8
|
|
9
9
|
def log_verbose(message)
|
10
|
-
if
|
11
|
-
log_stderr Term::ANSIColor.
|
10
|
+
if ::I18n::Tasks.verbose?
|
11
|
+
log_stderr Term::ANSIColor.bright_blue(message)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
def log_warn(message)
|
16
|
-
log_stderr Term::ANSIColor.yellow "
|
16
|
+
log_stderr Term::ANSIColor.yellow "#{program_name}: [WARN] #{message}"
|
17
17
|
end
|
18
18
|
|
19
19
|
def log_error(message)
|
20
|
-
log_stderr Term::ANSIColor.red Term::ANSIColor.bold "
|
20
|
+
log_stderr Term::ANSIColor.red Term::ANSIColor.bold "#{program_name}: #{message}"
|
21
21
|
end
|
22
22
|
|
23
23
|
def log_stderr(*args)
|
24
24
|
$stderr.puts(*args)
|
25
25
|
end
|
26
|
+
|
27
|
+
def program_name
|
28
|
+
@program_name ||= File.basename($PROGRAM_NAME)
|
29
|
+
end
|
26
30
|
end
|
@@ -10,17 +10,15 @@ module I18n::Tasks::Scanners
|
|
10
10
|
|
11
11
|
attr_reader :config, :key_filter, :ignore_lines_res
|
12
12
|
|
13
|
+
ALWAYS_EXCLUDE = %w(*.jpg *.png *.gif *.svg *.ico *.eot *.ttf *.woff *.woff2 *.pdf
|
14
|
+
*.css *.sass *.scss *.less *.yml *.json)
|
15
|
+
|
13
16
|
def initialize(config = {})
|
14
17
|
@config = config.dup.with_indifferent_access.tap do |conf|
|
15
|
-
conf[:relative_roots] = %w(app/views) if conf[:relative_roots].blank?
|
18
|
+
conf[:relative_roots] = %w(app/views app/controllers app/helpers app/presenters) if conf[:relative_roots].blank?
|
16
19
|
conf[:paths] = %w(app/) if conf[:paths].blank?
|
17
20
|
conf[:include] = Array(conf[:include]) if conf[:include].present?
|
18
|
-
|
19
|
-
conf[:exclude] = Array(conf[:exclude])
|
20
|
-
else
|
21
|
-
# exclude common binary extensions by default (images and fonts)
|
22
|
-
conf[:exclude] = %w(*.jpg *.png *.gif *.svg *.ico *.eot *.ttf *.woff *.woff2 *.pdf)
|
23
|
-
end
|
21
|
+
conf[:exclude] = Array(conf[:exclude]) + ALWAYS_EXCLUDE
|
24
22
|
# Regexps for lines to ignore per extension
|
25
23
|
if conf[:ignore_lines] && !conf[:ignore_lines].is_a?(Hash)
|
26
24
|
warn_deprecated "search.ignore_lines must be a Hash, found #{conf[:ignore_lines].class.name}"
|
@@ -28,6 +26,7 @@ module I18n::Tasks::Scanners
|
|
28
26
|
end
|
29
27
|
conf[:ignore_lines] ||= {
|
30
28
|
'rb' => %q(^\s*#(?!\si18n-tasks-use)),
|
29
|
+
'opal' => %q(^\s*#(?!\si18n-tasks-use)),
|
31
30
|
'haml' => %q(^\s*-\s*#(?!\si18n-tasks-use)),
|
32
31
|
'slim' => %q(^\s*(?:-#|/)(?!\si18n-tasks-use)),
|
33
32
|
'coffee' => %q(^\s*#(?!\si18n-tasks-use)),
|