i18n-tasks 0.2.22 → 0.3.0.rc1

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