i18n-tasks 0.6.3 → 0.7.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.
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