i18n-tasks 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/Gemfile +2 -1
  4. data/README.md +80 -78
  5. data/bin/i18n-tasks +24 -30
  6. data/config/i18n-tasks.yml +87 -0
  7. data/config/locales/en.yml +95 -0
  8. data/i18n-tasks.gemspec +1 -0
  9. data/lib/i18n/tasks.rb +10 -0
  10. data/lib/i18n/tasks/base_task.rb +6 -2
  11. data/lib/i18n/tasks/command/collection.rb +18 -0
  12. data/lib/i18n/tasks/command/commander.rb +72 -0
  13. data/lib/i18n/tasks/command/commands/data.rb +73 -0
  14. data/lib/i18n/tasks/command/commands/eq_base.rb +20 -0
  15. data/lib/i18n/tasks/command/commands/health.rb +26 -0
  16. data/lib/i18n/tasks/command/commands/meta.rb +35 -0
  17. data/lib/i18n/tasks/command/commands/missing.rb +73 -0
  18. data/lib/i18n/tasks/command/commands/tree.rb +92 -0
  19. data/lib/i18n/tasks/command/commands/usages.rb +70 -0
  20. data/lib/i18n/tasks/command/commands/xlsx.rb +27 -0
  21. data/lib/i18n/tasks/command/dsl.rb +27 -0
  22. data/lib/i18n/tasks/command/dsl/cmd.rb +19 -0
  23. data/lib/i18n/tasks/command/dsl/cmd_opt.rb +19 -0
  24. data/lib/i18n/tasks/command/dsl/enum_opt.rb +26 -0
  25. data/lib/i18n/tasks/command/options/common.rb +48 -0
  26. data/lib/i18n/tasks/command/options/enum_opt.rb +44 -0
  27. data/lib/i18n/tasks/command/options/list_opt.rb +11 -0
  28. data/lib/i18n/tasks/command/options/locales.rb +47 -0
  29. data/lib/i18n/tasks/command/options/trees.rb +101 -0
  30. data/lib/i18n/tasks/command_error.rb +3 -0
  31. data/lib/i18n/tasks/commands.rb +22 -169
  32. data/lib/i18n/tasks/configuration.rb +1 -16
  33. data/lib/i18n/tasks/console_context.rb +1 -1
  34. data/lib/i18n/tasks/data.rb +13 -8
  35. data/lib/i18n/tasks/data/file_formats.rb +29 -18
  36. data/lib/i18n/tasks/data/file_system_base.rb +35 -4
  37. data/lib/i18n/tasks/data/router/conservative_router.rb +18 -11
  38. data/lib/i18n/tasks/data/tree/node.rb +5 -15
  39. data/lib/i18n/tasks/data/tree/nodes.rb +0 -3
  40. data/lib/i18n/tasks/data/tree/siblings.rb +32 -2
  41. data/lib/i18n/tasks/data/tree/traversal.rb +117 -96
  42. data/lib/i18n/tasks/google_translation.rb +25 -25
  43. data/lib/i18n/tasks/html_keys.rb +10 -0
  44. data/lib/i18n/tasks/key_pattern_matching.rb +1 -0
  45. data/lib/i18n/tasks/locale_list.rb +19 -0
  46. data/lib/i18n/tasks/missing_keys.rb +32 -33
  47. data/lib/i18n/tasks/plural_keys.rb +1 -1
  48. data/lib/i18n/tasks/reports/base.rb +4 -9
  49. data/lib/i18n/tasks/reports/spreadsheet.rb +5 -5
  50. data/lib/i18n/tasks/reports/terminal.rb +62 -38
  51. data/lib/i18n/tasks/scanners/base_scanner.rb +5 -4
  52. data/lib/i18n/tasks/slop_command.rb +27 -0
  53. data/lib/i18n/tasks/stats.rb +20 -0
  54. data/lib/i18n/tasks/string_interpolation.rb +14 -0
  55. data/lib/i18n/tasks/unused_keys.rb +0 -10
  56. data/lib/i18n/tasks/version.rb +1 -1
  57. data/spec/commands/data_commands_spec.rb +38 -0
  58. data/spec/commands/tree_commands_spec.rb +68 -0
  59. data/spec/fixtures/app/views/index.html.slim +1 -0
  60. data/spec/google_translate_spec.rb +5 -3
  61. data/spec/i18n_spec.rb +18 -0
  62. data/spec/i18n_tasks_spec.rb +8 -8
  63. data/spec/spec_helper.rb +3 -3
  64. data/spec/support/test_codebase.rb +4 -1
  65. data/spec/used_keys_spec.rb +7 -7
  66. data/templates/config/i18n-tasks.yml +2 -2
  67. metadata +48 -4
  68. data/lib/i18n/tasks/commands_base.rb +0 -107
  69. data/lib/i18n/tasks/fill_tasks.rb +0 -40
