i18n-tasks 0.1.5 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f197d49c3a8d79992339e4ba4a23e1b9f7df23ff
4
- data.tar.gz: 5f08596a492cfec055aab5425353dc8ec2fda1f8
3
+ metadata.gz: b5394593809b1be9eabef55d84ee0cf8ce692884
4
+ data.tar.gz: 5629b833e0ef043538a387a3eff45a1e16fdbc6f
5
5
  SHA512:
6
- metadata.gz: caedad036795f4861f8bdc1fff7309af8c1682f8e3d61779cd8aca8ab681cad1d62b922b57a8ec51eb84608d72572873bc49b5f90ee62340fb6b3e2bd53010a9
7
- data.tar.gz: 4a185853088b92aa109b392f18cd940fb30c1aeafd98e083633034886d5a411a69c3a97f834ce7bacfa4358c00e74dd8dfa052d3b7553e031cb0efea6a19e9e1
6
+ metadata.gz: 80141dbe9b294f9e83b3d1a812aa5f3381e2909ddf8175cec2df66dae8da310e6aedda8d36bb7a11c511d04e41a59972469eabe6475b88f0cfa0065034efa9b7
7
+ data.tar.gz: a0c620f7af53c0aa9f820afc057eb658f8c84d43caad4a9affdb1854d1b5ecacf76ceef4288b7265eaecca2382e0f3a8e6de226e7c21205c9aaf0d3a54a441a1
data/CHANGES.md CHANGED
@@ -1,5 +1,10 @@
1
+ ## v0.1.6
2
+
3
+ * New key pattern syntax for i18n-tasks.yml a la globbing (@glebm)
4
+
1
5
  ## v0.1.5
2
- * Removed get_locale_data, added data configuration options
6
+
7
+ * Removed get_locale_data, added data configuration options (@glebm)
3
8
 
4
9
  ## v0.1.4
5
10
 
@@ -8,10 +13,10 @@
8
13
 
9
14
  ## v0.1.3
10
15
 
11
- * detect countable keys as used for unused task
16
+ * detect countable keys as used for unused task (@glebm)
12
17
  * account for non-string keys coming from yaml (thanks @lichtamberg)
13
18
 
14
19
  ## v0.1.2
15
20
 
16
21
  * added grep config options (thanks @dmke)
17
- * improved terminal output
22
+ * improved terminal output (@glebm)
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in i18n-tasks.gemspec
4
4
  gemspec
5
+
6
+ #gem 'byebug'
data/README.md CHANGED
@@ -42,7 +42,7 @@ Tasks may incorrectly report framework i18n keys as missing. You can add `config
42
42
  # do not report these keys as missing (both on blank value and no key)
43
43
  ignore_missing:
44
44
  - devise.errors.unauthorized # ignore this key
45
- - pagination.views. # ignore the whole pattern (note the .)
45
+ - pagination.views.* # ignore the whole pattern
46
46
 
47
47
  # do not report these keys when they have the same value as the base locale version
48
48
  ignore_eq_base:
@@ -53,11 +53,19 @@ ignore_eq_base:
53
53
 
54
54
  # do not report these keys as unused
55
55
  ignore_unused:
56
- - category.
56
+ - category.*
57
57
 
58
58
  # do not report these keys ever
59
59
  ignore:
60
- - kaminari.
60
+ - kaminari.*
61
+
62
+ # where to read locale data from
63
+ data:
64
+ # read paths for a given %{locale} (supports globs)
65
+ paths:
66
+ - 'config/locales/%{locale}.yml'
67
+ # you can also implement a custom storage layer, see the yaml one below
68
+ class: I18n::Tasks::Data::Yaml
61
69
 
62
70
  # search configuration (grep arguments)
63
71
  grep:
@@ -70,15 +78,6 @@ grep:
70
78
  - '*.html*'
71
79
  # explicitly exclude files (default: blank = exclude no files)
72
80
  exclude: '*.js'
73
-
74
- # where to read locale data from
75
- data:
76
- # file patterns for a given %{locale} (supports globs)
77
- paths:
78
- # default:
79
- - 'config/locales/%{locale}.yml'
80
- # you can also implement a custom loader, use the default as an example
81
- class: I18n::Tasks::Data::Yaml
82
81
  ```
