i18n-tasks 0.8.5 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
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: