r18n-core 0.3.2 → 0.4

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 (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