data/i18n-tasks.gemspec CHANGED
@@ -34,6 +34,7 @@ It can also can pre-fill missing keys, including from Google Translate, and it c
34
34
  s.add_dependency 'terminal-table'
35
35
  s.add_dependency 'highline'
36
36
  s.add_dependency 'slop', '>= 3.5.0'
37
+ s.add_dependency 'i18n'
37
38
  s.add_development_dependency 'axlsx', '~> 2.0'
38
39
  s.add_development_dependency 'bundler', '~> 1.3'
39
40
  s.add_development_dependency 'rake'
data/lib/i18n/tasks.rb CHANGED
@@ -2,6 +2,11 @@
2
2
  # define all the modules to be able to use ::
3
3
  module I18n
4
4
  module Tasks
5
+
6
+ def self.gem_path
7
+ File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
8
+ end
9
+
5
10
  module Data
6
11
  end
7
12
  end
@@ -19,3 +24,8 @@ require 'erubis'
19
24
  require 'i18n/tasks/version'
20
25
  require 'i18n/tasks/base_task'
21
26
 
27
+ # Add internal locale data to i18n gem load path
28
+ require 'i18n'
29
+ Dir[File.join(I18n::Tasks.gem_path, 'config', 'locales', '*.yml')].each do |locale_file|
30
+ I18n.config.load_path << locale_file
31
+ end
@@ -4,15 +4,18 @@ require 'i18n/tasks/split_key'
4
4
  require 'i18n/tasks/key_pattern_matching'
5
5
  require 'i18n/tasks/logging'
6
6
  require 'i18n/tasks/plural_keys'
7
+ require 'i18n/tasks/html_keys'
7
8
  require 'i18n/tasks/used_keys'
8
9
  require 'i18n/tasks/ignore_keys'
9
10
  require 'i18n/tasks/missing_keys'
10
11
  require 'i18n/tasks/unused_keys'
11
12
  require 'i18n/tasks/google_translation'
12
- require 'i18n/tasks/fill_tasks'
13
13
  require 'i18n/tasks/locale_pathname'
14
+ require 'i18n/tasks/locale_list'
15
+ require 'i18n/tasks/string_interpolation'
14
16
  require 'i18n/tasks/data'
15
17
  require 'i18n/tasks/configuration'
18
+ require 'i18n/tasks/stats'
16
19
 
17
20
  module I18n
18
21
  module Tasks
@@ -20,15 +23,16 @@ module I18n
20
23
  include SplitKey
21
24
  include KeyPatternMatching
22
25
  include PluralKeys
26
+ include HtmlKeys
23
27
  include UsedKeys
24
28
  include IgnoreKeys
25
29
  include MissingKeys
26
30
  include UnusedKeys
27
- include FillTasks
28
31
  include GoogleTranslation
29
32
  include Logging
30
33
  include Configuration
31
34
  include Data
35
+ include Stats
32
36
 
33
37
  def initialize(config = {})
34
38
  self.config = config || {}
