r18n-core 0.3.2 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/ChangeLog +11 -0
  2. data/README.rdoc +225 -53
  3. data/lib/r18n-core/filters.rb +34 -34
  4. data/lib/r18n-core/i18n.rb +110 -46
  5. data/lib/r18n-core/locale.rb +109 -103
  6. data/lib/r18n-core/translated.rb +9 -4
  7. data/lib/r18n-core/translated_string.rb +10 -0
  8. data/lib/r18n-core/translation.rb +55 -99
  9. data/lib/r18n-core/unsupported_locale.rb +1 -13
  10. data/lib/r18n-core/untranslated.rb +18 -16
  11. data/lib/r18n-core/utils.rb +0 -12
  12. data/lib/r18n-core/version.rb +1 -1
  13. data/lib/r18n-core/yaml_loader.rb +67 -0
  14. data/lib/r18n-core.rb +25 -3
  15. data/locales/cs.rb +30 -16
  16. data/locales/de.rb +21 -0
  17. data/locales/en-us.rb +9 -4
  18. data/locales/en.rb +35 -20
  19. data/locales/eo.rb +20 -0
  20. data/locales/es.rb +20 -0
  21. data/locales/fr.rb +24 -9
  22. data/locales/it.rb +21 -9
  23. data/locales/kk.rb +26 -0
  24. data/locales/pl.rb +30 -18
  25. data/locales/pt-br.rb +24 -0
  26. data/locales/ru.rb +30 -12
  27. data/locales/zh.rb +19 -0
  28. data/spec/filters_spec.rb +24 -7
  29. data/spec/i18n_spec.rb +137 -50
  30. data/spec/locale_spec.rb +91 -53
  31. data/spec/locales/cs_spec.rb +9 -7
  32. data/spec/locales/pl_spec.rb +8 -9
  33. data/spec/locales/ru_spec.rb +9 -9
  34. data/spec/r18n_spec.rb +32 -13
  35. data/spec/spec_helper.rb +28 -4
  36. data/spec/translated_spec.rb +1 -1
  37. data/spec/translation_spec.rb +33 -67
  38. data/spec/translations/two/en.yml +0 -1
  39. data/spec/yaml_loader_spec.rb +33 -0
  40. metadata +11 -16
  41. data/locales/cs.yml +0 -26
  42. data/locales/de.yml +0 -26
  43. data/locales/en-us.yml +0 -10
  44. data/locales/en.yml +0 -26
  45. data/locales/eo.yml +0 -26
  46. data/locales/es.yml +0 -26
  47. data/locales/fr.yml +0 -26
  48. data/locales/it.yml +0 -26
  49. data/locales/kk.yml +0 -26
  50. data/locales/pl.yml +0 -26
  51. data/locales/pt-br.yml +0 -26
  52. data/locales/ru.yml +0 -26
  53. data/locales/zh.yml +0 -26
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
19
19
  =end
20
20
 
21
21
  require 'date'
22
+ require 'pathname'
22
23
 
23
24
  module R18n
24
25
  # General class to i18n support in your application. It load Translation and
@@ -28,8 +29,30 @@ module R18n
28
29
  # translation’s name or <tt>[name]</tt> method. Translations will be also
29
30
  # loaded for default locale, +sublocales+ from first in +locales+ and general
30
31
  # languages for dialects (it will load +fr+ for +fr_CA+ too).
32
+ #
33
+ # Translations will loaded by loader object, which must have 2 methods:
34
+ # * <tt>available</tt> – return array of locales of available translations;
35
+ # * <tt>load(locale)</tt> – return Hash of translation.
36
+ # If you will use default loader (+R18n.default_loader+) you can pass to I18n
37
+ # only constructor argument for loader:
38
+ #
39
+ # R18n::I18n.new('en', R18n::Loader::YAML.new('dir/with/translations'))
40
+ #
41
+ # is a same as:
42
+ #
43
+ # R18n::I18n.new('en', 'dir/with/translations')
44
+ #
45
+ # In translation file you can use strings, numbers, floats (any YAML types)
46
+ # and pluralizable values (<tt>!!pl</tt>). You can use params in string
47
+ # values, which you can replace in program. Just write <tt>%1</tt>,
48
+ # <tt>%2</tt>, etc and set it values as method arguments, when you will be get
49
+ # value.
50
+ #
51
+ # You can use filters for some YAML type or for all strings. See R18n::Filters
52
+ # for details.
31
53
  #
