i18n-tasks 0.7.6 → 0.7.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34457139e93868f537a6258613716aefc75094a6
4
- data.tar.gz: 1208e54c90f1504f021e05a7bc13904d38f10c61
3
+ metadata.gz: e4b9791c830f1768141d6d43dba65b1e3b963e90
4
+ data.tar.gz: ddc91d95b7bdf1a0590af2c4175d71ac5a896fa7
5
5
  SHA512:
6
- metadata.gz: 02aded94bed5ff6e30a729c6352d987fa4f675e44a51e0fa784a79d430bcc951934f61c5a1ee2985832ac1353193da88062a556b6c85cc1faa57ac39bc7aa176
7
- data.tar.gz: 4ec6497da3b91b0bf2612cb25cc9e743d11ed5354d51bbfbec75edfce26adb35b87f986d8c1f83ea54fb0c3c3b1074defbcbfcc0b923973b5eb1e90bcc0cd751
6
+ metadata.gz: fd0ea56aef3a916fd7f1a1ae6f51bdd83b01b69aa66f9c9ed8a38e0f82e3d59550443ac116a731227b8330f2866fc8eb69fca8841d2731c013a681f2021f9f13
7
+ data.tar.gz: b4f2afc081cda21da193205db3d5f4555e41cb986c47cb9f85a29387fee4daf81d99c41436fd3a7c6be4c5510ac8f23f836fb864432d66aca96d98fb5f544452
data/CHANGES.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.7.7
2
+
3
+ * Fix regression: keys are sorted once again [#92](https://github.com/glebm/i18n-tasks/issues/92).
4
+
1
5
  ## 0.7.6
2
6
 
3
7
  * Add a post-install notice with setup commands
data/README.md CHANGED
@@ -2,23 +2,19 @@
2
2
 
3
3
  i18n-tasks helps you find and manage missing and unused translations.
4
4
 
5
- ## How?
5
+ <img width="539" height="331" src="https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-tasks.png">
6
6
 
7
- i18n-tasks analyses the code statically for key usages, such as `I18n.t('some.key')`, in order to:
7
+ This gem analyses code statically for key usages, such as `I18n.t('some.key')`, in order to:
8
8
 
9
9
  * Report keys that are missing or unused.
10
10
  * Pre-fill missing keys, optionally from Google Translate.
11
11
  * Remove unused keys.
12
12
 
13
- ## Why?
14
-
15
- This gem addresses these drawbacks of [i18n gem][i18n-gem] design:
13
+ Thus addressing the two main problems of [i18n gem][i18n-gem] design:
16
14
 
17
15
  * Missing keys only blow up at runtime.
18
16
  * Keys no longer in use may accumulate and introduce overhead, without you knowing it.
19
17
 
20
- <img width="539" height="331" src="https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-tasks.png">
21
-
22
18
  ## Installation
23
19
 
24
20
  i18n-tasks can be used with any project using [i18n][i18n-gem] (default in Rails), or similar, even if it isn't ruby.
@@ -26,7 +22,7 @@ i18n-tasks can be used with any project using [i18n][i18n-gem] (default in Rails
26
22
  Add it to the Gemfile:
27
23
 
28
24
  ```ruby
29
- gem 'i18n-tasks', '~> 0.7.6'
25
+ gem 'i18n-tasks', '~> 0.7.7'
30
26
  ```
31
27
 
32
28
  Copy default [configuration file](#configuration) (optional):
data/bin/i18n-tasks CHANGED
@@ -13,10 +13,10 @@ require 'i18n/tasks/commands'
13
13
  require 'slop'
14
14
 
15
15
  err = proc { |message, exit_code|
16
- if STDERR.isatty
17
- STDERR.puts Term::ANSIColor.yellow('i18n-tasks: ' + message)
16
+ if $stderr.isatty
17
+ $stderr.puts Term::ANSIColor.yellow('i18n-tasks: ' + message)
18
18
  else
19
- STDERR.puts message
19
+ $stderr.puts message
20
20
  end
21
21
  exit exit_code
22
22
  }
@@ -1,102 +1,102 @@
1
1
  ---
2
2
  en:
3
3
  i18n_tasks:
4
- common:
5
- locale: Locale
6
- type: Type
7
- key: Key
8
- value: Value
9
- base_value: Base Value
10
- details: Details
11
- continue_q: Continue?
12
- n_more: "%{count} more"
13
- google_translate:
14
- errors:
15
- no_results: Google Translate returned no results. Make sure billing information is set at
16
- https://code.google.com/apis/console.
17
- remove_unused:
18
- confirm:
19
- one: One translations will be removed from %{locales}.
20
- other: "%{count} translation will be removed from %{locales}."
21
- removed: Removed %{count} keys
22
- noop: No unused keys to remove
23
- translate_missing:
24
- translated: Translated %{count} keys
25
4
  add_missing:
26
5
  added: Added %{count} keys
27
- unused:
28
- none: Every translation is in use.
29
- missing:
30
- none: No translations are missing.
31
- usages:
32
- none: No key usages found.
33
- health:
34
- no_keys_detected: No keys detected. Check data.read in config/i18n-tasks.yml.
35
- data_stats:
36
- title: Forest (%{locales})
37
- text: has %{key_count} keys across %{locale_count} locales. On average, values are %{value_chars_avg}
38
- characters long, keys have %{key_segments_avg} segments, a locale has %{per_locale_avg} keys.
39
- text_single_locale: has %{key_count} keys in total. On average, values are %{value_chars_avg}
40
- characters long, keys have %{key_segments_avg} segments.
41
6
  cmd:
42
- encourage:
43
- - Good job!
44
- - Well done!
45
- - Perfect!
7
+ args:
8
+ default_all: 'Default: all'
9
+ default_text: 'Default: %{value}'
10
+ desc:
11
+ confirm: Confirm automatically
12
+ data_format: 'Data format: %{valid_text}. %{default_text}.'
13
+ key_pattern: Filter by key pattern (e.g. 'common.*')
14
+ key_pattern_to_rename: Full key (pattern) to rename. Required
15
+ keys: List of keys separated by commas (,), spaces, or newlines.
16
+ locale: 'Locale. Default: base'
17
+ locale_to_translate_from: 'Locale to translate from (default: base)'
18
+ locales_filter: 'Comma-separated list of locale(s) to process. Default: all. Special: base.'
19
+ missing_types: 'Filter by types: %{valid}. Default: all'
20
+ new_key_name: New name, interpolates original name as %{key}. Required
21
+ nostdin: Do not read from stdin
22
+ out_format: 'Output format: %{valid_text}. %{default_text}.'
23
+ pattern_router: 'Use pattern router: keys moved per config data.write'
24
+ strict: Do not infer dynamic key usage such as `t("category.\#{category.name}")`
25
+ value: 'Value. Interpolates: %{value}, %{human_key}, %{value_or_human_key}'
46
26
  desc:
47
- normalize: 'normalize translation data: sort and move to the right files'
27
+ add_missing: add missing keys to locale data
28
+ config: display i18n-tasks configuration
48
29
  data: show locale data
49
30
  data_merge: merge locale data with trees
50
- data_write: replace locale data with tree
51
31
  data_remove: remove keys present in tree from data
52
- health: is everything OK?
32
+ data_write: replace locale data with tree
33
+ eq_base: show translations equal to base value
53
34
  find: show where keys are used in the code
54
- unused: show unused translations
35
+ gem_path: show path to the gem
36
+ health: is everything OK?
37
+ irb: start REPL session within i18n-tasks context
55
38
  missing: show missing translations
56
- translate_missing: translate missing keys with Google Translate
57
- add_missing: add missing keys to locale data
39
+ normalize: 'normalize translation data: sort and move to the right files'
58
40
  remove_unused: remove unused keys
59
- eq_base: show translations equal to base value
60
- tree_translate: Google Translate a tree to root locales
61
- tree_merge: merge trees
41
+ translate_missing: translate missing keys with Google Translate
42
+ tree_convert: convert tree between formats
62
43
  tree_filter: filter tree by key pattern
44
+ tree_merge: merge trees
63
45
  tree_rename_key: rename tree node
64
- tree_subtract: tree A minus the keys in tree B
65
46
  tree_set_value: set values of keys, optionally match a pattern
66
- tree_convert: convert tree between formats
67
- config: display i18n-tasks configuration
68
- gem_path: show path to the gem
69
- irb: start REPL session within i18n-tasks context
47
+ tree_subtract: tree A minus the keys in tree B
48
+ tree_translate: Google Translate a tree to root locales
49
+ unused: show unused translations
70
50
  xlsx_report: save missing and unused translations to an Excel file
71
- args:
72
- default_text: 'Default: %{value}'
73
- default_all: 'Default: all'
74
- desc:
75
- out_format: 'Output format: %{valid_text}. %{default_text}.'
76
- data_format: 'Data format: %{valid_text}. %{default_text}.'
77
- keys: List of keys separated by commas (,), spaces, or newlines.
78
- locales_filter: 'Comma-separated list of locale(s) to process. Default: all. Special: base.'
79
- locale: 'Locale. Default: base'
80
- locale_to_translate_from: 'Locale to translate from (default: base)'
81
- confirm: Confirm automatically
82
- nostdin: Do not read from stdin
83
- strict: Do not infer dynamic key usage such as `t("category.\#{category.name}")`
84
- missing_types: 'Filter by types: %{valid}. Default: all'
85
- key_pattern: Filter by key pattern (e.g. 'common.*')
86
- key_pattern_to_rename: Full key (pattern) to rename. Required
87
- new_key_name: New name, interpolates original name as %{key}. Required
88
- value: 'Value. Interpolates: %{value}, %{human_key}, %{value_or_human_key}'
89
- pattern_router: 'Use pattern router: keys moved per config data.write'
90
- enum_opt:
91
- desc: "%{valid_text}. %{default_text}"
92
- invalid: "%{invalid} is not one of: %{valid}."
51
+ encourage:
52
+ - Good job!
53
+ - Well done!
54
+ - Perfect!
93
55
  enum_list_opt:
94
56
  desc: 'Comma-separated list of: %{valid_text}. %{default_text}'
95
57
  invalid: "%{invalid} is not in: %{valid}."
58
+ enum_opt:
59
+ desc: "%{valid_text}. %{default_text}"
60
+ invalid: "%{invalid} is not one of: %{valid}."
96
61
  errors:
97
- pass_forest: Pass locale forest
98
- invalid_locale: Invalid locale %{invalid}
99
62
  invalid_format: 'Unknown format %{invalid}. Valid: %{valid}.'
63
+ invalid_locale: Invalid locale %{invalid}
100
64
  invalid_missing_type:
101
65
  one: 'Unknown type %{invalid}. Valid: %{valid}.'
102
66
  other: 'Unknown types: %{invalid}. Valid: %{valid}.'
67
+ pass_forest: Pass locale forest
68
+ common:
69
+ base_value: Base Value
70
+ continue_q: Continue?
71
+ details: Details
72
+ key: Key
73
+ locale: Locale
74
+ n_more: "%{count} more"
75
+ type: Type
76
+ value: Value
77
+ data_stats:
78
+ text: has %{key_count} keys across %{locale_count} locales. On average, values are %{value_chars_avg}
79
+ characters long, keys have %{key_segments_avg} segments, a locale has %{per_locale_avg} keys.
80
+ text_single_locale: has %{key_count} keys in total. On average, values are %{value_chars_avg}
81
+ characters long, keys have %{key_segments_avg} segments.
82
+ title: Forest (%{locales})
83
+ google_translate:
84
+ errors:
85
+ no_results: Google Translate returned no results. Make sure billing information is set at
86
+ https://code.google.com/apis/console.
87
+ health:
88
+ no_keys_detected: No keys detected. Check data.read in config/i18n-tasks.yml.
89
+ missing:
90
+ none: No translations are missing.
91
+ remove_unused:
92
+ confirm:
93
+ one: One translations will be removed from %{locales}.
94
+ other: "%{count} translation will be removed from %{locales}."
95
+ noop: No unused keys to remove
96
+ removed: Removed %{count} keys
97
+ translate_missing:
98
+ translated: Translated %{count} keys
99
+ unused:
100
+ none: Every translation is in use.
101
+ usages:
102
+ none: No key usages found.
@@ -1,103 +1,103 @@
1
1
  ---
2
2
  ru:
3
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
4
  add_missing:
26
5
  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
6
  cmd:
42
- encourage:
43
- - "Хорошая работа!"
44
- - "Отлично!"
45
- - "Прекрасно!"
7
+ args:
8
+ default_all: "По умолчанию: все"
9
+ default_text: "По умолчанию: %{value}"
10
+ desc:
11
+ confirm: "Подтвердить автоматом"
12
+ data_format: "Формат данных: %{valid_text}. %{default_text}."
13
+ key_pattern: "Маска ключа (например, common.*)"
14
+ key_pattern_to_rename: "Полный ключ (шаблон) для переименования. Необходимый параметр."
15
+ keys: "Список ключей, разделенных запятыми (,), пробелами или символами новой строки."
16
+ locale: "Язык. По умолчанию: base"
17
+ locale_to_translate_from: "Язык, с которого переводить (по умолчанию: base)"
18
+ locales_filter: "Список языков для обработки, разделенный запятыми (,). По умолчанию: все.
19
+ Специальное значение: base."
20
+ missing_types: "Типы недостающих переводов: %{valid}. По умолчанию: все"
21
+ new_key_name: "Новое имя, интерполирует оригинальное название как %{key}. Необходимый параметр."
22
+ nostdin: "Не читать дерево из стандартного ввода"
23
+ out_format: "Формат вывода: %{valid_text}. %{default_text}."
24
+ pattern_router: "Использовать pattern_router: ключи распределятся по файлам согласно data.write"
25
+ strict: Не угадывать динамические использования ключей, например `t("category.#{category.key}")`
26
+ value: "Значение, интерполируется с %{value}, %{human_key}, %{value_or_human_key}"
46
27
  desc:
47
- normalize: "нормализовать файлы переводов (сортировка и распределение)"
28
+ add_missing: "добавить недостающие ключи к переводам"
29
+ config: "показать конфигурацию"
48
30
  data: "показать данные переводов"
49
31
  data_merge: "добавить дерево к переводам"
50
- data_write: "заменить переводы деревом"
51
32
  data_remove: "удалить ключи, которые есть в дереве, из данных"
52
- health: "Всё ОК?"
33
+ data_write: "заменить переводы деревом"
34
+ eq_base: "показать переводы, равные значениям в основном языке"
53
35
  find: "показать, где ключи используются в коде"
54
- unused: "показать неиспользуемые переводы"
36
+ gem_path: "показать путь к ruby gem"
37
+ health: "Всё ОК?"
38
+ irb: "начать REPL сессию в контексте i18n-tasks"
55
39
  missing: "показать недостающие переводы"
56
- translate_missing: "перевести недостающие переводы с Google Translate"
57
- add_missing: "добавить недостающие ключи к переводам"
40
+ normalize: "нормализовать файлы переводов (сортировка и распределение)"
58
41
  remove_unused: "удалить неиспользуемые ключи"
59
- eq_base: "показать переводы, равные значениям в основном языке"
60
- tree_merge: "объединенить деревья"
42
+ translate_missing: "перевести недостающие переводы с Google Translate"
43
+ tree_convert: "преобразовать дерево между форматами"
61
44
  tree_filter: "фильтровать дерево по ключу"
45
+ tree_merge: "объединенить деревья"
62
46
  tree_rename_key: "переименовать узел дерева"
63
- tree_subtract: "дерево A минус ключи в дереве B"
64
47
  tree_set_value: "заменить значения ключей"
65
- tree_convert: "преобразовать дерево между форматами"
66
- config: "показать конфигурацию"
67
- gem_path: "показать путь к ruby gem"
68
- irb: "начать REPL сессию в контексте i18n-tasks"
69
- xlsx_report: "сохранить недостающие и неиспользуемые переводы в Excel-файл"
48
+ tree_subtract: "дерево A минус ключи в дереве B"
70
49
  tree_translate: "Перевести дерево при помощи Google Translate на язык корневых узлов"
71
- args:
72
- default_text: "По умолчанию: %{value}"
73
- default_all: "По умолчанию: все"
74
- desc:
75
- out_format: "Формат вывода: %{valid_text}. %{default_text}."
76
- data_format: "Формат данных: %{valid_text}. %{default_text}."
77
- keys: "Список ключей, разделенных запятыми (,), пробелами или символами новой строки."
78
- locales_filter: "Список языков для обработки, разделенный запятыми (,). По умолчанию: все.
79
- Специальное значение: base."
80
- locale_to_translate_from: "Язык, с которого переводить (по умолчанию: base)"
81
- locale: "Язык. По умолчанию: base"
82
- confirm: "Подтвердить автоматом"
83
- nostdin: "Не читать дерево из стандартного ввода"
84
- strict: Не угадывать динамические использования ключей, например `t("category.#{category.key}")`
85
- missing_types: "Типы недостающих переводов: %{valid}. По умолчанию: все"
86
- key_pattern: "Маска ключа (например, common.*)"
87
- key_pattern_to_rename: "Полный ключ (шаблон) для переименования. Необходимый параметр."
88
- new_key_name: "Новое имя, интерполирует оригинальное название как %{key}. Необходимый параметр."
89
- value: "Значение, интерполируется с %{value}, %{human_key}, %{value_or_human_key}"
90
- pattern_router: "Использовать pattern_router: ключи распределятся по файлам согласно data.write"
91
- enum_opt:
92
- desc: "%{valid_text}. %{default_text}"
93
- invalid: "%{invalid} не является одним из: %{valid}."
50
+ unused: "показать неиспользуемые переводы"
51
+ xlsx_report: "сохранить недостающие и неиспользуемые переводы в Excel-файл"
52
+ encourage:
53
+ - "Хорошая работа!"
54
+ - "Отлично!"
55
+ - "Прекрасно!"
94
56
  enum_list_opt:
95
57
  desc: "Разделенных запятыми список: %{valid_text}. %{default_text}"
96
58
  invalid: "%{invalid} не в: %{valid}."
59
+ enum_opt:
60
+ desc: "%{valid_text}. %{default_text}"
61
+ invalid: "%{invalid} не является одним из: %{valid}."
97
62
  errors:
98
- pass_forest: "Передайте дерево"
99
- invalid_locale: "Неверный язык %{invalid}"
100
63
  invalid_format: "Неизвестный формат %{invalid}. Форматы: %{valid}."
64
+ invalid_locale: "Неверный язык %{invalid}"
101
65
  invalid_missing_type:
102
66
  one: "Неизвестный тип %{invalid}. Типы: %{valid}."
103
67
  other: "Неизвестные типы: %{invalid}. Типы: %{valid}."
68
+ pass_forest: "Передайте дерево"
69
+ common:
70
+ base_value: "Исходное значение"
71
+ continue_q: "Продолжить?"
72
+ details: "Детали"
73
+ key: "Ключ"
74
+ locale: "Язык"
75
+ n_more: "ещё %{count}"
76
+ type: "Тип"
77
+ value: "Значение"
78
+ data_stats:
79
+ text: "%{key_count} ключей в %{locale_count} языках. В среднем, длина строки: %{value_chars_avg},
80
+ сегменты ключей: %{key_segments_avg}, ключей в языке %{per_locale_avg}."
81
+ text_single_locale: "%{key_count} ключей. В среднем, длина строки: %{value_chars_avg}, сегменты
82
+ ключей: %{key_segments_avg}."
83
+ title: "Данные (%{locales}):"
84
+ google_translate:
85
+ errors:
86
+ no_results: Google Translate не дал результатов. Убедитесь в том, что платежная информация
87
+ добавлена в в https://code.google.com/apis/console.
88
+ health:
89
+ no_keys_detected: "Ключи не обнаружены. Проверьте data.read в config/i18n-tasks.yml."
90
+ missing:
91
+ none: "Всё переведено."
92
+ remove_unused:
93
+ confirm:
94
+ one: "Один перевод будут удалён из %{locales}."
95
+ other: "Переводы (%{count}) будут удалены из %{locales}."
96
+ noop: "Нет неиспользуемых ключей"
97
+ removed: "Удалены ключи (%{count})"
98
+ translate_missing:
99
+ translated: "Переведены ключи (%{count})"
100
+ unused:
101
+ none: "Все переводы используются."
102
+ usages:
103
+ none: "Не найдено использований."
data/i18n-tasks.gemspec CHANGED
@@ -12,15 +12,15 @@ Gem::Specification.new do |s|
12
12
  s.description = <<-TEXT
13
13
  i18n-tasks helps you find and manage missing and unused translations.
14
14
 
15
- It scans calls such as `I18n.t('some.key')` and provides reports on key usage, missing, and unused keys.
16
- It can also can pre-fill missing keys, including from Google Translate, and it can remove unused keys as well.
15
+ It analyses code statically for key usages, such as `I18n.t('some.key')`, in order to report keys that are missing or unused,
16
+ pre-fill missing keys (optionally from Google Translate), and remove unused keys.
17
+ well.
17
18
  TEXT
18
19
  s.post_install_message = <<-TEXT
19
20
  # Install default configuration:
20
21
  cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
21
22
  # Add an RSpec for missing and unused keys:
22
23
  cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
23
- }
24
24
  TEXT
25
25
  s.homepage = 'https://github.com/glebm/i18n-tasks'
26
26
  if s.respond_to?(:metadata=)
@@ -29,7 +29,7 @@ module I18n::Tasks
29
29
  when 'keys'
30
30
  puts forest.key_names(root: true)
31
31
  when *enum_opt(:data_format)
32
- puts i18n.data.adapter_dump forest, format
32
+ puts i18n.data.adapter_dump forest.to_hash(true), format
33
33
  end
34
34
  end
35
35
 
@@ -18,7 +18,7 @@ module I18n::Tasks
18
18
  IRB.setup nil
19
19
  ctx = IRB::Irb.new.context
20
20
  IRB.conf[:MAIN_CONTEXT] = ctx
21
- STDERR.puts Messages.banner
21
+ $stderr.puts Messages.banner
22
22
  require 'irb/ext/multi-irb'
23
23
  IRB.irb nil, new
24
24
  end
@@ -40,7 +40,7 @@ module I18n
40
40
  def write_tree(path, tree)
41
41
  ::FileUtils.mkpath(File.dirname path)
42
42
  ::File.open(path, 'w') { |f|
43
- f.write adapter_dump(tree.to_hash, self.class.adapter_name_for_path(path))
43
+ f.write adapter_dump(tree.to_hash(true), self.class.adapter_name_for_path(path))
44
44
  }
45
45
  end
46
46
 
@@ -127,9 +127,9 @@ module I18n::Tasks::Data::Tree
127
127
  parent && parent.children || Siblings.new(nodes: [self])
128
128
  end
129
129
 
130
- def to_hash
131
- @hash ||= begin
132
- children_hash = (children || {}).map(&:to_hash).reduce(:deep_merge) || {}
130
+ def to_hash(sort = false)
131
+ (@hash ||= {})[sort] ||= begin
132
+ children_hash = children ? children.to_hash(sort) : {}
133
133
  if key.nil?
134
134
  children_hash
135
135
  elsif leaf?
@@ -144,13 +144,13 @@ module I18n::Tasks::Data::Tree
144
144
  delegate :to_yaml, to: :to_hash
145
145
 
146
146
  def inspect(level = 0)
147
- if key.nil?
148
- label = Term::ANSIColor.dark '∅'
149
- else
150
- label = [Term::ANSIColor.color(1 + level % 15, self.key),
151
- (": #{Term::ANSIColor.cyan(self.value.to_s)}" if leaf?),
152
- (" #{self.data}" if data?)].compact.join
153
- end
147
+ label = if key.nil?
148
+ Term::ANSIColor.dark '∅'
149
+ else
150
+ [Term::ANSIColor.color(1 + level % 15, key),
151
+ (": #{Term::ANSIColor.cyan(value.to_s)}" if leaf?),
152
+ (" #{data}" if data?)].compact.join
153
+ end
154
154
  [' ' * level, label, ("\n" + children.map { |c| c.inspect(level + 1) }.join("\n") if children?)].compact.join
155
155
  end
156
156
 
@@ -29,8 +29,14 @@ module I18n::Tasks::Data::Tree
29
29
  self.class.new(attr)
30
30
  end
31
31
 
32
- def to_hash
33
- @hash ||= map(&:to_hash).reduce(:deep_merge!) || {}
32
+ def to_hash(sort = false)
33
+ (@hash ||= {})[sort] ||= begin
34
+ if sort
35
+ self.sort { |a, b| a.key <=> b.key }
36
+ else
37
+ self
38
+ end.map { |node| node.to_hash(sort) }.reduce({}, :deep_merge!)
39
+ end
34
40
  end
35
41
 
36
42
  delegate :to_json, to: :to_hash
@@ -40,7 +46,7 @@ module I18n::Tasks::Data::Tree
40
46
  if present?
41
47
  map(&:inspect) * "\n"
42
48
  else
43
- Term::ANSIColor.dark '∅'
49
+ Term::ANSIColor.dark '{}'
44
50
  end
45
51
  end
46
52
 
@@ -45,7 +45,7 @@ module I18n::Tasks::Data::Tree
45
45
  key_to_node[new_node.key] = new_node
46
46
  end
47
47
 
48
- include SplitKey
48
+ include ::I18n::Tasks::SplitKey
49
49
 
50
50
  # @return [Node] by full key
51
51
  def get(full_key)
@@ -110,22 +110,7 @@ module I18n::Tasks::Data::Tree
110
110
  def merge!(nodes)
111
111
  nodes = Siblings.from_nested_hash(nodes) if nodes.is_a?(Hash)
112
112
  nodes.each do |node|
113
- if key_to_node.key?(node.key)
114
- our = key_to_node[node.key]
115
- next if our == node
116
- our.value = node.value if node.leaf?
117
- our.data.merge!(node.data) if node.data?
118
- if node.children?
119
- if our.children
120
- our.children.merge!(node.children)
121
- else
122
- warn_add_children_to_leaf our
123
- our.children = node.children
124
- end
125
- end
126
- else
127
- key_to_node[node.key] = node.derive(parent: parent)
128
- end
113
+ merge_node! node
129
114
  end
130
115
  @list = key_to_node.values
131
116
  dirty!
@@ -161,12 +146,31 @@ module I18n::Tasks::Data::Tree
161
146
 
162
147
  private
163
148
 
149
+ def merge_node!(node)
150
+ if key_to_node.key?(node.key)
151
+ our = key_to_node[node.key]
152
+ return if our == node
153
+ our.value = node.value if node.leaf?
154
+ our.data.merge!(node.data) if node.data?
155
+ if node.children?
156
+ if our.children
157
+ our.children.merge!(node.children)
158
+ else
159
+ warn_add_children_to_leaf our
160
+ our.children = node.children
161
+ end
162
+ end
163
+ else
164
+ key_to_node[node.key] = node.derive(parent: parent)
165
+ end
166
+ end
167
+
164
168
  def warn_add_children_to_leaf(node)
165
169
  ::I18n::Tasks::Logging.log_warn "'#{node.full_key}' was a leaf, now has children (value <- scope conflict)"
166
170
  end
167
171
 
168
172
  class << self
169
- include SplitKey
173
+ include ::I18n::Tasks::SplitKey
170
174
 
171
175
  def null
172
176
  new
@@ -21,7 +21,11 @@ module I18n::Tasks
21
21
  nodes = to_nodes
22
22
  unless nodes.empty?
23
23
  block.yield nodes
24
- Nodes.new(nodes.children).levels(&block)
24
+ if nodes.children.size == 1
25
+ first.children
26
+ else
27
+ Nodes.new(nodes: nodes.children)
28
+ end.levels(&block)
25
29
  end
26
30
  self
27
31
  end
@@ -21,6 +21,6 @@ module I18n::Tasks::Logging
21
21
  end
22
22
 
23
23
  def log_stderr(*args)
24
- STDERR.puts(*args)
24
+ $stderr.puts(*args)
25
25
  end
26
26
  end
@@ -1,65 +1,69 @@
1
- module SplitKey
2
- extend self
1
+ module I18n
2
+ module Tasks
3
+ module SplitKey
4
+ extend self
3
5
 
4
- # split a key by dots (.)
5
- # dots inside braces or parenthesis are not split on
6
- #
7
- # split_key 'a.b' # => ['a', 'b']
8
- # split_key 'a.#{b.c}' # => ['a', '#{b.c}']
9
- # split_key 'a.b.c', 2 # => ['a', 'b.c']
10
- def split_key(key, max = Float::INFINITY)
11
- parts = []
12
- pos = 0
13
- return [key] if max == 1
14
- key_parts(key) do |part|
15
- parts << part
16
- pos += part.length + 1
17
- if parts.length + 1 >= max
18
- parts << key.from(pos) unless pos == key.length
19
- break
6
+ # split a key by dots (.)
7
+ # dots inside braces or parenthesis are not split on
8
+ #
9
+ # split_key 'a.b' # => ['a', 'b']
10
+ # split_key 'a.#{b.c}' # => ['a', '#{b.c}']
11
+ # split_key 'a.b.c', 2 # => ['a', 'b.c']
12
+ def split_key(key, max = Float::INFINITY)
13
+ parts = []
14
+ pos = 0
15
+ return [key] if max == 1
16
+ key_parts(key) do |part|
17
+ parts << part
18
+ pos += part.length + 1
19
+ if parts.length + 1 >= max
20
+ parts << key.from(pos) unless pos == key.length
21
+ break
22
+ end
23
+ end
24
+ parts
20
25
  end
21
- end
22
- parts
23
- end
24
26
 
25
- def last_key_part(key)
26
- last = nil
27
- key_parts(key) { |part| last = part }
28
- last
29
- end
27
+ def last_key_part(key)
28
+ last = nil
29
+ key_parts(key) { |part| last = part }
30
+ last
31
+ end
30
32
 
31
- # yield each key part
32
- # dots inside braces or parenthesis are not split on
33
- def key_parts(key, &block)
34
- return enum_for(:key_parts, key) unless block
35
- nesting = PARENS
36
- counts = PARENS_ZEROS # dup'd later if key contains parenthesis
37
- delim = '.'.freeze
38
- from = to = 0
39
- key.each_char do |char|
40
- if char == delim && PARENS_ZEROS == counts
41
- block.yield key[from...to]
42
- from = to = (to + 1)
43
- else
44
- nest_i, nest_inc = nesting[char]
45
- if nest_i
46
- counts = counts.dup if counts.frozen?
47
- counts[nest_i] += nest_inc
33
+ # yield each key part
34
+ # dots inside braces or parenthesis are not split on
35
+ def key_parts(key, &block)
36
+ return enum_for(:key_parts, key) unless block
37
+ nesting = PARENS
38
+ counts = PARENS_ZEROS # dup'd later if key contains parenthesis
39
+ delim = '.'.freeze
40
+ from = to = 0
41
+ key.each_char do |char|
42
+ if char == delim && PARENS_ZEROS == counts
43
+ block.yield key[from...to]
44
+ from = to = (to + 1)
45
+ else
46
+ nest_i, nest_inc = nesting[char]
47
+ if nest_i
48
+ counts = counts.dup if counts.frozen?
49
+ counts[nest_i] += nest_inc
50
+ end
51
+ to += 1
52
+ end
48
53
  end
49
- to += 1
54
+ block.yield(key[from...to]) if from < to && to <= key.length
55
+ true
50
56
  end
57
+
58
+ PARENS = %w({} [] ()).inject({}) { |h, s|
59
+ i = h.size / 2
60
+ h[s[0].freeze] = [i, 1].freeze
61
+ h[s[1].freeze] = [i, -1].freeze
62
+ h
63
+ }.freeze
64
+ PARENS_ZEROS = Array.new(PARENS.size, 0).freeze
65
+ private_constant :PARENS
66
+ private_constant :PARENS_ZEROS
51
67
  end
52
- block.yield(key[from...to]) if from < to && to <= key.length
53
- true
54
68
  end
55
-
56
- PARENS = %w({} [] ()).inject({}) { |h, s|
57
- i = h.size / 2
58
- h[s[0].freeze] = [i, 1].freeze
59
- h[s[1].freeze] = [i, -1].freeze
60
- h
61
- }.freeze
62
- PARENS_ZEROS = Array.new(PARENS.size, 0).freeze
63
- private_constant :PARENS
64
- private_constant :PARENS_ZEROS
65
69
  end
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
  module I18n
3
3
  module Tasks
4
- VERSION = '0.7.6'
4
+ VERSION = '0.7.7'
5
5
  end
6
6
  end
@@ -5,6 +5,17 @@ require 'fileutils'
5
5
  describe 'i18n-tasks' do
6
6
  delegate :run_cmd, :i18n_task, :in_test_app_dir, to: :TestCodebase
7
7
 
8
+ describe 'health' do
9
+ it 'outputs stats' do
10
+ t = i18n_task
11
+ stats = in_test_app_dir { t.forest_stats(t.data_forest t.locales) }
12
+ out = capture_stderr { run_cmd :health }
13
+ stats.values.each do |v|
14
+ expect(out).to include(v.to_s)
15
+ end
16
+ end
17
+ end
18
+
8
19
  describe 'missing' do
9
20
  let (:expected_missing_keys) {
10
21
  %w( en.used_but_missing.key en.relative.index.missing
@@ -57,7 +68,7 @@ describe 'i18n-tasks' do
57
68
  it 'removes unused' do
58
69
  in_test_app_dir do
59
70
  t = i18n_task
60
- unused = expected_unused_keys.map { |k| SplitKey.split_key(k, 2)[1] }
71
+ unused = expected_unused_keys.map { |k| ::I18n::Tasks::SplitKey.split_key(k, 2)[1] }
61
72
  unused.each do |key|
62
73
  expect(t.key_value?(key, :en)).to be true
63
74
  expect(t.key_value?(key, :es)).to be true
@@ -76,6 +87,21 @@ describe 'i18n-tasks' do
76
87
  end
77
88
 
78
89
  describe 'normalize' do
90
+ it 'sorts the keys' do
91
+ in_test_app_dir do
92
+ run_cmd :normalize
93
+ en_yml_data = i18n_task.data.reload['en'].select_keys { |_k, node|
94
+ node.data[:path] == 'config/locales/en.yml'
95
+ }
96
+ expect(en_yml_data).to be_present
97
+ en_yml_data.nodes { |nodes|
98
+ next unless nodes.children
99
+ keys = nodes.children.map(&:key)
100
+ expect(keys).to eq keys.sort
101
+ }
102
+ end
103
+ end
104
+
79
105
  it 'moves keys to the corresponding files as per data.write' do
80
106
  in_test_app_dir {
81
107
  expect(File).to_not exist 'config/locales/devise.en.yml'
@@ -87,5 +87,10 @@ describe 'Tree siblings / forest' do
87
87
  t['a.b.c.' + node.key] = node
88
88
  expect(t['a.b.c.d'].value).to eq('e')
89
89
  end
90
+
91
+ it '#inspect' do
92
+ expect(build_tree(a_hash).inspect).to eq "a: 1\nb\n ba: 1\n bb: 2"
93
+ expect(build_tree({}).inspect).to eq '{∅}'
94
+ end
90
95
  end
91
96
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'SplitKey' do
4
- include SplitKey
4
+ include ::I18n::Tasks::SplitKey
5
5
 
6
6
  [['', %w()],
7
7
  ['a', %w(a)],
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.6
4
+ version: 0.7.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-13 00:00:00.000000000 Z
11
+ date: 2014-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubis
@@ -195,8 +195,9 @@ dependencies:
195
195
  description: |
196
196
  i18n-tasks helps you find and manage missing and unused translations.
197
197
 
198
- It scans calls such as `I18n.t('some.key')` and provides reports on key usage, missing, and unused keys.
199
- It can also can pre-fill missing keys, including from Google Translate, and it can remove unused keys as well.
198
+ It analyses code statically for key usages, such as `I18n.t('some.key')`, in order to report keys that are missing or unused,
199
+ pre-fill missing keys (optionally from Google Translate), and remove unused keys.
200
+ well.
200
201
  email:
201
202
  - glex.spb@gmail.com
202
203
  executables:
@@ -319,7 +320,6 @@ post_install_message: |
319
320
  cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
320
321
  # Add an RSpec for missing and unused keys:
321
322
  cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
322
- }
323
323
  rdoc_options: []
324
324
  require_paths:
325
325
  - lib