i18n-tasks 0.7.1 → 0.7.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1adbf899220bdaffacd8f8b95f74eb2c102268a6
4
- data.tar.gz: 028937e7d90a8af43ad66497a0b3f869c52fb599
3
+ metadata.gz: 1da0127b4f5d5823790e0b3e95609d72bc5d89ce
4
+ data.tar.gz: 4b3444adf44532e8c4cfa4906658f23f95f385c3
5
5
  SHA512:
6
- metadata.gz: 16ee9ac1ea60d4ed1d04881d9c40fdc88478c16c9d0471d8b94d31a77a2a44e915b4236ad8d13a7c3517d40e7179ff9909c5215f828b8d193b19b61b59ee0747
7
- data.tar.gz: 025e2fee7f5590377e4b8ddfb2cec98256e45ff120ef5219d545064d745a23430b15cd071683edf0aa44618e22cdb3b78073be13c1c64a826e18e958db5cc4b7
6
+ metadata.gz: 7d914cabb6ace1ccf7f1eebf8dd68cd885a8929713edc095aebb7073e771cf06bb0ddcce78e1ff6df51562bc68ee11d8e6b1110f3120ad6d3093f985ae44fcb2
7
+ data.tar.gz: 2b8add28adae0c70a297e253dd104bc5877f95a68c4b5d16038f99d313b088b6268da15ca6e052e099be69308cfd2b758428ba276e64d94fdfb6e8cad6fbe2af
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.7.2
2
+
3
+ * i18n-tasks now analyses itself! `internal_locale` setting has been added, that controls i18n-tasks reporting language.
4
+ English and Russian are available in this release.
5
+
1
6
  ## 0.7.1
2
7
 
3
8
  * 1.9.3 compatibility