32
- # See Translation and Locale documentation.
54
+ # R18n contain translations for common words (such as “OK”, “Cancel”, etc)
55
+ # for most supported locales. See <tt>base/</tt> dir.
33
56
  #
34
57
  # == Usage
35
58
  # translations/ru.yml
@@ -57,6 +80,10 @@ module R18n
57
80
  # i18n.l Time.now, :date #=> "21.09.2008"
58
81
  # i18n.l Time.now, :time #=> "22:10"
59
82
  # i18n.l Time.now, '%A' #=> "Воскресенье"
83
+ #
84
+ # i18n.yes #=> "Yes"
85
+ # i18n.ok #=> "OK"
86
+ # i18n.cancel #=> "Cancel"
60
87
  class I18n
61
88
  @@default = 'en'
62
89
 
@@ -87,27 +114,44 @@ module R18n
87
114
  locales.map! { |i| i[0] }
88
115
  end
89
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
+ # Load default loader for elements in +places+ with only constructor
123
+ # argument.
124
+ def self.convert_places(places)
125
+ Array(places).map! do |loader|
126
+ if loader.respond_to? :available and loader.respond_to? :load
127
+ loader
128
+ else
129
+ R18n.default_loader.new(loader)
130
+ end
131
+ end
132
+ end
133
+
90
134
  # User locales, ordered by priority
91
135
  attr_reader :locales
92
136
 
93
- # Dirs with translations files
94
- attr_reader :translation_dirs
137
+ # Loaders with translations files
138
+ attr_reader :translation_places
95
139
 
96
140
  # First locale with locale file
97
141
  attr_reader :locale
98
142
 
99
- # Create i18n for +locales+ with translations from +translation_dirs+ and
143
+ # Create i18n for +locales+ with translations from +translation_places+ and
100
144
  # locales data. Translations will be also loaded for default locale,
101
145
  # +sublocales+ from first in +locales+ and general languages for dialects
102
146
  # (it will load +fr+ for +fr_CA+ too).
103
147
  #
104
148
  # +Locales+ must be a locale code (RFC 3066) or array, ordered by priority.
105
- # +Translation_dirs+ must be a string with path or array.
106
- def initialize(locales, translation_dirs = nil)
107
- locales = [locales] if locales.is_a? String
149
+ # +Translation_places+ must be a string with path or array.
150
+ def initialize(locales, translation_places = nil)
151
+ locales = Array(locales)
108
152
 
109
153
  if not locales.empty? and Locale.exists? locales.first
110
- locales += Locale.load(locales.first)['sublocales']
154
+ locales += Locale.load(locales.first).sublocales
111
155
  end
112
156
  locales << @@default
113
157
  locales.each_with_index do |locale, i|
@@ -115,18 +159,10 @@ module R18n
115
159
  locales.insert(i + 1, locale.match(/([^_-]+)[_-]/)[1])
116
160
  end
117
161
  end
118
- locales.uniq!
162
+ locales.map! { |i| i.to_s.downcase }.uniq!
163
+ @locales_codes = locales
119
164
  @locales = locales.map { |i| Locale.load(i) }
120
165
 
121
- if translation_dirs.nil?
122
- @translation_dirs = []
123
- @translation = Translation.load(@locales,
124
- Translation.extension_translations)
125
- else
126
- @translation_dirs = translation_dirs
127
- @translation = Translation.load(@locales, @translation_dirs)
128
- end
129
-
130
166
  @locale = @locales.first
