i18n-tasks 0.7.8 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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