r18n-core 0.4.14 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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)