i18n-tasks 0.2.19 → 0.2.20

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +5 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +51 -39
  5. data/doc/img/i18n-usages.png +0 -0
  6. data/i18n-tasks.gemspec +1 -1
  7. data/lib/i18n/tasks.rb +7 -2
  8. data/lib/i18n/tasks/base_task.rb +2 -4
  9. data/lib/i18n/tasks/configuration.rb +2 -4
  10. data/lib/i18n/tasks/data/adapter/json_adapter.rb +22 -0
  11. data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +21 -0
  12. data/lib/i18n/tasks/data/file_system.rb +13 -0
  13. data/lib/i18n/tasks/data/storage/file_storage.rb +105 -0
  14. data/lib/i18n/tasks/data/yaml.rb +6 -65
  15. data/lib/i18n/tasks/data_traversal.rb +2 -2
  16. data/lib/i18n/tasks/fill_tasks.rb +2 -11
  17. data/lib/i18n/tasks/ignore_keys.rb +3 -1
  18. data/lib/i18n/tasks/key.rb +60 -0
  19. data/lib/i18n/tasks/key_group.rb +65 -0
  20. data/lib/i18n/tasks/missing_keys.rb +51 -18
  21. data/lib/i18n/tasks/reports/base.rb +8 -4
  22. data/lib/i18n/tasks/reports/spreadsheet.rb +8 -8
  23. data/lib/i18n/tasks/reports/terminal.rb +39 -16
  24. data/lib/i18n/tasks/scanners/base_scanner.rb +36 -2
  25. data/lib/i18n/tasks/scanners/pattern_scanner.rb +9 -7
  26. data/lib/i18n/tasks/translation_data.rb +4 -1
  27. data/lib/i18n/tasks/unused_keys.rb +9 -7
  28. data/lib/i18n/tasks/{source_keys.rb → used_keys.rb} +5 -6
  29. data/lib/i18n/tasks/version.rb +1 -1
  30. data/lib/tasks/i18n-tasks.rake +24 -20
  31. data/spec/file_system_data_spec.rb +68 -0
  32. data/spec/fixtures/config/i18n-tasks.yml +2 -2
  33. data/spec/i18n_tasks_spec.rb +5 -5
  34. data/spec/key_group_spec.rb +48 -0
  35. data/spec/used_keys_spec.rb +22 -0
  36. metadata +17 -7
  37. data/lib/i18n/tasks/untranslated_keys.rb +0 -50
  38. data/spec/yaml_adapter_spec.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c8a1d39250f5a17141d7e2ef77087cfbb2350723
4
- data.tar.gz: 4c24ee7926d87036337ef73c85fd19963f512c9c
3
+ metadata.gz: ab1b5fe079fcb795dc047f1e0e4f3d777b83c2ef
4
+ data.tar.gz: 23b4a9f9e87b2f0ed8ac2c8c8a2b1992edc7b90d
5
5
  SHA512:
6
- metadata.gz: fda620af8e5ed8bb3481d2aa64f7961086eed48ad8958f28f54df723bec60e35f2ad006f01d33e9a51abdd0ce1bf338741c9959cc1c5d286e6f93a8026f6d1c1
7
- data.tar.gz: 8f971a9e3024f79310958b5701e9ec255fb14278e3e90464338c42a097cc5fbcbfeaa3e8455975ad5651fc0ceb494f7a5f6c88bce37eecb3d387f8d1871547ce
6
+ metadata.gz: 4100f55d87da5b5131c3adf1794ae10d2fb4b1de691966ff6b24129bf660fca3035a857d841707ceb8946214926646b28aad8f0b2154b77b294a98422b533cd3
7
+ data.tar.gz: 335f197c93d93fc41c27600815a74a964851778050d1d699de15be38998f17a3f5e3354dddcb52708ec50151a5a058cab41fd232de814c6b47e18ddfb6768595
data/CHANGES.md CHANGED
@@ -1,4 +1,8 @@
1
- ## v0.2.17..v0.2.18
1
+ ## v0.2.20
2
+
3
+ * `rake i18n:usages` report
4
+
5
+ ## v0.2.17..v0.2.19
2
6
 
3
7
  * Bugfixes
4
8
 
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Gleb Mazovetskiy
1
+ Copyright (c) 2013-2014 Gleb Mazovetskiy
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -5,6 +5,21 @@ Tasks to manage translations in ruby applications using I18n.
5
5
 
6
6
  ![i18n-screenshot](https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-tasks.gif "i18n-tasks output screenshot")
7
7
 
