r18n-core 0.4 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.4.1 (Lazy Boole)
2
+ * Add passive filters.
3
+ * Receive filter position as option Hash.
4
+ * Fix base translations (by Pavel Kunc).
5
+
1
6
  == 0.4 (D-Day)
2
7
  * Rails I18n compatibility.
3
8
  * Rewrite a lot of core code to fast and cleanup version.
@@ -46,7 +51,7 @@
46
51
  * Ruby 1.9 compatibility.
47
52
  * Add German locale (by Benjamin Meichsner).
48
53
 
49
- == 0.2 (Freedom of language)
54
+ == 0.2 (Freedom of Language)
50
55
  * Locale class can be extended for special language (for example, Indian locale
51
56
  may has another digits grouping).
52
57
  * Load translations from several dirs.
data/README.rdoc CHANGED
@@ -220,12 +220,13 @@ See <tt>base/</tt> in dir in gem.
220
220
  === Filters
221
221
 
222
222
  You can also add you own filter for translations: escape HTML entries, convert
223
- from Markdown syntax, etc.
223
+ from Markdown syntax, etc. Filters can be passive to process translation only on
224
+ loading.
224
225
 
225
226
  R18n::Filters.add('custom_type', :filter_name) do |content, config, replace|
226
227
  content.gsub(' ', replace)
227
228
  end
228
- R18n::Filters.add('custom_type') do |content, config, replace|
229
+ R18n::Filters.add('custom_type', :passive => true) do |content, config|
229
230
  content + '!'
230
231
  end
231
232
 
data/base/cs.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Uložit
3
3
  cancel: Zrušit
4
- yes: Ano
5
- no: Ne
4
+ 'yes': Ano
5
+ 'no': Ne
6
6
  exit: Konec
7
7
  delete: Smazat
8
8
 
data/base/de.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Speichern
3
3
  cancel: Abbrechen
4
- yes: Ja
5
- no: Nein
4
+ 'yes': Ja
5
+ 'no': Nein
6
6
  exit: Ausgang
7
7
  delete: Löschen
8
8
 
@@ -28,4 +28,4 @@ human_time:
28
28
  yesterday: gestern
29
29
  days_ago: !!pl
30
30
  1: %1 Tag vor
31
- n: %1 Tagen vor
31
+ n: %1 Tagen vor
data/base/en.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Save
3
3
  cancel: Cancel
4
- yes: Yes
5
- no: No
4
+ 'yes': 'Yes'
5
+ 'no': 'No'
6
6
  exit: Exit
7
7
  delete: Delete
8
8
 
data/base/eo.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: Ek
2
2
  save: Savi
3
3
  cancel: Nuligi
4
- yes: Jes
5
- no: Ne
4
+ 'yes': Jes
5
+ 'no': Ne
6
6
  exit: Eliro
7
7
  delete: Forviŝi
8
8
 
data/base/es.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Guardar
3
3
  cancel: Cancelar
4
- yes: Sí
5
- no: No
4
+ 'yes': Sí
5
+ 'no': 'No'
6
6
  exit: Salir
7
7
  delete: Suprimir
8
8
 
data/base/fr.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: O.K.
2
2
  save: Enregistrer
3
3
  cancel: Annuler
4
- yes: Oui
5
- no: Non
4
+ 'yes': Oui
5
+ 'no': Non
6
6
  exit: Sortie
7
7
  delete: Supprimer
8
8
 
data/base/it.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Salva
3
3
  cancel: Annulla
4
- yes: Sì
5
- no: No
4
+ 'yes': Sì
5
+ 'no': 'No'
6
6
  exit: Esci
7
7
  delete: Cancella
8
8
 
data/base/kk.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  ok: OK
2
2
  save: Сақтау
3
3
  cancel: Болдырмау
4
- yes: Иә
5
- no: Жоқ
4
+ 'yes': Иә
5
+ 'no': Жоқ
6
6
  exit: Шығу
7
7
  delete: Алыстату