data/README.md CHANGED
@@ -25,7 +25,7 @@ i18n-tasks can be used with any project using [i18n][i18n-gem] (default in Rails
25
25
  Add to Gemfile:
26
26
 
27
27
  ```ruby
28
- gem 'i18n-tasks', '~> 0.7.1'
28
+ gem 'i18n-tasks', '~> 0.7.2'
29
29
  ```
30
30
 
31
31
  Copy default [configuration file](#configuration) (optional):
@@ -137,12 +137,12 @@ $ i18n-tasks normalize -p
137
137
  Relative keys (`t '.title'`) and plural keys (`key.{one,many,other,...}`) are fully supported.
138
138
  Scope argument is supported, but only when it is the first keyword argument ([improvements welcome](/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb)):
139
139
 
140
- ```ruby
141
- # this is supported
142
- t :invalid, scope: [:auth, :password], attempts: 5
143
- # but not this
144
- t :invalid, attempts: 5, scope: [:auth, :password]
145
- ```
140
+ ```ruby
141
+ # this is supported
142
+ t :invalid, scope: [:auth, :password], attempts: 5
143
+ # but not this
144
+ t :invalid, attempts: 5, scope: [:auth, :password]
145
+ ```
146
146
 
147
147
  Unused report will detect certain dynamic key forms and not report them, e.g.:
148
148
 
@@ -175,6 +175,12 @@ base_locale: en
175
175
  locales: [es, fr] # This includes base_locale by default
176
176
  ```
177
177
 
178
+ `internal_locale` controls the language i18n-tasks reports in. Locales available are `en` and `ru` (pull request to add more!).
179
+
180
+ ```yaml
181
+ internal_locale: en
182
+ ```
183
+
178
184
  ### Storage
179
185
 
180
186
  The default data adapter supports YAML and JSON files.
@@ -22,21 +22,23 @@ err = proc { |message, exit_code|
22
22
  }
23
23
 
24
24
  begin
25
- ran = false
25
+ ran = false
26
26
  commander = ::I18n::Tasks::Commands
27
+ instance = commander.new
28
+ instance.set_internal_locale!
27
29
  slop_adapter = ::I18n::Tasks::SlopCommand
28
- args = ARGV.dup
29
- args = ['--help'] if args.empty?
30
+ args = ARGV.dup
31
+ args = ['--help'] if args.empty?
30
32
  Slop.parse(args, help: true) do
31
33
  on('-v', '--version', 'Print the version') {
32
34
  puts I18n::Tasks::VERSION
33
35
  exit
34
36
  }
35
37
  commander.cmds.each do |name, attr|
36
- slop_dsl = slop_adapter.slop_command(name, attr) { |name, opts, args|
38
+ slop_dsl = slop_adapter.slop_command(name, attr) { |name, opts|
37
39
  begin
38
40
  ran = true
39
- commander.run_command name, slop_adapter.parse_slop_opts_args(opts, args)
41
+ instance.safe_run name, opts
40
42
  rescue Errno::EPIPE
41
43
  # ignore Errno::EPIPE which is throw when pipe breaks, e.g.:
42
44
  # i18n-tasks missing | head
@@ -5,6 +5,9 @@ base_locale: en
5
5
  ## uncomment to set locales explicitly
6
6
  # locales: [en, es, fr]
7
7
 
8
+ ## i18n-tasks report locale, default: en, available: en, ru
9
+ internal_locale: ru
10
+
8
11
  # Read and write locale data
9
12
  data:
10
13
  ## by default, translation data are read from the file system, or you can provide a custom data adapter
@@ -66,26 +66,32 @@ en:
66
66
  config: display i18n-tasks configuration
67
67
  gem_path: show path to the gem
68
68
  irb: start REPL session within i18n-tasks context
69
+ xlsx_report: save missing and unused translations to an Excel file
69
70
  args:
70
71
  default_text: 'Default: %{value}'
72
+ default_all: 'Default: all'
71
73
  desc:
72
74
  out_format: 'Output format: %{valid_text}. %{default_text}.'
73
75
  data_format: 'Data format: %{valid_text}. %{default_text}.'
74
76
  keys: List of keys separated by commas (,), spaces, or newlines.
75
77
  locales_filter: 'Comma-separated list of locale(s) to process. Default: all. Special: base.'
78
+ locales_to_translate_from: 'Locale to translate from (default: base)'
76
79
  locale: 'Locale. Default: base'
77
80
  confirm: Confirm automatically
78
81
  nostdin: Do not read from stdin
79
82
  strict: Do not infer dynamic key usage such as `t("category.\#{category.name}")`
80
- missing_types: 'Filter by types: %{valid}.'
83
+ missing_types: 'Filter by types: %{valid}. Default: all'
81
84
  key_pattern: Filter by key pattern (e.g. 'common.*')
85
+ key_pattern_to_rename: Full key (pattern) to rename. Required
86
+ new_key_name: New name, interpolates original name as %{key}. Required
82
87
  value: 'Value. Interpolates: %{value}, %{human_key}, %{value_or_human_key}'
83
88
  pattern_router: 'Use pattern router: keys moved per config data.write'
84
89
  enum_opt:
85
- desc:
86
- default: "%{valid_text}. %{default_text}"
87
- invalid_one: "%{invalid} is not one of: %{valid}."
88
- invalid_list: "%{invalid} is not in: %{valid}."
90
+ desc: "%{valid_text}. %{default_text}"
91
+ invalid: "%{invalid} is not one of: %{valid}."
92
+ enum_list_opt:
93
+ desc: 'Comma-separated list of: %{valid_text}. %{default_text}'
94
+ invalid: "%{invalid} is not in: %{valid}."
89
95
  errors:
90
96
  pass_forest: Pass locale forest
91
97
  invalid_locale: Invalid locale %{invalid}
@@ -0,0 +1,102 @@
1
+ ---
2
+ ru:
3
+ i18n_tasks:
4
+ common:
5
+ locale: "Язык"
6
+ type: "Тип"
7
+ key: "Ключ"
8
+ value: "Значение"
9
+ base_value: "Исходное значение"
10
+ details: "Детали"
11
+ continue_q: "Продолжить?"
12
+ n_more: "ещё %{count}"
13
+ google_translate:
14
+ errors:
15
+ no_results: Google Translate не дал результатов. Убедитесь в том, что платежная информация
16
+ добавлена в в https://code.google.com/apis/console.
17
+ remove_unused:
18
+ confirm:
19
+ one: "Один перевод будут удалён из %{locales}."
20
+ other: "Переводы (%{count}) будут удалены из %{locales}."
21
+ removed: "Удалены ключи (%{count})"
22
+ noop: "Нет неиспользуемых ключей"
23
+ translate_missing:
24
+ translated: "Переведены ключи (%{count})"
25
+ add_missing:
26
+ added: "Добавлены ключи (%{count})"
27
+ unused:
28
+ none: "Все переводы используются."
29
+ missing:
30
+ none: "Всё переведено."
31
+ usages:
32
+ none: "Не найдено использований."
33
+ health:
34
+ no_keys_detected: "Ключи не обнаружены. Проверьте data.read в config/i18n-tasks.yml."
35
+ data_stats:
36
+ title: "Данные (%{locales}):"
37
+ text: "%{key_count} ключей в %{locale_count} языках. В среднем, длина строки: %{value_chars_avg},
38
+ сегменты ключей: %{key_segments_avg}, ключей в языке %{per_locale_avg}."
39
+ text_single_locale: "%{key_count} ключей. В среднем, длина строки: %{value_chars_avg}, сегменты
40
+ ключей: %{key_segments_avg}."
41
+ cmd:
42
+ encourage:
43
+ - "Хорошая работа!"
44
+ - "Отлично!"
45
+ - "Прекрасно!"
46
+ desc:
47
+ normalize: "нормализовать файлы переводов (сортировка и распределение)"
48
+ data: "показать данные переводов"
49
+ data_merge: "добавить дерево к переводам"
50
+ data_write: "заменить переводы деревом"
51
+ data_remove: "удалить ключи, которые есть в дереве, из данных"
52
+ health: "Всё ОК?"
53
+ find: "показать, где ключи используются в коде"
54
+ unused: "показать неиспользуемые переводы"
55
+ missing: "показать недостающие переводы"
56
+ translate_missing: "перевести недостающие переводы с Google Translate"
57
+ add_missing: "добавить недостающие ключи к переводам"
58
+ remove_unused: "удалить неиспользуемые ключи"
59
+ eq_base: "показать переводы, равные значениям в основном языке"
60
+ tree_merge: "объединенить деревья"
61
+ tree_filter: "фильтровать дерево по ключу"
62
+ tree_rename_key: "переименовать узел дерева"
63
+ tree_subtract: "дерево A минус ключи в дереве B"
64
+ tree_set_value: "заменить значения ключей"
65
+ tree_convert: "преобразовать дерево между форматами"
66
+ config: "показать конфигурацию"
67
+ gem_path: "показать путь к ruby gem"
68
+ irb: "начать REPL сессию в контексте i18n-tasks"
69
+ xlsx_report: "сохранить недостающие и неиспользуемые переводы в Excel-файл"
70
+ args:
71
+ default_text: "По умолчанию: %{value}"
72
+ default_all: "По умолчанию: все"
73
+ desc:
74
+ out_format: "Формат вывода: %{valid_text}. %{default_text}."
75
+ data_format: "Формат данных: %{valid_text}. %{default_text}."
76
+ keys: "Список ключей, разделенных запятыми (,), пробелами или символами новой строки."
77
+ locales_filter: "Список языков для обработки, разделенный запятыми (,). По умолчанию: все.
78
+ Специальное значение: base."
79
+ locales_to_translate_from: "Язык с которого переводить (по умолчанию: base)"
80
+ locale: "Язык. По умолчанию: base"
81
+ confirm: "Подтвердить автоматом"
82
+ nostdin: "Не читать дерево из стандартного ввода"
83
+ strict: Не угадывать динамические использования ключей, например `t("category.#{category.key}")`
84
+ missing_types: "Типы недостающих переводов: %{valid}. По умолчанию: все"
85
+ key_pattern: "Маска ключа (например, common.*)"
86
+ key_pattern_to_rename: "Полный ключ (шаблон) для переименования. Необходимый параметр."
87
+ new_key_name: "Новое имя, интерполирует оригинальное название как %{key}. Необходимый параметр."
88
+ value: "Значение, интерполируется с %{value}, %{human_key}, %{value_or_human_key}"
89
+ pattern_router: "Использовать pattern_router: ключи распределятся по файлам согласно data.write"
90
+ enum_opt:
91
+ desc: "%{valid_text}. %{default_text}"
92
+ invalid: "%{invalid} не является одним из: %{valid}."
93
+ enum_list_opt:
94
+ desc: "Разделенных запятыми список: %{valid_text}. %{default_text}"
95
+ invalid: "%{invalid} не в: %{valid}."
96
+ errors:
97
+ pass_forest: "Передайте дерево"
98
+ invalid_locale: "Неверный язык %{invalid}"
99
+ invalid_format: "Неизвестный формат %{invalid}. Форматы: %{valid}."
100
+ invalid_missing_type:
101
+ one: "Неизвестный тип %{invalid}. Типы: %{valid}."
102
+ other: "Неизвестные типы: %{invalid}. Типы: %{valid}."
@@ -12,12 +12,6 @@ module I18n::Tasks
12
12
  @i18n = i18n
13
13
  end
14
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
15
  def safe_run(name, opts)
22
16
  begin
23
17
  coloring_was = Term::ANSIColor.coloring?
@@ -32,16 +26,23 @@ module I18n::Tasks
32
26
  end
33
27
  end
34
28
 
35
- def run(name, opts)
29
+ def run(name, opts = {})
30
+ name = name.to_sym
31
+ public_name = name.to_s.tr '_', '-'
32
+ SlopCommand.parse_opts! opts, self.class.cmds[name][:opt], self
36
33
  if opts.empty?
37
- log_verbose "run #{name.tr('_', '-')} without arguments"
34
+ log_verbose "run #{public_name} without arguments"
38
35
  send name
39
36
  else
40
- log_verbose "run #{name.tr('_', '-')} with #{opts.map { |k, v| "#{k}=#{v}" } * ' '}"
37
+ log_verbose "run #{public_name} with #{opts.map { |k, v| "#{k}=#{v}" } * ' '}"
41
38
  send name, opts
42
39
  end
43
40
  end
44
41
 
42
+ def set_internal_locale!
43
+ I18n.locale = i18n.internal_locale
44
+ end
45
+
45
46
  protected
46
47
 
47
48
  def terminal_report
@@ -52,12 +53,6 @@ module I18n::Tasks
52
53
  @spreadsheet_report ||= I18n::Tasks::Reports::Spreadsheet.new(i18n)
53
54
  end
54
55
 
55
- class << self
56
- def run_command(name, opts)
57
- ::I18n::Tasks::Commands.new.safe_run(name, opts)
58
- end
59
- end
60
-
61
56
  def desc(name)
62
57
  self.class.cmds.try(:[], name).try(:desc)
63
58
  end
@@ -7,38 +7,34 @@ module I18n::Tasks
7
7
  cmd_opt :pattern_router, {
8
8
  short: :p,
9
9
  long: :pattern_router,
10
- desc: I18n.t('i18n_tasks.cmd.args.desc.pattern_router'),
10
+ desc: proc { I18n.t('i18n_tasks.cmd.args.desc.pattern_router') },
11
11
  conf: {argument: false, optional: true}
12
12
  }
13
13
 
14
14
  cmd :normalize,
15
15
  args: '[locale ...]',
16
- desc: I18n.t('i18n_tasks.cmd.desc.normalize'),
16
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.normalize') },
17
17
  opt: cmd_opts(:locales, :pattern_router)
18
18
 
19
19
  def normalize(opt = {})
20
- opt_locales! opt
21
20
  i18n.normalize_store! opt[:locales], opt[:pattern_router]
22
21
  end
23
22
 
24
23
  cmd :data,
25
24
  args: '[locale ...]',
26
- desc: I18n.t('i18n_tasks.cmd.desc.data'),
25
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.data') },
27
26
  opt: cmd_opts(:locales, :out_format)
28
27
 
29
28
  def data(opt = {})
30
- opt_locales! opt
31
- opt_output_format! opt
32
29
  print_forest i18n.data_forest(opt[:locales]), opt
33
30
  end
34
31
 
35
32
  cmd :data_merge,
36
33
  args: '[tree ...]',
37
- desc: I18n.t('i18n_tasks.cmd.desc.data_merge'),
34
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.data_merge') },
38
35
  opt: cmd_opts(:data_format, :nostdin)
39
36
 
40
37
  def data_merge(opt = {})
41
- opt_data_format! opt
42
38
  forest = opt_forests_merged_stdin_args!(opt)
43
39
  merged = i18n.data.merge!(forest)
44
40
  print_forest merged, opt
@@ -46,11 +42,10 @@ module I18n::Tasks
46
42
 
47
43
  cmd :data_write,
48
44
  args: '[tree]',
49
- desc: I18n.t('i18n_tasks.cmd.desc.data_write'),
45
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.data_write') },
50
46
  opt: cmd_opts(:data_format, :nostdin)
51
47
 
52
48
  def data_write(opt = {})
53
- opt_data_format! opt
54
49
  forest = opt_forest_arg_or_stdin!(opt)
55
50
  i18n.data.write forest
56
51
  print_forest forest, opt
@@ -58,11 +53,10 @@ module I18n::Tasks
58
53
 
59
54
  cmd :data_remove,
60
55
  args: '[tree]',
61
- desc: I18n.t('i18n_tasks.cmd.desc.data_remove'),
56
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.data_remove') },
62
57
  opt: cmd_opts(:data_format, :nostdin)
63
58
 
64
59
  def data_remove(opt = {})
65
- opt_data_format! opt
66
60
  removed = i18n.data.remove_by_key!(opt_forest_arg_or_stdin!(opt))
67
61
  log_stderr 'Removed:'
68
62
  print_forest removed, opt
@@ -6,12 +6,10 @@ module I18n::Tasks
6
6
 
7
7
  cmd :eq_base,
8
8
  args: '[locale ...]',
9
- desc: I18n.t('i18n_tasks.cmd.desc.eq_base'),
9
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.eq_base') },
10
10
  opt: cmd_opts(:locales, :out_format)
11
11
 
12
12
  def eq_base(opt = {})
13
- opt_locales! opt
14
- opt_output_format! opt
15
13
  print_forest i18n.eq_base_keys(opt), opt, :eq_base_keys
16
14
  end
17
15
  end
@@ -6,11 +6,10 @@ module I18n::Tasks
6
6
 
7
7
  cmd :health,
8
8
  args: '[locale ...]',
9
- desc: I18n.t('i18n_tasks.cmd.desc.health'),
9
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.health') },
10
10
  opt: cmd_opts(:locales, :out_format)
11
11
 
12
12
  def health(opt = {})
13
- opt_locales! opt
14
13
  forest = i18n.data_forest(opt[:locales])
15
14
  stats = i18n.forest_stats(forest)
16
15
  if stats[:key_count].zero?
@@ -6,7 +6,7 @@ module I18n::Tasks
6
6
 
7
7
  cmd :config,
8
8
  args: '[section ...]',
9
- desc: I18n.t('i18n_tasks.cmd.desc.config')
9
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.config') }
10
10
 
11
11
  def config(opts = {})
12
12
  cfg = i18n.config_for_inspect
@@ -17,7 +17,7 @@ module I18n::Tasks
17
17
  puts cfg
18
18
  end
19
19
 
20
- cmd :gem_path, desc: I18n.t('i18n_tasks.cmd.desc.gem_path')
20
+ cmd :gem_path, desc: proc { I18n.t('i18n_tasks.cmd.desc.gem_path') }
21
21
 
22
22
  def gem_path
23
23
  puts I18n::Tasks.gem_path
@@ -4,38 +4,31 @@ module I18n::Tasks
4
4
  module Missing
5
5
  include Command::Collection
6
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}'
7
+ enum_opt :missing_types, I18n::Tasks::MissingKeys.missing_keys_types
8
+ cmd_opt :missing_types, enum_list_opt_attr(
9
+ :t, :types=, enum_opt(:missing_types),
10
+ proc { |valid, default| I18n.t('i18n_tasks.cmd.args.desc.missing_types', valid: valid, default: default) },
11
+ proc { |invalid, valid| I18n.t('i18n_tasks.cmd.errors.invalid_missing_type', invalid: invalid * ', ', valid: valid * ', ', count: invalid.length) })
15
12
 
16
13
  cmd :missing,
17
14
  args: '[locale ...]',
18
- desc: I18n.t('i18n_tasks.cmd.desc.missing'),
15
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.missing') },
19
16
  opt: cmd_opts(:locales, :out_format, :missing_types)
20
17
 
21
18
  def missing(opt = {})
22
- opt_locales!(opt)
23
- opt_output_format!(opt)
24
- opt_missing_types!(opt)
25
19
  print_forest i18n.missing_keys(opt), opt, :missing_keys
26
20
  end
27
21
 
28
22
  cmd :translate_missing,
29
23
  args: '[locale ...]',
30
- desc: I18n.t('i18n_tasks.cmd.desc.translate_missing'),
24
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.translate_missing') },
31
25
  opt: [cmd_opt(:locales),
32
- cmd_opt(:locale).merge(short: :f, long: :from=, desc: 'Locale to translate from (default: base)'),
26
+ cmd_opt(:locale).merge(short: :f, long: :from=, desc: proc {
27
+ I18n.t('i18n_tasks.cmd.args.desc.locales_to_translate_from') }),
33
28
  cmd_opt(:out_format).except(:short)]
34
29
 
35
30
  def translate_missing(opt = {})
36
- opt_locales! opt
37
- opt_output_format! opt
38
- from = opt_locale! opt, :from
31
+ from = opt[:from]
39
32
  translated = (opt[:locales] - [from]).inject i18n.empty_forest do |result, locale|
40
33
  result.merge! i18n.google_translate_forest i18n.missing_tree(locale, from), from, locale
41
34
  end
@@ -44,29 +37,20 @@ module I18n::Tasks
44
37
  print_forest translated, opt
45
38
  end
46
39
 
40
+ DEFAULT_ADD_MISSING_VALUE = '%{value_or_human_key}'
41
+
47
42
  cmd :add_missing,
48
43
  args: '[locale ...]',
49
- desc: I18n.t('i18n_tasks.cmd.desc.add_missing'),
44
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.add_missing') },
50
45
  opt: cmd_opts(:locales, :out_format) <<
51
46
  cmd_opt(:value).merge(desc: "#{cmd_opt(:value)[:desc]}. #{I18n.t('i18n_tasks.cmd.args.default_text', value: DEFAULT_ADD_MISSING_VALUE)}")
52
47
 
53
48
  def add_missing(opt = {})
54
- opt_locales! opt
55
- opt_output_format! opt
56
49
  forest = i18n.missing_keys(opt).set_each_value!(opt[:value] || DEFAULT_ADD_MISSING_VALUE)
57
50
  i18n.data.merge! forest
58
51
  log_stderr I18n.t('i18n_tasks.add_missing.added', count: forest.leaves.count)
59
52
  print_forest forest, opt
60
53
  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
54
  end
71
55
  end
72
56
  end
@@ -6,7 +6,7 @@ module I18n::Tasks
6
6
 
7
7
  cmd :tree_merge,
8
8
  args: '[tree ...]',
9
- desc: I18n.t('i18n_tasks.cmd.desc.tree_merge'),
9
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.tree_merge') },
10
10
  opt: cmd_opts(:data_format, :nostdin)
11
11
 
12
12
  def tree_merge(opts = {})
@@ -15,12 +15,11 @@ module I18n::Tasks
15
15
 
16
16
  cmd :tree_filter,
17
17
  args: '[pattern] [tree]',
18
- desc: I18n.t('i18n_tasks.cmd.desc.tree_filter'),
18
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.tree_filter') },
19
19
  opt: cmd_opts(:data_format, :pattern)
20
20
 
21
21
  def tree_filter(opt = {})
22
- opt_data_format! opt
23
- pattern = opt_or_arg!(:pattern, opt)
22
+ pattern = opt_or_arg! :pattern, opt
24
23
  forest = opt_forest_arg_or_stdin!(opt)
25
24
  unless pattern.blank?
26
25
  pattern_re = i18n.compile_key_pattern(pattern)
@@ -31,15 +30,17 @@ module I18n::Tasks
31
30
 
32
31
  cmd :tree_rename_key,
33
32
  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')]
33
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.tree_rename_key') },
34
+ opt: [
35
+ cmd_opt(:pattern).merge(short: :k, long: :key=, desc: proc {
36
+ I18n.t('i18n_tasks.cmd.args.desc.key_pattern_to_rename') }),
37
+ cmd_opt(:pattern).merge(short: :n, long: :name=, desc: proc {
38
+ I18n.t('i18n_tasks.cmd.args.desc.new_key_name') })
39
+ ] + cmd_opts(:data_format)
38
40
 
39
41
  def tree_rename_key(opt = {})
40
42
  key = opt_or_arg! :key, opt
41
43
  name = opt_or_arg! :name, opt
42
- opt_data_format! opt
43
44
  forest = opt_forest_arg_or_stdin! opt
44
45
  raise CommandError.new('pass full key to rename (-k, --key)') if key.blank?
45
46
  raise CommandError.new('pass new name (-n, --name)') if name.blank?
@@ -49,11 +50,10 @@ module I18n::Tasks
49
50
 
50
51
  cmd :tree_subtract,
51
52
  args: '[tree A] [tree B ...]',
52
- desc: I18n.t('i18n_tasks.cmd.desc.tree_subtract'),
53
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.tree_subtract') },
53
54
  opt: cmd_opts(:data_format, :nostdin)
54
55
 
55
56
  def tree_subtract(opt = {})
56
- opt_data_format! opt
57
57
  forests = opt_forests_stdin_args! opt, 2
58
58
  forest = forests.reduce(:subtract_by_key) || empty_forest
59
59
  print_forest forest, opt
@@ -61,11 +61,10 @@ module I18n::Tasks
61
61
 
62
62
  cmd :tree_set_value,
63
63
  args: '[value] [tree]',
64
- desc: I18n.t('i18n_tasks.cmd.desc.tree_set_value'),
64
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.tree_set_value') },
65
65
  opt: cmd_opts(:value, :data_format, :nostdin, :pattern)
66
66
 
67
67
  def tree_set_value(opt = {})
68
- opt_data_format! opt
69
68
  value = opt_or_arg! :value, opt
70
69
  forest = opt_forest_arg_or_stdin!(opt)
71
70
  key_pattern = opt[:pattern]
@@ -76,13 +75,11 @@ module I18n::Tasks
76
75
 
77
76
  cmd :tree_convert,
78
77
  args: '<tree>',
79
- desc: I18n.t('i18n_tasks.cmd.desc.tree_convert'),
78
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.tree_convert') },
80
79
  opt: [cmd_opt(:data_format).merge(short: :f, long: :from=),
81
80
  cmd_opt(:out_format).merge(short: :t, long: :to=)]
82
81
 
83
82
  def tree_convert(opt = {})
84
- opt_data_format! opt, :from
85
- opt_output_format! opt, :to
86
83
  forest = opt_forest_arg_or_stdin! opt.merge(format: opt[:from])
87
84
  print_forest forest, opt.merge(format: opt[:to])
88
85
  end
@@ -7,39 +7,34 @@ module I18n::Tasks
7
7
  cmd_opt :strict, {
8
8
  short: :s,
9
9
  long: :strict,
10
- desc: I18n.t('i18n_tasks.cmd.args.desc.strict')
10
+ desc: proc { I18n.t('i18n_tasks.cmd.args.desc.strict') }
11
11
  }
12
12
 
13
13
  cmd :find,
14
14
  args: '[pattern]',
15
- desc: I18n.t('i18n_tasks.cmd.desc.find'),
15
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.find') },
16
16
  opt: cmd_opts(:out_format, :pattern)
17
17
 
18
18
  def find(opt = {})
19
- opt_output_format! opt
20
19
  opt[:filter] ||= opt.delete(:pattern) || opt[:arguments].try(:first)
21
20
  print_forest i18n.used_tree(key_filter: opt[:filter].presence, source_occurrences: true), opt, :used_keys
22
21
  end
23
22
 
24
23
  cmd :unused,
25
24
  args: '[locale ...]',
26
- desc: I18n.t('i18n_tasks.cmd.desc.unused'),
25
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.unused') },
27
26
  opt: cmd_opts(:locales, :out_format, :strict)
28
27
 
29
28
  def unused(opt = {})
30
- opt_locales! opt
31
- opt_output_format! opt
32
29
  print_forest i18n.unused_keys(opt), opt, :unused_keys
33
30
  end
34
31
 
35
32
  cmd :remove_unused,
36
33
  args: '[locale ...]',
37
- desc: I18n.t('i18n_tasks.cmd.desc.remove_unused'),
34
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.remove_unused') },
38
35
  opt: cmd_opts(:locales, :out_format, :strict, :confirm)
39
36
 
40
37
  def remove_unused(opt = {})
41
- opt_locales! opt
42
- opt_output_format! opt
43
38
  unused_keys = i18n.unused_keys(opt)
44
39
  if unused_keys.present?
45
40
  terminal_report.unused_keys(unused_keys)
@@ -6,7 +6,7 @@ module I18n::Tasks
6
6
 
7
7
  cmd :xlsx_report,
8
8
  args: '[locale...]',
9
- desc: 'save missing and unused translations to an Excel file',
9
+ desc: proc { I18n.t('i18n_tasks.cmd.desc.xlsx_report') },
10
10
  opt: [cmd_opt(:locales),
11
11
  {short: :p, long: :path=, desc: 'Destination path', conf: {default: 'tmp/i18n-report.xlsx'}}]
12
12
 
@@ -18,7 +18,6 @@ module I18n::Tasks
18
18
  log_stderr Term::ANSIColor.red Term::ANSIColor.bold message
19
19
  exit 1
20
20
  end
21
- opt_locales! opt
22
21
  spreadsheet_report.save_report opt[:path], opt.except(:path)
23
22
  end
24
23
  end
@@ -11,14 +11,33 @@ module I18n::Tasks
11
11
  end
12
12
 
13
13
  DEFAULT_ENUM_OPT_DESC = proc { |valid, default|
14
- I18n.t('i18n_tasks.cmd.enum_opt.desc.default', valid_text: valid, default_text: default)
14
+ I18n.t('i18n_tasks.cmd.enum_opt.desc', valid_text: valid, default_text: default)
15
15
  }
16
16
 
17
- def enum_opt_attr(short, long, valid, &desc)
17
+ def enum_opt_attr(short, long, valid, desc, error_msg)
18
18
  desc ||= DEFAULT_ENUM_OPT_DESC
19
- {short: short, long: long.to_sym,
20
- desc: desc.call(valid * ', ', I18n.t('i18n_tasks.cmd.args.default_text', value: valid.first)),
21
- conf: {default: valid.first, argument: true, optional: false}}
19
+ desc_proc = proc { desc.call(valid * ', ', I18n.t('i18n_tasks.cmd.args.default_text', value: valid.first)) }
20
+ {short: short, long: long, desc: desc_proc,
21
+ conf: {default: valid.first, argument: true, optional: false},
22
+ parse: enum_parse_proc(:parse_enum_opt, valid, &error_msg)}
23
+ end
24
+
25
+ DEFAULT_LIST_OPT_DESC = proc { |valid, default|
26
+ I18n.t('i18n_tasks.cmd.enum_list_opt.desc', valid_text: valid, default_text: default)
27
+ }
28
+
29
+ def enum_list_opt_attr(short, long, valid, desc, error_msg)
30
+ desc ||= DEFAULT_LIST_OPT_DESC
31
+ desc_proc = proc { desc.call(valid * ', ', I18n.t('i18n_tasks.cmd.args.default_all')) }
32
+ {short: short, long: long, desc: desc_proc,
33
+ conf: {as: Array, delimiter: /\s*[+:,]\s*/},
34
+ parse: enum_parse_proc(:parse_enum_list_opt, valid, &error_msg)}
35
+ end
36
+
37
+ def enum_parse_proc(method, valid, &error)
38
+ proc { |opt, key|
39
+ opt[key] = send(method, opt[key], valid, &error)
40
+ }
22
41
  end
23
42
  end
24
43
  end
@@ -14,28 +14,28 @@ module I18n::Tasks
14
14
  cmd_opt :nostdin, {
15
15
  short: :S,
16
16
  long: :nostdin,
17
- desc: I18n.t('i18n_tasks.cmd.args.desc.nostdin'),
17
+ desc: proc { I18n.t('i18n_tasks.cmd.args.desc.nostdin') },
18
18
  conf: {default: false}
19
19
  }
20
20
 
21
21
  cmd_opt :confirm, {
22
22
  short: :y,
23
23
  long: :confirm,
24
- desc: I18n.t('i18n_tasks.cmd.args.desc.confirm'),
24
+ desc: proc { I18n.t('i18n_tasks.cmd.args.desc.confirm') },
25
25
  conf: {default: false}
26
26
  }
27
27
 
28
28
  cmd_opt :pattern, {
29
29
  short: :p,
30
30
  long: :pattern=,
31
- desc: I18n.t('i18n_tasks.cmd.args.desc.key_pattern'),
31
+ desc: proc { I18n.t('i18n_tasks.cmd.args.desc.key_pattern') },
32
32
  conf: {argument: true, optional: false}
33
33
  }
34
34
 
35
35
  cmd_opt :value, {
36
36
  short: :v,
37
37
  long: :value=,
38
- desc: I18n.t('i18n_tasks.cmd.args.desc.value'),
38
+ desc: proc { I18n.t('i18n_tasks.cmd.args.desc.value') },
39
39
  conf: {argument: true, optional: false}
40
40
  }
41
41
 
@@ -3,7 +3,7 @@ module I18n::Tasks
3
3
  module Options
4
4
  module EnumOpt
5
5
  DEFAULT_ENUM_OPT_ERROR = proc { |bad, good|
6
- I18n.t('i18n_tasks.cmd.enum_opt.invalid_one', invalid: bad, valid: good * ', ')
6
+ I18n.t('i18n_tasks.cmd.enum_opt.invalid', invalid: bad, valid: good * ', ')
7
7
  }
8
8
 
9
9
  def parse_enum_opt(value, valid, &error_msg)
@@ -23,14 +23,18 @@ module I18n::Tasks
23
23
  end
24
24
 
25
25
  DEFAULT_ENUM_LIST_ERROR = proc { |bad, good|
26
- I18n.t('i18n_tasks.cmd.enum_opt.invalid_list', invalid: bad * ', ', valid: good * ', ')
26
+ I18n.t('i18n_tasks.cmd.enum_list_opt.invalid', invalid: bad * ', ', valid: good * ', ')
27
27
  }
28
28
 
29
29
  def parse_enum_list_opt(values, valid, &error_msg)
30
30
  values = explode_list_opt(values)
31
31
  invalid = values - valid.map(&:to_s)
32
32
  if invalid.empty?
33
- values
33
+ if values.empty?
34
+ valid
35
+ else
36
+ values
37
+ end
34
38
  else
35
39
  error_msg ||= DEFAULT_ENUM_LIST_ERROR
36
40
  raise CommandError.new error_msg.call(invalid, valid)
@@ -8,17 +8,20 @@ module I18n::Tasks
8
8
  short: :l,
9
9
  long: :locales=,
10
10
  desc: I18n.t('i18n_tasks.cmd.args.desc.locales_filter'),
11
- conf: {as: Array, delimiter: /\s*[+:,]\s*/, default: 'all', argument: true, optional: false}
11
+ conf: {as: Array, delimiter: /\s*[+:,]\s*/, default: 'all', argument: true, optional: false},
12
+ parse: :parse_locales
12
13
  }
14
+
13
15
  cmd_opt :locale, {
14
16
  short: :l,
15
17
  long: :locale=,
16
18
  desc: I18n.t('i18n_tasks.cmd.args.desc.locale'),
17
- conf: {default: 'base', argument: true, optional: false}
19
+ conf: {default: 'base', argument: true, optional: false},
20
+ parse: :parse_locale
18
21
  }
19
22
 
20
- def opt_locales!(opt)
21
- argv = Array(opt[:arguments]) + Array(opt[:locales])
23
+ def parse_locales(opt, key = :locales)
24
+ argv = Array(opt[:arguments]) + Array(opt[key])
22
25
  locales = if argv == ['all'] || argv == 'all' || argv.blank?
23
26
  i18n.locales
24
27
  else
@@ -26,16 +29,15 @@ module I18n::Tasks
26
29
  end
27
30
  locales.each { |locale| validate_locale!(locale) }
28
31
  log_verbose "locales for the command are #{locales.inspect}"
29
- opt[:locales] = locales
32
+ opt[key] = locales
30
33
  end
31
34
 
32
- def opt_locale!(opt, key = :locale)
35
+ def parse_locale(opt, key = :locale)
33
36
  val = opt[key]
34
37
  opt[key] = base_locale if val.blank? || val == 'base'
35
38
  opt[key]
36
39
  end
37
40
 
38
-
39
41
  VALID_LOCALE_RE = /\A\w[\w\-_\.]*\z/i
40
42
 
41
43
  def validate_locale!(locale)
@@ -3,27 +3,24 @@ module I18n::Tasks
3
3
  module Options
4
4
  module Trees
5
5
  extend Command::DSL
6
+ format_opt = proc { |type|
7
+ enum_opt_attr :f, :format=, enum_opt(type),
8
+ proc { |valid, default|
9
+ I18n.t("i18n_tasks.cmd.args.desc.#{type}", valid_text: valid, default_text: default) },
10
+ proc { |value, valid|
11
+ I18n.t('i18n_tasks.cmd.errors.invalid_format', invalid: value, valid: valid * ', ') }
12
+ }
6
13
 
7
14
  enum_opt :data_format, %w(yaml json keys)
8
- cmd_opt :data_format, enum_opt_attr(:f, :format=, enum_opt(:data_format)) { |valid_text, default_text|
9
- I18n.t('i18n_tasks.cmd.args.desc.data_format', valid_text: valid_text, default_text: default_text)
10
- }
15
+ # i18n-tasks-use t('i18n_tasks.cmd.args.desc.data_format')
16
+ cmd_opt :data_format, format_opt.call(:data_format)
11
17
 
12
18
  enum_opt :out_format, ['terminal-table', *enum_opt(:data_format), 'inspect']
13
- cmd_opt :out_format, enum_opt_attr(:f, :format=, enum_opt(:out_format)) { |valid_text, default_text|
14
- I18n.t('i18n_tasks.cmd.args.desc.out_format', valid_text: valid_text, default_text: default_text)
15
- }
16
-
17
- cmd_opt :keys, {
18
- short: :k,
19
- long: :keys=,
20
- desc: I18n.t('i18n_tasks.cmd.args.desc.keys'),
21
- conf: {as: Array, delimiter: /[+:,]/, argument: true, optional: false}
22
- }
19
+ # i18n-tasks-use t('i18n_tasks.cmd.args.desc.out_format')
20
+ cmd_opt :out_format, format_opt.call(:out_format)
23
21
 
24
22
  def print_forest(forest, opt, version = :show_tree)
25
23
  format = opt[:format].to_s
26
-
27
24
  case format
28
25
  when 'terminal-table'
29
26
  terminal_report.send(version, forest)
@@ -36,34 +33,18 @@ module I18n::Tasks
36
33
  end
37
34
  end
38
35
 
39
- INVALID_FORMAT_MSG = proc do |value, valid|
40
- I18n.t('i18n_tasks.cmd.errors.invalid_format', invalid: value, valid: valid * ', ')
41
- end
42
-
43
- def opt_output_format!(opt = {}, key = :format)
44
- opt[key] = parse_enum_opt opt[key], :out_format, &INVALID_FORMAT_MSG
45
- end
46
-
47
- def opt_data_format!(opt = {}, key = :format)
48
- opt[key] = parse_enum_opt opt[key], :data_format, &INVALID_FORMAT_MSG
49
- end
50
-
51
- def opt_args_keys!(opt = {})
52
- opt[:keys] = explode_list_opt(opt[:keys]) + Array(opt[:arguments])
53
- end
54
-
55
- def opt_forest_arg_or_stdin!(opt)
36
+ def opt_forest_arg_or_stdin!(opt, format = opt[:format])
56
37
  src = opt[:arguments].try(:shift) || $stdin.read
57
- parse_forest(src, opt)
38
+ parse_forest(src, format)
58
39
  end
59
40
 
60
- def opt_forests_stdin_args!(opt, num = false)
41
+ def opt_forests_stdin_args!(opt, num = false, format = opt[:format])
61
42
  args = opt[:arguments] || []
62
43
  if opt[:nostdin]
63
44
  sources = []
64
45
  else
65
46
  sources = [$stdin.read]
66
- num -= 1 if num
47
+ num -= 1 if num
67
48
  end
68
49
  if num
69
50
  num.times { sources << args.shift }
@@ -71,7 +52,7 @@ module I18n::Tasks
71
52
  sources += args
72
53
  args.clear
73
54
  end
74
- sources.map { |src| parse_forest(src, opt) }
55
+ sources.map { |src| parse_forest(src, format) }
75
56
  end
76
57
 
77
58
  def opt_forests_merged_stdin_args!(opt)
@@ -80,11 +61,10 @@ module I18n::Tasks
80
61
  }
81
62
  end
82
63
 
83
- def parse_forest(src, opt = {})
64
+ def parse_forest(src, format)
84
65
  if !src
85
66
  raise CommandError.new(I18n.t('i18n_tasks.cmd.errors.pass_forest'))
86
67
  end
87
- format = opt_data_format!(opt)
88
68
  if format == 'keys'
89
69
  Data::Tree::Siblings.from_key_names parse_keys(src)
90
70
  else
@@ -70,6 +70,9 @@ module I18n::Tasks::Configuration
70
70
  @config_sections[:base_locale] ||= (config[:base_locale] || 'en').to_s
71
71
  end
72
72
 
73
+ def internal_locale
74
+ @config_sections[:internal_locale] ||= (config[:internal_locale] || 'en').to_s
75
+ end
73
76
 
74
77
  def ignore_config(type = nil)
75
78
  key = type ? "ignore_#{type}" : 'ignore'
@@ -81,6 +84,7 @@ module I18n::Tasks::Configuration
81
84
  def config_sections
82
85
  # init all sections
83
86
  base_locale
87
+ internal_locale
84
88
  locales
85
89
  data_config
86
90
  search_config
@@ -4,24 +4,37 @@ module I18n::Tasks::SlopCommand
4
4
  def slop_command(name, attr, &block)
5
5
  proc {
6
6
  command name.tr('_', '-') do
7
- opts = attr[:opt]
8
7
  args = attr[:args]
9
8
  banner "Usage: i18n-tasks #{name} [options] #{args}" if args.present?
10
9
  desc = attr[:desc]
10
+ desc = desc.call if desc.respond_to?(:call)
11
11
  description desc if desc
12
- if opts
13
- opts.each do |opt|
14
- on *[:short, :long, :desc, :conf].map { |k| opt[k] }.compact
15
- end
12
+ attr[:opt].try :each do |opt|
13
+ on *opt.values_at(:short, :long, :desc, :conf).compact.map { |v| v.respond_to?(:call) ? v.call : v }
16
14
  end
17
- run { |opts, args| block.call(name, opts, args) }
15
+ run { |slop_opts, slop_args|
16
+ slop_opts = slop_opts.to_hash(true).reject { |k, v| v.nil? }
17
+ slop_opts.merge!(arguments: slop_args) unless slop_args.empty?
18
+ block.call name, slop_opts
19
+ }
18
20
  end
19
21
  }
20
22
  end
21
23
 
22
- def parse_slop_opts_args(opts, args)
23
- opts = opts.to_hash(true).reject { |k, v| v.nil? }
24
- opts.merge!(arguments: args) unless args.empty?
25
- opts
24
+ def parse_opts!(opts, opts_conf, context)
25
+ return if !opts_conf
26
+ opts_conf.each do |opt_conf|
27
+ parse = opt_conf[:parse]
28
+ if parse
29
+ key = opt_conf[:long].to_s.sub(/=\z/, '').to_sym
30
+ if parse.respond_to?(:call)
31
+ context.instance_exec opts, key, &parse
32
+ elsif Symbol === parse
33
+ context.instance_exec do
34
+ send parse, opts, key
35
+ end
36
+ end
37
+ end
38
+ end
26
39
  end
27
40
  end
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
  module I18n
3
3
  module Tasks
4
- VERSION = '0.7.1'
4
+ VERSION = '0.7.2'
5
5
  end
6
6
  end
@@ -53,7 +53,7 @@ describe 'Google Translation' do
53
53
  }
54
54
  })
55
55
 
56
- cmd.translate_missing
56
+ cmd.run :translate_missing
57
57
  expect(task.t('common.hello', 'es')).to eq(text_test[2])
58
58
  expect(task.t('common.hello_html', 'es')).to eq(html_test[2])
59
59
  expect(task.t('common.array_key', 'es')).to eq(array_test[2])
@@ -24,7 +24,7 @@ module TestCodebase
24
24
  def run_cmd(name, *args, &block)
25
25
  in_test_app_dir do
26
26
  silence_stderr {
27
- capture_stdout { i18n_cmd.send(name, *args, &block) }
27
+ capture_stdout { i18n_cmd.run(name, *args, &block) }
28
28
  }
29
29
  end
30
30
  end
@@ -5,6 +5,9 @@ base_locale: en
5
5
  ## uncomment to set locales explicitly
6
6
  # locales: [en, es, fr]
7
7
 
8
+ ## i18n-tasks report locale, default: en, available: en, ru
9
+ # internal_locale: ru
10
+
8
11
  # Read and write locale data
9
12
  data:
10
13
  ## by default, translation data are read from the file system, or you can provide a custom data adapter
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.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-30 00:00:00.000000000 Z
11
+ date: 2014-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubis
@@ -217,6 +217,7 @@ files:
217
217
  - bin/i18n-tasks
218
218
  - config/i18n-tasks.yml
219
219
  - config/locales/en.yml
220
+ - config/locales/ru.yml
220
221
  - i18n-tasks.gemspec
221
222
  - lib/i18n/tasks.rb
222
223
  - lib/i18n/tasks/base_task.rb