83
82
 
84
83
  ## HTML report
@@ -89,6 +88,7 @@ While i18n-tasks does not provide an HTML version of the report, it's easy to ro
89
88
 
90
89
  This was originally developed for [Zuigo](http://zuigo.com/), a platform to organize and discover events.
91
90
 
91
+ [MIT license](/LICENSE.txt)
92
92
 
93
93
 
94
94
  [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/glebm/i18n-tasks/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
data/lib/i18n/tasks.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'i18n/tasks/version'
2
2
  require 'i18n/tasks/railtie'
3
+ require 'i18n/tasks/key_pattern_matching'
4
+
3
5
  require 'i18n/tasks/data/yaml'
4
6
  require 'active_support/hash_with_indifferent_access'
5
7
  require 'active_support/core_ext/string/access'
@@ -1,158 +1,27 @@
1
1
  # coding: utf-8
2
2
  require 'term/ansicolor'
3
- require 'i18n/tasks/task_helpers'
3
+ require 'i18n/tasks/usage_search'
4
+ require 'i18n/tasks/fuzzy_source_keys'
5
+ require 'i18n/tasks/plural_keys'
6
+ require 'i18n/tasks/relative_keys'
7
+ require 'i18n/tasks/translation_data'
8
+ require 'i18n/tasks/ignore_keys'
4
9
 
5
10
  module I18n
6
11
  module Tasks
7
12
  class BaseTask
8
- include TaskHelpers
9
-
10
- # locale data hash, with locale name as root
11
- # @return [Hash{String => String,Hash}] locale data in nested hash format
12
- def locale_data(locale)
13
- locale = locale.to_s
14
- (@locale_data ||= {})[locale] ||= data_source.get(locale)
15
- end
16
-
17
- def data_source
18
- return @source if @source
19
- conf = config[:data] || {}
20
- @source = if conf[:class]
21
- conf[:class].constantize.new(conf.except(:class))
22
- else
23
- I18n::Tasks::Data::Yaml.new(
24
- paths: Array(conf[:paths].presence || ['config/locales/%{locale}.yml'])
25
- )
26
- end
27
- end
28
-
29
- # main locale file path (for writing to)
30
- # @return [String]
31
- def locale_file_path(locale)
32
- "config/locales/#{locale}.yml"
33
- end
34
-
35
- # find all keys in the source (relative keys are returned in absolutized)
36
- # @return [Array<String>]
37
- def find_source_keys
38
- @source_keys ||= begin
39
- if (grep_out = run_grep)
40
- grep_out.split("\n").map { |r|
41
- key = r.match(/['"](.*?)['"]/)[1]
42
- if key.start_with? '.'
43
- absolutize_key key, r.split(':')[0]
44
- else
45
- key
46
- end
47
- }.uniq.reject { |k| k !~ /^[\w.\#{}]+$/ }
48
- else
49
- []
50
- end
51
- end
52
- end
53
-
54
- # whether the key is used in the source
55
- def used_key?(key)
56
- @used_keys ||= find_source_keys.to_set
57
- @used_keys.include?(key)
58
- end
59
-
60
- # whether to ignore the key. ignore_type one of :missing, :eq_base, :blank, :unused.
61
- # will apply global ignore rules as well
62
- def ignore_key?(key, ignore_type, locale = nil)
63
- key =~ ignore_pattern(ignore_type, locale)
64
- end
65
-
66
- # dynamically generated keys in the source, e.g t("category.#{category_key}")
67
- def pattern_key?(key)
68
- @pattern_keys_re ||= compile_start_with_re(pattern_key_prefixes)
69
- key =~ @pattern_keys_re
70
- end
71
-
72
- # keys in the source that end with a ., e.g. t("category.#{cat.i18n_key}") or t("category." + category.key)
73
- def pattern_key_prefixes
74
- @pattern_keys_prefixes ||=
75
- find_source_keys.select { |k| k =~ /\#{.*?}/ || k.ends_with?('.') }.map { |k| k.split(/\.?#/)[0].presence }.compact
76
- end
77
-
78
- # whether the value for key exists in locale (defaults: base_locale)
79
- def key_has_value?(key, locale = base_locale)
80
- t(locale_data(locale)[locale], key).present?
13
+ include UsageSearch
14
+ include PluralKeys
15
+ include RelativeKeys
16
+ include FuzzySourceKeys
17
+ include TranslationData
18
+ include IgnoreKeys
19
+
20
+ # i18n-tasks config (defaults + config/i18n-tasks.yml)
21
+ # @return [Hash{String => String,Hash,Array}]
22
+ def config
23
+ I18n::Tasks.config
81
24
  end
82
-
83
- # traverse hash, yielding with full key and value
84
- # @param hash [Hash{String => String,Hash}] translation data to traverse
85
- # @yield [full_key, value] yields full key and value for every translation in #hash
86
- # @return [nil]
87
- def traverse(path = '', hash)
88
- q = [[path, hash]]
89
- until q.empty?
90
- path, value = q.pop
91
- if value.is_a?(Hash)
92
- value.each { |k, v| q << ["#{path}.#{k}", v] }
93
- else
94
- yield path[1..-1], value
95
- end
96
- end
97
- end
98
-
99
- # translation of the key found in the passed hash or nil
100
- # @return [String,nil]
101
- def t(hash, key)
102
- key.split('.').inject(hash) { |r, seg| r[seg] if r }
103
- end
104
-
105
- # @param key [String] relative i18n key (starts with a .)
106
- # @param path [String] path to the file containing the key
107
- # @return [String] absolute version of the key
108
- def absolutize_key(key, path)
109
- # normalized path
110
- path = Pathname.new(File.expand_path path).relative_path_from(Pathname.new(Dir.pwd)).to_s
111
- # key prefix based on path
112
- prefix = path.gsub(%r(app/views/|(\.[^/]+)*$), '').tr('/', '.').gsub(%r(\._), '.')
113
- "#{prefix}#{key}"
114
- end
115
-
116
- PLURAL_KEY_RE = /\.(?:zero|one|two|few|many|other)$/
117
-
118
- # @param key [String] i18n key
119
- # @param data [Hash{String => String,Hash}] locale data
120
- # @return the base form if the key is a specific plural form (e.g. apple for apple.many), and the key as passed otherwise
121
- def depluralize_key(key, data)
122
- return key if key !~ PLURAL_KEY_RE || t(data, key).is_a?(Hash)
123
- parent_key = key.split('.')[0..-2] * '.'
124
- plural_versions = t(data, parent_key)
125
- if plural_versions.is_a?(Hash) && plural_versions.all? { |k, v| ".#{k}" =~ PLURAL_KEY_RE && !v.is_a?(Hash) }
126
- parent_key
127
- else
128
- key
129
- end
130
- end
131
-
132
-
133
- # @return [String] default i18n locale
134
- def base_locale
135
- I18n.default_locale.to_s
136
- end
137
-
138
- # @return [Hash{String => String,Hash}] default i18n locale data
139
- def base_locale_data
140
- locale_data(base_locale)[base_locale]
141
- end
142
-
143
- # Run grep searching for source keys and return grep output
144
- # @return [String] output of the grep command
145
- def run_grep
146
- args = ['grep', '-HoRI']
147
- [:include, :exclude].each do |opt|
148
- next unless (val = grep_config[opt]).present?
149
- args += Array(val).map { |v| "--#{opt}=#{v}" }
150
- end
151
- args += [%q{\\bt(\\?\\s*['"]\\([^'"]*\\)['"]}, *grep_config[:paths]]
152
- args.compact!
153
- run_command *args
154
- end
155
-
156
25
  end
157
26
  end
158
27
  end
@@ -1,19 +1,19 @@
1
1
  module I18n::Tasks
2
2
  module Data
3
3
  class Yaml
4
- attr_reader :paths
4
+ attr_reader :options
5
5
 
6
6
  def initialize(options)
7
- @paths = options[:paths]
7
+ @options = options
8
8
  end
9
9
 
10
10
  def get(locale)
11
- paths.map do |path|
11
+ options[:paths].map do |path|
12
12
  Dir.glob path % { locale: locale }
13
13
  end.flatten.map do |locale_file|
14
14
  YAML.load_file locale_file
15
15
  end.inject({}) do |hash, locale_data|
16
- hash.deep_merge! locale_data
16
+ hash.deep_merge! locale_data || {}
17
17
  hash
18
18
  end
19
19
  end
@@ -0,0 +1,20 @@
1
+ require 'i18n/tasks/key_pattern_matching'
2
+
3
+ # e.g t("category.#{category_key}")
4
+ module I18n::Tasks
5
+ module FuzzySourceKeys
6
+ include KeyPatternMatching
7
+
8
+ # dynamically generated keys in the source, e.g t("category.#{category_key}")
9
+ def pattern_key?(key)
10
+ @pattern_keys_re ||= compile_start_with_re(pattern_key_prefixes)
11
+ !!(key =~ @pattern_keys_re)
12
+ end
13
+
14
+ # keys in the source that end with a ., e.g. t("category.#{cat.i18n_key}") or t("category." + category.key)
15
+ def pattern_key_prefixes
16
+ @pattern_keys_prefixes ||=
17
+ find_source_keys.select { |k| k =~ /\#{.*?}/ || k.ends_with?('.') }.map { |k| k.split(/\.?#/)[0].presence }.compact
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module I18n::Tasks::IgnoreKeys
2
+ # whether to ignore the key. ignore_type one of :missing, :eq_base, :blank, :unused.
3
+ # will apply global ignore rules as well
4
+ def ignore_key?(key, ignore_type, locale = nil)
5
+ key =~ ignore_pattern(ignore_type, locale)
6
+ end
7
+
8
+ # @param type [:missing, :eq_base, :unused] type
9
+ # @param locale [String] only when type is :eq_base
10
+ # @return [Regexp] a regexp that matches all the keys ignored for the type (and locale)
11
+ def ignore_pattern(type, locale = nil)
12
+ ((@ignore_patterns ||= HashWithIndifferentAccess.new)[type] ||= {})[locale] = begin
13
+ global, type_ignore = config[:ignore].presence || [], config["ignore_#{type}"].presence || []
14
+ if type_ignore.is_a?(Array)
15
+ patterns = global + type_ignore
16
+ elsif type_ignore.is_a?(Hash)
17
+ # ignore per locale
18
+ patterns = global + (type_ignore[:all] || []) +
19
+ type_ignore.select { |k, v| k.to_s =~ /\b#{locale}\b/ }.values.flatten(1).compact
20
+ end
21
+ compile_patterns_re patterns
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,39 @@
1
+ module I18n::Tasks::KeyPatternMatching
2
+ MATCH_NOTHING = /\z\A/
3
+
4
+ # one regex to match any
5
+ def compile_patterns_re(key_patterns)
6
+ if key_patterns.blank?
7
+ # match nothing
8
+ MATCH_NOTHING
9
+ else
10
+ /(?:#{ key_patterns.map { |p| key_pattern_to_re p } * '|' })/m
11
+ end
12
+ end
13
+
14
+ # convert key.* to key\..*
15
+ def key_pattern_to_re(key_pattern)
16
+ if key_pattern.end_with? '.'
17
+ $stderr.puts %Q(i18n-tasks: Deprecated "#{key_pattern}", please change to "#{key_pattern += '*'}".)
18
+ end
19
+ /^#{key_pattern.
20
+ gsub(/\./, '\.').
21
+ gsub(/\*/, '.*')}$/
22
+ end
23
+
24
+ # @return [Array<String>] keys sans passed patterns
25
+ def exclude_patterns(keys, patterns)
26
+ pattern_re = compile_patterns_re patterns.select { |p| p.end_with?('.') }
27
+ (keys - patterns).reject { |k| k =~ pattern_re }
28
+ end
29
+
30
+ # compile prefix matching Regexp from the list of prefixes
31
+ # @return [Regexp] regexp matching any of the prefixes
32
+ def compile_start_with_re(prefixes)
33
+ if prefixes.blank?
34
+ MATCH_NOTHING # match nothing
35
+ else
36
+ /^(?:#{prefixes.map { |p| Regexp.escape(p) }.join('|')})/
37
+ end
38
+ end
39
+ end
@@ -18,8 +18,12 @@ module I18n
18
18
 
19
19
  def unused(unused)
20
20
  $stderr.puts bold cyan("Unused i18n keys (#{unused.length})")
21
- key_col_width = unused.max_by { |x| x[0].length }[0].length + 2
22
- unused.each { |(key, value)| puts "#{magenta key.ljust(key_col_width)}#{cyan value.to_s.strip}" }
21
+ if unused.present?
22
+ key_col_width = unused.max_by { |x| x[0].length }[0].length + 2
23
+ unused.each { |(key, value)| puts "#{magenta key.ljust(key_col_width)}#{cyan value.to_s.strip}" }
24
+ else
25
+ $stderr.puts(bold green 'Good job! Every translation is used!')
26
+ end
23
27
  end
24
28
 
25
29
  private
@@ -34,7 +38,10 @@ module I18n
34
38
  def print_missing_translation(m, opts)
35
39
  locale, key, base_value, status_text = m[:locale], m[:key], m[:base_value].to_s.try(:strip), " #{STATUS_TEXTS[m[:type]]}"
36
40
 
37
- key = magenta key.ljust(opts[:key_col_width])
41
+ long = base_value.length > 50
42
+
43
+ key = magenta "#{key}#{':' if long}".ljust(opts[:key_col_width])
44
+ base_value = "\n#{indent(base_value, 13)}\n" if long
38
45
  s = if m[:type] == :none
39
46
  "#{red bold locale.ljust(4)} #{status_text} #{key}"
40
47
  else
@@ -42,6 +49,12 @@ module I18n
42
49
  end
43
50
  puts s
44
51
  end
52
+
53
+ private
54
+ def indent(txt, n = 2)
55
+ spaces = ' ' * n
56
+ txt.gsub /^/, spaces
57
+ end
45
58
  end
46
59
  end
47
60
  end
@@ -0,0 +1,17 @@
1
+ module I18n::Tasks::PluralKeys
2
+ PLURAL_KEY_RE = /\.(?:zero|one|two|few|many|other)$/
3
+
4
+ # @param key [String] i18n key
5
+ # @param data [Hash{String => String,Hash}] locale data
6
+ # @return the base form if the key is a specific plural form (e.g. apple for apple.many), and the key as passed otherwise
7
+ def depluralize_key(key, data)
8
+ return key if key !~ PLURAL_KEY_RE || t(data, key).is_a?(Hash)
9
+ parent_key = key.split('.')[0..-2] * '.'
10
+ plural_versions = t(data, parent_key)
11
+ if plural_versions.is_a?(Hash) && plural_versions.all? { |k, v| ".#{k}" =~ PLURAL_KEY_RE && !v.is_a?(Hash) }
12
+ parent_key
13
+ else
14
+ key
15
+ end
16
+ end
17
+ end
@@ -3,6 +3,7 @@ require 'i18n/tasks/base_task'
3
3
  module I18n
4
4
  module Tasks
5
5
  class Prefill < BaseTask
6
+ # todo refactor to allow configuring output targets
6
7
  def perform
7
8
  # Will also rewrite en, good for ordering
8
9
  I18n.available_locales.map(&:to_s).each do |target_locale|
@@ -11,6 +12,12 @@ module I18n
11
12
  File.open(locale_file_path(target_locale), 'w'){ |f| f.write prefilled.to_yaml }
12
13
  end
13
14
  end
15
+
16
+ # main locale file path (for writing to)
17
+ # @return [String]
18
+ def locale_file_path(locale)
19
+ "config/locales/#{locale}.yml"
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -0,0 +1,13 @@
1
+ module I18n::Tasks::RelativeKeys
2
+
3
+ # @param key [String] relative i18n key (starts with a .)
4
+ # @param path [String] path to the file containing the key
5
+ # @return [String] absolute version of the key
6
+ def absolutize_key(key, path)
7
+ # normalized path
8
+ path = Pathname.new(File.expand_path path).relative_path_from(Pathname.new(Dir.pwd)).to_s
9
+ # key prefix based on path
10
+ prefix = path.gsub(%r(app/views/|(\.[^/]+)*$), '').tr('/', '.').gsub(%r(\._), '.')
11
+ "#{prefix}#{key}"
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ module I18n::Tasks::TranslationData
2
+ # locale data hash, with locale name as root
3
+ # @return [Hash{String => String,Hash}] locale data in nested hash format
4
+ def locale_data(locale)
5
+ locale = locale.to_s
6
+ (@locale_data ||= {})[locale] ||= data_source.get(locale) || {}
7
+ end
8
+
9
+ # I18n data provider
10
+ def data_source
11
+ return @source if @source
12
+ conf = config[:data] || {}
13
+ @source = if conf[:class]
14
+ conf[:class].constantize.new(conf.except(:class))
15
+ else
16
+ I18n::Tasks::Data::Yaml.new(
17
+ paths: Array(conf[:paths].presence || ['config/locales/%{locale}.yml'])
18
+ )
19
+ end
20
+ end
21
+
22
+ # translation of the key found in the passed hash or nil
23
+ # @return [String,nil]
24
+ def t(hash, key)
25
+ key.split('.').inject(hash) { |r, seg| r[seg] if r }
26
+ end
27
+
28
+ # traverse hash, yielding with full key and value
29
+ # @param hash [Hash{String => String,Hash}] translation data to traverse
30
+ # @yield [full_key, value] yields full key and value for every translation in #hash
31
+ # @return [nil]
32
+ def traverse(path = '', hash)
33
+ q = [[path, hash]]
34
+ until q.empty?
35
+ path, value = q.pop
36
+ if value.is_a?(Hash)
37
+ value.each { |k, v| q << ["#{path}.#{k}", v] }
38
+ else
39
+ yield path[1..-1], value
40
+ end
41
+ end
42
+ end
43
+
44
+ # @return [String] default i18n locale
45
+ def base_locale
46
+ I18n.default_locale.to_s
47
+ end
48
+
49
+ # @return [Hash{String => String,Hash}] default i18n locale data
50
+ def base_locale_data
51
+ locale_data(base_locale)[base_locale]
52
+ end
53
+
54
+ # whether the value for key exists in locale (defaults: base_locale)
55
+ def key_has_value?(key, locale = base_locale)
56
+ t(locale_data(locale)[locale], key).present?
57
+ end
58
+ end
@@ -0,0 +1,60 @@
1
+ require 'open3'
2
+ module I18n::Tasks::UsageSearch
3
+ # grep config, also from config/i18n-tasks.yml
4
+ # @return [Hash{String => String,Hash,Array}]
5
+ def grep_config
6
+ @grep_config ||= (config[:grep] || {}).with_indifferent_access.tap do |conf|
7
+ conf[:paths] = ['app/'] if conf[:paths].blank?
8
+ end
9
+ end
10
+
11
+ # whether the key is used in the source
12
+ def used_key?(key)
13
+ @used_keys ||= find_source_keys.to_set
14
+ @used_keys.include?(key)
15
+ end
16
+
17
+ # find all keys in the source (relative keys are returned in absolutized)
18
+ # @return [Array<String>]
19
+ def find_source_keys
20
+ @source_keys ||= begin
21
+ if (grep_out = run_grep)
22
+ grep_out.split("\n").map { |r|
23
+ key = r.match(/['"](.*?)['"]/)[1]
24
+ if key.start_with? '.'
25
+ absolutize_key key, r.split(':')[0]
26
+ else
27
+ key
28
+ end
29
+ }.uniq.reject { |k| k !~ /^[\w.\#{}]+$/ }
30
+ else
31
+ []
32
+ end
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ # Run grep searching for source keys and return grep output
39
+ # @return [String] output of the grep command
40
+ def run_grep
41
+ args = ['grep', '-HoRI']
42
+ [:include, :exclude].each do |opt|
43
+ next unless (val = grep_config[opt]).present?
44
+ args += Array(val).map { |v| "--#{opt}=#{v}" }
45
+ end
46
+ args += [%q{\\bt(\\?\\s*['"]\\([^'"]*\\)['"]}, *grep_config[:paths]]
47
+ args.compact!
48
+ run_command *args
49
+ end
50
+
51
+
52
+ # Run command and get only stdout output
53
+ # @return [String] output
54
+ # @raise [RuntimeError] if grep returns with exit code other than 0
55
+ def run_command(*args)
56
+ o, e, s = Open3.capture3(*args)
57
+ raise "#{args[0]} failed with status #{s.exitstatus} (stderr: #{e})" unless s.success?
58
+ o
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  module I18n
2
2
  module Tasks
3
- VERSION = '0.1.5'
3
+ VERSION = '0.1.6'
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  # do not report these keys as missing:
2
2
  ignore_missing:
3
3
  - ignored_missing_key.a # one key to ignore
4
- - ignored_pattern. # ignore the whole pattern
4
+ - ignored_pattern.* # ignore the whole pattern
5
5
 
6
6
  # do not report these keys when they have the same value as the base locale version
7
7
  ignore_eq_base:
@@ -34,8 +34,8 @@ grep:
34
34
 
35
35
  # how to get the locale data
36
36
  data:
37
+ class: I18n::Tasks::Data::Yaml
37
38
  paths:
38
39
  # files for a given %{locale}
39
40
  - 'config/locales/%{locale}.yml'
40
41
  - 'config/locales/*.%{locale}.yml'
41
- class: I18n::Tasks::Data::Yaml
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+ describe 'README.md' do
3
+ let(:readme) { File.read('README.md') }
4
+ it 'has valid YAML in ```yaml blocks' do
5
+ readme.scan /```yaml\n(.*)(?=^)\n```/ do |m|
6
+ expect { YAML.load(m[0]) }.to_not raise_errors
7
+ end
8
+ end
9
+ end
@@ -7,14 +7,13 @@ RSpec::Matchers.define :be_i18n_keys do |expected|
7
7
  def extract_keys(actual)
8
8
  locales = I18n.available_locales.map(&:to_s)
9
9
  actual.split("\n").map { |x|
10
- x.strip!
11
10
  key = x.gsub(/\s+/, ' ').split(' ').reverse.detect { |p| p && p.include?('.') }
12
11
  if x =~ locale_re && locales.include?(x[0..1]) && !(key =~ locale_re && locales.include?(key[0..1]))
13
- x.split(' ', 2)[0] + '.' + key
14
- else
15
- key
12
+ key = x.split(' ', 2)[0] + '.' + key
16
13
  end
17
- }
14
+ key = key[0..-2] if key.end_with?(':')
15
+ key
16
+ }.compact
18
17
  end
19
18
 
20
19
  match do |actual|
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.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-04 00:00:00.000000000 Z
11
+ date: 2013-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -128,12 +128,18 @@ files:
128
128
  - lib/i18n/tasks.rb
129
129
  - lib/i18n/tasks/base_task.rb
130
130
  - lib/i18n/tasks/data/yaml.rb
131
+ - lib/i18n/tasks/fuzzy_source_keys.rb
132
+ - lib/i18n/tasks/ignore_keys.rb
133
+ - lib/i18n/tasks/key_pattern_matching.rb
131
134
  - lib/i18n/tasks/missing.rb
132
135
  - lib/i18n/tasks/output/terminal.rb
136
+ - lib/i18n/tasks/plural_keys.rb
133
137
  - lib/i18n/tasks/prefill.rb
134
138
  - lib/i18n/tasks/railtie.rb
135
- - lib/i18n/tasks/task_helpers.rb
139
+ - lib/i18n/tasks/relative_keys.rb
140
+ - lib/i18n/tasks/translation_data.rb
136
141
  - lib/i18n/tasks/unused.rb
142
+ - lib/i18n/tasks/usage_search.rb
137
143
  - lib/i18n/tasks/version.rb
138
144
  - lib/tasks/i18n-tasks.rake
139
145
  - spec/fixtures/app/assets/javascripts/application.js
@@ -142,6 +148,7 @@ files:
142
148
  - spec/fixtures/app/views/relative/index.html.slim
143
149
  - spec/fixtures/config/i18n-tasks.yml
144
150
  - spec/i18n_tasks_spec.rb
151
+ - spec/readme_spec.rb
145
152
  - spec/spec_helper.rb
146
153
  - spec/support/fixtures.rb
147
154
  - spec/support/i18n_tasks_output_matcher.rb
@@ -178,6 +185,7 @@ test_files:
178
185
  - spec/fixtures/app/views/relative/index.html.slim
179
186
  - spec/fixtures/config/i18n-tasks.yml
180
187
  - spec/i18n_tasks_spec.rb
188
+ - spec/readme_spec.rb
181
189
  - spec/spec_helper.rb
182
190
  - spec/support/fixtures.rb
183
191
  - spec/support/i18n_tasks_output_matcher.rb
@@ -1,64 +0,0 @@
1
- # coding: utf-8
2
- require 'open3'
3
-
4
- module I18n
5
- module Tasks
6
- module TaskHelpers
7
- # Run command and get only stdout output
8
- # @return [String] output
9
- # @raise [RuntimeError] if grep returns with exit code other than 0
10
- def run_command(*args)
11
- o, e, s = Open3.capture3(*args)
12
- raise "#{args[0]} failed with status #{s.exitstatus} (stderr: #{e})" unless s.success?
13
- o
14
- end
15
-
16
- # compile prefix matching Regexp from the list of prefixes
17
- # @return [Regexp] regexp matching any of the prefixes
18
- def compile_start_with_re(prefixes)
19
- if prefixes.blank?
20
- /\Z\A/ # match nothing
21
- else
22
- /^(?:#{prefixes.map { |p| Regexp.escape(p) }.join('|')})/
23
- end
24
- end
25
-
26
- # @return [Array<String>] keys sans passed patterns
27
- def exclude_patterns(keys, patterns)
28
- pattern_re = compile_start_with_re patterns.select { |p| p.end_with?('.') }
29
- (keys - patterns).reject { |k| k =~ pattern_re }
30
- end
31
-
32
- # @param type [:missing, :eq_base, :unused] type
33
- # @param locale [String] only when type is :eq_base
34
- # @return [Regexp] a regexp that matches all the keys ignored for the type (and locale)
35
- def ignore_pattern(type, locale = nil)
36
- ((@ignore_patterns ||= HashWithIndifferentAccess.new)[type] ||= {})[locale] = begin
37
- global, type_ignore = config[:ignore].presence || [], config["ignore_#{type}"].presence || []
38
- if type_ignore.is_a?(Array)
39
- patterns = global + type_ignore
40
- elsif type_ignore.is_a?(Hash)
41
- # ignore per locale
42
- patterns = global + (type_ignore[:all] || []) +
43
- type_ignore.select { |k, v| k.to_s =~ /\b#{locale}\b/ }.values.flatten(1).compact
44
- end
45
- compile_start_with_re patterns
46
- end
47
- end
48
-
49
- # i18n-tasks config (defaults + config/i18n-tasks.yml)
50
- # @return [Hash{String => String,Hash,Array}]
51
- def config
52
- I18n::Tasks.config
53
- end
54
-
55
- # grep config, also from config/i18n-tasks.yml
56
- # @return [Hash{String => String,Hash,Array}]
57
- def grep_config
58
- @grep_config ||= (config[:grep] || {}).with_indifferent_access.tap do |conf|
59
- conf[:paths] = ['app/'] if conf[:paths].blank?
60
- end
61
- end
62
- end
63
- end
64
- end