8
+ ## Installation
9
+
10
+ Simply add to Gemfile:
11
+
12
+ ```ruby
13
+ gem 'i18n-tasks', '~> 0.2.20'
14
+ ```
15
+
16
+ If not using Rails, require the tasks in Rakefile:
17
+
18
+ ```ruby
19
+ # Rakefile
20
+ load 'tasks/i18n-tasks.rake'
21
+ ```
22
+
8
23
  ## Usage
9
24
 
10
25
  Use `rake -T i18n` to get the list of tasks with descriptions. These are [the tasks](/lib/tasks/i18n-tasks.rake) available:
@@ -56,29 +71,13 @@ rake i18n:normalize
56
71
  `i18n:unused` will detect pattern translations and not report them, e.g.:
57
72
 
58
73
  ```ruby
59
- t 'category.' + category.key # 'category.arts_and_crafts' considered used
74
+ t 'category.' + category.key # category.* keys are all considered used
60
75
  t "category.#{category.key}" # also works
61
76
  ```
62
77
 
63
78
  Relative keys (`t '.title'`) and plural keys (key.one/many/other/etc) are fully supported.
64
79
 
65
- For more examples see [the tests](/spec/i18n_tasks_spec.rb).
66
-
67
-
68
- ## Installation
69
-
70
- Simply add to Gemfile:
71
-
72
- ```ruby
73
- gem 'i18n-tasks', '~> 0.2.10'
74
- ```
75
-
76
- If not using Rails, require the tasks in Rakefile:
77
-
78
- ```ruby
79
- # Rakefile
80
- load 'tasks/i18n-tasks.rake'
81
- ```
80
+ Translation data storage, key usage search, and other [settings](#configuration) are compatible with Rails by default.
82
81
 
83
82
  ## Configuration
84
83
 
@@ -92,24 +91,31 @@ base_locale: en
92
91
  locales: [es, fr]
93
92
  ```
94
93
 
94
+ On Rails, if locales are set in the config file, you can make i18n tasks a lot faster by adding this to `Rakefile`:
95
+
96
+ ```ruby
97
+ # disable loading :environment for i18n-tasks
98
+ Rake::Task['i18n:setup'].clear_prerequisites
99
+ ```
100
+
95
101
  ### Storage
96
102
 
97
103
  ```yaml
98
104
  # i18n data storage
99
105
  data:
100
- # The default YAML adapter supports reading from and writing to YAML files
101
- adapter: yaml
106
+ # The default file adapter supports YAML and JSON files. You can provide a custom class name here.
107
+ adapter: file_system
102
108
  # a list of file globs to read from per-locale
103
109
  read:
104
- # this one is default:
110
+ # default:
105
111
  - 'config/locales/%{locale}.yml'
106
- # add this one to also read from namespaced files, e.g. simple_form.en.yml:
112
+ # to also read from namespaced files, e.g. simple_form.en.yml:
107
113
  - 'config/locales/*.%{locale}.yml'
108
114
  # a list of {key pattern => file} routes, matched top to bottom
109
115
  write:
110
- # this would save all devise keys in it's own file (per locale):
116
+ # save all devise keys in it's own file (per locale):
111
117
  - ['devise.*', 'config/locales/devise.%{locale}.yml']
112
- # this is the default catch-all:
118
+ # default catch-all:
113
119
  - 'config/locales/%{locale}.yml' # path is short for ['*', path]
114
120
  ```
115
121
 
@@ -127,13 +133,24 @@ Example:
127
133
  data:
128
134
  write:
129
135
  # store sorcery and simple_form keys in the respective files:
130
- - ['{sorcery,simple_form}.*', 'config/locales/\\1.%{locale}.yml']
131
- # write every namespace to its own file:
136
+ - ['{sorcery,simple_form}.*', 'config/locales/\1.%{locale}.yml']
137
+ # write every key namespace to its own file:
132
138
  - ['{:}.*', 'config/locales/\1.%{locale}.yml']
133
139
  ```
134
140
 
135
141
  ### Usage search
136
142
 
143
+ Inspect all the usages with:
144
+
145
+ ```bash
146
+ rake i18n:usages
147
+ ```
148
+
149
+ ![i18n-screenshot](https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-usages.png "rake i18n:usages output screenshot")
150
+
151
+
152
+ Configure usage search in `config/i18n-tasks.yml`:
153
+
137
154
  ```yaml
138
155
  # i18n usage search in source
139
156
  search:
@@ -164,8 +181,8 @@ relative_roots:
164
181
  - app/views-mobile
165
182
  ```
166
183
 
167
- It is also possible to use a key scanner by setting `search.scanner`.
168
- See [the default scanner](/lib/i18n/tasks/scanners/pattern_scanner.rb) for reference.
184
+ It is also possible to use a custom key usage scanner by setting `search.scanner` to a class name.
185
+ See [the default pattern scanner](/lib/i18n/tasks/scanners/pattern_scanner.rb) for reference.
169
186
 
170
187
 
171
188
  ### Fine-tuning
@@ -215,24 +232,23 @@ You might want to test for missing and unused translations as part of your test
215
232
  This is how you can do it with rspec:
216
233
 
217
234
  ```ruby
218
- # spec_helper.rb
235
+ # spec/i18n_keys_spec.rb:
236
+ require 'spec_helper'
237
+
219
238
  require 'i18n/tasks'
220
- require 'i18n/tasks/base_task'
221
239
 
222
- # spec/i18n_keys_spec.rb
223
- require 'spec_helper'
224
- describe 'translation keys' do
240
+ describe 'Translation keys' do
225
241
  let(:i18n) { I18n::Tasks::BaseTask.new }
226
242
 
227
243
  it 'are all present' do
228
- expect(i18n.untranslated_keys).to have(0).keys
244
+ expect(i18n.missing_keys).to have(0).keys
229
245
  end
230
246
 
231
247
  it 'are all used' do
232
248
  expect(i18n.unused_keys).to have(0).keys
233
249
  end
234
-
235
250
  end
251
+
236
252
  ```
237
253
 
238
254
  ## XLSX
@@ -253,7 +269,3 @@ While i18n-tasks does not provide an HTML version of the report, you can add [on
253
269
  This was originally developed for [Zuigo](http://zuigo.com/), a platform to organize and discover events.
254
270
 
255
271
  [MIT license](/LICENSE.txt)
256
-
257
-
258
- [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/glebm/i18n-tasks/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
259
-
Binary file
@@ -34,6 +34,6 @@ Gem::Specification.new do |s|
34
34
  s.add_development_dependency 'axlsx', '~> 2.0'
35
35
  s.add_development_dependency 'bundler', '~> 1.3'
36
36
  s.add_development_dependency 'rake'
37
- s.add_development_dependency 'rspec-rails'
37
+ s.add_development_dependency 'rspec'
38
38
  s.add_development_dependency 'yard'
39
39
  end
@@ -1,4 +1,3 @@
1
- require 'i18n/tasks/version'
2
1
  require 'active_support/core_ext/hash'
3
2
  require 'active_support/core_ext/string'
4
3
  require 'active_support/core_ext/module/delegation'
@@ -6,7 +5,11 @@ require 'active_support/core_ext/object/try'
6
5
  require 'active_support/core_ext/object/blank'
7
6
  require 'term/ansicolor'
8
7
  require 'erubis'
9
- require 'i18n/tasks/railtie' if defined?(Rails)
8
+
9
+ require 'i18n/tasks/version'
10
+ require 'i18n/tasks/key'
11
+ require 'i18n/tasks/key_group'
12
+ require 'i18n/tasks/base_task'
10
13
 
11
14
  module I18n
12
15
  module Tasks
@@ -29,3 +32,5 @@ module I18n
29
32
  end
30
33
  end
31
34
  end
35
+
36
+ require 'i18n/tasks/railtie' if defined?(Rails)
@@ -3,11 +3,10 @@ require 'i18n/tasks/configuration'
3
3
  require 'i18n/tasks/key_pattern_matching'
4
4
  require 'i18n/tasks/relative_keys'
5
5
  require 'i18n/tasks/plural_keys'
6
- require 'i18n/tasks/source_keys'
6
+ require 'i18n/tasks/used_keys'
7
7
  require 'i18n/tasks/translation_data'
8
8
  require 'i18n/tasks/ignore_keys'
9
9
  require 'i18n/tasks/missing_keys'
10
- require 'i18n/tasks/untranslated_keys'
11
10
  require 'i18n/tasks/unused_keys'
12
11
  require 'i18n/tasks/google_translation'
13
12
  require 'i18n/tasks/fill_tasks'
@@ -21,9 +20,8 @@ module I18n
21
20
  include DataTraversal
22
21
  include RelativeKeys
23
22
  include PluralKeys
24
- include SourceKeys
23
+ include UsedKeys
25
24
  include MissingKeys
26
- include UntranslatedKeys
27
25
  include UnusedKeys
28
26
  include TranslationData
29
27
  include FillTasks
@@ -1,6 +1,4 @@
1
1
  module I18n::Tasks::Configuration
2
- extend ::ActiveSupport::Concern
3
-
4
2
  # i18n-tasks config (defaults + config/i18n-tasks.yml)
5
3
  # @return [Hash{String => String,Hash,Array}]
6
4
  def config
@@ -18,7 +16,7 @@ module I18n::Tasks::Configuration
18
16
  def data_config
19
17
  @config_sections[:data] ||= begin
20
18
  conf = (config[:data] || {}).with_indifferent_access
21
- adapter = (conf[:adapter].presence || conf[:class].presence || :yaml).to_s
19
+ adapter = (conf[:adapter].presence || conf[:class].presence || :file_system).to_s
22
20
  if adapter !~ /[A-Z]/
23
21
  adapter = "I18n::Tasks::Data::#{adapter.camelize}"
24
22
  end
@@ -34,7 +32,7 @@ module I18n::Tasks::Configuration
34
32
  # @return [Hash{String => String,Hash,Array}]
35
33
  def translation_config
36
34
  @config_sections[:translation] ||= begin
37
- conf = (config[:translation] ||= {}).with_indifferent_access
35
+ conf = (config[:translation] || {}).with_indifferent_access
38
36
  conf[:api_key] ||= ENV['GOOGLE_TRANSLATE_API_KEY']
39
37
  conf
40
38
  end
@@ -0,0 +1,22 @@
1
+ require 'json'
2
+
3
+ module I18n::Tasks
4
+ module Data
5
+ module Adapter
6
+ module JsonAdapter
7
+ extend self
8
+
9
+ # @return [Hash] locale tree
10
+ def parse(str)
11
+ JSON.parse(str)
12
+ end
13
+
14
+ # @return [String]
15
+ def dump(tree)
16
+ JSON.generate(tree)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ require 'yaml'
2
+ module I18n::Tasks
3
+ module Data
4
+ module Adapter
5
+ module YamlAdapter
6
+ extend self
7
+
8
+ # @return [Hash] locale tree
9
+ def parse(str)
10
+ YAML.load(str)
11
+ end
12
+
13
+ # @return [String]
14
+ def dump(tree)
15
+ tree.to_yaml
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ require 'i18n/tasks/data/storage/file_storage'
2
+ require 'i18n/tasks/data/adapter/json_adapter'
3
+ require 'i18n/tasks/data/adapter/yaml_adapter'
4
+
5
+ module I18n::Tasks
6
+ module Data
7
+ class FileSystem
8
+ include Storage::FileStorage
9
+ register_adapter '*.yml', Adapter::YamlAdapter
10
+ register_adapter '*.json', Adapter::JsonAdapter
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,105 @@
1
+ require 'i18n/tasks/data_traversal'
2
+ require 'i18n/tasks/key_pattern_matching'
3
+
4
+ module I18n::Tasks
5
+ module Data
6
+ module Storage
7
+ module FileStorage
8
+
9
+ def self.included(base)
10
+ base.extend KlassMethods
11
+ end
12
+
13
+ include ::I18n::Tasks::DataTraversal
14
+ include ::I18n::Tasks::KeyPatternMatching
15
+ attr_reader :config
16
+
17
+ DEFAULTS = {
18
+ read: ['config/locales/%{locale}.yml'],
19
+ write: ['config/locales/%{locale}.yml']
20
+ }.with_indifferent_access
21
+
22
+ def initialize(config = {})
23
+ self.config = config
24
+ end
25
+
26
+ def config=(config)
27
+ opt = DEFAULTS.deep_merge((config || {}).with_indifferent_access)
28
+ @read = opt[:read]
29
+ @write = opt[:write].map { |x| x.is_a?(String) ? ['*', x] : x }.map { |x|
30
+ [compile_key_pattern(x[0]), x[1]]
31
+ }
32
+ @locale_data = {}
33
+ end
34
+
35
+ # get locale tree
36
+ def get(locale)
37
+ locale = locale.to_s
38
+ @locale_data[locale] ||= begin
39
+ @read.map do |path|
40
+ Dir.glob path % {locale: locale}
41
+ end.flatten.map do |locale_file|
42
+ load_file locale_file
43
+ end.inject({}) do |hash, locale_data|
44
+ hash.deep_merge! locale_data || {}
45
+ hash
46
+ end[locale.to_s] || {}
47
+ end
48
+ end
49
+
50
+ alias [] get
51
+
52
+ # set locale tree
53
+ def set(locale, value_tree)
54
+ locale = locale.to_s
55
+ out = {}
56
+ traverse value_tree do |key, value|
57
+ route = @write.detect { |route| route[0] =~ key }
58
+ key_match = $~
59
+ path = route[1] % {locale: locale}
60
+ path.gsub!(/[\\]\d+/) { |m| key_match[m[1..-1].to_i] }
61
+ (out[path] ||= []) << [key, value]
62
+ end
63
+ out.each do |path, data|
64
+ tree = {locale => list_to_tree(data)}
65
+ write_tree(path, tree)
66
+ end
67
+ @locale_data[locale] = nil
68
+ end
69
+
70
+ alias []= set
71
+
72
+ def reload
73
+ @locale_data = {}
74
+ end
75
+
76
+ protected
77
+
78
+ def load_file(file)
79
+ adapter_for(file).parse ::File.read(file)
80
+ end
81
+
82
+ def write_tree(path, tree)
83
+ ::File.open(path, 'w') { |f| f.write(adapter_for(path).dump(tree)) }
84
+ end
85
+
86
+ def adapter_for(file)
87
+ self.class.adapter_for(file)
88
+ end
89
+
90
+ module KlassMethods
91
+ # @param pattern [String] File.fnmatch pattern
92
+ # @param adapter [responds to parse(string)->hash and dump(hash)->string]
93
+ def register_adapter(pattern, adapter)
94
+ (@fn_patterns ||= {})[pattern] = adapter
95
+ end
96
+
97
+ def adapter_for(path)
98
+ @fn_patterns.detect { |pattern, adapter| ::File.fnmatch(pattern, path) }[1] or
99
+ raise "Adapter not found for #{path}. Registered adapters: #{@fn_patterns.inspect}"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -1,72 +1,13 @@
1
- require 'i18n/tasks/data_traversal'
2
- require 'i18n/tasks/key_pattern_matching'
3
- require 'yaml'
1
+ require 'i18n/tasks/data/file_system'
4
2
 
5
3
  module I18n::Tasks
6
4
  module Data
7
- class Yaml
8
- include ::I18n::Tasks::DataTraversal
9
- include ::I18n::Tasks::KeyPatternMatching
10
- attr_reader :config
11
-
12
- DEFAULTS = {
13
- read: ['config/locales/%{locale}.yml'],
14
- write: ['config/locales/%{locale}.yml']
15
- }.with_indifferent_access
16
-
17
- def initialize(config = {})
18
- self.config = config
19
- end
20
-
21
- def config=(config)
22
- opt = DEFAULTS.deep_merge((config || {}).with_indifferent_access)
23
- @read = opt[:read]
24
- @write = opt[:write].map { |x| x.is_a?(String) ? ['*', x] : x }.map { |x|
25
- [compile_key_pattern(x[0]), x[1]]
26
- }
27
- @locale_data = {}
28
- end
29
-
30
- # get locale tree
31
- def get(locale)
32
- locale = locale.to_s
33
- @locale_data[locale] ||= begin
34
- @read.map do |path|
35
- Dir.glob path % {locale: locale}
36
- end.flatten.map do |locale_file|
37
- YAML.load_file locale_file
38
- end.inject({}) do |hash, locale_data|
39
- hash.deep_merge! locale_data || {}
40
- hash
41
- end[locale.to_s] || {}
42
- end
43
- end
44
-
45
- alias [] get
46
-
47
- # set locale tree
48
- def set(locale, value_tree)
49
- locale = locale.to_s
50
- out = {}
51
- traverse value_tree do |key, value|
52
- route = @write.detect { |route| route[0] =~ key }
53
- key_match = $~
54
- path = route[1] % {locale: locale}
55
- path.gsub!(/[\\]\d+/) { |m| key_match[m[1..-1].to_i] }
56
- (out[path] ||= []) << [key, value]
57
- end
58
- out.each do |path, data|
59
- tree = { locale => list_to_tree(data) }
60
- File.open(path, 'w') { |f| f.write(tree.to_yaml) }
61
- end
62
- @locale_data[locale] = nil
63
- end
64
-
65
- alias []= set
66
-
67
- def reload
68
- @locale_data = {}
5
+ class Yaml < FileSystem
6
+ def initialize(*args)
7
+ super
8
+ I18n::Tasks.warn_deprecated "data.adapter set to 'yaml'. please use 'file_system' instead"
69
9
  end
10
+ register_adapter '*.yml', Adapter::YamlAdapter
70
11
  end
71
12
  end
72
13
  end