@@ -0,0 +1,18 @@
1
+ require 'i18n/tasks/command/options/common'
2
+ require 'i18n/tasks/command/options/locales'
3
+ require 'i18n/tasks/command/options/trees'
4
+
5
+ module I18n::Tasks
6
+ module Command
7
+ module Collection
8
+ def self.included(base)
9
+ base.module_eval do
10
+ extend Command::DSL
11
+ include Command::Options::Common
12
+ include Command::Options::Locales
13
+ include Command::Options::Trees
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,72 @@
1
+ # coding: utf-8
2
+ require 'i18n/tasks/slop_command'
3
+ require 'i18n/tasks/reports/terminal'
4
+ require 'i18n/tasks/reports/spreadsheet'
5
+
6
+ module I18n::Tasks
7
+ module Command
8
+ class Commander
9
+ include ::I18n::Tasks::Logging
10
+
11
+ def initialize(i18n = nil)
12
+ @i18n = i18n
13
+ end
14
+
15
+ def args_with_stdin(opt)
16
+ sources = opt[:arguments] || []
17
+ sources.unshift $stdin.read unless opt[:nostdin]
18
+ sources
19
+ end
20
+
21
+ def safe_run(name, opts)
22
+ begin
23
+ coloring_was = Term::ANSIColor.coloring?
24
+ Term::ANSIColor.coloring = ENV['I18N_TASKS_COLOR'] || STDOUT.isatty
25
+ run name, opts
26
+ rescue CommandError => e
27
+ log_error e.message
28
+ log_verbose e.backtrace * "\n"
29
+ exit 78
30
+ ensure
31
+ Term::ANSIColor.coloring = coloring_was
32
+ end
33
+ end
34
+
35
+ def run(name, opts)
36
+ if opts.empty?
37
+ log_verbose "run #{name.tr('_', '-')} without arguments"
38
+ send name
39
+ else
40
+ log_verbose "run #{name.tr('_', '-')} with #{opts.map { |k, v| "#{k}=#{v}" } * ' '}"
41
+ send name, opts
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ def terminal_report
48
+ @terminal_report ||= I18n::Tasks::Reports::Terminal.new(i18n)
49
+ end
50
+
51
+ def spreadsheet_report
52
+ @spreadsheet_report ||= I18n::Tasks::Reports::Spreadsheet.new(i18n)
53
+ end
54
+
55
+ class << self
56
+ def run_command(name, opts)
57
+ ::I18n::Tasks::Commands.new.safe_run(name, opts)
58
+ end
59
+ end
60
+
61
+ def desc(name)
62
+ self.class.cmds.try(:[], name).try(:desc)
63
+ end
64
+
65
+ def i18n
66
+ @i18n ||= I18n::Tasks::BaseTask.new
67
+ end
68
+
69
+ delegate :base_locale, :t, to: :i18n
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,73 @@
1
+ module I18n::Tasks
2
+ module Command
3
+ module Commands
4
+ module Data
5
+ include Command::Collection
6
+
7
+ cmd_opt :pattern_router, {
8
+ short: :p,
9
+ long: :pattern_router,
10
+ desc: I18n.t('i18n_tasks.cmd.args.desc.pattern_router'),
11
+ conf: {argument: false, optional: true}
12
+ }
13
+
14
+ cmd :normalize,
15
+ args: '[locale ...]',
16
+ desc: I18n.t('i18n_tasks.cmd.desc.normalize'),
17
+ opt: cmd_opts(:locales, :pattern_router)
18
+
19
+ def normalize(opt = {})
20
+ opt_locales! opt
21
+ i18n.normalize_store! opt[:locales], opt[:pattern_router]
22
+ end
23
+
24
+ cmd :data,
25
+ args: '[locale ...]',
26
+ desc: I18n.t('i18n_tasks.cmd.desc.data'),
27
+ opt: cmd_opts(:locales, :out_format)
28
+
29
+ def data(opt = {})
30
+ opt_locales! opt
31
+ opt_output_format! opt
32
+ print_forest i18n.data_forest(opt[:locales]), opt
33
+ end
34
+
35
+ cmd :data_merge,
36
+ args: '[tree ...]',
37
+ desc: I18n.t('i18n_tasks.cmd.desc.data_merge'),
38
+ opt: cmd_opts(:data_format, :nostdin)
39
+
40
+ def data_merge(opt = {})
41
+ opt_data_format! opt
42
+ forest = opt_forests_merged_stdin_args!(opt)
43
+ merged = i18n.data.merge!(forest)
44
+ print_forest merged, opt
45
+ end
46
+
47
+ cmd :data_write,
48
+ args: '[tree]',
49
+ desc: I18n.t('i18n_tasks.cmd.desc.data_write'),
50
+ opt: cmd_opts(:data_format, :nostdin)
51
+
52
+ def data_write(opt = {})
53
+ opt_data_format! opt
54
+ forest = opt_forest_arg_or_stdin!(opt)
55
+ i18n.data.write forest
56
+ print_forest forest, opt
57
+ end
58
+
59
+ cmd :data_remove,
60
+ args: '[tree]',
61
+ desc: I18n.t('i18n_tasks.cmd.desc.data_remove'),
62
+ opt: cmd_opts(:data_format, :nostdin)
63
+
64
+ def data_remove(opt = {})
65
+ opt_data_format! opt
66
+ removed = i18n.data.remove_by_key!(opt_forest_arg_or_stdin!(opt))
67
+ log_stderr 'Removed:'
68
+ print_forest removed, opt
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,20 @@
1
+ module I18n::Tasks
2
+ module Command
3
+ module Commands
4
+ module EqBase
5
+ include Command::Collection
6
+
7
+ cmd :eq_base,
8
+ args: '[locale ...]',
9
+ desc: I18n.t('i18n_tasks.cmd.desc.eq_base'),
10
+ opt: cmd_opts(:locales, :out_format)
11
+
12
+ def eq_base(opt = {})
13
+ opt_locales! opt
14
+ opt_output_format! opt
15
+ print_forest i18n.eq_base_keys(opt), opt, :eq_base_keys
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ module I18n::Tasks
2
+ module Command
3
+ module Commands
4
+ module Health
5
+ include Command::Collection
6
+
7
+ cmd :health,
8
+ args: '[locale ...]',
9
+ desc: I18n.t('i18n_tasks.cmd.desc.health'),
10
+ opt: cmd_opts(:locales, :out_format)
11
+
12
+ def health(opt = {})
13
+ opt_locales! opt
14
+ forest = i18n.data_forest(opt[:locales])
15
+ stats = i18n.forest_stats(forest)
16
+ if stats[:key_count].zero?
17
+ raise CommandError.new I18n.t('i18n_tasks.health.no_keys_detected')
18
+ end
19
+ terminal_report.forest_stats forest, stats
20
+ missing opt
21
+ unused opt
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ module I18n::Tasks
2
+ module Command
3
+ module Commands
4
+ module Meta
5
+ include Command::Collection
6
+
7
+ cmd :config,
8
+ args: '[section ...]',
9
+ desc: I18n.t('i18n_tasks.cmd.desc.config')
10
+
11
+ def config(opts = {})
12
+ cfg = i18n.config_for_inspect
13
+ cfg = cfg.slice(*opts[:arguments]) if opts[:arguments]
14
+ cfg = cfg.to_yaml
15
+ cfg.sub! /\A---\n/, ''
16
+ cfg.gsub! /^([^\s-].+?:)/, Term::ANSIColor.cyan(Term::ANSIColor.bold('\1'))
17
+ puts cfg
18
+ end
19
+
20
+ cmd :gem_path, desc: I18n.t('i18n_tasks.cmd.desc.gem_path')
21
+
22
+ def gem_path
23
+ puts I18n::Tasks.gem_path
24
+ end
25
+
26
+ cmd :irb, desc: I18n.t('i18n_tasks.cmd.desc.irb')
27
+
28
+ def irb
29
+ require 'i18n/tasks/console_context'
30
+ ::I18n::Tasks::ConsoleContext.start
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ module I18n::Tasks
2
+ module Command
3
+ module Commands
4
+ module Missing
5
+ include Command::Collection
6
+
7
+ cmd_opt :missing_types, {
8
+ short: :t,
9
+ long: :types=,
10
+ desc: I18n.t('i18n_tasks.cmd.args.desc.missing_types', valid: I18n::Tasks::MissingKeys.missing_keys_types * ', '),
11
+ conf: {as: Array, delimiter: /\s*[+:,]\s*/}
12
+ }
13
+
14
+ DEFAULT_ADD_MISSING_VALUE = '%{value_or_human_key}'
15
+
16
+ cmd :missing,
17
+ args: '[locale ...]',
18
+ desc: I18n.t('i18n_tasks.cmd.desc.missing'),
19
+ opt: cmd_opts(:locales, :out_format, :missing_types)
20
+
21
+ def missing(opt = {})
22
+ opt_locales!(opt)
23
+ opt_output_format!(opt)
24
+ opt_missing_types!(opt)
25
+ print_forest i18n.missing_keys(opt), opt, :missing_keys
26
+ end
27
+
28
+ cmd :translate_missing,
29
+ args: '[locale ...]',
30
+ desc: I18n.t('i18n_tasks.cmd.desc.translate_missing'),
31
+ opt: [cmd_opt(:locales),
32
+ cmd_opt(:locale).merge(short: :f, long: :from=, desc: 'Locale to translate from (default: base)'),
33
+ cmd_opt(:out_format).except(:short)]
34
+
35
+ def translate_missing(opt = {})
36
+ opt_locales! opt
37
+ opt_output_format! opt
38
+ from = opt_locale! opt, :from
39
+ translated = (opt[:locales] - [from]).inject i18n.empty_forest do |result, locale|
40
+ result.merge! i18n.google_translate_forest i18n.missing_tree(locale, from), from, locale
41
+ end
42
+ i18n.data.merge! translated
43
+ log_stderr I18n.t('i18n_tasks.translate_missing.translated', count: translated.leaves.count)
44
+ print_forest translated, opt
45
+ end
46
+
47
+ cmd :add_missing,
48
+ args: '[locale ...]',
49
+ desc: I18n.t('i18n_tasks.cmd.desc.add_missing'),
50
+ opt: cmd_opts(:locales, :out_format) <<
51
+ cmd_opt(:value).merge(desc: "#{cmd_opt(:value)[:desc]}. #{I18n.t('i18n_tasks.cmd.args.default_text', value: DEFAULT_ADD_MISSING_VALUE)}")
52
+
53
+ def add_missing(opt = {})
54
+ opt_locales! opt
55
+ opt_output_format! opt
56
+ forest = i18n.missing_keys(opt).set_each_value!(opt[:value] || DEFAULT_ADD_MISSING_VALUE)
57
+ i18n.data.merge! forest
58
+ log_stderr I18n.t('i18n_tasks.add_missing.added', count: forest.leaves.count)
59
+ print_forest forest, opt
60
+ end
61
+
62
+ private
63
+
64
+ def opt_missing_types!(opt)
65
+ parse_enum_list_opt(opt[:types], I18n::Tasks::MissingKeys.missing_keys_types) do |invalid, valid|
66
+ I18n.t('i18n_tasks.cmd.errors.invalid_missing_type',
67
+ invalid: invalid * ', ', valid: valid * ', ', count: invalid.length)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,92 @@
1
+ module I18n::Tasks
2
+ module Command
3
+ module Commands
4
+ module Tree
5
+ include Command::Collection
6
+
7
+ cmd :tree_merge,
8
+ args: '[tree ...]',
9
+ desc: I18n.t('i18n_tasks.cmd.desc.tree_merge'),
10
+ opt: cmd_opts(:data_format, :nostdin)
11
+
12
+ def tree_merge(opts = {})
13
+ print_forest opt_forests_merged_stdin_args!(opts), opts
14
+ end
15
+
16
+ cmd :tree_filter,
17
+ args: '[pattern] [tree]',
18
+ desc: I18n.t('i18n_tasks.cmd.desc.tree_filter'),
19
+ opt: cmd_opts(:data_format, :pattern)
20
+
21
+ def tree_filter(opt = {})
22
+ opt_data_format! opt
23
+ pattern = opt_or_arg!(:pattern, opt)
24
+ forest = opt_forest_arg_or_stdin!(opt)
25
+ unless pattern.blank?
26
+ pattern_re = i18n.compile_key_pattern(pattern)
27
+ forest = forest.select_keys { |full_key, _node| full_key =~ pattern_re }
28
+ end
29
+ print_forest forest, opt
30
+ end
31
+
32
+ cmd :tree_rename_key,
33
+ args: '<key> <name> [tree]',
34
+ desc: I18n.t('i18n_tasks.cmd.desc.tree_rename_key'),
35
+ opt: cmd_opts(:data_format) + [
36
+ cmd_opt(:pattern).merge(short: :k, long: :key=, desc: 'Full key (pattern) to rename. Required'),
37
+ cmd_opt(:pattern).merge(short: :n, long: :name=, desc: 'New name, interpolates original name as %{key}. Required')]
38
+
39
+ def tree_rename_key(opt = {})
40
+ key = opt_or_arg! :key, opt
41
+ name = opt_or_arg! :name, opt
42
+ opt_data_format! opt
43
+ forest = opt_forest_arg_or_stdin! opt
44
+ raise CommandError.new('pass full key to rename (-k, --key)') if key.blank?
45
+ raise CommandError.new('pass new name (-n, --name)') if name.blank?
46
+ forest.rename_each_key!(key, name)
47
+ print_forest forest, opt
48
+ end
49
+
50
+ cmd :tree_subtract,
51
+ args: '[tree A] [tree B ...]',
52
+ desc: I18n.t('i18n_tasks.cmd.desc.tree_subtract'),
53
+ opt: cmd_opts(:data_format, :nostdin)
54
+
55
+ def tree_subtract(opt = {})
56
+ opt_data_format! opt
57
+ forests = opt_forests_stdin_args! opt, 2
58
+ forest = forests.reduce(:subtract_by_key) || empty_forest
59
+ print_forest forest, opt
60
+ end
61
+
62
+ cmd :tree_set_value,
63
+ args: '[value] [tree]',
64
+ desc: I18n.t('i18n_tasks.cmd.desc.tree_set_value'),
65
+ opt: cmd_opts(:value, :data_format, :nostdin, :pattern)
66
+
67
+ def tree_set_value(opt = {})
68
+ opt_data_format! opt
69
+ value = opt_or_arg! :value, opt
70
+ forest = opt_forest_arg_or_stdin!(opt)
71
+ key_pattern = opt[:pattern]
72
+ raise CommandError.new('pass value (-v, --value)') if value.blank?
73
+ forest.set_each_value!(value, key_pattern)
74
+ print_forest forest, opt
75
+ end
76
+
77
+ cmd :tree_convert,
78
+ args: '<tree>',
79
+ desc: I18n.t('i18n_tasks.cmd.desc.tree_convert'),
80
+ opt: [cmd_opt(:data_format).merge(short: :f, long: :from=),
81
+ cmd_opt(:out_format).merge(short: :t, long: :to=)]
82
+
83
+ def tree_convert(opt = {})
84
+ opt_data_format! opt, :from
85
+ opt_output_format! opt, :to
86
+ forest = opt_forest_arg_or_stdin! opt.merge(format: opt[:from])
87
+ print_forest forest, opt.merge(format: opt[:to])
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end