i18n-tasks 0.2.22 → 0.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +5 -1
  3. data/Gemfile +0 -1
  4. data/README.md +59 -49
  5. data/bin/i18n-tasks +38 -0
  6. data/i18n-tasks.gemspec +1 -0
  7. data/lib/i18n/tasks/commands.rb +121 -0
  8. data/lib/i18n/tasks/commands_base.rb +54 -0
  9. data/lib/i18n/tasks/configuration.rb +39 -1
  10. data/lib/i18n/tasks/data/storage/file_storage.rb +1 -1
  11. data/lib/i18n/tasks/data_traversal.rb +6 -7
  12. data/lib/i18n/tasks/fill_tasks.rb +20 -48
  13. data/lib/i18n/tasks/google_translation.rb +1 -1
  14. data/lib/i18n/tasks/key.rb +11 -26
  15. data/lib/i18n/tasks/key/key_group.rb +44 -0
  16. data/lib/i18n/tasks/key/match_pattern.rb +23 -0
  17. data/lib/i18n/tasks/key/usages.rb +11 -0
  18. data/lib/i18n/tasks/key_pattern_matching.rb +6 -2
  19. data/lib/i18n/tasks/missing_keys.rb +15 -12
  20. data/lib/i18n/tasks/plural_keys.rb +3 -3
  21. data/lib/i18n/tasks/reports/base.rb +3 -2
  22. data/lib/i18n/tasks/reports/spreadsheet.rb +2 -1
  23. data/lib/i18n/tasks/reports/terminal.rb +6 -6
  24. data/lib/i18n/tasks/scanners/base_scanner.rb +20 -14
  25. data/lib/i18n/tasks/scanners/pattern_scanner.rb +31 -5
  26. data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +75 -0
  27. data/lib/i18n/tasks/translation_data.rb +32 -11
  28. data/lib/i18n/tasks/unused_keys.rb +3 -2
  29. data/lib/i18n/tasks/used_keys.rb +14 -11
  30. data/lib/i18n/tasks/version.rb +1 -1
  31. data/lib/tasks/i18n-tasks.rake +34 -85
  32. data/spec/fixtures/app/controllers/events_controller.rb +23 -3
  33. data/spec/fixtures/app/views/index.html.slim +4 -1
  34. data/spec/fixtures/app/views/usages.html.slim +2 -0
  35. data/spec/fixtures/config/i18n-tasks.yml +1 -1
  36. data/spec/i18n_tasks_spec.rb +66 -38
  37. data/spec/pattern_scanner_spec.rb +1 -1
  38. data/spec/spec_helper.rb +2 -1
  39. data/spec/support/capture_std.rb +17 -0
  40. data/spec/support/fixtures.rb +9 -2
  41. data/spec/support/test_codebase.rb +5 -18
  42. data/spec/support/test_codebase_env.rake +4 -2
  43. data/spec/used_keys_spec.rb +1 -0
  44. metadata +31 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 563f9b661577921d66001c94c5f6e860762bb5a6
4
- data.tar.gz: 14ef151e45ddaf53c720fc91c48630465bfe05aa
3
+ metadata.gz: 79c771676a7ab2418350137799e74f5cfe9a84c9
4
+ data.tar.gz: fa7170a9f5aa0f0a557b87499c48a393ebf47a81
5
5
  SHA512:
6
- metadata.gz: c253a433900be103dccdee48ecdad65053b694a0ca497f75c3acf7d1a094d4e4b6bf9c279667b391db5701c5a4e56b5b761638129e1a8016892e43927cfefb83
7
- data.tar.gz: 16ad12978adba7925e0ad2851d753e3c266b07fdcfc58c44717b93c07992629343b2c56c372feb5dc09e9007eed8e96813c6349e1de72c830e59383289c0a495
6
+ metadata.gz: b9af27eac0d3d7e2c68154a5b21f9b1aa3bd1d29940cceced793c57fe0263346cb20c5e3a9f5faf2ecb0bc8160e561b7b566e40b5634e934782b973c4bfb451d
7
+ data.tar.gz: 133cf042e3d976e3e6fc8a670428c2b91743e0abfba53c58bdf5d67e6c8511b61bd97ae5c5ca5d231e81cb5a6db536e01e7ca017d8e8f4343f90d682bad8fb62
data/CHANGES.md CHANGED
@@ -1,4 +1,8 @@
1
- ## v0.2.21
1
+ ## v0.3.0 (unreleased)
2
+
3
+ * i18n-tasks provides a binary instead of rake tasks now (though the tasks are still there for now).
4
+
5
+ ## v0.2.21..v0.2.22
2
6
 
3
7
  * `rake i18n:usages[pattern]`
4
8
  * performance regression fixes
data/Gemfile CHANGED
@@ -14,4 +14,3 @@ end
14
14
  group :development do
15
15
  gem 'coveralls', require: false
16
16
  end
17
-
data/README.md CHANGED
@@ -7,83 +7,101 @@ Tasks to manage translations in ruby applications using I18n.
7
7
 
8
8
  ## Installation
9
9
 
10
- Simply add to Gemfile:
10
+ 1. Add to Gemfile:
11
11
 
12
12
  ```ruby
13
- gem 'i18n-tasks', '~> 0.2.21'
13
+ gem 'i18n-tasks', '~> 0.3.0'
14
14
  ```
15
15
 
16
- If not using Rails, require the tasks in Rakefile:
16
+ 2. Create a config file at `config/i18n-tasks.yml`:
17
17
 
18
- ```ruby
19
- # Rakefile
20
- load 'tasks/i18n-tasks.rake'
18
+ ```yaml
19
+ # config/i18n-tasks.yml
20
+ base_locale: en
21
+ locales: [es, fr]
21
22
  ```
22
23
 
23
24
  ## Usage
24
25
 
25
- Use `rake -T i18n` to get the list of tasks with descriptions. These are [the tasks](/lib/tasks/i18n-tasks.rake) available:
26
-
27
- There are reports for missing and unused translations:
26
+ Run `i18n-tasks` to get the list of tasks with short descriptions.
28
27
 
29
28
  ```bash
30
- rake i18n:missing
31
- rake i18n:unused
32
- ```
33
-
34
- To remove unused translations run:
35
-
36
- ```bash
37
- rake i18n:remove_unused # this will print the unused report and ask for confirmation before deleting keys
29
+ $ i18n-tasks
30
+ Usage: i18n-tasks [command] [options]
31
+ -v, --version Print the version
32
+ -h, --help Display this help message.
33
+
34
+ Available commands:
35
+
36
+ missing show missing translations
37
+ unused show unused translations
38
+ translate-missing translate missing keys with Google Translate
39
+ add-missing add missing keys to the locales
40
+ find show where the keys are used in the code
41
+ normalize normalize translation data: sort and move to the right files
42
+ remove-unused remove unused keys
43
+ config display i18n-tasks configuration
44
+ xlsx-report save missing and unused translations to an Excel file
45
+
46
+ See `<command> --help` for more information on a specific command.
38
47
  ```
39
48
 
49
+ There are reports for missing and unused translations.
40
50
  i18n-tasks can add missing keys to the locale data, and it can also fill untranslated values.
41
51
 
42
- To add the keys that are not in the base locale but detected in the source do:
52
+
53
+ Whenever you can pass locales as arguments, you can use special values `base` and `all`.
54
+ Add placeholders for missing keys to the base locale only:
43
55
 
44
56
  ```bash
45
- # add missing keys to the base locale data (I18n.default_locale)
46
- # values set to to the optional [argument] or key.humanize
47
- rake i18n:add_missing
57
+ i18n-tasks add-missing[base]
48
58
  ```
49
59
 
50
60
  Prefill empty translations using Google Translate ([more below on the API key](#translation-config)).
51
61
 
52
62
  ```bash
