i18n-tasks 0.3.11 → 0.4.0.beta1

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: 84ba1f92ba2da94773501e168515c76c3c6c21ab
4
- data.tar.gz: 5d72ea2de6df682ab6f57b8f76d051f6d8b0f3e8
3
+ metadata.gz: 328ef834a8ef2756aa154f9e4eb5207df02486d4
4
+ data.tar.gz: d3d0d63e4fb566037f64606cdfd2a140928492c1
5
5
  SHA512:
6
- metadata.gz: 46efbb3c38b02738ee34069bc4d9384603485f5f0aeba4227139596ce7bd383cb5c9b9b2f98ddadacd9c1c28bec47170e137f02856d2b4de56a7d724ab853a76
7
- data.tar.gz: a8b7a4968b32960205152674040c913e4032d999ee90f2b48e789593c1f81cfef3264c5f747d34608246548dd9fbbaafb94e7b01f8b825849515f7cf802956b7
6
+ metadata.gz: 0ea465b2b3799df29d77c5db1c7fdbb5deb824b838399f663ef82411f62dee84f14fef9e34498b958e06e53deacd91bbf53252933f9792ef4ad2f0cd3b2c0a5f
7
+ data.tar.gz: f0e8d5a760021dd976741f28f06bc5aa2e4fcdc2158573b2eeb9795f8984f6c15c39a46a717f7de44a4bcd8757e858541b74a08230c9433534eb148922d831b2
data/.jrubyrc ADDED
@@ -0,0 +1,2 @@
1
+ compat.version=2.0
2
+
data/.travis.yml CHANGED
@@ -1,10 +1,13 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.0
3
+ - 2.1.1
4
4
  - 2.0.0
5
- - 1.9.3
6
5
  - jruby
7
6
  - rbx
7
+ matrix:
8
+ allow_failures:
9
+ # no keyword argument support in rbx
10
+ - rbx
8
11
  env:
9
12
  global:
10
13
  - TRAVIS=1