data/base/pl.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Zapisz
3
3
  cancel: Anuluj
4
- yes: Tak
5
- no: Nie
4
+ 'yes': Tak
5
+ 'no': Nie
6
6
  exit: Wyjście
7
7
  delete: Usuń
8
8
 
data/base/pt_br.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Salvar
3
3
  cancel: Cancelar
4
- yes: Sim
5
- no: Não
4
+ 'yes': Sim
5
+ 'no': Não
6
6
  exit: Sair
7
7
  delete: Apagar
8
8
 
data/base/ru.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: Сохранить
3
3
  cancel: Отмена
4
- yes: Да
5
- no: Нет
4
+ 'yes': Да
5
+ 'no': Нет
6
6
  exit: Выход
7
7
  delete: Удалить
8
8
 
data/base/zh.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  ok: OK
2
2
  save: 保存
3
3
  cancel: 取消
4
- yes: 是
5
- no: 否
4
+ 'yes': 是
5
+ 'no': 否
6
6
  exit: 退出
7
7
  delete: 删除
8
8
 
@@ -30,12 +30,13 @@ module R18n
30
30
  # This content will be processed by filter
31
31
  #
32
32
  # Filter function will be receive filtered content as first argument, struct
33
- # with filter config as second and filter parameters as next arguments.
33
+ # with filter config as second and filter parameters as next arguments. You
34
+ # can set passive filter, which will process translation only on loading.
34
35
  #
35
36
  # R18n::Filters.add('custom_type', :no_space) do |content, config, replace|
36
37
  # content.gsub(' ', replace)
37
38
  # end
38
- # R18n::Filters.add('custom_type') do |content, config, replace|
39
+ # R18n::Filters.add('custom_type', :passive => true) do |content, config|
39
40
  # content + '!'
40
41
  # end
41
42
  #
@@ -48,7 +49,7 @@ module R18n
48
49
  # end
49
50
  #
50
51
  # Filter config contain two parameters: translation locale and path. But it is
51
- # OpenStruct and you can add you own parameter to cross-filter communications:
52
+ # Hash and you can add you own parameter to cross-filter communications:
52
53
  #
53
54
  # R18n::Filters.add(String, :hide_truth) do |content, config|
54
55
  # return content if config[:censorship_check]
@@ -73,67 +74,82 @@ module R18n
73
74
  # i18n.filtered('_') #=> "This_content_will_be_processed_by_filter!"
74
75
  # R18n::Filters.delete(:no_space)
75
76
  module Filters
76
- Filter = Struct.new(:name, :type, :block, :enabled) do
77
- def call(*params)
78
- block.call(*params)
79
- end
80
-
81
- def enabled?
82
- enabled
83
- end
84
- end
85
-
86
77
  class << self
87
78
  # Hash of filter names to Filters.
88
- def defined
89
- @defined ||= {}
90
- end
91
-
92
- # Hash of types to enabled Filters.
93
- def enabled
94
- @enabled ||= Hash.new([])
95
- end
79
+ attr_accessor :defined
96
80
 
97
81
  # Hash of types to all Filters.
98
- def by_type
99
- @by_type ||= Hash.new([])
100
- end
82
+ attr_accessor :by_type
83
+
84
+ # Hash of types to enabled active filters.
85
+ attr_accessor :active_enabled
101
86
 
102
- # Process +value+ by global filters and filters for special +type+.
103
- def process(type, value, locale, path, params)
87
+ # Hash of types to enabled passive filters.
88
+ attr_accessor :passive_enabled
89
+
90
+ # Hash of types to enabled passive and active filters.
91
+ attr_accessor :enabled
92
+
93
+ # Process +value+ by filters in +enabled+.
94
+ def process(enabled, type, value, locale, path, params)
104
95
  config = { :locale => locale, :path => path }
105
96
 
106
- Filters.enabled[type].each { |f| value = f.call(value, config, *params)}
97
+ enabled[type].each do |filter|
98
+ value = filter.call(value, config, *params)
99
+ end
107
100
 
108
101
  if value.is_a? String
109
102
  value = TranslatedString.new(value, locale, path)
110
- process_string(value, config, params)
103
+ process_string(enabled, value, config, params)
111
104
  else
112
105
  value
113
106
  end
114
107
  end
115
108
 
116
- # Process +value+ by global filters.
117
- def process_string(value, config, params)
109
+ # Process +value+ by global filters in +enabled+.
110
+ def process_string(enabled, value, config, params)
118
111
  if config.is_a? String
119
112
  config = { :locale => value.locale, :path => config }
120
113
  end
121
- Filters.enabled[String].each do |f|
114
+ enabled[String].each do |f|
122
115
  value = f.call(value, config, *params)
123
116
  end
124
117
  value
125
118
  end
126
119
 
120
+ # Rebuild +active_enabled+ and +passive_enabled+ for +type+.
121
+ def rebuild_enabled!(type)
122
+ @passive_enabled[type] = []
123
+ @active_enabled[type] = []
124
+ @enabled[type] = []
125
+
126
+ @by_type[type].each do |filter|
127
+ if filter.enabled?
128
+ @enabled[type] << filter
129
+ if filter.passive?
130
+ @passive_enabled[type] << filter
131
+ else
132
+ @active_enabled[type] << filter
133
+ end
134
+ end
135
+ end
136
+ end
137
+
127
138
  # Add new filter for +type+ with +name+ and return filter object. You
128
139
  # can use String class as +type+ to add global filter for all translated
129
140
  # string.
130
141
  #
131
- # Several filters for same type will be call consecutively, but you can
132
- # set +position+ in call list.
133
- #
134
142
  # Filter content will be sent to +block+ as first argument, struct with
135
143
  # config as second and filters parameters will be in next arguments.
136
- def add(type, name = nil, position = nil, &block)
144
+ #
145
+ # Options:
146
+ # * +position+ – change order on processing several filters for same type.
147
+ # Note that passive filters will be always run before active.
148
+ # * +passive+ – if +true+, filter will process only on translation
149
+ # loading. Note that you must add all passive before load translation.
150
+ def add(type, name = nil, options = {}, &block)
151
+ options, name = name, nil if name.is_a? Hash
152
+
137
153
  unless name
138
154
  @last_auto_name ||= 0
139
155
  begin
@@ -144,52 +160,61 @@ module R18n
144
160
  delete(name)
145
161
  end
146
162
 
147
- filter = Filter.new(name, type, block, true)
148
- defined[name] = filter
163
+ @by_type[type] = [] unless @by_type.has_key? type
149
164
 
150
- unless enabled.has_key? type
151
- enabled[type] = []
152
- by_type[type] = []
153
- end
154
- if position
155
- enabled[type].insert(position, filter)
156
- by_type[type].insert(position, filter)
165
+ filter = Filter.new(name, type, block, true, options[:passive])
166
+ @defined[name] = filter
167
+
168
+ if options.has_key? :position
169
+ @by_type[type].insert(options[:position], filter)
157
170
  else
158
- enabled[type] << filter
159
- by_type[type] << defined[name]
171
+ @by_type[type] << filter
160
172
  end
173
+ rebuild_enabled! type
161
174
 
162
175
  filter
163
176
  end
164
177
 
165
178
  # Delete +filter+ by name or Filter object.
166
179
  def delete(filter)
167
- filter = defined[filter] unless filter.is_a? Filter
180
+ filter = @defined[filter] unless filter.is_a? Filter
168
181
  return unless filter
169
182
 
170
- defined.delete(filter.name)
171
- by_type[filter.type].delete(filter)
172
- enabled[filter.type].delete(filter)
183
+ @defined.delete(filter.name)
184
+ @by_type[filter.type].delete(filter)
185
+ rebuild_enabled! filter.type
173
186
  end
174
187
 
175
188
  # Disable +filter+ by name or Filter object.
176
189
  def off(filter)
177
- filter = defined[filter] unless filter.is_a? Filter
190
+ filter = @defined[filter] unless filter.is_a? Filter
178
191
  return unless filter
179
192
 
180
193
  filter.enabled = false
181
- enabled[filter.type].delete(filter)
194
+ rebuild_enabled! filter.type
182
195
  end
183
196
 
184
197
  # Turn on disabled +filter+ by name or Filter object.
185
198
  def on(filter)
186
- filter = defined[filter] unless filter.is_a? Filter
199
+ filter = @defined[filter] unless filter.is_a? Filter
187
200
  return unless filter
188
201
 
189
202
  filter.enabled = true
190
- enabled[filter.type] = by_type[filter.type].reject { |i| !i.enabled? }
203
+ rebuild_enabled! filter.type
191
204
  end
192
205
  end
206
+
207
+ Filters.defined = {}
208
+ Filters.by_type = Hash.new([])
209
+ Filters.active_enabled = Hash.new([])
210
+ Filters.passive_enabled = Hash.new([])
211
+ Filters.enabled = Hash.new([])
212
+
213
+ Filter = Struct.new(:name, :type, :block, :enabled, :passive) do
214
+ def call(*params); block.call(*params); end
215
+ def enabled?; enabled; end
216
+ def passive?; passive; end
217
+ end
193
218
  end
194
219
 
195
220
  Filters.add('proc', :procedure) do |content, config, *params|
@@ -218,31 +243,31 @@ module R18n
218
243
  "#{translated}[#{untranslated}]"
219
244
  end
220
245
 
221
- Filters.add('escape', :escape_html) do |content, config|
246
+ Filters.add('escape', :escape_html, :passive => true) do |content, config|
222
247
  config[:dont_escape_html] = true
223
248
  Utils.escape_html(content)
224
249
  end
225
250
 
226
- Filters.add('html', :dont_escape_html) do |content, config|
251
+ Filters.add('html', :dont_escape_html, :passive => true) do |content, config|
227
252
  config[:dont_escape_html] = true
228
253
  content
229
254
  end
230
255
 
231
- Filters.add(String, :global_escape_html) do |content, config|
256
+ Filters.add(String, :global_escape_html, :passive => true) do |html, config|
232
257
  if config[:dont_escape_html]
233
- content
258
+ html
234
259
  else
235
- Utils.escape_html(content)
260
+ Utils.escape_html(html)
236
261
  end
237
262
  end
238
263
  Filters.off(:global_escape_html)
239
264
 
240
- Filters.add('markdown', :maruku) do |content, config|
265
+ Filters.add('markdown', :maruku, :passive => true) do |content, config|
241
266
  require 'maruku'
242
267
  ::Maruku.new(content).to_html
243
268
  end
244
269
 
245
- Filters.add('textile', :redcloth) do |content, config|
270
+ Filters.add('textile', :redcloth, :passive => true) do |content, config|
246
271
  require 'redcloth'
247
272
  ::RedCloth.new(content).to_html
248
273
  end
@@ -133,10 +133,12 @@ module R18n
133
133
  path = "\#{self.class.name}##{name}"
134
134
  type = self.class.translation_types[#{name.inspect}]
135
135
  if type
136
- return R18n::Filters.process(type, result, locale, path, params)
136
+ return R18n::Filters.process(R18n::Filters.enabled,
137
+ type, result, locale, path, params)
137
138
  else
138
139
  result = TranslatedString.new(result, locale, path)
139
- return R18n::Filters.process_string(result, path, params)
140
+ return R18n::Filters.process_string(R18n::Filters.enabled,
141
+ result, path, params)
140
142
  end
141
143
  end
142
144
 
@@ -91,12 +91,15 @@ module R18n
91
91
  when Hash
92
92
  value = Translation.new(@locale, path, locale, value)
93
93
  when String
94
- value = TranslatedString.new(value, locale, path)
95
- when YAML::PrivateType
96
- value = Typed.new(value.type_id, value.value, locale, path)
94
+ v = TranslatedString.new(value, locale, path)
95
+ value = Filters.process_string(Filters.passive_enabled, v, path, {})
97
96
  when Typed
98
97
  value.locale = locale
99
98
  value.path = path
99
+ unless Filters.passive_enabled[value.type].empty?
100
+ value = Filters.process(Filters.passive_enabled, value.type,
101
+ value.value, value.locale, value.path, {})
102
+ end
100
103
  end
101
104
  @data[name] = value
102
105
  elsif @data[name].is_a? Translation
@@ -107,7 +110,8 @@ module R18n
107
110
 
108
111
  # Use untranslated filter to print path.
109
112
  def to_s
110
- Filters.process(Untranslated, @path, @locale, @path, [@path, '', @path])
113
+ Filters.process(Filters.enabled, Untranslated, @path, @locale, @path,
114
+ [@path, '', @path])
111
115
  end
112
116
 
113
117
  # Return +default+.
@@ -133,10 +137,10 @@ module R18n
133
137
  value = @data[name.to_s]
134
138
  case value
135
139
  when TranslatedString
136
- Filters.process_string(value, @path, params)
140
+ Filters.process_string(Filters.active_enabled, value, @path, params)
137
141
  when Typed
138
- Filters.process(value.type, value.value, value.locale, value.path,
139
- params)
142
+ Filters.process(Filters.active_enabled, value.type, value.value,
143
+ value.locale, value.path, params)
140
144
  when nil
141
145
  translated = @path.empty? ? '' : "#{@path}."
142
146
  Untranslated.new(translated, name.to_s, @locale)
@@ -75,7 +75,7 @@ module R18n
75
75
  end
76
76
 
77
77
  def to_s
78
- Filters.process(Untranslated, path, @locale, path,
78
+ Filters.process(Filters.enabled, Untranslated, path, @locale, path,
79
79
  [@translated_path, @untranslated_path, path])
80
80
  end
81
81
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module R18n
3
- VERSION = '0.4' unless defined? R18n::VERSION
3
+ VERSION = '0.4.1' unless defined? R18n::VERSION
4
4
  end
@@ -50,7 +50,8 @@ module R18n
50
50
 
51
51
  # Return Hash with translations for +locale+.
52
52
  def load(locale)
53
- ::YAML::load(IO.read(File.join(@dir, "#{locale.code.downcase}.yml")))
53
+ file = File.join(@dir, "#{locale.code.downcase}.yml")
54
+ transform(::YAML::load(IO.read(file)))
54
55
  end
55
56
 
56
57
  # YAML loader with same +dir+ will be have same +hash+.
@@ -62,6 +63,18 @@ module R18n
62
63
  def ==(loader)
63
64
  self.class == loader.class and self.dir == loader.dir
64
65
  end
66
+
67
+ # Wrap YAML private types to Typed.
68
+ def transform(hash)
69
+ Hash[hash.map { |key, value|
70
+ if value.is_a? ::YAML::PrivateType
71
+ value = Typed.new(value.type_id, value.value)
72
+ elsif value.is_a? Hash
73
+ value = transform(value)
74
+ end
75
+ [key, value]
76
+ }]
77
+ end
65
78
  end
66
79
  end
67
80
  end
data/spec/filters_spec.rb CHANGED
@@ -6,6 +6,7 @@ describe R18n::Filters do
6
6
  @system = R18n::Filters.defined.values
7
7
  @enabled = R18n::Filters.defined.values.reject { |i| !i.enabled? }
8
8
  @i18n = R18n::I18n.new('en', DIR)
9
+ @i18n.reload!
9
10
  end
10
11
 
11
12
  after do
@@ -31,10 +32,24 @@ describe R18n::Filters do
31
32
  @i18n.my_tree_filter.should == {'name' => 'value'}
32
33
  end
33
34
 
35
+ it "should use passive filters" do
36
+ filter = mock()
37
+ filter.should_receive(:process).twice.and_return(1)
38
+
39
+ R18n::Filters.add('my', :passive, :passive => true) { filter.process }
40
+
41
+ @i18n.my_filter.should.should == 'value'
42
+ @i18n.reload!
43
+
44
+ @i18n.my_tree_filter.should == 1
45
+ @i18n.my_filter.should == 1
46
+ @i18n.my_filter.should == 1
47
+ end
48
+
34
49
  it "should use cascade filters" do
35
- filter = R18n::Filters.add('my', :one) { |i, config| i + '1' }
36
- filter = R18n::Filters.add('my', :two) { |i, config| i + '2' }
37
- filter = R18n::Filters.add('my', :three, 0) { |i, config| i + '3' }
50
+ filter = R18n::Filters.add('my', :one) { |i, config| i + '1' }
51
+ filter = R18n::Filters.add('my', :two) { |i, config| i + '2' }
52
+ filter = R18n::Filters.add('my', :three, :position => 0) { |i, c| i + '3' }
38
53
  @i18n.my_filter.should == 'value312'
39
54
  end
40
55
 
@@ -42,7 +57,7 @@ describe R18n::Filters do
42
57
  R18n::Filters.instance_variable_set(:@last_auto_name, 0)
43
58
 
44
59
  R18n::Filters.add('some').name.should == 1
45
- R18n::Filters.add('some').name.should == 2
60
+ R18n::Filters.add('some', :position => 0).name.should == 2
46
61
 
47
62
  R18n::Filters.add('some', 3)
48
63
  R18n::Filters.add('some').name.should == 4
@@ -101,7 +116,7 @@ describe R18n::Filters do
101
116
  R18n::Filters.add('my') { |content, config| config[:new_secret] ? 2 : 1 }
102
117
  @i18n.my_filter.should == 1
103
118
 
104
- R18n::Filters.add('my', nil, 0) do |content, config|
119
+ R18n::Filters.add('my', :second, :position => 0) do |content, config|
105
120
  config[:new_secret] = true
106
121
  content
107
122
  end
@@ -175,13 +190,16 @@ describe R18n::Filters do
175
190
  @i18n.greater.should == '1 < 2 is true'
176
191
 
177
192
  R18n::Filters.on(:global_escape_html)
193
+ @i18n.reload!
178
194
  @i18n.greater.should == '1 &lt; 2 is true'
179
195
  @i18n.html.should == '&lt;script&gt;true &amp;&amp; false&lt;/script&gt;'
180
196
  end
181
197
 
182
198
  it "should have filter to disable global HTML escape" do
183
199
  @i18n.no_escape.should == '<b>Warning</b>'
200
+
184
201
  R18n::Filters.on(:global_escape_html)
202
+ @i18n.reload!
185
203
  @i18n.no_escape.should == '<b>Warning</b>'
186
204
  end
187
205
 
@@ -25,8 +25,6 @@ my_filter: !!my value
25
25
  my_tree_filter: !!my
26
26
  name: value
27
27
 
28
- unknown_filter: !!unknown value
29
-
30
28
  html: !!escape
31
29
  <script>true && false</script>
32
30
  greater: 1 < 2 is true
@@ -3,3 +3,5 @@ one: Один
3
3
  in:
4
4
  another:
5
5
  level: Иерархический
6
+
7
+ typed: !!my value
@@ -23,7 +23,8 @@ describe R18n::Loader::YAML do
23
23
 
24
24
  it "should load translation" do
25
25
  @loader.load(R18n::Locale.load('ru')).should == {
26
- 'one' => 'Один', 'in' => {'another' => {'level' => 'Иерархический'}} }
26
+ 'one' => 'Один', 'in' => {'another' => {'level' => 'Иерархический'}},
27
+ 'typed' => R18n::Typed.new('my', 'value') }
27
28
  end
28
29
 
29
30
  it "should return hash by dir" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: r18n-core
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.4"
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey "A.I." Sitnik
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-03 00:00:00 +03:00
12
+ date: 2010-01-11 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15