i18n-tasks 0.7.8 → 0.7.9

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: adc5e2843018cd679f4c0ee2b0b424e662d161e5
4
- data.tar.gz: e0b636d1930cde221e2c72aac7b46aad29e32130
3
+ metadata.gz: b44f57ad429c2e9ad05d5f237299ad934190e0dd
4
+ data.tar.gz: c7cdd740505581db566108513875172f0aad5144
5
5
  SHA512:
6
- metadata.gz: fd93fca95e142f5b01753b6841b77b1d64f22e4366e38e2777d418dafc6c3b553daacb8ecc3773a28b7816dca3d03f0dd33a10e60882e7147a8eb95c7b9ac6ea
7
- data.tar.gz: e2ca0ffa8f077911d495ba53a704ffb1d255a801e6f076e0e1b9b194e047ae7f5d3a412f8b6cb81c27f037cf0a11f50971dadc9aed6dd0dc8a1a8016fa37f0fa
6
+ metadata.gz: 03118fb5bddbc83d39876598d43442f1d02e9006c1a8d2e4db89b0707f9109551d94afdb1b87aa756af1e596cdca927cb2cbdb7996bd9928ef7229cc6475a113
7
+ data.tar.gz: ebac18266685ad2eaf436d90aa4a74d833d6486785a642f3918af4c699c234266a9459deaf783fc8fd7dc66adae5d73dc8e3499fde5542f733daa7783d858dca
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.3
3
+ - 2.1.5
4
4
  - 2.0.0
5
5
  - 1.9.3