53
- rake i18n:fill:google_translate
63
+ i18n-tasks translate-missing
54
64
  # this task and the ones below can also accept specific locales:
55
- rake i18n:fill:google_translate[es+de]
65
+ i18n-tasks translate-missing -l es,de
56
66
  ```
57
67
 
58
- Prefill using values from the base locale - `I8n.default_locale`:
68
+ Sort the keys and write them to their respective files:
69
+
59
70
  ```bash
60
- rake i18n:fill:base_value
71
+ # always happens on add-missing or translate-missing
72
+ i18n-tasks normalize
61
73
  ```
62
74
 
63
- i18n-tasks sorts the keys and writes them to their respective files:
75
+ Unused report will detect pattern translations and not report them, e.g.:
64
76
 
65
- ```bash
66
- # this happens automatically on any i18n:fill:* task
67
- rake i18n:normalize
77
+ ```ruby
78
+ t 'category.' + category.key # all 'category.*' keys are considered used
79
+ t "category.#{category.key}.name" # all 'category.*.name' keys are considered used
68
80
  ```
69
81
 
82
+ Relative keys (`t '.title'`) and plural keys (key.one/many/other/etc) are fully supported.
70
83
 
71
- `i18n:unused` will detect pattern translations and not report them, e.g.:
84
+ Scope argument is supported, but only when it is the first keyword argument ([this](/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb) can be improved):
72
85
 
73
86
  ```ruby
74
- t 'category.' + category.key # category.* keys are all considered used
75
- t "category.#{category.key}" # also works
87
+ # this is supported
88
+ t :invalid, scope: [:auth, :password], attempts: 5
89
+ # but not this
90
+ t :invalid, attempts: 5, scope: [:auth, :password]
76
91
  ```
77
92
 
