i18n-tasks 0.1.5 → 0.1.6

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: 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