i18n-tasks 0.7.6 → 0.7.7

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: 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