78
- Relative keys (`t '.title'`) and plural keys (key.one/many/other/etc) are fully supported.
79
-
80
93
  Translation data storage, key usage search, and other [settings](#configuration) are compatible with Rails by default.
81
94
 
82
95
  ## Configuration
83
96
 
84
97
  Configuration is read from `config/i18n-tasks.yml` or `config/i18n-tasks.yml.erb`.
98
+ See current configuration with `i18n-tasks config`.
85
99
 
86
- By default, `i18n-tasks` will work with `I18n.default_locale` and `I18n.available_locales`, but you can override this:
100
+ ### Locales
101
+
102
+ By default, `i18n-tasks` will read `I18n.default_locale` and `I18n.available_locales`.
103
+ However, i18n-tasks does not load application environment by default,
104
+ so it is recommended to set locale settings explicitly:
87
105
 
88
106
  ```yaml
89
107
  # config/i18n-tasks.yml
@@ -91,13 +109,6 @@ base_locale: en
91
109
  locales: [es, fr]
92
110
  ```
93
111
 
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
-
101
112
  ### Storage
102
113
 
103
114
  ```yaml
@@ -143,14 +154,13 @@ data:
143
154
  Inspect all the usages with:
144
155
 
145
156
  ```bash
146
- rake i18n:usages
157
+ i18n-tasks find
147
158
  # Filter by a key pattern
148
- rake i18n:usages[auth.*]
149
- # Because commas are not allowed inside rake arguments, + is used here instead
150
- rake i18n:usages['{number+currency}.format.*']
159
+ i18n-tasks find 'auth.*
160
+ i18n-tasks find '{number,currency}.format.*'
151
161
  ```
152
162
 
153
- ![i18n-screenshot](https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-usages.png "rake i18n:usages output screenshot")
163
+ ![i18n-screenshot](https://raw.github.com/glebm/i18n-tasks/master/doc/img/i18n-usages.png "i18n-tasks find output screenshot")
154
164
 
155
165
 
156
166
  Configure usage search in `config/i18n-tasks.yml`:
@@ -222,7 +232,7 @@ ignore:
222
232
  <a name="translation-config"></a>
223
233
  ### Google Translate
224
234
 
225
- `rake i18n:fill:google_translate` requires a Google Translate API key, get it at [Google API Console](https://code.google.com/apis/console).
235
+ `i18n-tasks translate-missing` requires a Google Translate API key, get it at [Google API Console](https://code.google.com/apis/console).
226
236
  Put the key in `GOOGLE_TRANSLATE_API_KEY` environment variable or in the config file.
227
237
 
228
238
  ```yaml
@@ -260,7 +270,7 @@ end
260
270
  Export missing and unused data to XLSX:
261
271
 
262
272
  ```bash
263
- rake i18n:spreadsheet_report
273
+ i18n-tasks xlsx-report
264
274
  ```
265
275
 
266
276
 
data/bin/i18n-tasks ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'i18n/tasks'
4
+ require 'i18n/tasks/commands'
5
+ require 'slop'
6
+
7
+ command = nil
8
+ slop = Slop.parse(help: true) do
9
+ on('-v', '--version', 'Print the version') {
10
+ puts I18n::Tasks::VERSION
11
+ exit
12
+ }
13
+ ::I18n::Tasks::Commands.cmds.each do |name, attr|
14
+ command name.tr('_', '-') do
15
+ description attr.desc if attr.desc
16
+ instance_exec(&attr.opts) if attr.opts
17
+ run do |opts, args|
18
+ command = [name, opts, args]
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ if command
25
+ cmd = ::I18n::Tasks::Commands.new
26
+ meth = command[0]
27
+ if cmd.method(meth).arity == 0
28
+ cmd.send meth
29
+ else
30
+ opt = command[1].to_hash.merge(arguments: command[2])
31
+ STDERR.puts Term::ANSIColor.green "i18n-tasks: #{meth.tr('_', '-')} #{opt.reject { |k, v| v.blank? }.map { |k, v| "#{k}=#{v}" } * ' '}"
32
+ cmd.send meth, opt
33
+ end
34
+ else
35
+ STDERR.puts Term::ANSIColor.red "Command unknown: #{ARGV[0]}" if ARGV[0]
36
+ puts slop.help
37
+ exit 64
38
+ end
data/i18n-tasks.gemspec CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
31
31
  s.add_dependency 'term-ansicolor'
32
32
  s.add_dependency 'terminal-table'
33
33
  s.add_dependency 'highline'
34
+ s.add_dependency 'slop', '>= 3.4.7'
34
35
  s.add_development_dependency 'axlsx', '~> 2.0'
35
36
  s.add_development_dependency 'bundler', '~> 1.3'
36
37
  s.add_development_dependency 'rake'
@@ -0,0 +1,121 @@
1
+ require 'i18n/tasks/commands_base'
2
+ require 'i18n/tasks/reports/terminal'
3
+ require 'i18n/tasks/reports/spreadsheet'
4
+
5
+ module I18n::Tasks
6
+ class Commands < CommandsBase
7
+ include Term::ANSIColor
8
+ require 'highline/import'
9
+
10
+ desc 'show missing translations'
11
+ opts do
12
+ on '-l', :locales=, 'Filter by locale', as: Array, delimiter: /[+:,]/, default: 'all'
13
+ on '-t', :types, 'Filter by type (types: missing_from_base, eq_base, missing_from_locale)', as: Array, delimiter: /[+:,]/
14
+ end
15
+ cmd :missing do |opt = {}|
16
+ opt[:locales] = locales_opt(opt[:locales])
17
+ terminal_report.missing_keys i18n_task.missing_keys(opt)
18
+ end
19
+
20
+ desc 'show unused translations'
21
+ cmd :unused do
22
+ terminal_report.unused_keys
23
+ end
24
+
25
+ desc 'translate missing keys with Google Translate'
26
+ opts do
27
+ on '-l', :locales, 'Only specified locales', as: Array, delimiter: /[+:,]/
28
+ on '-f', :from, 'Locale to translate from (default: base)', default: 'base'
29
+ end
30
+ cmd :translate_missing do |opt = {}|
31
+ opt[:from] = base_locale if opt[:from].blank? || opt[:from] == 'base'
32
+ opt[:locales] = locales_opt(opt[:locale] || opt[:locales])
33
+ i18n_task.fill_missing_google_translate opt
34
+ end
35
+
36
+ desc 'add missing keys to the locales'
37
+ opts do
38
+ on '-p', :placeholder, 'Value for empty keys (default: base value or key.humanize)'
39
+ on '-l', :locales, 'Only for specified locales', as: Array, delimiter: /[+:,]/
40
+ end
41
+ cmd :add_missing do |opt = {}|
42
+ opt[:locales] = locales_opt(opt[:locale] || opt[:locales])
43
+ opt[:value] ||= opt.delete(:placeholder)
44
+ opt[:value] ||= proc { |key, locale|
45
+ # default to base value or key.humanize
46
+ locale == base_locale && t(locale, base_locale) || key.split('.').last.to_s.humanize
47
+ }
48
+ i18n_task.fill_missing_value opt
49
+ end
50
+
51
+ desc 'show where the keys are used in the code'
52
+ opts do
53
+ on '-p', :pattern, 'Show only keys matching pattern', argument: true, optional: false
54
+ end
55
+ cmd :find do |opt = {}|
56
+ opt[:filter] ||= opt.delete(:pattern) || opt[:arguments].try(:first)
57
+ filter = opt[:filter].is_a?(String) ? opt[:filter].tr('+', ',') : nil
58
+ used_keys = i18n_task.scanner.with_key_filter(filter) {
59
+ i18n_task.used_keys true
60
+ }
61
+ terminal_report.used_keys used_keys
62
+ end
63
+
64
+ desc 'normalize translation data: sort and move to the right files'
65
+ opts do
66
+ on '-l', :locales=, 'Only for specified locales', as: Array, delimiter: /[+:,]/
67
+ end
68
+ cmd :normalize do |opt = {}|
69
+ i18n_task.normalize_store! locales_opt(opt[:locales])
70
+ end
71
+
72
+ desc 'remove unused keys'
73
+ opts do
74
+ on '-l', :locales=, 'Only for specified locales', as: Array, delimiter: /[+:,]/
75
+ end
76
+ cmd :remove_unused do |opt = {}|
77
+ locales = locales_opt opt[:locales]
78
+ unused_keys = i18n_task.unused_keys
79
+ if unused_keys.present?
80
+ terminal_report.unused_keys(unused_keys)
81
+ unless ENV['CONFIRM']
82
+ exit 1 unless agree(red "All these translations will be removed in #{bold locales * ', '}#{red '.'} " + yellow('Continue? (yes/no)') + ' ')
83
+ end
84
+ i18n_task.remove_unused!(locales)
85
+ $stderr.puts "Removed #{unused_keys.size} keys"
86
+ else
87
+ $stderr.puts bold green 'No unused keys to remove'
88
+ end
89
+ end
90
+
91
+ desc 'display i18n-tasks configuration'
92
+ cmd :config do
93
+ puts i18n_task.config_for_inspect.to_yaml.sub(/\A---\n/, '').gsub('!ruby/hash:ActiveSupport::HashWithIndifferentAccess', '')
94
+ end
95
+
96
+ desc 'save missing and unused translations to an Excel file'
97
+ opts do
98
+ on :path, 'Destination path', default: 'tmp/i18n-report.xlsx'
99
+ end
100
+ cmd :xlsx_report do |opt = {}|
101
+ begin
102
+ require 'axlsx'
103
+ rescue LoadError
104
+ message = %Q(For spreadsheet report please add axlsx gem to Gemfile:\ngem 'axlsx', '~> 2.0')
105
+ STDERR.puts Term::ANSIColor.red Term::ANSIColor.bold message
106
+ exit 1
107
+ end
108
+ spreadsheet_report.save_report opt[:path]
109
+ end
110
+
111
+ protected
112
+
113
+ def terminal_report
114
+ @terminal_report ||= I18n::Tasks::Reports::Terminal.new(i18n_task)
115
+ end
116
+
117
+ def spreadsheet_report
118
+ @spreadsheet_report ||= I18n::Tasks::Reports::Spreadsheet.new(i18n_task)
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,54 @@
1
+ require 'ostruct'
2
+ module I18n::Tasks
3
+ class CommandsBase
4
+ def locales_opt(value, default = nil)
5
+ if value.is_a?(String)
6
+ value = value.strip.split(/\s*\+\s*/).compact.presence
7
+ end
8
+ return i18n_task.locales if value == ['all']
9
+ if value.present?
10
+ value = value.map { |v| v == 'base' ? base_locale : v }
11
+ value
12
+ else
13
+ default || i18n_task.locales
14
+ end
15
+ end
16
+
17
+ class << self
18
+ def cmds
19
+ @cmds ||= {}.with_indifferent_access
20
+ end
21
+
22
+ def cmd(name, &block)
23
+ cmds[name] = OpenStruct.new(@next_def)
24
+ @next_def = {}
25
+ define_method(name, &block)
26
+ end
27
+
28
+ def desc(text)
29
+ next_def[:desc] = text
30
+ end
31
+
32
+ def opts(&block)
33
+ next_def[:opts] = block
34
+ end
35
+
36
+ private
37
+ def next_def
38
+ @next_def ||= {}
39
+ end
40
+ end
41
+
42
+ def desc(name)
43
+ self.class.cmds.try(:[], name).try(:desc)
44
+ end
45
+
46
+ protected
47
+
48
+ def i18n_task
49
+ @i18n_task ||= I18n::Tasks::BaseTask.new
50
+ end
51
+
52
+ delegate :base_locale, :t, to: :i18n_task
53
+ end
54
+ end
@@ -33,8 +33,46 @@ module I18n::Tasks::Configuration
33
33
  def translation_config
34
34
  @config_sections[:translation] ||= begin
35
35
  conf = (config[:translation] || {}).with_indifferent_access
36
- conf[:api_key] ||= ENV['GOOGLE_TRANSLATE_API_KEY']
36
+ conf[:api_key] ||= ENV['GOOGLE_TRANSLATE_API_KEY'] if ENV.key?('GOOGLE_TRANSLATE_API_KEY')
37
37
  conf
38
38
  end
39
39
  end
40
+
41
+ def search_config
42
+ @config_sections[:search] ||= {
43
+ scanner: scanner.class.name,
44
+ config: scanner.config
45
+ }
46
+ end
47
+
48
+ # @return [Array<String>] all available locales
49
+ def locales
50
+ @config_sections[:locales] ||= begin
51
+ locales = (config[:locales] || I18n.available_locales).map(&:to_s)
52
+ locales = [base_locale] + locales unless locales.include?(base_locale)
53
+ locales
54
+ end
55
+ end
56
+
57
+ # @return [String] default i18n locale
58
+ def base_locale
59
+ @config_sections[:base_locale] ||= config[:base_locale] || I18n.default_locale.to_s
60
+ end
61
+
62
+ # evaluated configuration (as the app sees it)
63
+ def config_sections
64
+ # init all sections
65
+ base_locale
66
+ locales
67
+ data_config
68
+ search_config
69
+ relative_roots
70
+ translation_config
71
+ @config_sections
72
+ end
73
+
74
+ def config_for_inspect
75
+ # hide empty sections, stringify keys
76
+ Hash[config_sections.reject { |k, v| v.empty? }.map { |k, v| [k.to_s, v.respond_to?(:stringify_keys) ? v.stringify_keys : v] }]
77
+ end
40
78
  end