131
167
  unless @locale.supported?
132
168
  @locales.each do |locale|
@@ -136,15 +172,58 @@ module R18n
136
172
  end
137
173
  end
138
174
  end
175
+
176
+ if translation_places
177
+ @original_places = translation_places
178
+ else
179
+ @original_places = R18n.extension_places
180
+ end
181
+
182
+ @translation_places = self.class.convert_places(@original_places)
183
+
184
+ reload! unless @translation = R18n.cache[translation_cache_key]
139
185
  end
140
186
 
141
- # Return Hash with titles (or code for unsupported locales) for available
142
- # translations.
143
- def translations
144
- Translation.available(@translation_dirs).inject({}) do |all, code|
145
- all[code] = Locale.load(code).title
146
- all
187
+
188
+ # Return unique key for current locales in translation and places.
189
+ def translation_cache_key
190
+ available = @translation_places.inject([]) { |all, i|
191
+ all + i.available }.uniq.map { |i| i.code }
192
+ (@locales_codes & available).join(',') + '@' +
193
+ R18n.default_loader.hash.to_s +
194
+ @translation_places.hash.to_s + R18n.extension_places.hash.to_s
195
+ end
196
+
197
+ # Reload translations.
198
+ def reload!
199
+ @translation_places = self.class.convert_places(@original_places)
200
+
201
+ available_in_places = @translation_places.map { |i| [i, i.available] }
202
+ available_in_extensions = R18n.extension_places.map { |i| [i, i.available] }
203
+
204
+ @translation = Translation.new @locale
205
+ @locales.each do |locale|
206
+ loaded = false
207
+ available_in_places.each do |place, available|
208
+ if available.include? locale
209
+ @translation.merge! place.load(locale), locale
210
+ loaded = true
211
+ end
212
+ end
213
+ if loaded
214
+ available_in_extensions.each do |extension, available|
215
+ if available.include? locale
216
+ @translation.merge! extension.load(locale), locale
217
+ end
218
+ end
219
+ end
147
220
  end
221
+ R18n.cache[translation_cache_key] = @translation
222
+ end
223
+
224
+ # Return Array of locales with available translations.
225
+ def available_locales
226
+ @available ||= self.class.available_locales(@translation_places)
148
227
  end
149
228
 
150
229
  # Convert +object+ to String, according to the rules of the current locale.
@@ -161,30 +240,15 @@ module R18n
161
240
  # i18n.l Time.now, :human #=> "now"
162
241
  # i18n.l Time.now, :full #=> "Jule 1st, 2009 12:59"
163
242
  def localize(object, format = nil, *params)
164
- if object.is_a? Integer
165
- locale.format_integer(object)
166
- elsif object.is_a? Float
167
- locale.format_float(object)
168
- elsif object.is_a? Time or object.is_a? DateTime or object.is_a? Date
169
- if format.is_a? String
170
- locale.strftime(object, format)
171
- else
172
- if :month == format
173
- return locale.data['months']['standalone'][object.month - 1]
174
- end
175
- type = object.is_a?(Date) ? 'date' : 'time'
176
- format = :standard unless format
177
-
178
- unless [:human, :full, :standard].include? format
179
- raise ArgumentError, "Unknown time formatter #{format}"
180
- end
181
-
182
- locale.send "format_#{type}_#{format}", self, object, *params
183
- end
184
- end
243
+ locale.localize(object, format, self, *params)
185
244
  end
186
245
  alias :l :localize
187
246
 
247
+ # Return translations.
248
+ def t
249
+ @translation
250
+ end
251
+
188
252
  # Short and pretty way to get translation by method name. If translation
189
253
  # has name like object methods (+new+, +to_s+, +methods+) use <tt>[]</tt>
190
254
  # method to access.
@@ -192,7 +256,7 @@ module R18n
192
256
  # Translation can contain variable part. Just set is as <tt>%1</tt>,
193
257
  # <tt>%2</tt>, etc in translations file and set values as methods params.
194
258
  def method_missing(name, *params)
195
- @translation[name.to_s, *params]
259
+ @translation[name, *params]
196
260
  end
197
261
 
198
262
  # Return translation with special +name+.
@@ -19,14 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
19
19
  =end
20
20
 
21
21
  require 'pathname'
22
- require 'yaml'
22
+ require 'singleton'
23
23
 
24
24
  module R18n
25
25
  # Information about locale (language, country and other special variant
26
26
  # preferences). Locale was named by RFC 3066. For example, locale for French
27
- # speaking people in Canada will be +fr_CA+.
27
+ # speaking people in Canada will be +fr-CA+.
28
28
  #
29
- # Locale files is placed in <tt>locales/</tt> dir in YAML files.
29
+ # Locale classes are placed in <tt>R18n::Locales</tt> module and storage
30
+ # install <tt>locales/</tt> dir.
30
31
  #
31
32
  # Each locale has +sublocales+ – often known languages for people from this
32
33
  # locale. For example, many Belorussians know Russian and English. If there
@@ -44,110 +45,86 @@ module R18n
44
45
  #
45
46
  # == Available data
46
47
  #
47
- # * +code+: locale RFC 3066 code;
48
- # * +title+: locale name on it language;
49
- # * +direction+: writing direction, +ltr+ or +rtl+ (for Arabic and Hebrew);
50
- # * +sublocales+: often known languages for people from this locale;
51
- # * +include+: locale code to include it data, optional.
48
+ # * +code+ locale RFC 3066 code;
49
+ # * +title+ locale name on it language;
50
+ # * +ltr?+ – true on left-to-right writing direction, false for Arabic and
51
+ # Hebrew);
52
+ # * +sublocales+ often known languages for people from this locale;
53
+ # * +week_start+ – does week start from +:monday+ or +:sunday+.
52
54
  #
53
55
  # You can see more available data about locale in samples in
54
56
  # <tt>locales/</tt> dir.
55
- #
56
- # == Extend locale
57
- # If language need some special logic (for example, another pluralization or
58
- # time formatters) you can just change Locale class. Create
59
- # R18n::Locales::_Code_ class in base/_code_.rb, extend R18n::Locale and
60
- # rewrite methods (for example, +pluralization+ or +format_date_full+).
61
57
  class Locale
62
58
  LOCALES_DIR = Pathname(__FILE__).dirname.expand_path + '../../locales/'
59
+
60
+ @@loaded = {}
63
61
 
64
- # All available locales
62
+ # Codes of all available locales.
65
63
  def self.available
66
- Dir.glob(File.join(LOCALES_DIR, '*.yml')).map do |i|
67
- File.basename(i, '.yml')
64
+ Dir.glob(File.join(LOCALES_DIR, '*.rb')).map do |i|
65
+ File.basename(i, '.rb')
68
66
  end
69
67
  end
70
68
 
71
- # Is +locale+ has info file
69
+ # Is +locale+ has info file.
72
70
  def self.exists?(locale)
73
- File.exists?(File.join(LOCALES_DIR, locale + '.yml'))
71
+ File.exists?(File.join(LOCALES_DIR, locale.to_s + '.rb'))
74
72
  end
75
73
 
76
- # Load locale by RFC 3066 +code+
74
+ # Load locale by RFC 3066 +code+.
77
75
  def self.load(code)
78
- code = code.to_s
79
- code.delete! '/'
80
- code.delete! '\\'
81
- code.delete! ';'
82
- original = code
83
- code = code.downcase
84
-
85
- return UnsupportedLocale.new(original) unless exists? code
86
-
87
- data = {}
88
- klass = R18n::Locale
89
- default_loaded = false
76
+ original = code.to_s.gsub(/[^-a-zA-Z]/, '')
77
+ code = original.downcase
90
78
 
91
- while code and exists? code
92
- file = LOCALES_DIR + "#{code}.yml"
93
- default_loaded = true if I18n.default == code
94
-
95
- if R18n::Locale == klass and File.exists? LOCALES_DIR + "#{code}.rb"
79
+ @@loaded[code] ||= begin
80
+ if exists? code
96
81
  require LOCALES_DIR + "#{code}.rb"
97
82
  name = code.gsub(/[\w\d]+/) { |i| i.capitalize }.gsub('-', '')
98
- klass = eval 'R18n::Locales::' + name
99
- end
100
-
101
- loaded = YAML.load_file(file)
102
- code = loaded['include']
103
- data = Utils.deep_merge! loaded, data
104
- end
105
-
106
- unless default_loaded
107
- code = I18n.default
108
- while code and exists? code
109
- loaded = YAML.load_file(LOCALES_DIR + "#{code}.yml")
110
- code = loaded['include']
111
- data = Utils.deep_merge! loaded, data
83
+ eval('R18n::Locales::' + name).new
84
+ else
85
+ UnsupportedLocale.new(original)
112
86
  end
113
87
  end
114
-
115
- klass.new(data)
116
88
  end
117
89
 
118
- attr_reader :data
119
-
120
- # Create locale object with locale +data+.
121
- #
122
- # This is internal a constructor. To load translation use
123
- # <tt>R18n::Translation.load(locales, translations_dir)</tt>.
124
- def initialize(data)
125
- @data = data
90
+ # Set locale +properties+. Locale class will have methods for each propetry
91
+ # name, which return propetry value:
92
+ #
93
+ # class R18n::Locales::En < R18n::Locale
94
+ # set :title => 'English',
95
+ # :code => 'en'
96
+ # end
97
+ #
98
+ # locale = R18n::Locales::En.new
99
+ # locale.title #=> "English"
100
+ # locale.code #=> "en"
101
+ def self.set(properties)
102
+ properties.each_pair do |key, value|
103
+ define_method(key) { value }
104
+ end
126
105
  end
127
106
 
128
107
  # Locale RFC 3066 code.
129
108
  def code
130
- @data['code']
109
+ self.class.name.split('::').last.downcase
131
110
  end
132
111
 
133
- # Locale title.
134
- def title
135
- @data['title']
136
- end
112
+ set :sublocales => %w{en},
113
+ :week_start => :monday,
114
+ :time_am => 'AM',
115
+ :time_pm => 'PM',
116
+ :time_format => ' %H:%M',
117
+ :full_format => '%e %B',
118
+ :year_format => '_ %Y'
119
+
120
+ def month_standalone; month_names; end
137
121
 
138
122
  # Is locale has left-to-right write direction.
139
- def ltr?
140
- @data['direction'] == 'ltr'
141
- end
142
-
143
- # Get information about locale
144
- def [](name)
145
- @data[name]
146
- end
123
+ def ltr?; true; end
147
124
 
148
- # Is another locale has same code
125
+ # Is another locale has same code.
149
126
  def ==(locale)
150
- code.downcase == locale.code.downcase
127
+ self.class == locale.class
151
128
  end
152
129
 
153
130
  # Is locale has information file. In this class always return true.
@@ -155,17 +132,48 @@ module R18n
155
132
  true
156
133
  end
157
134
 
158
- # Human readable locale code and title
135
+ # Human readable locale code and title.
159
136
  def inspect
160
137
  "Locale #{code} (#{title})"
161
138
  end
162
139
 
140
+ # Convert +object+ to String. It support Fixnum, Bignum, Float, Time, Date
141
+ # and DateTime.
142
+ #
143
+ # For time classes you can set +format+ in standard +strftime+ form,
144
+ # <tt>:full</tt> (“01 Jule, 2009”), <tt>:human</tt> (“yesterday”),
145
+ # <tt>:standard</tt> (“07/01/09”) or <tt>:month</tt> for standalone month
146
+ # name. Default format is <tt>:standard</tt>.
147
+ def localize(obj, format = nil, *params)
148
+ case obj
149
+ when Integer
150
+ format_integer(obj)
151
+ when Float
152
+ format_float(obj)
153
+ when Time, DateTime, Date
154
+ return strftime(obj, format) if format.is_a? String
155
+ return month_standalone[obj.month - 1] if :month == format
156
+ return obj.to_s if :human == format and not params.first.is_a? I18n
157
+
158
+ type = obj.is_a?(Date) ? 'date' : 'time'
159
+ format = :standard unless format
160
+
161
+ unless [:human, :full, :standard].include? format
162
+ raise ArgumentError, "Unknown time formatter #{format}"
163
+ end
164
+
165
+ send "format_#{type}_#{format}", obj, *params
166
+ else
167
+ obj.to_s
168
+ end
169
+ end
170
+
163
171
  # Returns the integer in String form, according to the rules of the locale.
164
172
  # It will also put real typographic minus.
165
173
  def format_integer(integer)
166
174
  str = integer.to_s
167
175
  str[0] = '−' if 0 > integer # Real typographic minus
168
- group = @data['numbers']['group_delimiter']
176
+ group = number_group
169
177
 
170
178
  str.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) do |match|
171
179
  match + group
@@ -175,7 +183,7 @@ module R18n
175
183
  # Returns the float in String form, according to the rules of the locale.
176
184
  # It will also put real typographic minus.
177
185
  def format_float(float)
178
- decimal = @data['numbers']['decimal_separator']
186
+ decimal = number_decimal
179
187
  self.format_integer(float.to_i) + decimal + float.to_s.split('.').last
180
188
  end
181
189
 
@@ -187,19 +195,15 @@ module R18n
187
195
  format.scan(/%[EO]?.|./o) do |c|
188
196
  case c.sub(/^%[EO]?(.)$/o, '%\\1')
189
197
  when '%A'
190
- translated << @data['week']['days'][time.wday]
198
+ translated << wday_names[time.wday]
191
199
  when '%a'
192
- translated << @data['week']['abbrs'][time.wday]
200
+ translated << wday_abbrs[time.wday]
193
201
  when '%B'
194
- translated << @data['months']['names'][time.month - 1]
202
+ translated << month_names[time.month - 1]
195
203
  when '%b'
196
- translated << @data['months']['abbrs'][time.month - 1]
204
+ translated << month_abbrs[time.month - 1]
197
205
  when '%p'
198
- translated << if time.hour < 12
199
- @data['time']['am']
200
- else
201
- @data['time']['pm']
202
- end
206
+ translated << (time.hour < 12 ? time_am : time_pm)
203
207
  else
204
208
  translated << c
205
209
  end
@@ -209,17 +213,17 @@ module R18n
209
213
 
210
214
  # Format +time+ without date. For example, “12:59”.
211
215
  def format_time(time)
212
- strftime(time, @data['time']['time'])
216
+ strftime(time, time_format)
213
217
  end
214
218
 
215
219
  # Format +time+ in human usable form. For example “5 minutes ago” or
216
220
  # “yesterday”. In +now+ you can set base time, which be used to get relative
217
221
  # time. For special cases you can replace it in locale’s class.
218
- def format_time_human(i18n, time, now = Time.now)
222
+ def format_time_human(time, i18n, now = Time.now, *params)
219
223
  minutes = (time - now) / 60.0
220
224
  if time.mday != now.mday and minutes.abs > 720 # 12 hours
221
- format_date_human(i18n, R18n::Utils.to_date(time),
222
- R18n::Utils.to_date(now)) + format_time(time)
225
+ format_date_human(R18n::Utils.to_date(time), i18n,
226
+ R18n::Utils.to_date(now)) + format_time(time)
223
227
  else
224
228
  case minutes
225
229
  when -60..-1
@@ -240,20 +244,20 @@ module R18n
240
244
  end
241
245
 
242
246
  # Format +time+ in compact form. For example, “12/31/09 12:59”.
243
- def format_time_standard(i18n, time)
244
- format_date_standard(i18n, time) + format_time(time)
247
+ def format_time_standard(time, *params)
248
+ format_date_standard(time) + format_time(time)
245
249
  end
246
250
 
247
251
  # Format +time+ in most official form. For example, “December 31st, 2009
248
252
  # 12:59”. For special cases you can replace it in locale’s class.
249
- def format_time_full(i18n, time)
250
- format_date_full(i18n, time) + format_time(time)
253
+ def format_time_full(time, *params)
254
+ format_date_full(time) + format_time(time)
251
255
  end
252
256
 
253
257
  # Format +date+ in human usable form. For example “5 days ago” or
254
258
  # “yesterday”. In +now+ you can set base time, which be used to get relative
255
259
  # time. For special cases you can replace it in locale’s class.
256
- def format_date_human(i18n, date, now = Date.today)
260
+ def format_date_human(date, i18n, now = Date.today, *params)
257
261
  days = (date - now).to_i
258
262
  case days
259
263
  when -6..-2
@@ -267,21 +271,21 @@ module R18n
267
271
  when 2..6
268
272
  i18n.human_time.after_days(days)
269
273
  else
270
- format_date_full(i18n, date, date.year != now.year)
274
+ format_date_full(date, date.year != now.year)
271
275
  end
272
276
  end
273
277
 
274
278
  # Format +date+ in compact form. For example, “12/31/09”.
275
- def format_date_standard(i18n, date)
276
- strftime(date, @data['time']['date'])
279
+ def format_date_standard(date, *params)
280
+ strftime(date, date_format)
277
281
  end
278
282
 
279
283
  # Format +date+ in most official form. For example, “December 31st, 2009”.
280
284
  # For special cases you can replace it in locale’s class. If +year+ is false
281
285
  # date will be without year.
282
- def format_date_full(i18n, date, year = true)
283
- format = @data['time']['full']
284
- format = @data['time']['year'].sub('_', format) if year
286
+ def format_date_full(date, year = true, *params)
287
+ format = full_format
288
+ format = year_format.sub('_', format) if year
285
289
  strftime(date, format)
286
290
  end
287
291
 
@@ -298,4 +302,6 @@ module R18n
298
302
  end
299
303
  end
300
304
  end
305
+
306
+ module Locales; end
301
307
  end
@@ -123,8 +123,6 @@ module R18n
123
123
 
124
124
  class_eval <<-EOS, __FILE__, __LINE__
125
125
  def #{name}(*params)
126
- path = "\#{self.class.name}##{name}"
127
-
128
126
  unlocalized = self.class.unlocalized_getters(#{name.inspect})
129
127
  R18n.get.locales.each do |locale|
130
128
  code = locale.code
@@ -132,11 +130,18 @@ module R18n
132
130
  result = method(unlocalized[code]).#{call}
133
131
  next unless result
134
132
 
133
+ path = "\#{self.class.name}##{name}"
135
134
  type = self.class.translation_types[#{name.inspect}]
136
- return R18n::Filters.process(result, locale, path, type, params)
135
+ if type
136
+ return R18n::Filters.process(type, result, locale, path, params)
137
+ else
138
+ result = TranslatedString.new(result, locale, path)
139
+ return R18n::Filters.process_string(result, path, params)
140
+ end
137
141
  end
138
142
 
139
- R18n::Untranslated.new(path, '#{name}', R18n.get.locales)
143
+ R18n::Untranslated.new("\#{self.class.name}\#", '#{name}',
144
+ R18n.get.locale)
140
145
  end
141
146
  EOS
142
147
 
@@ -34,5 +34,15 @@ module R18n
34
34
  @locale = locale
35
35
  @path = path
36
36
  end
37
+
38
+ # Return self for translated string.
39
+ def |(default)
40
+ self
41
+ end
42
+
43
+ # Return true for translated strings.
44
+ def translated?
45
+ true
46
+ end
37
47
  end
38
48
  end