r18n-core 0.4.14 → 1.0.0

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 (56) hide show
  1. data/.yardopts +1 -1
  2. data/ChangeLog +21 -0
  3. data/LICENSE +2 -2
  4. data/README.md +456 -0
  5. data/Rakefile +2 -1
  6. data/base/gl.yml +31 -0
  7. data/base/it.yml +1 -1
  8. data/lib/r18n-core/filter_list.rb +157 -0
  9. data/lib/r18n-core/filters.rb +40 -36
  10. data/lib/r18n-core/i18n.rb +17 -9
  11. data/lib/r18n-core/locale.rb +5 -2
  12. data/lib/r18n-core/translated.rb +18 -15
  13. data/lib/r18n-core/translated_string.rb +14 -0
  14. data/lib/r18n-core/translation.rb +19 -20
  15. data/lib/r18n-core/untranslated.rb +8 -5
  16. data/lib/r18n-core/utils.rb +5 -1
  17. data/lib/r18n-core/version.rb +1 -1
  18. data/lib/r18n-core/yaml_loader.rb +2 -2
  19. data/lib/r18n-core.rb +60 -13
  20. data/locales/ca.rb +1 -0
  21. data/locales/cs.rb +0 -1
  22. data/locales/en-au.rb +0 -1
  23. data/locales/en-gb.rb +0 -1
  24. data/locales/en-us.rb +0 -1
  25. data/locales/en.rb +1 -1
  26. data/locales/fr.rb +1 -1
  27. data/locales/gl.rb +21 -0
  28. data/locales/hu.rb +1 -2
  29. data/locales/nb-no.rb +2 -3
  30. data/locales/nl.rb +4 -2
  31. data/locales/pt-br.rb +0 -1
  32. data/locales/pt.rb +0 -1
  33. data/locales/sv-se.rb +2 -3
  34. data/locales/th.rb +1 -1
  35. data/locales/tr.rb +2 -4
  36. data/locales/zh-cn.rb +7 -0
  37. data/locales/zh-tw.rb +7 -0
  38. data/r18n-core.gemspec +10 -10
  39. data/spec/filters_spec.rb +38 -7
  40. data/spec/i18n_spec.rb +15 -18
  41. data/spec/locale_spec.rb +19 -14
  42. data/spec/locales/cs_spec.rb +1 -1
  43. data/spec/locales/pl_spec.rb +1 -1
  44. data/spec/locales/ru_spec.rb +1 -1
  45. data/spec/locales/sk_spec.rb +1 -1
  46. data/spec/r18n_spec.rb +64 -22
  47. data/spec/spec_helper.rb +2 -3
  48. data/spec/translated_spec.rb +18 -4
  49. data/spec/translation_spec.rb +17 -6
  50. data/spec/translations/general/en.yml +1 -0
  51. data/spec/yaml_loader_spec.rb +10 -10
  52. metadata +163 -141
  53. data/Gemfile +0 -5
  54. data/Gemfile.lock +0 -35
  55. data/README.rdoc +0 -482
  56. data/spec/translations/empty/en.yml +0 -0
@@ -41,7 +41,7 @@ module R18n
41
41
  # end
42
42
  #
43
43
  # i18n.filtered('_') #=> "This_content_will_be_processed_by_filter!"
44
- #
44
+ #
45
45
  # Use String class as type to add global filter for all translated strings:
46
46
  #
47
47
  # R18n::Filters.add(String, :escape_html) do |content, config, params|
@@ -73,6 +73,11 @@ module R18n
73
73
  # R18n::Filters.on(:no_space)
74
74
  # i18n.filtered('_') #=> "This_content_will_be_processed_by_filter!"
75
75
  # R18n::Filters.delete(:no_space)
76
+ #
77
+ # You can enabled/disabled filters only for special I18n object:
78
+ #
79
+ # R18n::I18n.new('en', nil, :on_filters => [:untranslated_html, :no_space],
80
+ # :off_filters => :untranslated )
76
81
  module Filters
77
82
  class << self
78
83
  # Hash of filter names to Filters.
@@ -90,33 +95,6 @@ module R18n
90
95
  # Hash of types to enabled passive and active filters.
91
96
  attr_accessor :enabled
92
97
 
93
- # Process +value+ by filters in +enabled+.
94
- def process(enabled, type, value, locale, path, params)
95
- config = { :locale => locale, :path => path }
96
-
97
- enabled[type].each do |filter|
98
- value = filter.call(value, config, *params)
99
- end
100
-
101
- if value.is_a? String
102
- value = TranslatedString.new(value, locale, path)
103
- process_string(enabled, value, config, params)
104
- else
105
- value
106
- end
107
- end
108
-
109
- # Process +value+ by global filters in +enabled+.
110
- def process_string(enabled, value, config, params)
111
- if config.is_a? String
112
- config = { :locale => value.locale, :path => config }
113
- end
114
- enabled[String].each do |f|
115
- value = f.call(value, config, *params)
116
- end
117
- value
118
- end
119
-
120
98
  # Rebuild +active_enabled+ and +passive_enabled+ for +type+.
121
99
  def rebuild_enabled!(type)
122
100
  @passive_enabled[type] = []
@@ -138,10 +116,10 @@ module R18n
138
116
  # Add new filter for +type+ with +name+ and return filter object. You
139
117
  # can use String class as +type+ to add global filter for all translated
140
118
  # string.
141
- #
119
+ #
142
120
  # Filter content will be sent to +block+ as first argument, struct with
143
121
  # config as second and filters parameters will be in next arguments.
144
- #
122
+ #
145
123
  # Options:
146
124
  # * +position+ – change order on processing several filters for same type.
147
125
  # Note that passive filters will be always run before active.
@@ -174,6 +152,7 @@ module R18n
174
152
  rebuild_enabled! type
175
153
  end
176
154
 
155
+ @new_filter_listener.call(filter) if @new_filter_listener
177
156
  filter
178
157
  end
179
158
 
@@ -206,6 +185,15 @@ module R18n
206
185
  filter.enabled = true
207
186
  filter.types.each { |type| rebuild_enabled! type }
208
187
  end
188
+
189
+ # Return filters, which be added inside +block+.
190
+ def listen(&block)
191
+ filters = []
192
+ @new_filter_listener = proc { |i| filters << i }
193
+ yield
194
+ @new_filter_listener = nil
195
+ filters
196
+ end
209
197
  end
210
198
 
211
199
  Filters.defined = {}
@@ -239,7 +227,11 @@ module R18n
239
227
  Filters.add(String, :variables) do |content, config, *params|
240
228
  content = content.clone
241
229
  params.each_with_index do |param, i|
242
- content.gsub! "%#{i+1}", config[:locale].localize(param)
230
+ param = config[:locale].localize(param)
231
+ if defined? ActiveSupport::SafeBuffer
232
+ param = ActiveSupport::SafeBuffer.new + param
233
+ end
234
+ content.gsub! "%#{i+1}", param
243
235
  end
244
236
  content
245
237
  end
@@ -248,9 +240,21 @@ module R18n
248
240
  "#{translated}[#{untranslated}]"
249
241
  end
250
242
 
243
+ Filters.add(Untranslated, :untranslated_bash) do |v, c, transl, untransl|
244
+ "#{transl}\e[0;31m[#{untransl}]\e[0m"
245
+ end
246
+ Filters.off(:untranslated_bash)
247
+
251
248
  Filters.add(Untranslated, :untranslated_html) do |v, c, transl, untransl|
252
- Utils.escape_html(transl) <<
253
- '<span style="color: red">' << Utils.escape_html(untransl) << '</span>'
249
+ if defined? ActiveSupport::SafeBuffer
250
+ transl <<
251
+ '<span style="color: red">['.html_safe <<
252
+ untransl <<
253
+ ']</span>'.html_safe
254
+ else
255
+ Utils.escape_html(transl) <<
256
+ '<span style="color: red">[' << Utils.escape_html(untransl) << ']</span>'
257
+ end
254
258
  end
255
259
  Filters.off(:untranslated_html)
256
260
 
@@ -275,9 +279,9 @@ module R18n
275
279
  end
276
280
  Filters.off(:global_escape_html)
277
281
 
278
- Filters.add('markdown', :maruku, :passive => true) do |content, config|
279
- require 'maruku'
280
- ::Maruku.new(content).to_html
282
+ Filters.add('markdown', :kramdown, :passive => true) do |content, config|
283
+ require 'kramdown'
284
+ ::Kramdown::Document.new(content).to_html
281
285
  end
282
286
 
283
287
  Filters.add('textile', :redcloth, :passive => true) do |content, config|
@@ -114,11 +114,6 @@ module R18n
114
114
  locales.map! { |i| i[0] }
115
115
  end
116
116
 
117
- # Return Array of locales with available translations.
118
- def self.available_locales(places)
119
- convert_places(places).map { |i| i.available }.flatten.uniq
120
- end
121
-
122
117
  # Load default loader for elements in +places+ with only constructor
123
118
  # argument.
124
119
  def self.convert_places(places)
@@ -147,7 +142,7 @@ module R18n
147
142
  #
148
143
  # +Locales+ must be a locale code (RFC 3066) or array, ordered by priority.
149
144
  # +Translation_places+ must be a string with path or array.
150
- def initialize(locales, translation_places = nil)
145
+ def initialize(locales, translation_places = nil, opts = {})
151
146
  locales = Array(locales)
152
147
 
153
148
  if not locales.empty? and Locale.exists? locales.first
@@ -172,6 +167,12 @@ module R18n
172
167
 
173
168
  @translation_places = self.class.convert_places(@original_places)
174
169
 
170
+ if opts[:on_filters] or opts[:off_filters]
171
+ @filters = CustomFilterList.new(opts[:on_filters], opts[:off_filters])
172
+ else
173
+ @filters = GlobalFilterList.instance
174
+ end
175
+
175
176
  key = translation_cache_key
176
177
  if R18n.cache.has_key? key
177
178
  @locale, @translation = *R18n.cache[key]
@@ -180,13 +181,20 @@ module R18n
180
181
  end
181
182
  end
182
183
 
184
+ # Return custom filters list
185
+ def filter_list
186
+ @filters
187
+ end
188
+
183
189
  # Return unique key for current locales in translation and places.
184
190
  def translation_cache_key
185
191
  @available_codes ||= @translation_places.inject([]) { |all, i|
186
192
  all + i.available }.uniq.map { |i| i.code.downcase }
187
193
  (@locales_codes & @available_codes).join(',') + '@' +
194
+ @filters.hash.to_s +
188
195
  R18n.default_loader.hash.to_s +
189
- @translation_places.hash.to_s + R18n.extension_places.hash.to_s
196
+ @translation_places.hash.to_s +
197
+ R18n.extension_places.hash.to_s
190
198
  end
191
199
 
192
200
  # Reload translations.
@@ -218,7 +226,7 @@ module R18n
218
226
  end
219
227
  end
220
228
 
221
- @translation = Translation.new @locale
229
+ @translation = Translation.new(@locale, '', :filters => @filters)
222
230
  @locales.each do |locale|
223
231
  loaded = false
224
232
  available_in_places.each do |place, available|
@@ -241,7 +249,7 @@ module R18n
241
249
 
242
250
  # Return Array of locales with available translations.
243
251
  def available_locales
244
- @available ||= self.class.available_locales(@translation_places)
252
+ @available ||= R18n.available_locales(@translation_places)
245
253
  end
246
254
 
247
255
  # Convert +object+ to String, according to the rules of the current locale.
@@ -38,7 +38,7 @@ module R18n
38
38
  #
39
39
  # Get Russian locale and print it information
40
40
  #
41
- # ru = R18n::Locale.load('ru')
41
+ # ru = R18n.locale('ru')
42
42
  # ru.title #=> "Русский"
43
43
  # ru.code #=> "ru"
44
44
  # ru.ltr? #=> true
@@ -106,7 +106,9 @@ module R18n
106
106
 
107
107
  # Locale RFC 3066 code.
108
108
  def code
109
- self.class.name.split('::').last.downcase
109
+ name = self.class.name.split('::').last
110
+ lang, culture = name.match(/([A-Z][a-z]+)([A-Z]\w+)?/).captures
111
+ lang.downcase + (culture ? '-' + culture.upcase : '')
110
112
  end
111
113
 
112
114
  set :sublocales => %w{en},
@@ -119,6 +121,7 @@ module R18n
119
121
 
120
122
  def month_standalone; month_names; end
121
123
  def month_abbrs; month_names; end
124
+ def wday_abbrs; wday_names; end
122
125
 
123
126
  # Is locale has left-to-right write direction.
124
127
  def ltr?; true; end
@@ -27,8 +27,6 @@ module R18n
27
27
  # proxy-method +title+, which will use +title_ru+ for Russian users and
28
28
  # +title_en+ for English:
29
29
  #
30
- # require 'r18n-core/translated'
31
- #
32
30
  # class Product
33
31
  # include DataMapper::Resource
34
32
  # property :title_ru, String
@@ -61,10 +59,9 @@ module R18n
61
59
  # Proxy-method support all funtion from I18n: global and type filters,
62
60
  # pluralization, variables. It also return TranslatedString or Untranslated.
63
61
  #
64
- # Note, you must set your I18n object by <tt>R18n.set</tt> and don’t forget
65
- # to require <tt>'r18n-core/translated'</tt>. R18n plugins (sinatra-r18n,
66
- # r18-desktop) set I18n object by <tt>R18n.set</tt> automatically, but you
67
- # must call <tt>i18n</tt> helper in Sinatra before use models.
62
+ # By default it use <tt>R18n.get</tt> to get I18n object (so, you must set it
63
+ # before use model), but you can overwrite object +r18n+ method to change
64
+ # default logic.
68
65
  #
69
66
  # See R18n::Translated::Base for class method documentation.
70
67
  #
@@ -89,6 +86,12 @@ module R18n
89
86
  end
90
87
  end
91
88
 
89
+ # Access to I18n object. By default return global I18n object from
90
+ # <tt>R18n.get</tt>.
91
+ def r18n
92
+ R18n.get
93
+ end
94
+
92
95
  # Module with class methods, which be added after R18n::Translated include.
93
96
  module Base
94
97
  # Hash of translation method names to it type for filters.
@@ -96,7 +99,7 @@ module R18n
96
99
 
97
100
  # Add several proxy +methods+. See R18n::Translated for description.
98
101
  # It’s more compact, that +translation+.
99
- #
102
+ #
100
103
  # translations :title, :keywords, [:desciption, {:type => 'markdown'}]
101
104
  def translations(*methods)
102
105
  methods.each do |method|
@@ -106,7 +109,7 @@ module R18n
106
109
 
107
110
  # Add proxy-method +name+. See R18n::Translated for description.
108
111
  # It’s more useful to set options.
109
- #
112
+ #
110
113
  # translation :desciption, :type => 'markdown'
111
114
  def translation(name, options = {})
112
115
  if options[:methods]
@@ -124,7 +127,7 @@ module R18n
124
127
  class_eval <<-EOS, __FILE__, __LINE__
125
128
  def #{name}(*params)
126
129
  unlocalized = self.class.unlocalized_getters(#{name.inspect})
127
- R18n.get.locales.each do |locale|
130
+ r18n.locales.each do |locale|
128
131
  code = locale.code
129
132
  next unless unlocalized.has_key? code
130
133
  result = send unlocalized[code]#{params}
@@ -133,19 +136,19 @@ module R18n
133
136
  path = "\#{self.class.name}##{name}"
134
137
  type = self.class.translation_types[#{name.inspect}]
135
138
  if type
136
- return R18n::Filters.process(R18n::Filters.enabled,
137
- type, result, locale, path, params)
139
+ return r18n.filter_list.process(:all, type, result, locale,
140
+ path, params)
138
141
  elsif result.is_a? String
139
142
  result = TranslatedString.new(result, locale, path)
140
- return R18n::Filters.process_string(R18n::Filters.enabled,
141
- result, path, params)
143
+ return r18n.filter_list.process_string(:all, result, path,
144
+ params)
142
145
  else
143
146
  return result
144
147
  end
145
148
  end
146
149
 
147
150
  R18n::Untranslated.new("\#{self.class.name}\#", '#{name}',
148
- R18n.get.locale)
151
+ r18n.locale, r18n.filter_list)
149
152
  end
150
153
  EOS
151
154
 
@@ -153,7 +156,7 @@ module R18n
153
156
  class_eval <<-EOS, __FILE__, __LINE__
154
157
  def #{name}=(*params)
155
158
  unlocalized = self.class.unlocalized_setters(#{name.inspect})
156
- R18n.get.locales.each do |locale|
159
+ r18n.locales.each do |locale|
157
160
  code = locale.code
158
161
  next unless unlocalized.has_key? code
159
162
  return send unlocalized[code], *params
@@ -44,5 +44,19 @@ module R18n
44
44
  def translated?
45
45
  true
46
46
  end
47
+
48
+ # Mark translated string as html safe, because R18n has own escape system.
49
+ def html_safe?
50
+ true
51
+ end
52
+
53
+ # Override to_s to make string html safe if `html_safe` method is defined.
54
+ def to_s
55
+ if respond_to? :html_safe
56
+ html_safe
57
+ else
58
+ self
59
+ end
60
+ end
47
61
  end
48
62
  end
@@ -70,15 +70,15 @@ module R18n
70
70
  # i18n.comments(0) #=> "no comments"
71
71
  # i18n.comments(10) #=> "10 comments"
72
72
  class Translation
73
- # Create translation hash for +path+ with messages in +data+ for +locale+.
74
- #
75
73
  # This is internal a constructor. To load translation use
76
- # <tt>R18n::Translation.load(locales, translations_dir)</tt>.
77
- def initialize(main_locale, path = '', locale = nil, data = {})
78
- @path = path
79
- @data = {}
80
- @locale = main_locale
81
- merge! data, locale unless data.empty?
74
+ # <tt>R18n::I18n.new(locales, translations_dir)</tt>.
75
+ def initialize(locale, path = '', options = {})
76
+ @data = {}
77
+ @locale = locale
78
+ @path = path
79
+ @filters = options[:filters] || GlobalFilterList.instance
80
+
81
+ merge! options[:translations], options[:locale] if options[:translations]
82
82
  end
83
83
 
84
84
  # Add another hash with +translations+ for some +locale+. Current data is
@@ -89,17 +89,17 @@ module R18n
89
89
  path = @path.empty? ? name : "#{@path}.#{name}"
90
90
  case value
91
91
  when Hash
92
- value = Translation.new(@locale, path, locale, value)
92
+ value = Translation.new(@locale, path,
93
+ :locale => locale, :translations => value, :filters => @filters)
93
94
  when String
94
95
  c = { :locale => locale, :path => path }
95
- v = Filters.process_string(Filters.passive_enabled, value, c, [])
96
+ v = @filters.process_string(:passive, value, c, [])
96
97
  value = TranslatedString.new(v, locale, path)
97
98
  when Typed
98
99
  value.locale = locale
99
- value.path = path
100
- unless Filters.passive_enabled[value.type].empty?
101
- value = Filters.process(Filters.passive_enabled, value.type,
102
- value.value, value.locale, value.path, {})
100
+ value.path = path
101
+ unless @filters.passive(value.type).empty?
102
+ value = @filters.process_typed(:passive, value, { })
103
103
  end
104
104
  end
105
105
  @data[name] = value
@@ -111,12 +111,12 @@ module R18n
111
111
 
112
112
  # Use untranslated filter to print path.
113
113
  def to_s
114
- Filters.process(Filters.enabled, Untranslated, @path, @locale, @path,
114
+ @filters.process(:all, Untranslated, @path, @locale, @path,
115
115
  [@path, '', @path])
116
116
  end
117
117
 
118
118
  # Return current translation keys.
119
- def _keys
119
+ def translation_keys
120
120
  @data.keys
121
121
  end
122
122
 
@@ -133,13 +133,12 @@ module R18n
133
133
  value = @data[name.to_s]
134
134
  case value
135
135
  when TranslatedString
136
- Filters.process_string(Filters.active_enabled, value, @path, params)
136
+ @filters.process_string(:active, value, @path, params)
137
137
  when Typed
138
- Filters.process(Filters.active_enabled, value.type, value.value,
139
- value.locale, value.path, params)
138
+ @filters.process_typed(:active, value, params)
140
139
  when nil
141
140
  translated = @path.empty? ? '' : "#{@path}."
142
- Untranslated.new(translated, name.to_s, @locale)
141
+ Untranslated.new(translated, name.to_s, @locale, @filters)
143
142
  else
144
143
  value
145
144
  end
@@ -46,10 +46,11 @@ module R18n
46
46
  # Path, that exists in translation.
47
47
  attr_reader :translated_path
48
48
 
49
- def initialize(translated_path, untranslated_path, locale)
50
- @translated_path = translated_path
49
+ def initialize(translated_path, untranslated_path, locale, filters)
50
+ @translated_path = translated_path
51
51
  @untranslated_path = untranslated_path
52
- @locale = locale
52
+ @locale = locale
53
+ @filters = filters
53
54
  end
54
55
 
55
56
  # Path to translation.
@@ -67,7 +68,7 @@ module R18n
67
68
 
68
69
  def [](*params)
69
70
  Untranslated.new(translated_path, "#{@untranslated_path}.#{params.first}",
70
- @locale)
71
+ @locale, @filters)
71
72
  end
72
73
 
73
74
  def |(default)
@@ -75,8 +76,10 @@ module R18n
75
76
  end
76
77
 
77
78
  def to_s
78
- Filters.process(Filters.enabled, Untranslated, path, @locale, path,
79
+ @filters.process(:all, Untranslated, path, @locale, path,
79
80
  [@translated_path, @untranslated_path, path])
80
81
  end
82
+
83
+ alias :to_str :to_s
81
84
  end
82
85
  end
@@ -35,7 +35,11 @@ module R18n
35
35
 
36
36
  # Escape HTML entries (<, >, &). Copy from HAML helper.
37
37
  def self.escape_html(content)
38
- content.to_s.gsub(/[><&]/) { |s| HTML_ENTRIES[s] }
38
+ if defined? ActiveSupport::SafeBuffer
39
+ ActiveSupport::SafeBuffer.new + content
40
+ else
41
+ content.to_s.gsub(/[><&]/) { |s| HTML_ENTRIES[s] }
42
+ end
39
43
  end
40
44
 
41
45
  # Invokes +block+ once for each key and value of +hash+. Creates a new hash
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module R18n
3
- VERSION = '0.4.14'.freeze unless defined? R18n::VERSION
3
+ VERSION = '1.0.0'.freeze unless defined? R18n::VERSION
4
4
  end
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
19
19
  =end
20
20
 
21
21
  require 'yaml'
22
- require 'syck' if '1.8.' != RUBY_VERSION[0..3]
22
+ require 'syck' if '1.8.' != RUBY_VERSION[0..3] and RUBY_PLATFORM != 'java'
23
23
 
24
24
  module R18n
25
25
  module Loader
@@ -53,7 +53,7 @@ module R18n
53
53
  def available
54
54
  Dir.glob(File.join(@dir, '**/*.yml')).
55
55
  map { |i| File.basename(i, '.yml') }.uniq.
56
- map { |i| R18n::Locale.load(i) }
56
+ map { |i| R18n.locale(i) }
57
57
  end
58
58
 
59
59
  # Return Hash with translations for +locale+.
data/lib/r18n-core.rb CHANGED
@@ -30,40 +30,46 @@ require dir.join('unsupported_locale').to_s
30
30
  require dir.join('translated_string').to_s
31
31
  require dir.join('untranslated').to_s
32
32
  require dir.join('filters').to_s
33
+ require dir.join('filter_list').to_s
33
34
  require dir.join('translation').to_s
34
35
  require dir.join('yaml_loader').to_s
35
36
  require dir.join('i18n').to_s
36
37
  require dir.join('helpers').to_s
37
38
 
38
39
  module R18n
40
+ autoload :Translated, 'r18n-core/translated'
41
+
39
42
  class << self
40
43
 
41
- # Set I18n object globally.
42
- def set(i18n = nil, dir = nil, &block)
44
+ # Set I18n object globally. You can miss translation +places+, it will be
45
+ # taken from <tt>R18n.default_places</tt>.
46
+ def set(i18n = nil, places = R18n.default_places, &block)
43
47
  if block_given?
44
48
  @setter = block
45
- @i18n = nil
49
+ @i18n = nil
46
50
  elsif i18n.is_a? I18n
47
51
  @i18n = i18n
48
52
  else
49
- @i18n = I18n.new(i18n, dir)
53
+ @i18n = I18n.new(i18n, places)
50
54
  end
51
55
  end
52
56
 
53
57
  # Set I18n object to current thread.
54
58
  def thread_set(i18n = nil, &block)
55
59
  if block_given?
56
- Thread.current[:r18n_setter] = block
57
- Thread.current[:r18n_i18n] = nil
60
+ thread[:r18n_setter] = block
61
+ thread[:r18n_i18n] = nil
58
62
  else
59
- Thread.current[:r18n_i18n] = i18n
63
+ thread[:r18n_i18n] = i18n
60
64
  end
61
65
  end
62
66
 
63
67
  # Get I18n object for current thread.
64
68
  def get
65
- (thread[:r18n_i18n] ||= ((block = thread[:r18n_setter]) && block.call)) ||
66
- (@i18n ||= (@setter && @setter.call))
69
+ thread[:r18n_i18n] ||
70
+ (thread[:r18n_setter] && thread_set(thread[:r18n_setter].call)) ||
71
+ @i18n ||
72
+ (@setter && set(@setter.call))
67
73
  end
68
74
 
69
75
  # Delete I18n object from current thread and global variable.
@@ -87,6 +93,46 @@ module R18n
87
93
  get.l(*params)
88
94
  end
89
95
 
96
+ # Return I18n object for +locale+. Useful to temporary change locale,
97
+ # for example, to show text in locales list:
98
+ #
99
+ # - R18n.available_locales.each do |locale|
100
+ # - R18n.change(locale).t.language_title
101
+ def change(locale)
102
+ locale = locale.code if locale.is_a? Locale
103
+ exists = get ? get.locales.map { |i| i.code } : []
104
+ places = get ? get.translation_places : R18n.default_places
105
+ R18n::I18n.new([locale] + exists, places)
106
+ end
107
+
108
+ # Return Locale object by locale code. It’s shortcut for
109
+ # <tt>R18n::Locale.load(code)</tt>.
110
+ def locale(code)
111
+ R18n::Locale.load(code)
112
+ end
113
+
114
+ # Return Array of locales with available translations. You can miss
115
+ # translation +places+, it will be taken from <tt>R18n.default_places</tt>.
116
+ def available_locales(places = R18n.default_places)
117
+ R18n::I18n.convert_places(places).map { |i| i.available }.flatten.uniq
118
+ end
119
+
120
+ # Default places for <tt>R18n.set</tt> and <tt>R18n.available_locales</tt>.
121
+ #
122
+ # You can set block to calculate places dynamically:
123
+ # R18n.default_places { settings.i18n_places }
124
+ attr_accessor :default_places
125
+
126
+ def default_places(&block)
127
+ if block_given?
128
+ @default_places = block
129
+ elsif @default_places.is_a? Proc
130
+ @default_places.call
131
+ else
132
+ @default_places
133
+ end
134
+ end
135
+
90
136
  # Default loader class, which will be used if you didn’t send loader to
91
137
  # +I18n.new+ (object with +available+ and +load+ methods).
92
138
  attr_accessor :default_loader
@@ -99,8 +145,9 @@ module R18n
99
145
  attr_accessor :cache
100
146
  end
101
147
 
102
- self.default_loader = R18n::Loader::YAML
103
- self.extension_places = [
104
- Loader::YAML.new(Pathname(__FILE__).dirname.expand_path + '../base')]
105
- self.cache = {}
148
+ dir = Pathname(__FILE__).dirname.expand_path
149
+ self.default_loader = R18n::Loader::YAML
150
+ self.default_places = nil
151
+ self.extension_places = [Loader::YAML.new(dir + '../base')]
152
+ self.cache = {}
106
153
  end
data/locales/ca.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  module R18n
3
3
  class Locales::Ca < Locale
4
4
  set :title => 'Català',
5
+ :sublocales => %w{es en},
5
6
 
6
7
  :wday_names => %w{diumenge dilluns dimarts dimecres dijous divendres
7
8
  dissabte},
data/locales/cs.rb CHANGED
@@ -35,4 +35,3 @@ module R18n
35
35
  end
36
36
  end
37
37
  end
38
-
data/locales/en-au.rb CHANGED
@@ -3,7 +3,6 @@ require File.join(File.dirname(__FILE__), 'en')
3
3
  module R18n::Locales
4
4
  class EnAu < En
5
5
  set :title => 'Australian English',
6
- :code => 'en-AU',
7
6
  :sublocales => %w{en}
8
7
  end
9
8
  end
data/locales/en-gb.rb CHANGED
@@ -3,7 +3,6 @@ require File.join(File.dirname(__FILE__), 'en')
3
3
  module R18n::Locales
4
4
  class EnGb < En
5
5
  set :title => 'British English',
6
- :code => 'en-GB',
7
6
  :sublocales => %w{en}
8
7
  end
9
8
  end
data/locales/en-us.rb CHANGED
@@ -5,7 +5,6 @@ require File.join(File.dirname(__FILE__), 'en')
5
5
  module R18n::Locales
6
6
  class EnUs < En
7
7
  set :title => 'American English',
8
- :code => 'en-US',
9
8
  :sublocales => %w{en},
10
9
 
11
10
  :time_format => ' %I:%M %p',
data/locales/en.rb CHANGED
@@ -17,7 +17,7 @@ module R18n
17
17
  :year_format => '_, %Y',
18
18
 
19
19
  :number_decimal => ".",
20
- :number_group => ","
20
+ :number_group => ","
21
21
 
22
22
  def ordinalize(n)
23
23
  if (11..13).include?(n % 100)