i18n-tasks 0.9.4 → 0.9.5

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: 4807ae41e4ea2c7274b08bc55c2a2286a6e601d5
4
- data.tar.gz: bdc921c3485e004c0e395a79afde8a5cfcfa888c
3
+ metadata.gz: d0229b3a4c2b8749a457c6b71e412a80802a6aa3
4
+ data.tar.gz: e43c66b6e67ef86dd28fcb7d7c9875c4578c9b84
5
5
  SHA512:
6
- metadata.gz: 3b58817464f4c4878f822606dda7ae226b33f53b6542b938a272864d1d4a92806a72d81f4d16571e8176e04687784861a7c541f1ac0e92b6ce055ed46a93b8da
7
- data.tar.gz: 276c9a94d0575d383ea163a8e20d56b26248cf0c16da4caa2efd53f7530ed4632f3f264e9dcf747013db063cd19c984cd218cb60829ace938f79b6c97db7130f
6
+ metadata.gz: d370ab448ca1126486d67e810b9dab6f20ea07b5145c297a3fdbc15bf03fc6c3118434d93803b9b7ba9147b57b36f30b9582ab7af27d6d31172fe79d84157737
7
+ data.tar.gz: 0f1a783ca1d8ca7024d26f9506fc814ac6eff22ddb1141f4730567e73730c287e192a216ad5f4971d399d0df664ade82a8cf72301a1486e1cffff15b4a254bf9
data/README.md CHANGED
@@ -24,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
24
24
  Add i18n-tasks to the Gemfile:
25
25
 
26
26
  ```ruby
27
- gem 'i18n-tasks', '~> 0.9.4'
27
+ gem 'i18n-tasks', '~> 0.9.5'
28
28
  ```
29
29
 