data/CHANGES.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.4.0 (not yet released)
2
+
3
+ * In addition to pattern router, a new conservative router that keeps the keys in place. (See [#57](https://github.com/glebm/i18n-tasks/issues/57))
4
+ * `i18n-tasks irb` for debugging
5
+ * This release is a major refactoring to use real trees internally (as opposed to nested hashes).
6
+ Real trees allow for much easier [traversal](/lib/i18n/tasks/data/tree/traversal.rb).
7
+ With these trees, information can be associated with each node, which allows for things like the conservative router.
8
+
1
9
  ## 0.3.11
2
10
 
3
11
  * Improve plural key handling
data/Gemfile CHANGED
@@ -3,15 +3,7 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in i18n-tasks.gemspec
4
4
  gemspec
5
5
 
6
- platform :rbx do
7
- gem 'rubysl', '~> 2.0'
8
- gem 'rubysl-mutex_m', '~> 2.0'
9
- gem 'psych'
10
- group :development do
11
- gem 'racc'
12
- end
13
- end
14
-
15
6
  group :development do
16
7
  gem 'coveralls', require: false
8
+ gem 'byebug', platform: :mri, require: false
17
9
  end
data/README.md CHANGED
@@ -18,7 +18,7 @@ i18n-tasks can be used with any project using [i18n][i18n-gem] (default in Rails
18
18
  Add to Gemfile:
19
19
 
20
20
  ```ruby
21
- gem 'i18n-tasks', '~> 0.3.10'
21
+ gem 'i18n-tasks', '~> 0.3.11'
22
22
  ```
23
23
 
24
24
  i18n-tasks does not load or execute any of the application's code but performs static-only analysic.
@@ -45,6 +45,7 @@ Available commands:
45
45
  remove-unused remove unused keys
46
46
  config display i18n-tasks configuration
47
47
  xlsx-report save missing and unused translations to an Excel file
48
+ irb irb session within i18n-tasks context
48
49
 
49
50
  See `<command> --help` for more information on a specific command.
50
51
  ```
@@ -90,6 +91,13 @@ i18n-tasks find '{number,currency}.format.*'
90
91
 
91
92
  ![i18n-screenshot][screenshot-find]
92
93
 
94
+ ### Find / remove unused keys
95
+
96
+ ```bash
97
+ i18n-tasks unused
98
+ i18n-tasks remove-unused
99
+ ```
100
+
93
101
  #### Features
94
102
 
95
103
  Relative keys (`t '.title'`) and plural keys (`key.{one,many,other,...}`) are fully supported.
@@ -144,12 +152,13 @@ data:
144
152
 
145
153
  #### Multiple locale files
146
154
 
147
- Use `data.read` and `data.write` options to work with locale data spread over multiple files.
155
+ Use `data` options to work with locale data spread over multiple files.
156
+
157
+ `data.read` accepts a list of file globs to read from per-locale:
148
158
 
149
159
  ```
150
160
  # config/i18n-tasks.yml
151
161
  data:
152
- # a list of file globs to read from per-locale
153
162
  read:
154
163
  # read from namespaced files, e.g. simple_form.en.yml
155
164
  - 'config/locales/*.%{locale}.yml'
@@ -157,6 +166,18 @@ data:
157
166
  - "<%= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
158
167
  # default
159
168
  - 'config/locales/%{locale}.yml'
169
+ ```
170
+
171
+ For writing to locale files i18n-tasks provides 2 options.
172
+
173
+ ##### Pattern router
174
+
175
+ Pattern router organizes keys based on a list of key patterns, as in the example below:
176
+
177
+ ```
178
+ data:
179
+ # pattern_router is default
180
+ router: pattern_router
160
181
  # a list of {key pattern => file} routes, matched top to bottom
161
182
  write:
162
183
  # write models.* and views.* keys to the respective files
@@ -167,6 +188,18 @@ data:
167
188
  - 'config/locales/%{locale}.yml'
168
189
  ```
169
190
 
191
+ ##### Conservative router (v0.4.0+)
192
+
193
+ Conservative router keeps the keys where they are found, or infers the path from base locale.
194
+ If the key is completely new, conservative router will fall back to the pattern router behaviour.
195
+
196
+ ```
197
+ data:
198
+ router: conservative_router
199
+ write:
200
+ - 'config/locales/%{locale}.yml'
201
+ ```
202
+
170
203
  #### Key pattern syntax
171
204
 
172
205
  | syntax | description |
data/Rakefile CHANGED
@@ -3,3 +3,9 @@ require 'rspec/core/rake_task'
3
3
  RSpec::Core::RakeTask.new(:spec)
4
4
  task :default => :spec
5
5
 
6
+ task :irb do
7
+ # $: << File.expand_path('lib', __FILE__)
8
+ require 'i18n/tasks'
9
+ require 'i18n/tasks/commands'
10
+ ::I18n::Tasks::Commands.new.irb
11
+ end
data/bin/i18n-tasks CHANGED
@@ -11,7 +11,9 @@ err = proc { |message, exit_code|
11
11
 
12
12
  command = nil
13
13
  begin
14
- slop = Slop.parse(help: true) do
14
+ args = ARGV.dup
15
+ args = ['--help'] if args.empty?
16
+ slop = Slop.parse(args, help: true) do
15
17
  on('-v', '--version', 'Print the version') {
16
18
  puts I18n::Tasks::VERSION
17
19
  exit
@@ -0,0 +1,4 @@
1
+ ---
2
+ en:
3
+ y: 3
4
+ z: 4
@@ -0,0 +1 @@
1
+ --- {}
data/i18n-tasks.gemspec CHANGED
@@ -29,11 +29,11 @@ It can also can pre-fill missing keys, including from Google Translate, and it c
29
29
 
30
30
  s.add_dependency 'erubis'
31
31
  s.add_dependency 'activesupport'
32
- s.add_dependency 'easy_translate', '>= 0.4.0'
32
+ s.add_dependency 'easy_translate', '>= 0.5.0'
33
33
  s.add_dependency 'term-ansicolor'
34
34
  s.add_dependency 'terminal-table'
35
35
  s.add_dependency 'highline'
36
- s.add_dependency 'slop', '>= 3.4.7'
36
+ s.add_dependency 'slop', '>= 3.5.0'
37
37
  s.add_development_dependency 'axlsx', '~> 2.0'
38
38
  s.add_development_dependency 'bundler', '~> 1.3'
39
39
  s.add_development_dependency 'rake'
data/lib/i18n/tasks.rb CHANGED
@@ -1,3 +1,12 @@
1
+ # define all the modules to be able to use ::
2
+ module I18n
3
+ module Tasks
4
+ module Data
5
+ end
6
+ end
7
+ end
8
+
9
+
1
10
  require 'active_support/core_ext/hash'
2
11
  require 'active_support/core_ext/string'
3
12
  require 'active_support/core_ext/module/delegation'
@@ -11,8 +20,3 @@ require 'i18n/tasks/key'
11
20
  require 'i18n/tasks/key_group'
12
21
  require 'i18n/tasks/base_task'
13
22
 
14
- module I18n
15
- module Tasks
16
-
17
- end
18
- end
@@ -8,6 +8,7 @@ require 'i18n/tasks/missing_keys'
8
8
  require 'i18n/tasks/unused_keys'
9
9
  require 'i18n/tasks/google_translation'
10
10
  require 'i18n/tasks/fill_tasks'
11
+ require 'i18n/tasks/file_structure'
11
12
  require 'i18n/tasks/data'
12
13
  require 'i18n/tasks/configuration'
13
14
 
@@ -24,15 +25,12 @@ module I18n
24
25
  include GoogleTranslation
25
26
  include Logging
26
27
  include Configuration
28
+ include FileStructure
27
29
  include Data
28
30
 
29
31
  def initialize(config = {})
30
32
  self.config = config || {}
31
33
  end
32
-
33
- def in_task(&block)
34
- instance_exec(&block)
35
- end
36
34
  end
37
35
  end
38
36
  end
@@ -117,6 +117,15 @@ module I18n::Tasks
117
117
  spreadsheet_report.save_report opt[:path]
118
118
  end
119
119
 
120
+ desc 'irb session within i18n-tasks context'
121
+ cmd :irb do
122
+ require 'irb'
123
+ IRB.setup nil
124
+ IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
125
+ require 'irb/ext/multi-irb'
126
+ IRB.irb nil, i18n_task
127
+ end
128
+
120
129
  protected
121
130
 
122
131
  def terminal_report
@@ -1,78 +1,95 @@
1
1
  require 'i18n/tasks/data/file_system'
2
2
 
3
- module I18n::Tasks::Data
4
- # I18n data provider
5
- # @see I18n::Tasks::Data::FileSystem
6
- def data
7
- @data ||= begin
8
- conf = (config[:data] || {}).with_indifferent_access
9
- adapter = (conf[:adapter].presence || conf[:class].presence || :file_system).to_s
10
- if adapter !~ /[A-Z]/
11
- adapter = "I18n::Tasks::Data::#{adapter.camelize}"
3
+ module I18n::Tasks
4
+ module Data
5
+
6
+ # I18n data provider
7
+ # @see I18n::Tasks::Data::FileSystem
8
+ def data
9
+ @data ||= begin
10
+ conf = (config[:data] || {}).with_indifferent_access
11
+ adapter = (conf[:adapter].presence || conf[:class].presence || :file_system).to_s
12
+ if adapter !~ /[A-Z]/
13
+ adapter = "I18n::Tasks::Data::#{adapter.camelize}"
14
+ end
15
+ adapter.constantize.new(conf.except(:adapter, :class).merge(base_locale: base_locale))
12
16
  end
13
- adapter.constantize.new(conf.except(:adapter, :class))
14
17
  end
15
- end
16
18
 
17
- def t(key, locale = base_locale)
18
- data.t(key, locale)
19
- end
19
+ def t(key, locale = base_locale)
20
+ data.t(key, locale)
21
+ end
20
22
 
21
- def t_proc(locale = base_locale)
22
- @t_proc ||= {}
23
- @t_proc[locale] ||= proc { |key| t(key, locale) }
24
- end
23
+ def tree(locale)
24
+ data[locale][locale].children
25
+ end
25
26
 
26
- # whether the value for key exists in locale (defaults: base_locale)
27
- def key_value?(key, locale = base_locale)
28
- t(key, locale).present?
29
- end
27
+ def node(key, locale = base_locale)
28
+ data[locale]["#{locale}.#{key}"]
29
+ end
30
30
 
31
- # write to store, normalizing all data
32
- def normalize_store!(from = nil)
33
- from = self.locales unless from
34
- Array(from).each do |target_locale|
35
- # the store itself handles normalization
36
- data[target_locale] = data[target_locale]
31
+ def build_tree(hash)
32
+ I18n::Tasks::Data::Tree::Siblings.from_nested_hash(hash)
37
33
  end
38
- end
39
34
 
40
- # if :locales option present, call update_locale_data for each locale
41
- # otherwise, call update_locale_data for :locale option or base locale
42
- # @option opts [Array] :locales
43
- # @option opts [String] :locale
44
- def update_data(opts = {})
45
- if opts.key?(:locales)
46
- locales = (Array(opts[:locales]).presence || self.locales).map(&:to_s)
47
- # make sure base_locale always comes first if present
48
- locales = [base_locale] + (locales - [base_locale]) if locales.include?(base_locale)
49
- opts = opts.except(:locales)
50
- locales.each { |locale| update_locale_data(locale, opts.merge(locale: locale)) }
51
- else
52
- update_locale_data(opts[:locale] || base_locale, opts)
35
+ def t_proc(locale = base_locale)
36
+ @t_proc ||= {}
37
+ @t_proc[locale] ||= proc { |key| t(key, locale) }
53
38
  end
54
- end
55
39
 
56
- # @param locale
57
- # @option opts [Array|Proc] :keys keys to update, if proc call with locale
58
- # @option opts [String|Proc] value, if proc call with each key
59
- # @option opts [String|Proc] values, if proc call with all the keys
60
- def update_locale_data(locale, opts = {})
61
- keys = opts[:keys]
62
- keys = keys.call(locale) if keys.respond_to?(:call)
63
- return if keys.empty?
40
+ # whether the value for key exists in locale (defaults: base_locale)
41
+ def key_value?(key, locale = base_locale)
42
+ !t(key, locale).nil?
43
+ end
64
44
 
65
- values = opts[:values]
66
- values = values.call(keys, locale) if values.respond_to?(:call)
67
- values ||= begin
68
- value = opts[:value] or raise 'pass value or values'
69
- if value.respond_to?(:call)
70
- keys.map { |key| value.call(key, locale) }
45
+ # write to store, normalizing all data
46
+ def normalize_store!(from = nil)
47
+ from = self.locales unless from
48
+ Array(from).each do |target_locale|
49
+ # the store itself handles normalization
50
+ data[target_locale] = data[target_locale]
51
+ end
52
+ end
53
+
54
+ # if :locales option present, call update_locale_data for each locale
55
+ # otherwise, call update_locale_data for :locale option or base locale
56
+ # @option opts [Array] :locales
57
+ # @option opts [String] :locale
58
+ def update_data(opts = {})
59
+ if opts.key?(:locales)
60
+ locales = (Array(opts[:locales]).presence || self.locales).map(&:to_s)
61
+ # make sure base_locale always comes first if present
62
+ locales = [base_locale] + (locales - [base_locale]) if locales.include?(base_locale)
63
+ opts = opts.except(:locales)
64
+ locales.each { |locale| update_locale_data(locale, opts.merge(locale: locale)) }
71
65
  else
72
- [value] * keys.size
66
+ update_locale_data(opts[:locale] || base_locale, opts)
73
67
  end
74
68
  end
75
- data[locale] += keys.map(&:to_s).zip(values)
76
- end
77
69
 
70
+ # @param locale
71
+ # @option opts [Array|Proc] :keys keys to update, if proc call with locale
72
+ # @option opts [String|Proc] value, if proc call with each key
73
+ # @option opts [String|Proc] values, if proc call with all the keys
74
+ def update_locale_data(locale, opts = {})
75
+ locale = locale.to_s
76
+ keys = opts[:keys]
77
+ keys = keys.call(locale) if keys.respond_to?(:call)
78
+ return if keys.empty?
79
+
80
+ values = opts[:values]
81
+ values = values.call(keys, locale) if values.respond_to?(:call)
82
+ values ||= begin
83
+ value = opts[:value] or raise 'pass value or values'
84
+ if value.respond_to?(:call)
85
+ keys.map { |key| value.call(key, locale) }
86
+ else
87
+ [value] * keys.size
88
+ end
89
+ end
90
+ data[locale] = tree(locale).merge!(
91
+ Tree::Siblings.from_flat_pairs keys.map(&:to_s).zip(values)
92
+ ).parent
93
+ end
94
+ end
78
95
  end
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+
1
3
  module I18n
2
4
  module Tasks
3
5
  module Data
@@ -19,6 +21,7 @@ module I18n
19
21
  end
20
22
 
21
23
  def write_tree(path, tree)
24
+ ::FileUtils.mkpath(File.dirname path)
22
25
  ::File.open(path, 'w') { |f|
23
26
  adapter_name, adapter_pattern, adapter = adapter_for(path)
24
27
  adapter_options = (config[adapter_name] || {})[:write]
@@ -1,12 +1,12 @@
1
- require 'i18n/tasks/data/locale_tree'
2
- require 'i18n/tasks/data/router'
1
+ require 'i18n/tasks/data/tree/node'
2
+ require 'i18n/tasks/data/router/pattern_router'
3
+ require 'i18n/tasks/data/router/conservative_router'
3
4
  require 'i18n/tasks/data/file_formats'
4
5
  require 'i18n/tasks/key_pattern_matching'
5
6
 
6
7
  module I18n::Tasks
7
8
  module Data
8
9
  class FileSystemBase
9
- include ::I18n::Tasks::Data::Router
10
10
  include ::I18n::Tasks::Data::FileFormats
11
11
 
12
12
  attr_reader :config
@@ -20,48 +20,32 @@ module I18n::Tasks
20
20
  self.config = config
21
21
  end
22
22
 
23
- def t(key, locale)
24
- get(locale).t(key)
25
- end
26
-
27
- def config=(config)
28
- @config = DEFAULTS.deep_merge((config || {}).with_indifferent_access)
29
- @config[:write] = compile_routes @config[:write]
30
- reload
31
- end
32
-
33
23
  # get locale tree
34
24
  def get(locale)
35
- locale = locale.to_s
36
- @locale_data[locale] ||= begin
37
- hash = config[:read].map do |path|
38
- Dir.glob path % {locale: locale}
39
- end.reduce(:+).map do |locale_file|
40
- load_file locale_file
41
- end.inject({}) do |hash, locale_data|
42
- hash.deep_merge! locale_data || {}
43
- hash
44
- end[locale.to_s] || {}
45
- LocaleTree.new locale, hash.to_hash
46
- end
25
+ locale = locale.to_s
26
+ @trees ||= {}
27
+ @trees[locale] ||= Tree::Siblings[locale => {}].merge!(
28
+ read_locale locale
29
+ )
47
30
  end
48
31
 
49
32
  alias [] get
50
33
 
51
34
  # set locale tree
52
- def set(locale, values)
35
+ def set(locale, tree)
53
36
  locale = locale.to_s
54
- route_values config[:write], values, locale do |path, tree|
55
- write_tree path, tree
37
+ router.route locale, tree do |path, tree_slice|
38
+ write_tree path, tree_slice
56
39
  end
57
- @locale_data[locale] = nil
40
+ @trees.delete(locale) if @trees
41
+ @available_locales = nil
58
42
  end
59
43
 
60
44
  alias []= set
61
45
 
62
46
  # @return self
63
47
  def reload
64
- @locale_data = {}
48
+ @trees = nil
65
49
  @available_locales = nil
66
50
  self
67
51
  end
@@ -85,6 +69,45 @@ module I18n::Tasks
85
69
  locales
86
70
  end
87
71
  end
72
+
73
+ def t(key, locale)
74
+ tree = self[locale.to_s]
75
+ return unless tree
76
+ tree[locale][key].try(:hash_or_value)
77
+ end
78
+
79
+ def config=(config)
80
+ @config = DEFAULTS.deep_merge((config || {}).with_indifferent_access)
81
+ reload
82
+ end
83
+
84
+ protected
85
+
86
+ def read_locale(locale)
87
+ Array(config[:read]).map do |path|
88
+ Dir.glob path % {locale: locale}
89
+ end.reduce(:+).map do |path|
90
+ [path.freeze, load_file(path) || {}]
91
+ end.map do |path, data|
92
+ Data::Tree::Siblings.from_nested_hash(data).tap do |s|
93
+ s.leaves { |x| x.data[:path] = path }
94
+ end
95
+ end.reduce(:merge!) || Tree::Siblings.null
96
+ end
97
+
98
+ def base_locale
99
+ config[:base_locale]
100
+ end
101
+
102
+ def router
103
+ @router ||= begin
104
+ name = @config[:router].presence || 'pattern_router'
105
+ if name[0] != name[0].upcase
106
+ name = "I18n::Tasks::Data::Router::#{name.classify}"
107
+ end
108
+ name.constantize.new(self, @config)
109
+ end
110
+ end
88
111
  end
89
112
  end
90
113
  end