6
6
  - jruby
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.7.9
2
+
3
+ * Support Rails controller method names by [Jessie A. Young](https://github.com/jessieay). [#46](https://github.com/glebm/i18n-tasks/issues/46)
4
+ * Minor fixes
5
+
1
6
  ## 0.7.8
2
7
 
3
8
  * Fix Google Translate issues with non-string keys [#100](https://github.com/glebm/i18n-tasks/pull/100)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # i18n-tasks [![Build Status][badge-travis]][travis] [![Coverage Status][badge-coverage]][coverage] [![Code Climate][badge-code-climate]][code-climate] [![Gemnasium][badge-gemnasium]][gemnasium]
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
3
  i18n-tasks helps you find and manage missing and unused translations.
4
4
 
@@ -22,7 +22,7 @@ i18n-tasks can be used with any project using [i18n][i18n-gem] (default in Rails
22
22
  Add it to the Gemfile:
23
23
 
24
24
  ```ruby
25
- gem 'i18n-tasks', '~> 0.7.8'
25
+ gem 'i18n-tasks', '~> 0.7.9'
26
26
  ```
27
27
 
28
28
  Copy default [configuration file](#configuration) (optional):
@@ -146,7 +146,7 @@ See the full list of tasks with `i18n-tasks --help`.
146
146
 
147
147
  ✔ Keys relative to the file path they are used in (see [relative roots configuration](#usage-search)) are supported.
148
148
 
149
- Keys relative to `controller.action_name` in Rails controllers are not supported.
149
+ Keys relative to `controller.action_name` in Rails controllers are supported. The closest `def` name is used.
150
150
 
151
151
  #### Plural keys
152
152
 
@@ -13,7 +13,7 @@ module I18n
13
13
  print_title missing_title(forest)
14
14
  print_table headings: [cyan(bold(I18n.t('i18n_tasks.common.locale'))), cyan(bold I18n.t('i18n_tasks.common.key')), I18n.t('i18n_tasks.common.details')] do |t|
15
15
  t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
16
- [{value: cyan(a[:locale]), alignment: :center}, cyan(a[:key]), wrap_string(key_info(a), 60)]
16
+ [{value: cyan(a[:locale]), alignment: :center}, cyan(a[:key]), key_info(a)]
17
17
  end
18
18
  end
19
19
  else
@@ -143,23 +143,6 @@ module I18n
143
143
  [green("#{first[:src_path]}:#{first[:line_num]}"),
144
144
  ("(#{I18n.t 'i18n_tasks.common.n_more', count: usages.length - 1})" if usages.length > 1)].compact.join(' ')
145
145
  end
146
-
147
- def wrap_string(s, max)
148
- chars = []
149
- dist = 0
150
- s.chars.each do |c|
151
- chars << c
152
- dist += 1
153
- if c == "\n"
154
- dist = 0
155
- elsif dist == max
156
- dist = 0
157
- chars << "\n"
158
- end
159
- end
160
- chars = chars[0..-2] if chars.last == "\n"
161
- chars.join
162
- end
163
146
  end
164
147
  end
165
148
  end
@@ -19,7 +19,7 @@ module I18n::Tasks::Scanners
19
19
  conf[:exclude] = Array(conf[:exclude])
20
20
  else
21
21
  # exclude common binary extensions by default (images and fonts)
22
- conf[:exclude] = %w(*.jpg *.png *.gif *.svg *.ico *.eot *.ttf *.woff *.pdf)
22
+ conf[:exclude] = %w(*.jpg *.png *.gif *.svg *.ico *.eot *.ttf *.woff *.woff2 *.pdf)
23
23
  end
24
24
  # Regexps for lines to ignore per extension
25
25
  if conf[:ignore_lines] && !conf[:ignore_lines].is_a?(Hash)
@@ -28,7 +28,7 @@ module I18n::Tasks::Scanners
28
28
  end
29
29
  conf[:ignore_lines] ||= {
30
30
  'rb' => %q(^\s*#(?!\si18n-tasks-use)),
31
- 'haml' => %q(^\s*-\s*#\s*(?!\si18n-tasks-use)),
31
+ 'haml' => %q(^\s*-\s*#(?!\si18n-tasks-use)),
32
32
  'slim' => %q(^\s*(?:-#|/)(?!\si18n-tasks-use)),
33
33
  'erb' => %q(^\s*<%\s*#(?!\si18n-tasks-use)),
34
34
  }
@@ -126,9 +126,9 @@ module I18n::Tasks::Scanners
126
126
  key
127
127
  end
128
128
 
129
- VALID_KEY_CHARS = /[-\w.?!;:]/
129
+ VALID_KEY_CHARS = /[-\w.?!;]/
130
130
  VALID_KEY_RE_STRICT = /^#{VALID_KEY_CHARS}+$/
131
- VALID_KEY_RE = /^(#{VALID_KEY_CHARS}|[\#{@}])+$/
131
+ VALID_KEY_RE = /^(#{VALID_KEY_CHARS}|[:\#{@}])+$/
132
132
 
133
133
  def valid_key?(key, strict = false)
134
134
  return false if @key_filter && @key_filter_pattern !~ key
@@ -13,13 +13,13 @@ module I18n::Tasks::Scanners
13
13
  text = opts[:text] || read_file(path)
14
14
  text.scan(pattern) do |match|
15
15
  src_pos = Regexp.last_match.offset(0).first
16
- key = match_to_key(match, path)
17
- next unless valid_key?(key, strict)
18
- key = key + ':' if key.end_with?('.')
19
16
  location = src_location(path, text, src_pos)
20
- unless exclude_line?(location[:line], path)
21
- keys << [key, data: location]
22
- end
17
+ next if exclude_line?(location[:line], path)
18
+ key = match_to_key(match, path, location)
19
+ next unless key
20
+ key = key + ':' if key.end_with?('.')
21
+ next unless valid_key?(key, strict)
22
+ keys << [key, data: location]
23
23
  end
24
24
  keys
25
25
  end
@@ -38,10 +38,31 @@ module I18n::Tasks::Scanners
38
38
  # @param [MatchData] match
39
39
  # @param [String] path
40
40
  # @return [String] full absolute key name
41
- def match_to_key(match, path)
41
+ def match_to_key(match, path, location)
42
42
  key = strip_literal(match[0])
43
- key = absolutize_key(key, path) if path && key.start_with?('.')
44
- key
43
+ absolute_key(key, path, location)
44
+ end
45
+
46
+ def absolute_key(key, path, location)
47
+ if key.start_with?('.')
48
+ if controller_file?(path)
49
+ absolutize_key(key, path, relative_roots, closest_method(location))
50
+ else
51
+ absolutize_key(key, path)
52
+ end
53
+ else
54
+ key
55
+ end
56
+ end
57
+
58
+ def controller_file?(path)
59
+ /controllers/.match(path)
60
+ end
61
+
62
+ def closest_method(location)
63
+ method = File.readlines(location[:src_path]).first(location[:line_num] - 1).reverse_each.find { |x| x=~ /\bdef\b/ }
64
+ method &&= method.strip.sub(/^def\s*/, '')
65
+ method
45
66
  end
46
67
 
47
68
  def pattern
@@ -20,7 +20,7 @@ module I18n::Tasks::Scanners
20
20
  # @param [MatchData] match
21
21
  # @param [String] path
22
22
  # @return [String] full absolute key name with scope resolved if any
23
- def match_to_key(match, path)
23
+ def match_to_key(match, path, location)
24
24
  key = super
25
25
  scope = match[1]
26
26
  if scope
@@ -6,14 +6,46 @@ module I18n
6
6
  # @param key [String] relative i18n key (starts with a .)
7
7
  # @param path [String] path to the file containing the key
8
8
  # @return [String] absolute version of the key
9
- def absolutize_key(key, path, roots = relative_roots)
10
- # normalized path
11
- path = File.expand_path path
12
- (path_root = roots.map { |path| File.expand_path path }.sort.reverse.detect { |root| path.start_with?(root + '/') }) or
13
- raise CommandError.new("Error scanning #{path}: cannot resolve relative key \"#{key}\".\nSet relative_roots in config/i18n-tasks.yml (currently #{relative_roots.inspect})")
14
- # key prefix based on path
15
- prefix = path.gsub(%r(#{path_root}/|(\.[^/]+)*$), '').tr('/', '.').gsub(%r(\._), '.')
16
- "#{prefix}#{key}"
9
+ def absolutize_key(key, path, roots = relative_roots, closest_method = "")
10
+ normalized_path = File.expand_path(path)
11
+ path_root(normalized_path, roots) or
12
+ raise CommandError.new(
13
+ "Error scanning #{normalized_path}: cannot resolve relative key
14
+ \"#{key}\".\nSet relative_roots in config/i18n-tasks.yml
15
+ (currently #{relative_roots.inspect})"
16
+ )
17
+
18
+ prefix_key_based_on_path(key, normalized_path, roots, closest_method: closest_method)
19
+ end
20
+
21
+ private
22
+
23
+ # Detect the appropriate relative path root
24
+ # @param [String] path /full/path
25
+ # @param [Array<String>] roots array of full paths
26
+ # @return [String] the closest ancestor root for path
27
+ def path_root(path, roots)
28
+ expanded_relative_roots(roots).sort.reverse_each.detect do |root|
29
+ path.start_with?(root + '/')
30
+ end
31
+ end
32
+
33
+ def expanded_relative_roots(roots)
34
+ roots.map { |path| File.expand_path(path) }
35
+ end
36
+
37
+ def prefix_key_based_on_path(key, normalized_path, roots, options = {})
38
+ "#{prefix(normalized_path, roots, options)}#{key}"
39
+ end
40
+
41
+ def prefix(normalized_path, roots, options = {})
42
+ file_name = normalized_path.gsub(%r(#{path_root(normalized_path, roots)}/|(\.[^/]+)*$), '')
43
+
44
+ if options[:closest_method].present?
45
+ "#{file_name.split('_').first}.#{options[:closest_method]}"
46
+ else
47
+ file_name.tr('/', '.').gsub(%r(\._), '.')
48
+ end
17
49
  end
18
50
  end
19
51
  end
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
  module I18n
3
3
  module Tasks
4
- VERSION = '0.7.8'
4
+ VERSION = '0.7.9'
5
5
  end
6
6
  end
@@ -1,5 +1,8 @@
1
1
  # coding: utf-8
2
2
  class EventsController < ApplicationController
3
+ def create
4
+ end
5
+
3
6
  def show
4
7
  redirect_to :edit, notice: I18n.t('cb.a')
5
8
 
@@ -26,5 +29,11 @@ class EventsController < ApplicationController
26
29
 
27
30
  # not missing
28
31
  I18n.t "hash.#{stuff}.a"
32
+
33
+ # relative key
34
+ I18n.t(".success")
35
+ end
36
+
37
+ def update
29
38
  end
30
39
  end
@@ -32,7 +32,11 @@ search:
32
32
  - '*.file'
33
33
  # explicitly exclude files (default: blank = exclude no files)
34
34
  exclude: '*.js'
35
- # search uses grep under the hood
35
+ # paths for relative key resolution:
36
+ relative_roots:
37
+ - app/views
38
+ - app/controllers
39
+
36
40
 
37
41
  # do not report these keys ever
38
42
  ignore:
@@ -9,7 +9,7 @@ describe 'Google Translation' do
9
9
  nil_value_test = ['nil-value-key', nil, nil],
10
10
  text_test = ['key', "Hello - %{user} O'neill!", "Hola - %{user} O'neill!"],
11
11
  html_test = ['html-key.html', "Hello - <b>%{user} O'neill</b>", "Hola - <b>%{user} O'neill</b>"],
12
- array_test = ['array-key', ['Hello.', nil, '', 'Goodbye.'], ['Hola.', nil, '', 'Adiós.']],
12
+ array_test = ['array-key', ['Hello.', nil, '', 'Goodbye.'], ['¡Hola.', nil, '', '¡Adiós.']],
13
13
  fixnum_test = ['numeric-key', 1, 1],
14
14
  ]
15
15
 
@@ -8,11 +8,11 @@ describe 'I18n' do
8
8
 
9
9
  it 'does not have missing keys' do
10
10
  expect(missing_keys).to be_empty,
11
- "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
11
+ "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
12
12
  end
13
13
 
14
14
  it 'does not have unused keys' do
15
- expect(i18n.unused_keys).to be_empty,
16
- "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
15
+ expect(unused_keys).to be_empty,
16
+ "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
17
17
  end
18
18
  end
@@ -18,21 +18,28 @@ describe 'i18n-tasks' do
18
18
 
19
19
  describe 'missing' do
20
20
  let (:expected_missing_keys) {
21
- %w( en.used_but_missing.key en.relative.index.missing
21
+ %w( en.used_but_missing.key
22
+ en.relative.index.missing
22
23
  es.missing_in_es.a
23
24
  en.present_in_es_but_not_en.a
24
- en.hash.pattern_missing.a en.hash.pattern_missing.b
25
- en.missing_symbol_key en.missing_symbol.key_two en.missing_symbol.key_three
26
- es.missing_in_es_plural_1.a es.missing_in_es_plural_2.a
25
+ en.hash.pattern_missing.a
26
+ en.hash.pattern_missing.b
27
+ en.missing_symbol_key
28
+ en.missing_symbol.key_two
29
+ en.missing_symbol.key_three
30
+ es.missing_in_es_plural_1.a
31
+ es.missing_in_es_plural_2.a
27
32
  en.missing-key-with-a-dash.key
28
33
  en.missing-key-question?.key
29
- en.fn_comment en.only_in_es
34
+ en.fn_comment
35
+ en.only_in_es
36
+ en.events.show.success
30
37
  )
31
38
  }
32
39
  it 'detects missing' do
33
40
  capture_stderr do
34
- expect(run_cmd :missing).to be_i18n_keys expected_missing_keys
35
41
  es_keys = expected_missing_keys.grep(/^es\./)
42
+ expect(run_cmd :missing).to be_i18n_keys expected_missing_keys
36
43
  # locale argument
37
44
  expect(run_cmd :missing, locales: %w(es)).to be_i18n_keys es_keys
38
45
  expect(run_cmd :missing, arguments: %w(es)).to be_i18n_keys es_keys
@@ -48,8 +55,18 @@ describe 'i18n-tasks' do
48
55
  end
49
56
  end
50
57
 
51
- let(:expected_unused_keys) { %w(unused.a unused.numeric unused.plural).map { |k| %w(en es).map { |l| "#{l}.#{k}" } }.reduce(:+) }
52
- let(:expected_unused_keys_strict) { expected_unused_keys + %w(hash.pattern.a hash.pattern2.a).map { |k| %w(en es).map { |l| "#{l}.#{k}" } }.reduce(:+) }
58
+ let(:expected_unused_keys) do
59
+ %w(unused.a unused.numeric unused.plural).map do |k|
60
+ %w(en es).map { |l| "#{l}.#{k}" }
61
+ end.reduce(:+)
62
+ end
63
+
64
+ let(:expected_unused_keys_strict) do
65
+ expected_unused_keys + %w(hash.pattern.a hash.pattern2.a).map do |k|
66
+ %w(en es).map { |l| "#{l}.#{k}" }
67
+ end.reduce(:+)
68
+ end
69
+
53
70
  describe 'unused' do
54
71
  it 'detects unused' do
55
72
  capture_stderr do
@@ -2,21 +2,57 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe 'Pattern Scanner' do
5
- describe 'default pattern' do
5
+ describe 'scan_file' do
6
+ it 'returns absolute keys from controllers' do
7
+ file_path = 'spec/fixtures/app/controllers/events_controller.rb'
8
+ scanner = I18n::Tasks::Scanners::PatternScanner.new
9
+ allow(scanner).to receive(:relative_roots).and_return(['spec/fixtures/app/controllers'])
10
+
11
+ keys = scanner.scan_file(file_path)
12
+
13
+ expect(keys).to include(
14
+ ["events.show.success",
15
+ {:data=>
16
+ {
17
+ :src_path=>"spec/fixtures/app/controllers/events_controller.rb",
18
+ :pos=>788,
19
+ :line_num=>34,
20
+ :line_pos=>10,
21
+ :line =>" I18n.t(\".success\")"}
22
+ }
23
+ ]
24
+ )
25
+ end
26
+ end
27
+
28
+ describe 'default_pattern' do
6
29
  let!(:pattern) { I18n::Tasks::Scanners::PatternScanner.new.default_pattern }
7
30
 
8
- ['t "a.b"', "t 'a.b'", 't("a.b")', "t('a.b')",
9
- "t('a.b', :arg => val)", "t('a.b', arg: val)",
10
- "t :a_b", "t :'a.b'", 't :"a.b"', "t(:ab)", "t(:'a.b')", 't(:"a.b")',
11
- 'I18n.t("a.b")', 'I18n.translate("a.b")'].each do |s|
12
- it "matches #{s}" do
13
- expect(pattern).to match s
31
+ [
32
+ 't(".a.b")',
33
+ 't "a.b"',
34
+ "t 'a.b'",
35
+ 't("a.b")',
36
+ "t('a.b')",
37
+ "t('a.b', :arg => val)",
38
+ "t('a.b', arg: val)",
39
+ "t :a_b",
40
+ "t :'a.b'",
41
+ 't :"a.b"',
42
+ "t(:ab)",
43
+ "t(:'a.b')",
44
+ 't(:"a.b")',
45
+ 'I18n.t("a.b")',
46
+ 'I18n.translate("a.b")'
47
+ ].each do |string|
48
+ it "matches #{string}" do
49
+ expect(pattern).to match string
14
50
  end
15
51
  end
16
52
 
17
- ["t \"a.b'", "t a.b"].each do |s|
18
- it "does not match #{s}" do
19
- expect(pattern).to_not match s
53
+ ["t \"a.b'", "t a.b"].each do |string|
54
+ it "does not match #{string}" do
55
+ expect(pattern).to_not match string
20
56
  end
21
57
  end
22
58
  end
@@ -17,6 +17,19 @@ describe 'Relative keys' do
17
17
  end
18
18
  end
19
19
 
20
- end
20
+ context 'relative key in controller' do
21
+ it 'works' do
22
+ base_scanner = I18n::Tasks::Scanners::BaseScanner.new
23
+
24
+ key = base_scanner.absolutize_key(
25
+ '.success',
26
+ 'app/controllers/users_controller.rb',
27
+ %w(app/controllers),
28
+ 'create'
29
+ )
21
30
 
31
+ expect(key).to eq('users.create.success')
32
+ end
33
+ end
34
+ end
22
35
  end
@@ -8,11 +8,11 @@ describe 'I18n' do
8
8
 
9
9
  it 'does not have missing keys' do
10
10
  expect(missing_keys).to be_empty,
11
- "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
11
+ "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
12
12
  end
13
13
 
14
14
  it 'does not have unused keys' do
15
- expect(i18n.unused_keys).to be_empty,
16
- "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
15
+ expect(unused_keys).to be_empty,
16
+ "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
17
17
  end
18
18
  end
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.8
4
+ version: 0.7.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-22 00:00:00.000000000 Z
11
+ date: 2014-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubis
@@ -334,7 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
334
334
  version: '0'
335
335
  requirements: []
336
336
  rubyforge_project:
337
- rubygems_version: 2.4.1
337
+ rubygems_version: 2.4.4
338
338
  signing_key:
339
339
  specification_version: 4
340
340
  summary: Manage localization and translation with the awesome power of static analysis