30
30
  Copy the default [configuration file](#configuration):
@@ -278,6 +278,12 @@ User.model_name.human
278
278
  ```
279
279
 
280
280
  You can also explicitly ignore keys appearing in locale files via `ignore*` settings.
281
+
282
+ If you have helper methods that generate translation keys, such as a `page_title` method that returns `t '.page_title'`,
283
+ or a `Spree.t(key)` method that returns `t "spree.#{key}"`, use the built-in `PatternMapper` to map these.
284
+
285
+ For more complex cases, you can implement a [custom scanner][custom-scanner-docs].
286
+
281
287
  See the [config file][config] to find out more.
282
288
 
283
289
  <a name="translation-config"></a>
@@ -332,3 +338,4 @@ Custom tasks can be added easily, see the examples [on the wiki](https://github.
332
338
  [screenshot-i18n-tasks]: https://i.imgur.com/XZBd8l7.png "i18n-tasks screenshot"
333
339
  [screenshot-find]: https://i.imgur.com/VxBrSfY.png "i18n-tasks find output screenshot"
334
340
  [adapter-example]: https://github.com/glebm/i18n-tasks/blob/master/lib/i18n/tasks/data/file_system_base.rb
341
+ [custom-scanner-docs]: https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example
@@ -14,23 +14,25 @@ class I18n::Tasks::CLI
14
14
  end
15
15
 
16
16
  def start(argv)
17
- auto_output_coloring do
18
- begin
19
- if run(argv) == :exit_1
20
- exit 1
21
- end
22
- rescue OptionParser::ParseError => e
23
- error e.message, 64
24
- rescue I18n::Tasks::CommandError => e
17
+ I18n.with_locale(base_task.internal_locale) do
18
+ auto_output_coloring do
25
19
  begin
26
- error e.message, 78
27
- ensure
28
- log_verbose e.backtrace * "\n"
20
+ if run(argv) == :exit_1
21
+ exit 1
22
+ end
23
+ rescue OptionParser::ParseError => e
24
+ error e.message, 64
25
+ rescue I18n::Tasks::CommandError => e
26
+ begin
27
+ error e.message, 78
28
+ ensure
29
+ log_verbose e.backtrace * "\n"
30
+ end
31
+ rescue Errno::EPIPE
32
+ # ignore Errno::EPIPE which is throw when pipe breaks, e.g.:
33
+ # i18n-tasks missing | head
34
+ exit 1
29
35
  end
30
- rescue Errno::EPIPE
31
- # ignore Errno::EPIPE which is throw when pipe breaks, e.g.:
32
- # i18n-tasks missing | head
33
- exit 1
34
36
  end
35
37
  end
36
38
  rescue ExecutionError => e
@@ -43,7 +45,7 @@ class I18n::Tasks::CLI
43
45
  end
44
46
 
45
47
  def context
46
- @context ||= ::I18n::Tasks::Commands.new(base_task).tap(&:set_internal_locale!)
48
+ @context ||= ::I18n::Tasks::Commands.new(base_task)
47
49
  end
48
50
 
49
51
  def commands
@@ -27,10 +27,6 @@ module I18n::Tasks
27
27
  end
28
28
  end
29
29
 
30
- def set_internal_locale!
31
- I18n.locale = i18n.internal_locale
32
- end
33
-
34
30
  protected
35
31
 
36
32
  def terminal_report
@@ -45,21 +45,23 @@ module I18n::Tasks
45
45
  cmd :add_missing,
46
46
  pos: '[locale ...]',
47
47
  desc: t('i18n_tasks.cmd.desc.add_missing'),
48
- args: [:locales, :out_format, arg(:value) + [{default: '%{value_or_default_or_human_key}'}]]
48
+ args: [:locales, :out_format, arg(:value) + [{default: '%{value_or_default_or_human_key}'}],
49
+ ['--nil-value', 'Set value to nil. Takes precedence over the value argument.']]
49
50
 
50
51
  def add_missing(opt = {})
51
52
  added = i18n.empty_forest
52
53
  locales = (opt[:locales] || i18n.locales)
54
+ value = opt[:'nil-value'] ? nil : opt[:value]
53
55
  if locales[0] == i18n.base_locale
54
56
  # Merge base locale first, as this may affect the value for the other locales
55
57
  forest = i18n.missing_keys({locales: [locales[0]]}.update(opt.slice(:types, :base_locale))).
56
- set_each_value!(opt[:value])
58
+ set_each_value!(value)
57
59
  i18n.data.merge! forest
58
60
  added.merge! forest
59
61
  locales = locales[1..-1]
60
62
  end
61
63
  forest = i18n.missing_keys({locales: locales}.update(opt.slice(:types, :base_locale))).
62
- set_each_value!(opt[:value])
64
+ set_each_value!(value)
63
65
  i18n.data.merge! forest
64
66
  added.merge! forest
65
67
  log_stderr t('i18n_tasks.add_missing.added', count: added.leaves.count)
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+ require 'i18n/tasks/scanners/file_scanner'
3
+ require 'i18n/tasks/scanners/relative_keys'
4
+ require 'i18n/tasks/scanners/occurrence_from_position'
5
+ require 'i18n/tasks/scanners/ruby_key_literals'
6
+
7
+ module I18n::Tasks::Scanners
8
+ # Maps the provided patterns to keys.
9
+ class PatternMapper < FileScanner
10
+ include I18n::Tasks::Scanners::RelativeKeys
11
+ include I18n::Tasks::Scanners::OccurrenceFromPosition
12
+ include I18n::Tasks::Scanners::RubyKeyLiterals
13
+
14
+ # @param patterns [Array<[String, String]> the list of pattern-key pairs
15
+ # the patterns follow the regular expression syntax, with a syntax addition for matching
16
+ # string/symbol literals: you can include %{key} in the pattern, and it will be converted to
17
+ # a named capture group, capturing ruby strings and symbols, that can then be used in the key:
18
+ #
19
+ # patterns: [['Spree\.t[( ]\s*%{key}', 'spree.%{key}']]
20
+ #
21
+ # All of the named capture groups are interpolated into the key with %{group_name} interpolations.
22
+ #
23
+ def initialize(config:, **args)
24
+ super
25
+ @patterns = configure_patterns(config[:patterns] || [])
26
+ end
27
+
28
+ protected
29
+
30
+ # @return [Array<[absolute key, Results::Occurrence]>]
31
+ def scan_file(path)
32
+ text = read_file(path)
33
+ @patterns.flat_map do |pattern, key|
34
+ result = []
35
+ text.scan(pattern) do |_|
36
+ match = Regexp.last_match
37
+ matches = Hash[match.names.map(&:to_sym).zip(match.captures)]
38
+ if matches.key?(:key)
39
+ matches[:key] = strip_literal(matches[:key])
40
+ next unless valid_key?(matches[:key])
41
+ end
42
+ result << [absolute_key(key % matches, path),
43
+ occurrence_from_position(path, text, match.offset(0).first)]
44
+ end
45
+ result
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ KEY_GROUP = "(?<key>#{LITERAL_RE})"
52
+
53
+ def configure_patterns(patterns)
54
+ patterns.map do |(pattern, key)|
55
+ [pattern.is_a?(Regexp) ? pattern : Regexp.new(pattern % {key: KEY_GROUP}), key]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -2,12 +2,14 @@
2
2
  require 'i18n/tasks/scanners/file_scanner'
3
3
  require 'i18n/tasks/scanners/relative_keys'
4
4
  require 'i18n/tasks/scanners/occurrence_from_position'
5
+ require 'i18n/tasks/scanners/ruby_key_literals'
5
6
 
6
7
  module I18n::Tasks::Scanners
7
8
  # Scan for I18n.t usages using a simple regular expression.
8
9
  class PatternScanner < FileScanner
9
10
  include RelativeKeys
10
11
  include OccurrenceFromPosition
12
+ include RubyKeyLiterals
11
13
 
12
14
  def initialize(**args)
13
15
  super
@@ -50,26 +52,13 @@ module I18n::Tasks::Scanners
50
52
  re && re =~ line
51
53
  end
52
54
 
53
- # remove the leading colon and unwrap quotes from the key match
54
- # @param literal [String] e.g: "key", 'key', or :key.
55
- # @return [String] key
56
- def strip_literal(literal)
57
- key = literal
58
- key = key[1..-1] if ':'.freeze == key[0]
59
- key = key[1..-2] if QUOTES.include?(key[0])
60
- key
61
- end
62
-
63
- QUOTES = ["'".freeze, '"'.freeze].freeze
64
- VALID_KEY_CHARS = /(?:[[:word:]]|[-.?!;À-ž])/
65
- VALID_KEY_RE_STRICT = /^#{VALID_KEY_CHARS}+$/
66
- VALID_KEY_RE = /^(#{VALID_KEY_CHARS}|[:\#{@}\[\]])+$/
55
+ VALID_KEY_RE_DYNAMIC = /^(#{VALID_KEY_CHARS}|[:\#{@}\[\]])+$/
67
56
 
68
57
  def valid_key?(key)
69
58
  if @config[:strict]
70
- key =~ VALID_KEY_RE_STRICT && !key.end_with?('.')
59
+ super(key)
71
60
  else
72
- key =~ VALID_KEY_RE
61
+ key =~ VALID_KEY_RE_DYNAMIC
73
62
  end
74
63
  end
75
64
 
@@ -87,13 +76,6 @@ module I18n::Tasks::Scanners
87
76
  /(?<=^|[^\w'\-.]|[^\w'\-]I18n\.|I18n\.)t(?:ranslate)?/
88
77
  end
89
78
 
90
- # Match literals:
91
- # * String: '', "#{}"
92
- # * Symbol: :sym, :'', :"#{}"
93
- def literal_re
94
- /:?".+?"|:?'.+?'|:\w+/
95
- end
96
-
97
79
  def default_pattern
98
80
  # capture only the first argument
99
81
  /
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module I18n::Tasks::Scanners
3
+ module RubyKeyLiterals
4
+ LITERAL_RE = /:?".+?"|:?'.+?'|:\w+/
5
+
6
+ # Match literals:
7
+ # * String: '', "#{}"
8
+ # * Symbol: :sym, :'', :"#{}"
9
+ def literal_re
10
+ LITERAL_RE
11
+ end
12
+
13
+ # remove the leading colon and unwrap quotes from the key match
14
+ # @param literal [String] e.g: "key", 'key', or :key.
15
+ # @return [String] key
16
+ def strip_literal(literal)
17
+ literal = literal[1..-1] if ':' == literal[0]
18
+ literal = literal[1..-2] if "'" == literal[0] || '"' == literal[0]
19
+ literal
20
+ end
21
+
22
+ VALID_KEY_CHARS = /(?:[[:word:]]|[-.?!;À-ž])/
23
+ VALID_KEY_RE = /^#{VALID_KEY_CHARS}+$/
24
+
25
+ def valid_key?(key)
26
+ key =~ VALID_KEY_RE && !key.end_with?('.')
27
+ end
28
+ end
29
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module I18n
3
3
  module Tasks
4
- VERSION = '0.9.4'
4
+ VERSION = '0.9.5'
5
5
  end
6
6
  end
@@ -105,3 +105,16 @@ search:
105
105
  ## Ignore these keys completely:
106
106
  # ignore:
107
107
  # - kaminari.*
108
+
109
+ ## Sometimes, it isn't possible for i18n-tasks to match the key correctly,
110
+ ## e.g. in case of a relative key defined in a helper method.
111
+ ## In these cases you can use the built-in PatternMapper to map patterns to keys, e.g.:
112
+ #
113
+ # <%#= I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
114
+ # only: %w(*.html.haml *.html.slim),
115
+ # patterns: [['= title\b', '.page_title']] %>
116
+ #
117
+ # The PatternMapper can also match key literals via a special %{key} interpolation, e.g.:
118
+ #
119
+ # <%#= I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
120
+ # patterns: [['\bSpree\.t[( ]\s*%{key}', 'spree.%{key}']] %>
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.9.4
4
+ version: 0.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-06 00:00:00.000000000 Z
11
+ date: 2016-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -281,6 +281,7 @@ files:
281
281
  - lib/i18n/tasks/scanners/files/file_finder.rb
282
282
  - lib/i18n/tasks/scanners/files/file_reader.rb
283
283
  - lib/i18n/tasks/scanners/occurrence_from_position.rb
284
+ - lib/i18n/tasks/scanners/pattern_mapper.rb
284
285
  - lib/i18n/tasks/scanners/pattern_scanner.rb
285
286
  - lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb
286
287
  - lib/i18n/tasks/scanners/relative_keys.rb
@@ -288,6 +289,7 @@ files:
288
289
  - lib/i18n/tasks/scanners/results/occurrence.rb
289
290
  - lib/i18n/tasks/scanners/ruby_ast_call_finder.rb
290
291
  - lib/i18n/tasks/scanners/ruby_ast_scanner.rb
292
+ - lib/i18n/tasks/scanners/ruby_key_literals.rb
291
293
  - lib/i18n/tasks/scanners/scanner.rb
292
294
  - lib/i18n/tasks/scanners/scanner_multiplexer.rb
293
295
  - lib/i18n/tasks/split_key.rb