i18n-tasks 0.8.5 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/CHANGES.md +9 -0
  4. data/README.md +3 -1
  5. data/bin/i18n-tasks.cmd +2 -0
  6. data/config/locales/en.yml +2 -2
  7. data/i18n-tasks.gemspec +1 -1
  8. data/lib/i18n/tasks/cli.rb +3 -1
  9. data/lib/i18n/tasks/command/commands/health.rb +1 -2
  10. data/lib/i18n/tasks/command/commands/missing.rb +3 -6
  11. data/lib/i18n/tasks/command/commands/usages.rb +4 -2
  12. data/lib/i18n/tasks/console_context.rb +1 -2
  13. data/lib/i18n/tasks/data/tree/node.rb +8 -1
  14. data/lib/i18n/tasks/data/tree/nodes.rb +1 -1
  15. data/lib/i18n/tasks/data/tree/siblings.rb +29 -11
  16. data/lib/i18n/tasks/data/tree/traversal.rb +24 -6
  17. data/lib/i18n/tasks/missing_keys.rb +32 -14
  18. data/lib/i18n/tasks/reports/base.rb +9 -0
  19. data/lib/i18n/tasks/reports/spreadsheet.rb +7 -4
  20. data/lib/i18n/tasks/reports/terminal.rb +2 -1
  21. data/lib/i18n/tasks/scanners/base_scanner.rb +4 -2
  22. data/lib/i18n/tasks/version.rb +1 -1
  23. data/spec/commands/data_commands_spec.rb +1 -1
  24. data/spec/commands/tree_commands_spec.rb +1 -1
  25. data/spec/conservative_router_spec.rb +1 -1
  26. data/spec/file_system_data_spec.rb +1 -1
  27. data/spec/google_translate_spec.rb +1 -1
  28. data/spec/i18n_spec.rb +1 -1
  29. data/spec/i18n_tasks_spec.rb +34 -24
  30. data/spec/key_pattern_matching_spec.rb +1 -1
  31. data/spec/locale_pathname_spec.rb +1 -1
  32. data/spec/locale_tree/siblings_spec.rb +1 -1
  33. data/spec/pattern_scanner_spec.rb +1 -1
  34. data/spec/plural_keys_spec.rb +1 -1
  35. data/spec/readme_spec.rb +1 -1
  36. data/spec/relative_keys_spec.rb +1 -1
  37. data/spec/spec_helper.rb +1 -0
  38. data/spec/split_key_spec.rb +1 -1
  39. data/spec/support/i18n_tasks_output_matcher.rb +2 -3
  40. data/spec/support/test_codebase.rb +8 -0
  41. data/spec/support/trees.rb +5 -0
  42. data/spec/used_keys_spec.rb +7 -7
  43. data/templates/config/i18n-tasks.yml +4 -3
  44. data/templates/rspec/i18n_spec.rb +1 -1
  45. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 82cf5495a5f1756309ed77f51f8425c0b0c6d86e
4
- data.tar.gz: 5169466a107b4260ee26d43e6f25126d3a9a88b0
3
+ metadata.gz: a70e643cadbeb135db42081bdf2b9ea9ed7d5163
4
+ data.tar.gz: 99b5ba4d17e4d93b646895172faa2a74d982d006
5
5
  SHA512:
6
- metadata.gz: 8c80cedb3419bf2b25b4de1331ad74be439db29f93526f7f9e66a853b873e8e331357b64f55ffb9a9fed987f01648a8baa39cfd9cb22ee7d7f1c35d2cdf72ac3
7
- data.tar.gz: b9894a72294a25dd552d2ad5dcd44df63687df41a2c22ae067ab1f0f0927abc8d60f9bdba623079a654b1c2f3b4baf1182973c47c7db84627cbcdf2147b047bd
6
+ metadata.gz: 15800850eefad7aea706c8275ac804976bff7047d84d13c1ee3a1b33b53e91215ac55515e5c74b7d93b02dd9bb764ce34a4076f88668899620dad141e87a8a65
7
+ data.tar.gz: 55820e43acb2cc6e033fee27cc0abc19cfe945c1334ba18aed67c5b03c27a96adba8ca0b6dc89c79ee4e1b5f2da36367a7c0b2914604c6de494a41302dad5633
@@ -0,0 +1,2 @@
1
+ * -lf
2
+ *.cmd -crlf
data/CHANGES.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.8.6
2
+
3
+ * Report missing keys found in source in all the locales. [#162](https://github.com/glebm/i18n-tasks/issues/162)
4
+ * Fix `data-remove` task. [#140](https://github.com/glebm/i18n-tasks/issues/140)
5
+ * Non-zero exit code on `health`, `missing`, and `unused` if such keys are present. [#151](https://github.com/glebm/i18n-tasks/issues/151)
6
+ * XLSX report compatibility with the OSX Numbers App. [#159](https://github.com/glebm/i18n-tasks/issues/159)
7
+ * RSpec template compatibility with `config.expose_dsl_globally = false`. [#148](https://github.com/glebm/i18n-tasks/issues/148)
8
+ * `bundle show vagrant` example in the config template is no longer interpolated .[#161](https://github.com/glebm/i18n-tasks/issues/161)
9
+
1
10
  ## 0.8.5
2
11
 
3
12
  * Fix regression: Plugin support [#153](https://github.com/glebm/i18n-tasks/issues/153).
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # i18n-tasks [![Build Status][badge-travis]][travis] [![Coverage Status][badge-coverage]][coverage] [![Code Climate][badge-code-climate]][code-climate] [![Gemnasium][badge-gemnasium]][gemnasium] [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/glebm/i18n-tasks?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2
2
 
3
+ <a href='https://pledgie.com/campaigns/28570'><img alt='Click here to lend your support to: i18n-tasks for Ruby and make a donation at pledgie.com !' src='https://pledgie.com/campaigns/28570.png?skin_name=chrome' border='0' ></a>
4
+
3
5
  i18n-tasks helps you find and manage missing and unused translations.
4
6
 
5
7
  <img width="539" height="331" src="https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-tasks.png">
@@ -22,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
22
24
  Add i18n-tasks to the Gemfile:
23
25
 
24
26
  ```ruby
25
- gem 'i18n-tasks', '~> 0.8.3'
27
+ gem 'i18n-tasks', '~> 0.8.6'
26
28
  ```
27
29
 
28
30
  Copy the default [configuration file](#configuration):
@@ -0,0 +1,2 @@
1
+ @echo off
2
+ ruby %~dp0\i18n-tasks %*
@@ -15,7 +15,7 @@ en:
15
15
  key_pattern_to_rename: Full key (pattern) to rename. Required
16
16
  locale: Locale
17
17
  locale_to_translate_from: Locale to translate from
18
- locales_filter: "Locale(s) to process. Special: base"
18
+ locales_filter: 'Locale(s) to process. Special: base'
19
19
  missing_types: 'Filter by types: %{valid}'
20
20
  new_key_name: New name, interpolates original name as %{key}. Required
21
21
  nostdin: Do not read from stdin
@@ -90,7 +90,7 @@ en:
90
90
  none: No translations are missing.
91
91
  remove_unused:
92
92
  confirm:
93
- one: One translations will be removed from %{locales}.
93
+ one: "%{count} translation will be removed from %{locales}."
94
94
  other: "%{count} translation will be removed from %{locales}."
95
95
  noop: No unused keys to remove
96
96
  removed: Removed %{count} keys
@@ -43,6 +43,6 @@ TEXT
43
43
  s.add_development_dependency 'axlsx', '~> 2.0'
44
44
  s.add_development_dependency 'bundler', '~> 1.3'
45
45
  s.add_development_dependency 'rake'
46
- s.add_development_dependency 'rspec', '~> 3.0'
46
+ s.add_development_dependency 'rspec', '~> 3.3'
47
47
  s.add_development_dependency 'yard'
48
48
  end
@@ -15,7 +15,9 @@ class I18n::Tasks::CLI
15
15
  def start(argv)
16
16
  auto_output_coloring do
17
17
  begin
18
- run(argv)
18
+ if run(argv) == :exit_1
19
+ exit 1
20
+ end
19
21
  rescue OptionParser::ParseError => e
20
22
  error e.message, 64
21
23
  rescue I18n::Tasks::CommandError => e
@@ -16,8 +16,7 @@ module I18n::Tasks
16
16
  raise CommandError.new t('i18n_tasks.health.no_keys_detected')
17
17
  end
18
18
  terminal_report.forest_stats forest, stats
19
- missing opt
20
- unused opt
19
+ [missing(opt), unused(opt)].detect { |result| result == :exit_1 }
21
20
  end
22
21
  end
23
22
  end
@@ -23,7 +23,9 @@ module I18n::Tasks
23
23
  args: [:locales, :out_format, :missing_types]
24
24
 
25
25
  def missing(opt = {})
26
- print_forest i18n.missing_keys(opt), opt, :missing_keys
26
+ forest = i18n.missing_keys(opt)
27
+ print_forest forest, opt, :missing_keys
28
+ :exit_1 unless forest.empty?
27
29
  end
28
30
 
29
31
  cmd :translate_missing,
@@ -47,11 +49,6 @@ module I18n::Tasks
47
49
  def add_missing(opt = {})
48
50
  forest = i18n.missing_keys(opt).set_each_value!(opt[:value])
49
51
  i18n.data.merge! forest
50
- # missing keys detected in the source are only returned in the base locale tree
51
- # merge again in case such keys have been added to add them to other locales
52
- forest_2 = i18n.missing_keys(opt).set_each_value!(opt[:value])
53
- i18n.data.merge! forest_2
54
- forest.merge! forest_2
55
52
  log_stderr t('i18n_tasks.add_missing.added', count: forest.leaves.count)
56
53
  print_forest forest, opt
57
54
  end
@@ -25,7 +25,9 @@ module I18n::Tasks
25
25
  args: [:locales, :out_format, :strict]
26
26
 
27
27
  def unused(opt = {})
28
- print_forest i18n.unused_keys(opt), opt, :unused_keys
28
+ forest = i18n.unused_keys(opt)
29
+ print_forest forest, opt, :unused_keys
30
+ :exit_1 unless forest.empty?
29
31
  end
30
32
 
31
33
  cmd :remove_unused,
@@ -50,7 +52,7 @@ module I18n::Tasks
50
52
 
51
53
  def confirm_remove_unused!(unused_keys, opt)
52
54
  return if ENV['CONFIRM'] || opt[:confirm]
53
- locales = bold(opt[:locales] * ', ')
55
+ locales = bold(unused_keys.flat_map { |root| root.key.split('+') }.sort.uniq * ', ')
54
56
  msg = [
55
57
  red(t('i18n_tasks.remove_unused.confirm', count: unused_keys.leaves.count, locales: locales)),
56
58
  yellow(t('i18n_tasks.common.continue_q')),
@@ -36,13 +36,12 @@ module I18n::Tasks
36
36
  green(bold "i18n-tasks IRB Quick Start guide") + "\n" + <<-TEXT
37
37
  #{yellow 'Data as trees'}
38
38
  tree(locale)
39
- missing_tree(locale, compared_to = base_locale)
40
39
  used_tree(source_occurrences: false, key_filter: nil)
41
40
  unused_tree(locale)
42
41
  build_tree('es' => {'hello' => 'Hola'})
43
42
 
44
43
  #{yellow 'Traversal'}
45
- tree = missing_tree(base_locale)
44
+ tree = missing_diff_tree('es')
46
45
  tree.nodes { |node| }
47
46
  tree.nodes.to_a
48
47
  tree.leaves { |node| }
@@ -63,7 +63,7 @@ module I18n::Tasks::Data::Tree
63
63
  end
64
64
 
65
65
  def children?
66
- children && children.any?
66
+ children && !children.empty?
67
67
  end
68
68
 
69
69
  def data
@@ -119,6 +119,13 @@ module I18n::Tasks::Data::Tree
119
119
  end
120
120
  end
121
121
 
122
+ def set(full_key, node)
123
+ (@children ||= Siblings.new(parent: self)).set(full_key, node)
124
+ dirty!
125
+ node
126
+ end
127
+ alias []= set
128
+
122
129
  def to_nodes
123
130
  Nodes.new([self])
124
131
  end
@@ -79,7 +79,7 @@ module I18n::Tasks::Data::Tree
79
79
  alias + merge!
80
80
 
81
81
  def children(&block)
82
- return to_enum(:children) { map { |c| c.children.size }.reduce(:+) } unless block
82
+ return to_enum(:children) { map { |c| c.children ? c.children.size : 0 }.reduce(:+) } unless block
83
83
  each do |node|
84
84
  node.children.each(&block) if node.children?
85
85
  end
@@ -1,11 +1,15 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'set'
4
+ require 'i18n/tasks/split_key'
3
5
  require 'i18n/tasks/data/tree/nodes'
4
6
  module I18n::Tasks::Data::Tree
5
7
  # Siblings represents a subtree sharing a common parent
6
8
  # in case of an empty parent (nil) it represents a forest
7
9
  # siblings' keys are unique
8
10
  class Siblings < Nodes
11
+ include ::I18n::Tasks::SplitKey
12
+
9
13
  attr_reader :parent, :key_to_node
10
14
 
11
15
  def initialize(opts = {})
@@ -44,8 +48,6 @@ module I18n::Tasks::Data::Tree
44
48
  key_to_node[new_node.key] = new_node
45
49
  end
46
50
 
47
- include ::I18n::Tasks::SplitKey
48
-
49
51
  # @return [Node] by full key
50
52
  def get(full_key)
51
53
  first_key, rest = split_key(full_key.to_s, 2)
@@ -121,15 +123,12 @@ module I18n::Tasks::Data::Tree
121
123
  end
122
124
 
123
125
  def subtract_keys(keys)
124
- exclude = {}
126
+ to_remove = Set.new
125
127
  keys.each do |full_key|
126
- if (node = get full_key)
127
- exclude[node] = true
128
- end
128
+ node = get full_key
129
+ to_remove << node if node
129
130
  end
130
- select_nodes { |node|
131
- not exclude[node] || node.children.try(:all?) { |c| exclude[c] }
132
- }
131
+ remove_nodes_collapsing_emptied_ancestors to_remove
133
132
  end
134
133
 
135
134
  def subtract_by_key(other)
@@ -143,8 +142,6 @@ module I18n::Tasks::Data::Tree
143
142
  self
144
143
  end
145
144
 
146
- private
147
-
148
145
  def merge_node!(node)
149
146
  if key_to_node.key?(node.key)
150
147
  our = key_to_node[node.key]
@@ -164,6 +161,27 @@ module I18n::Tasks::Data::Tree
164
161
  end
165
162
  end
166
163
 
164
+ # @param [Enumerable] nodes. Modified in-place.
165
+ def remove_nodes_collapsing_emptied_ancestors(nodes)
166
+ add_ancestors_that_only_contain_nodes! nodes
167
+ select_nodes { |node| !nodes.include?(node) }
168
+ end
169
+
170
+ # @param [Enumerable] nodes. Modified in-place.
171
+ def remove_nodes_collapsing_emptied_ancestors!(nodes)
172
+ add_ancestors_that_only_contain_nodes! nodes
173
+ select_nodes! { |node| !nodes.include?(node) }
174
+ end
175
+
176
+ private
177
+
178
+ # @param [Set] nodes. Modified in-place.
179
+ def add_ancestors_that_only_contain_nodes!(nodes)
180
+ levels.reverse_each do |level_nodes|
181
+ level_nodes.each { |node| nodes << node if node.children? && node.children.all? { |c| nodes.include?(c) } }
182
+ end
183
+ end
184
+
167
185
  def warn_add_children_to_leaf(node)
168
186
  ::I18n::Tasks::Logging.log_warn "'#{node.full_key}' was a leaf, now has children (value <- scope conflict)"
169
187
  end
@@ -18,14 +18,15 @@ module I18n::Tasks
18
18
 
19
19
  def levels(&block)
20
20
  return to_enum(:levels) unless block
21
- nodes = to_nodes
21
+ nodes = to_nodes
22
22
  unless nodes.empty?
23
23
  block.yield nodes
24
- if nodes.children.size == 1
25
- first.children
24
+ if nodes.size == 1
25
+ node = first
26
+ node.children.levels(&block) if node.children?
26
27
  else
27
- Nodes.new(nodes: nodes.children)
28
- end.levels(&block)
28
+ Nodes.new(nodes: nodes.children).levels(&block)
29
+ end
29
30
  end
30
31
  self
31
32
  end
@@ -76,7 +77,8 @@ module I18n::Tasks
76
77
 
77
78
  #-- modify / derive
78
79
 
79
- # @return Siblings
80
+ # Select the nodes for which the block returns true. Pre-order traversal.
81
+ # @return [Siblings] a new tree
80
82
  def select_nodes(&block)
81
83
  tree = Siblings.new
82
84
  each do |node|
@@ -90,6 +92,22 @@ module I18n::Tasks
90
92
  tree
91
93
  end
92
94
 
95
+ # Select the nodes for which the block returns true. Pre-order traversal.
96
+ # @return [Siblings] self
97
+ def select_nodes!(&block)
98
+ to_remove = []
99
+ each do |node|
100
+ if block.yield(node)
101
+ node.children.select_nodes!(&block) if node.children
102
+ else
103
+ # removing during each is unsafe
104
+ to_remove << node
105
+ end
106
+ end
107
+ to_remove.each { |node| remove! node }
108
+ self
109
+ end
110
+
93
111
  # @return Siblings
94
112
  def select_keys(opts = {}, &block)
95
113
  root = opts.key?(:root) ? opts[:root] : false
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+ require 'set'
2
3
  module I18n::Tasks
3
4
  module MissingKeys
4
5
 
@@ -48,20 +49,10 @@ module I18n::Tasks
48
49
  tree
49
50
  end
50
51
 
51
- def missing_used_forest(locales, base = base_locale)
52
- if locales.include?(base)
53
- missing_used_tree(base)
54
- else
55
- empty_forest
56
- end
57
- end
58
-
59
- def missing_tree(locale, compared_to)
60
- if locale == compared_to
61
- missing_used_tree locale
62
- else
63
- missing_diff_tree locale, compared_to
64
- end
52
+ def missing_used_forest(locales, _base = base_locale)
53
+ locales.inject(empty_forest) { |forest, locale|
54
+ forest.merge! missing_used_tree(locale)
55
+ }
65
56
  end
66
57
 
67
58
  # keys present in compared_to, but not in locale
@@ -96,5 +87,32 @@ module I18n::Tasks
96
87
  def locale_key_missing?(locale, key)
97
88
  !key_value?(key, locale) && !ignore_key?(key, :missing)
98
89
  end
90
+
91
+ # @param [::I18n::Tasks::Data::Tree::Siblings] forest
92
+ def collapse_missing_used_locales!(forest)
93
+ locales_and_nodes_by_key = {}
94
+ to_remove = []
95
+ forest.each do |root|
96
+ locale = root.key
97
+ root.leaves { |node|
98
+ if node.data[:type] == :missing_used
99
+ (locales_and_nodes_by_key[node.full_key(root: false)] ||= []) << [locale, node]
100
+ to_remove << node
101
+ end
102
+ }
103
+ end
104
+ forest.remove_nodes_collapsing_emptied_ancestors! to_remove
105
+ keys_and_nodes_by_locale = {}
106
+ locales_and_nodes_by_key.each { |key, locales_and_nodes|
107
+ locales = locales_and_nodes.map(&:first).sort.join('+')
108
+ (keys_and_nodes_by_locale[locales] ||= []) << [key, locales_and_nodes[0][1]]
109
+ }
110
+ keys_and_nodes_by_locale.map { |locales, keys_nodes|
111
+ keys_nodes.each { |(key, node)|
112
+ forest["#{locales}.#{key}"] = node
113
+ }
114
+ }
115
+ forest
116
+ end
99
117
  end
100
118
  end
@@ -51,5 +51,14 @@ module I18n::Tasks::Reports
51
51
  {key: key, value: node.value, type: node.data[:type], locale: node.root.key, data: node.data}
52
52
  }
53
53
  end
54
+
55
+ def format_locale(locale)
56
+ return '' unless locale
57
+ if locale.split('+') == task.locales.sort
58
+ 'all'
59
+ else
60
+ locale.tr '+', ' '
61
+ end
62
+ end
54
63
  end
55
64
  end
@@ -8,6 +8,7 @@ module I18n::Tasks::Reports
8
8
  def save_report(path, opts)
9
9
  path = path.presence || 'tmp/i18n-report.xlsx'
10
10
  p = Axlsx::Package.new
11
+ p.use_shared_strings = true # see #159
11
12
  add_missing_sheet p.workbook
12
13
  add_unused_sheet p.workbook
13
14
  add_eq_base_sheet p.workbook
@@ -19,17 +20,19 @@ module I18n::Tasks::Reports
19
20
  private
20
21
 
21
22
  def add_missing_sheet(wb)
22
- tree = task.missing_keys
23
+ forest = task.missing_keys
24
+ forest = task.collapse_plural_nodes!(forest)
25
+ forest = task.collapse_missing_used_locales!(forest)
23
26
  wb.styles do |s|
24
27
  type_cell = s.add_style :alignment => {:horizontal => :center}
25
28
  locale_cell = s.add_style :alignment => {:horizontal => :center}
26
29
  regular_style = s.add_style
27
- wb.add_worksheet(name: missing_title(tree)) { |sheet|
30
+ wb.add_worksheet(name: missing_title(forest)) { |sheet|
28
31
  sheet.page_setup.fit_to :width => 1
29
32
  sheet.add_row [I18n.t('i18n_tasks.common.type'), I18n.t('i18n_tasks.common.locale'), I18n.t('i18n_tasks.common.key'), I18n.t('i18n_tasks.common.base_value')]
30
33
  style_header sheet
31
- tree.keys do |key, node|
32
- locale, type = node.root.data[:locale], node.data[:type]
34
+ forest.keys do |key, node|
35
+ locale, type = format_locale(node.root.data[:locale]), node.data[:type]
33
36
  sheet.add_row [missing_type_info(type)[:summary], locale, key, task.t(key)],
34
37
  styles: [type_cell, locale_cell, regular_style, regular_style]
35
38
  end
@@ -9,13 +9,14 @@ module I18n
9
9
 
10
10
  def missing_keys(forest = task.missing_keys)
11
11
  forest = task.collapse_plural_nodes!(forest)
12
+ forest = task.collapse_missing_used_locales!(forest)
12
13
  if forest.present?
13
14
  print_title missing_title(forest)
14
15
  print_table headings: [cyan(bold(I18n.t('i18n_tasks.common.locale'))),
15
16
  cyan(bold I18n.t('i18n_tasks.common.key')),
16
17
  I18n.t('i18n_tasks.missing.details_title')] do |t|
17
18
  t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
18
- [{value: cyan(a[:locale]), alignment: :center}, cyan(a[:key]), missing_key_info(a)]
19
+ [{value: cyan(format_locale(a[:locale])), alignment: :center}, cyan(a[:key]), missing_key_info(a)]
19
20
  end
20
21
  end
21
22
  else
@@ -15,7 +15,9 @@ module I18n::Tasks::Scanners
15
15
 
16
16
  def initialize(config = {})
17
17
  @config = config.dup.with_indifferent_access.tap do |conf|
18
- conf[:relative_roots] = %w(app/views app/controllers app/helpers app/presenters) if conf[:relative_roots].blank?
18
+ if conf[:relative_roots].blank?
19
+ conf[:relative_roots] = %w(app/controllers app/helpers app/mailers app/presenters app/views)
20
+ end
19
21
  conf[:paths] = %w(app/) if conf[:paths].blank?
20
22
  conf[:include] = Array(conf[:include]) if conf[:include].present?
21
23
  conf[:exclude] = Array(conf[:exclude]) + ALWAYS_EXCLUDE
@@ -109,7 +111,7 @@ module I18n::Tasks::Scanners
109
111
  data = {src_path: path}
110
112
  if position
111
113
  line_begin = text.rindex(/^/, src_pos - 1)
112
- line_end = text.index(/.(?=\n|$)/, src_pos)
114
+ line_end = text.index(/.(?=\r?\n|$)/, src_pos)
113
115
  data.merge! pos: src_pos,
114
116
  line_num: text[0..src_pos].count("\n") + 1,
115
117
  line_pos: src_pos - line_begin + 1,
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
  module I18n
3
3
  module Tasks
4
- VERSION = '0.8.5'
4
+ VERSION = '0.8.6'
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'Data commands' do
3
+ RSpec.describe 'Data commands' do
4
4
  delegate :run_cmd, to: :TestCodebase
5
5
  def en_data
6
6
  {'en' => {'a' => '1', 'common' => {'hello' => 'Hello'}}}
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'Tree commands' do
3
+ RSpec.describe 'Tree commands' do
4
4
  delegate :run_cmd, to: :TestCodebase
5
5
  before do
6
6
  TestCodebase.setup
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- describe 'Conservative router' do
4
+ RSpec.describe 'Conservative router' do
5
5
  describe '#available_locales' do
6
6
  before do
7
7
  TestCodebase.setup(
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- describe 'File system i18n' do
4
+ RSpec.describe 'File system i18n' do
5
5
  describe '#available_locales' do
6
6
  before do
7
7
  TestCodebase.setup(
@@ -2,7 +2,7 @@
2
2
  require 'spec_helper'
3
3
  require 'i18n/tasks/commands'
4
4
 
5
- describe 'Google Translation' do
5
+ RSpec.describe 'Google Translation' do
6
6
  include I18n::Tasks::GoogleTranslation
7
7
 
8
8
  tests = [
@@ -1,6 +1,6 @@
1
1
  require 'i18n/tasks'
2
2
 
3
- describe 'I18n' do
3
+ RSpec.describe 'I18n' do
4
4
  let(:i18n) { I18n::Tasks::BaseTask.new }
5
5
  let(:missing_keys) { i18n.missing_keys }
6
6
  let(:unused_keys) { i18n.unused_keys }
@@ -4,8 +4,9 @@ require 'fileutils'
4
4
  require 'open3'
5
5
 
6
6
  # Integration tests
7
- describe 'i18n-tasks' do
8
- delegate :run_cmd, :run_cmd_capture_stderr, :i18n_task, :in_test_app_dir, to: :TestCodebase
7
+ RSpec.describe 'i18n-tasks' do
8
+ delegate :run_cmd, :run_cmd_capture_stdout_and_result, :run_cmd_capture_stderr, :i18n_task, :in_test_app_dir,
9
+ to: :TestCodebase
9
10
 
10
11
  describe 'bin/i18n-tasks' do
11
12
  it 'shows help when invoked with no arguments, shows version on --version' do
@@ -43,29 +44,36 @@ describe 'i18n-tasks' do
43
44
  end
44
45
 
45
46
  describe 'missing' do
46
- let (:expected_missing_keys) {
47
- %w( en.used_but_missing.key
48
- en.relative.index.missing
49
- es.missing_in_es.a
50
- en.present_in_es_but_not_en.a
51
- en.hash.pattern_missing.a
52
- en.hash.pattern_missing.b
53
- en.missing_symbol_key
54
- en.missing_symbol.key_two
55
- en.missing_symbol.key_three
56
- es.missing_in_es_plural_1.a
57
- es.missing_in_es_plural_2.a
58
- en.missing-key-with-a-dash.key
59
- en.missing-key-question?.key
60
- en.fn_comment
61
- en.only_in_es
62
- en.events.show.success
63
- )
47
+ let (:expected_missing_keys_in_source) {
48
+ %w(
49
+ used_but_missing.key
50
+ relative.index.missing
51
+ hash.pattern_missing.a
52
+ hash.pattern_missing.b
53
+ missing_symbol_key
54
+ missing_symbol.key_two
55
+ missing_symbol.key_three
56
+ missing-key-with-a-dash.key
57
+ missing-key-question?.key
58
+ fn_comment
59
+ events.show.success
60
+ )
61
+ }
62
+ let (:expected_missing_keys_diff) {
63
+ %w(
64
+ es.missing_in_es.a
65
+ en.present_in_es_but_not_en.a
66
+ es.missing_in_es_plural_1.a
67
+ es.missing_in_es_plural_2.a
68
+ en.only_in_es
69
+ )
64
70
  }
65
71
  it 'detects missing' do
66
- es_keys = expected_missing_keys.grep(/^es\./)
67
- expect(run_cmd 'missing').to be_i18n_keys expected_missing_keys
68
- # locale argument
72
+ es_keys = expected_missing_keys_diff.grep(/^es\./) + expected_missing_keys_in_source.map { |k| "es.#{k}" }
73
+ out, result = run_cmd_capture_stdout_and_result 'missing'
74
+ expect(result).to eq :exit_1
75
+ expect(out).to be_i18n_keys(expected_missing_keys_diff +
76
+ expected_missing_keys_in_source.map { |k| "all.#{k}" })
69
77
  expect(run_cmd 'missing', '-les').to be_i18n_keys es_keys
70
78
  expect(run_cmd 'missing', 'es').to be_i18n_keys es_keys
71
79
  end
@@ -91,7 +99,9 @@ describe 'i18n-tasks' do
91
99
 
92
100
  describe 'unused' do
93
101
  it 'detects unused' do
94
- expect(run_cmd 'unused').to be_i18n_keys expected_unused_keys
102
+ out, result = run_cmd_capture_stdout_and_result 'unused'
103
+ expect(result).to eq :exit_1
104
+ expect(out).to be_i18n_keys expected_unused_keys
95
105
  end
96
106
 
97
107
  it 'detects unused (--strict)' do
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- describe 'Key pattern' do
4
+ RSpec.describe 'Key pattern' do
5
5
  include I18n::Tasks::KeyPatternMatching
6
6
  describe 'matching' do
7
7
  describe '*' do
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'LocalePathname' do
3
+ RSpec.describe 'LocalePathname' do
4
4
  include ::I18n::Tasks::LocalePathname
5
5
  context '#replace_locale' do
6
6
 
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- describe 'Tree siblings / forest' do
4
+ RSpec.describe 'Tree siblings / forest' do
5
5
 
6
6
  context 'Node' do
7
7
  it '::new with children' do
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- describe 'Pattern Scanner' do
4
+ RSpec.describe 'Pattern Scanner' do
5
5
  describe 'scan_file' do
6
6
  it 'returns absolute keys from controllers' do
7
7
  file_path = 'spec/fixtures/app/controllers/events_controller.rb'
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- describe 'Plural keys' do
4
+ RSpec.describe 'Plural keys' do
5
5
  let(:task) { ::I18n::Tasks::BaseTask.new }
6
6
  before do
7
7
  TestCodebase.setup('config/locales/en.yml' => '')
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
- describe 'README.md' do
3
+ RSpec.describe 'README.md' do
4
4
  let(:readme) { File.read('README.md', encoding: 'UTF-8') }
5
5
  it 'has valid YAML in ```yaml blocks' do
6
6
  readme.scan /```yaml\n(.*)(?=^)\n```/ do |m|
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
- describe 'Relative keys' do
3
+ RSpec.describe 'Relative keys' do
4
4
  let(:scanner) { I18n::Tasks::Scanners::BaseScanner.new }
5
5
 
6
6
  describe 'absolutize_key' do
@@ -17,6 +17,7 @@ Term::ANSIColor::coloring = false
17
17
  Dir['spec/support/**/*.rb'].each { |f| require "./#{f}" }
18
18
 
19
19
  RSpec.configure do |config|
20
+ config.expose_dsl_globally = false
20
21
  config.include FixturesSupport
21
22
  config.include CaptureStd
22
23
  config.include Trees
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'SplitKey' do
3
+ RSpec.describe 'SplitKey' do
4
4
  include ::I18n::Tasks::SplitKey
5
5
 
6
6
  [['', %w()],
@@ -8,14 +8,13 @@ RSpec::Matchers.define :be_i18n_keys do |expected|
8
8
  actual = Term::ANSIColor.uncolor(actual).split("\n").map(&:presence).compact
9
9
  actual = actual[3..-2]
10
10
  actual = actual.map { |row|
11
- next if row =~ /^\|\s+\|/
12
- row.gsub(/(?:\s|^)\|(?:\s|$)/, ' ').gsub(/\s+/, ' ').strip.split(' ').map(&:presence).compact
11
+ row[1..-1].gsub(/(?:\s+|^)\|(?:\s+|$)/, '|').gsub(/\s+/, ' ').strip.split(/\s*\|\s*/)
13
12
  }.compact
14
13
  return [] if actual.empty?
15
14
  locale_col = 0
16
15
  key_col = 1
17
16
  actual.map { |row|
18
- key = "#{row[locale_col]}.#{row[key_col]}"
17
+ key = [row[locale_col], row[key_col]].map(&:presence).compact.join('.')
19
18
  key = key[0..-2] if key.end_with?(':')
20
19
  key
21
20
  }.compact
@@ -22,6 +22,14 @@ module TestCodebase
22
22
  } } }
23
23
  end
24
24
 
25
+ def run_cmd_capture_stdout_and_result(name, *args)
26
+ result = nil
27
+ out = capture_stdout { capture_stderr { in_test_app_dir {
28
+ result = run_cli(name, *args)
29
+ } } }
30
+ [out, result]
31
+ end
32
+
25
33
  def run_cmd_capture_stderr(name, *args)
26
34
  capture_stderr { capture_stdout { in_test_app_dir {
27
35
  run_cli(name, *args)
@@ -2,6 +2,11 @@
2
2
  module Trees
3
3
  def expect_node_key_data(node, key, data)
4
4
  expect(node.full_key(root: false)).to eq key
5
+ if Gem.win_platform?
6
+ # adjust position to account for \r on Windows
7
+ data = data.dup
8
+ data[:source_occurrences].map! { |occ| occ.dup.tap { |o| o[:pos] += o[:line_num] - 1 } }
9
+ end
5
10
  expect(node.data).to eq data
6
11
  end
7
12
 
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- describe 'UsedKeys' do
4
+ RSpec.describe 'UsedKeys' do
5
5
  let!(:task) { I18n::Tasks::BaseTask.new }
6
6
  let(:file_name) { 'a.html.slim' }
7
7
  let(:file_content) do
@@ -27,15 +27,15 @@ h1 = t 'b'
27
27
  leaves[0],
28
28
  'a',
29
29
  source_occurrences:
30
- [{pos: 6, line_num: 1, line_pos: 7, line: "div = t 'a'", src_path: 'a.html.slim'},
31
- {pos: 18, line_num: 2, line_pos: 7, line: " p = t 'a'", src_path: 'a.html.slim'}]
30
+ [{src_path: 'a.html.slim', pos: 6, line_num: 1, line_pos: 7, line: "div = t 'a'"},
31
+ {src_path: 'a.html.slim', pos: 18, line_num: 2, line_pos: 7, line: " p = t 'a'"}]
32
32
  )
33
33
 
34
34
  expect_node_key_data(
35
35
  leaves[1],
36
36
  'b',
37
37
  source_occurrences:
38
- [{pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'", src_path: 'a.html.slim'}]
38
+ [{src_path: 'a.html.slim', pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'"}]
39
39
  )
40
40
  end
41
41
 
@@ -46,7 +46,7 @@ h1 = t 'b'
46
46
  used_keys.leaves.first,
47
47
  'b',
48
48
  source_occurrences:
49
- [{pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'", src_path: 'a.html.slim'}]
49
+ [{src_path: 'a.html.slim', pos: 29, line_num: 3, line_pos: 6, line: "h1 = t 'b'"}]
50
50
  )
51
51
  end
52
52
 
@@ -67,8 +67,8 @@ h1 = t 'b'
67
67
  used_keys.leaves.first,
68
68
  'a',
69
69
  source_occurrences:
70
- [{pos: 15, line_num: 1, line_pos: 16, line: "#first{ title: t('a') }", src_path: 'a.html.haml'},
71
- {pos: 40, line_num: 2, line_pos: 17, line: ".second{ title: t('a') }", src_path: 'a.html.haml'}]
70
+ [{src_path: 'a.html.haml', pos: 15, line_num: 1, line_pos: 16, line: "#first{ title: t('a') }"},
71
+ {src_path: 'a.html.haml', pos: 40, line_num: 2, line_pos: 17, line: ".second{ title: t('a') }"}]
72
72
  )
73
73
  end
74
74
  end
@@ -19,8 +19,8 @@ data:
19
19
  # - config/locales/%{locale}.yml
20
20
  ## More files:
21
21
  # - config/locales/**/*.%{locale}.yml
22
- ## Another gem:
23
- # - "<%= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
22
+ ## Another gem (replace %#= with %=):
23
+ # - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
24
24
 
25
25
  # Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
26
26
  # `i18n-tasks normalize -p` will force move the keys according to these rules
@@ -54,10 +54,11 @@ search:
54
54
 
55
55
  ## Root directories for relative keys resolution.
56
56
  # relative_roots:
57
- # - app/views
58
57
  # - app/controllers
59
58
  # - app/helpers
59
+ # - app/mailers
60
60
  # - app/presenters
61
+ # - app/views
61
62
 
62
63
  ## Files or `File.fnmatch` patterns to exclude from search. Some files are always excluded regardless of this setting:
63
64
  ## %w(*.jpg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less *.yml *.json)
@@ -1,6 +1,6 @@
1
1
  require 'i18n/tasks'
2
2
 
3
- describe 'I18n' do
3
+ RSpec.describe 'I18n' do
4
4
  let(:i18n) { I18n::Tasks::BaseTask.new }
5
5
  let(:missing_keys) { i18n.missing_keys }
6
6
  let(:unused_keys) { i18n.unused_keys }
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.8.5
4
+ version: 0.8.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-28 00:00:00.000000000 Z
11
+ date: 2015-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubis
@@ -156,14 +156,14 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '3.0'
159
+ version: '3.3'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '3.0'
166
+ version: '3.3'
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: yard
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -187,10 +187,12 @@ email:
187
187
  - glex.spb@gmail.com
188
188
  executables:
189
189
  - i18n-tasks
190
+ - i18n-tasks.cmd
190
191
  extensions: []
191
192
  extra_rdoc_files: []
192
193
  files:
193
194
  - ".coveralls.yml"
195
+ - ".gitattributes"
194
196
  - ".gitignore"
195
197
  - ".travis.yml"
196
198
  - CHANGES.md
@@ -199,6 +201,7 @@ files:
199
201
  - README.md
200
202
  - Rakefile
201
203
  - bin/i18n-tasks
204
+ - bin/i18n-tasks.cmd
202
205
  - config/i18n-tasks.yml
203
206
  - config/locales/en.yml
204
207
  - config/locales/ru.yml
@@ -317,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
317
320
  version: '0'
318
321
  requirements: []
319
322
  rubyforge_project:
320
- rubygems_version: 2.4.6
323
+ rubygems_version: 2.4.8
321
324
  signing_key:
322
325
  specification_version: 4
323
326
  summary: Manage localization and translation with the awesome power of static analysis
@@ -352,4 +355,3 @@ test_files:
352
355
  - spec/support/test_codebase.rb
353
356
  - spec/support/trees.rb
354
357
  - spec/used_keys_spec.rb
355
- has_rdoc: