r18n-core 0.2.3 → 0.3
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.
- data/README.rdoc +157 -35
- data/base/cs.yml +34 -0
- data/base/de.yml +24 -0
- data/base/en.yml +24 -0
- data/base/fr.yml +24 -0
- data/base/pl.yml +24 -0
- data/base/ru.yml +30 -0
- data/lib/r18n-core/filters.rb +245 -0
- data/lib/r18n-core/i18n.rb +57 -40
- data/lib/r18n-core/locale.rb +116 -19
- data/lib/r18n-core/translated.rb +186 -0
- data/lib/r18n-core/translation.rb +33 -73
- data/lib/r18n-core/unsupported_locale.rb +27 -9
- data/lib/r18n-core/utils.rb +49 -0
- data/lib/r18n-core/version.rb +1 -1
- data/lib/r18n-core.rb +3 -15
- data/locales/cs.rb +23 -0
- data/locales/cs.yml +26 -0
- data/locales/de.yml +3 -8
- data/locales/en-us.rb +8 -0
- data/locales/en-us.yml +9 -0
- data/locales/en.rb +26 -0
- data/locales/en.yml +3 -8
- data/locales/eo.yml +3 -8
- data/locales/fr.rb +14 -0
- data/locales/fr.yml +3 -8
- data/locales/kk.yml +3 -8
- data/locales/pl.yml +4 -11
- data/locales/ru.yml +3 -8
- data/spec/filters_spec.rb +167 -0
- data/spec/i18n_spec.rb +61 -16
- data/spec/locale_spec.rb +46 -19
- data/spec/locales/cs_spec.rb +22 -0
- data/spec/locales/en-us_spec.rb +14 -0
- data/spec/locales/en_spec.rb +14 -0
- data/spec/locales/fr_spec.rb +10 -0
- data/spec/locales/pl_spec.rb +17 -17
- data/spec/locales/ru_spec.rb +2 -2
- data/spec/r18n_spec.rb +7 -3
- data/spec/spec_helper.rb +2 -0
- data/spec/translated_spec.rb +108 -0
- data/spec/translation_spec.rb +24 -56
- data/spec/translations/extension/{no_TR.yml → no-tr.yml} +0 -0
- data/spec/translations/general/en.yml +15 -2
- data/spec/translations/general/{no_LC.yml → no-lc.yml} +0 -0
- metadata +46 -31
- data/locales/en_US.yml +0 -14
data/lib/r18n-core/i18n.rb
CHANGED
@@ -48,9 +48,9 @@ module R18n
|
|
48
48
|
# i18n.one #=> "Один"
|
49
49
|
# i18n.two #=> "Two"
|
50
50
|
#
|
51
|
-
# i18n.locale
|
52
|
-
# i18n.locale
|
53
|
-
# i18n.locale
|
51
|
+
# i18n.locale.title #=> "Русский"
|
52
|
+
# i18n.locale.code #=> "ru"
|
53
|
+
# i18n.locale.ltr? #=> true
|
54
54
|
#
|
55
55
|
# i18n.l -11000.5 #=> "−11 000,5"
|
56
56
|
# i18n.l Time.now #=> "Вск, 21 сен 2008, 22:10:10 MSD"
|
@@ -91,56 +91,58 @@ module R18n
|
|
91
91
|
attr_reader :locales
|
92
92
|
|
93
93
|
# Dirs with translations files
|
94
|
-
attr_reader :
|
94
|
+
attr_reader :translation_dirs
|
95
95
|
|
96
96
|
# First locale with locale file
|
97
97
|
attr_reader :locale
|
98
98
|
|
99
|
-
# Create i18n for +locales+ with translations from +
|
99
|
+
# Create i18n for +locales+ with translations from +translation_dirs+ and
|
100
100
|
# locales data. Translations will be also loaded for default locale,
|
101
101
|
# +sublocales+ from first in +locales+ and general languages for dialects
|
102
102
|
# (it will load +fr+ for +fr_CA+ too).
|
103
103
|
#
|
104
104
|
# +Locales+ must be a locale code (RFC 3066) or array, ordered by priority.
|
105
|
-
# +
|
106
|
-
def initialize(locales,
|
105
|
+
# +Translation_dirs+ must be a string with path or array.
|
106
|
+
def initialize(locales, translation_dirs = nil)
|
107
107
|
locales = [locales] if locales.is_a? String
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
locales << @@default
|
112
|
-
if @locales.first.kind_of? Locale
|
113
|
-
locales += @locales.first['sublocales']
|
109
|
+
if not locales.empty? and Locale.exists? locales.first
|
110
|
+
locales += Locale.load(locales.first)['sublocales']
|
114
111
|
end
|
112
|
+
locales << @@default
|
115
113
|
locales.each_with_index do |locale, i|
|
116
|
-
if
|
117
|
-
locales.insert(i + 1, locale[
|
114
|
+
if locale =~ /[_-]/
|
115
|
+
locales.insert(i + 1, locale.match(/([^_-]+)[_-]/)[1])
|
118
116
|
end
|
119
117
|
end
|
120
118
|
locales.uniq!
|
119
|
+
@locales = locales.map { |i| Locale.load(i) }
|
121
120
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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)
|
127
128
|
end
|
128
129
|
|
129
|
-
|
130
|
-
|
131
|
-
@
|
130
|
+
@locale = @locales.first
|
131
|
+
unless @locale.supported?
|
132
|
+
@locales.each do |locale|
|
133
|
+
if locale.supported?
|
134
|
+
@locale.base = locale
|
135
|
+
break
|
136
|
+
end
|
137
|
+
end
|
132
138
|
end
|
133
139
|
end
|
134
140
|
|
135
141
|
# Return Hash with titles (or code for unsupported locales) for available
|
136
142
|
# translations.
|
137
143
|
def translations
|
138
|
-
Translation.available(@
|
139
|
-
all[code] =
|
140
|
-
Locale.load(code)['title']
|
141
|
-
else
|
142
|
-
code
|
143
|
-
end
|
144
|
+
Translation.available(@translation_dirs).inject({}) do |all, code|
|
145
|
+
all[code] = Locale.load(code).title
|
144
146
|
all
|
145
147
|
end
|
146
148
|
end
|
@@ -148,22 +150,37 @@ module R18n
|
|
148
150
|
# Convert +object+ to String, according to the rules of the current locale.
|
149
151
|
# It support Fixnum, Bignum, Float, Time, Date and DateTime.
|
150
152
|
#
|
151
|
-
# For time classes you can set +format+ in
|
152
|
-
#
|
153
|
-
# <tt>:
|
154
|
-
#
|
155
|
-
#
|
156
|
-
|
153
|
+
# For time classes you can set +format+ in standard +strftime+ form,
|
154
|
+
# <tt>:full</tt> (“01 Jule, 2009”), <tt>:human</tt> (“yesterday”),
|
155
|
+
# <tt>:standard</tt> (“07/01/09”) or <tt>:month</tt> for standalone month
|
156
|
+
# name. Default format is <tt>:standard</tt>.
|
157
|
+
#
|
158
|
+
# i18n.l -12000.5 #=> "−12,000.5"
|
159
|
+
# i18n.l Time.now #=> "07/01/09 12:59"
|
160
|
+
# i18n.l Time.now.to_date #=> "07/01/09"
|
161
|
+
# i18n.l Time.now, :human #=> "now"
|
162
|
+
# i18n.l Time.now, :full #=> "Jule 1st, 2009 12:59"
|
163
|
+
def localize(object, format = nil, *params)
|
157
164
|
if object.is_a? Integer
|
158
165
|
locale.format_integer(object)
|
159
166
|
elsif object.is_a? Float
|
160
167
|
locale.format_float(object)
|
161
|
-
elsif object.is_a? Time or object.is_a? DateTime
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
167
184
|
end
|
168
185
|
end
|
169
186
|
alias :l :localize
|
data/lib/r18n-core/locale.rb
CHANGED
@@ -38,9 +38,9 @@ module R18n
|
|
38
38
|
# Get Russian locale and print it information
|
39
39
|
#
|
40
40
|
# ru = R18n::Locale.load('ru')
|
41
|
-
# ru
|
42
|
-
# ru
|
43
|
-
# ru
|
41
|
+
# ru.title #=> "Русский"
|
42
|
+
# ru.code #=> "ru"
|
43
|
+
# ru.ltr? #=> true
|
44
44
|
#
|
45
45
|
# == Available data
|
46
46
|
#
|
@@ -52,6 +52,12 @@ module R18n
|
|
52
52
|
#
|
53
53
|
# You can see more available data about locale in samples in
|
54
54
|
# <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+).
|
55
61
|
class Locale
|
56
62
|
LOCALES_DIR = Pathname(__FILE__).dirname.expand_path + '../../locales/'
|
57
63
|
|
@@ -69,11 +75,14 @@ module R18n
|
|
69
75
|
|
70
76
|
# Load locale by RFC 3066 +code+
|
71
77
|
def self.load(code)
|
78
|
+
code = code.to_s
|
72
79
|
code.delete! '/'
|
73
80
|
code.delete! '\\'
|
74
81
|
code.delete! ';'
|
82
|
+
original = code
|
83
|
+
code = code.downcase
|
75
84
|
|
76
|
-
return UnsupportedLocale.new(
|
85
|
+
return UnsupportedLocale.new(original) unless exists? code
|
77
86
|
|
78
87
|
data = {}
|
79
88
|
klass = R18n::Locale
|
@@ -85,7 +94,8 @@ module R18n
|
|
85
94
|
|
86
95
|
if R18n::Locale == klass and File.exists? LOCALES_DIR + "#{code}.rb"
|
87
96
|
require LOCALES_DIR + "#{code}.rb"
|
88
|
-
|
97
|
+
name = code.gsub(/[\w\d]+/) { |i| i.capitalize }.gsub('-', '')
|
98
|
+
klass = eval 'R18n::Locales::' + name
|
89
99
|
end
|
90
100
|
|
91
101
|
loaded = YAML.load_file(file)
|
@@ -114,6 +124,21 @@ module R18n
|
|
114
124
|
def initialize(data)
|
115
125
|
@data = data
|
116
126
|
end
|
127
|
+
|
128
|
+
# Locale RFC 3066 code.
|
129
|
+
def code
|
130
|
+
@data['code']
|
131
|
+
end
|
132
|
+
|
133
|
+
# Locale title.
|
134
|
+
def title
|
135
|
+
@data['title']
|
136
|
+
end
|
137
|
+
|
138
|
+
# Is locale has left-to-right write direction.
|
139
|
+
def ltr?
|
140
|
+
@data['direction'] == 'ltr'
|
141
|
+
end
|
117
142
|
|
118
143
|
# Get information about locale
|
119
144
|
def [](name)
|
@@ -122,12 +147,17 @@ module R18n
|
|
122
147
|
|
123
148
|
# Is another locale has same code
|
124
149
|
def ==(locale)
|
125
|
-
|
150
|
+
code.downcase == locale.code.downcase
|
151
|
+
end
|
152
|
+
|
153
|
+
# Is locale has information file. In this class always return true.
|
154
|
+
def supported?
|
155
|
+
true
|
126
156
|
end
|
127
157
|
|
128
158
|
# Human readable locale code and title
|
129
159
|
def inspect
|
130
|
-
"Locale #{
|
160
|
+
"Locale #{code} (#{title})"
|
131
161
|
end
|
132
162
|
|
133
163
|
# Returns the integer in String form, according to the rules of the locale.
|
@@ -151,19 +181,8 @@ module R18n
|
|
151
181
|
|
152
182
|
# Same that <tt>Time.strftime</tt>, but translate months and week days
|
153
183
|
# names. In +time+ you can use Time, DateTime or Date object. In +format+
|
154
|
-
# you can use
|
155
|
-
# <tt>Time.strftime</tt> docs) or Symbol with format from locale file
|
156
|
-
# (<tt>:month</tt>, <tt>:time</tt>, <tt>:date</tt>, <tt>:short_data</tt>,
|
157
|
-
# <tt>:long_data</tt>, <tt>:datetime</tt>, <tt>:short_datetime</tt> or
|
158
|
-
# <tt>:long_datetime</tt>).
|
184
|
+
# you can use standard +strftime+ format.
|
159
185
|
def strftime(time, format)
|
160
|
-
if format.is_a? Symbol
|
161
|
-
if :month == format
|
162
|
-
return @data['months']['standalone'][time.month - 1]
|
163
|
-
end
|
164
|
-
format = @data['formats'][format.to_s]
|
165
|
-
end
|
166
|
-
|
167
186
|
translated = ''
|
168
187
|
format.scan(/%[EO]?.|./o) do |c|
|
169
188
|
case c.sub(/^%[EO]?(.)$/o, '%\\1')
|
@@ -187,6 +206,84 @@ module R18n
|
|
187
206
|
end
|
188
207
|
time.strftime(translated)
|
189
208
|
end
|
209
|
+
|
210
|
+
# Format +time+ without date. For example, “12:59”.
|
211
|
+
def format_time(time)
|
212
|
+
strftime(time, @data['time']['time'])
|
213
|
+
end
|
214
|
+
|
215
|
+
# Format +time+ in human usable form. For example “5 minutes ago” or
|
216
|
+
# “yesterday”. In +now+ you can set base time, which be used to get relative
|
217
|
+
# time. For special cases you can replace it in locale’s class.
|
218
|
+
def format_time_human(i18n, time, now = Time.now)
|
219
|
+
minutes = (time - now) / 60.0
|
220
|
+
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)
|
223
|
+
else
|
224
|
+
case minutes
|
225
|
+
when -60..-1
|
226
|
+
i18n.human_time.minutes_ago(minutes.round.abs)
|
227
|
+
when 1..60
|
228
|
+
i18n.human_time.after_minutes(minutes.round)
|
229
|
+
when -1..1
|
230
|
+
i18n.human_time.now
|
231
|
+
else
|
232
|
+
hours = (minutes / 60.0).abs.floor
|
233
|
+
if time > now
|
234
|
+
i18n.human_time.after_hours(hours)
|
235
|
+
else
|
236
|
+
i18n.human_time.hours_ago(hours)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# 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)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Format +time+ in most official form. For example, “December 31st, 2009
|
248
|
+
# 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)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Format +date+ in human usable form. For example “5 days ago” or
|
254
|
+
# “yesterday”. In +now+ you can set base time, which be used to get relative
|
255
|
+
# time. For special cases you can replace it in locale’s class.
|
256
|
+
def format_date_human(i18n, date, now = Date.today)
|
257
|
+
days = (date - now).to_i
|
258
|
+
case days
|
259
|
+
when -6..-2
|
260
|
+
i18n.human_time.days_ago(days.abs)
|
261
|
+
when -1
|
262
|
+
i18n.human_time.yesterday
|
263
|
+
when 0
|
264
|
+
i18n.human_time.today
|
265
|
+
when 1
|
266
|
+
i18n.human_time.tomorrow
|
267
|
+
when 2..6
|
268
|
+
i18n.human_time.after_days(days)
|
269
|
+
else
|
270
|
+
format_date_full(i18n, date, date.year != now.year)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Format +date+ in compact form. For example, “12/31/09”.
|
275
|
+
def format_date_standard(i18n, date)
|
276
|
+
strftime(date, @data['time']['date'])
|
277
|
+
end
|
278
|
+
|
279
|
+
# Format +date+ in most official form. For example, “December 31st, 2009”.
|
280
|
+
# For special cases you can replace it in locale’s class. If +year+ is false
|
281
|
+
# 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
|
285
|
+
strftime(date, format)
|
286
|
+
end
|
190
287
|
|
191
288
|
# Return pluralization type for +n+ items. This is simple form. For special
|
192
289
|
# cases you can replace it in locale’s class.
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
=begin
|
3
|
+
Add i18n support to any class.
|
4
|
+
|
5
|
+
Copyright (C) 2008 Andrey “A.I.” Sitnik <andrey@sitnik.ru>
|
6
|
+
|
7
|
+
This program is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
This program is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU Lesser General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU Lesser General Public License
|
18
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
=end
|
20
|
+
|
21
|
+
module R18n
|
22
|
+
# Module to add i18n support to any class. It will be useful for ORM or
|
23
|
+
# R18n plugin with out-of-box i18n support.
|
24
|
+
#
|
25
|
+
# Module can add proxy-methods to find translation in object methods. For
|
26
|
+
# example, if you class have +title_en+ and +title_ru+ methods, you can add
|
27
|
+
# proxy-method +title+, which will use +title_ru+ for Russian users and
|
28
|
+
# +title_en+ for English:
|
29
|
+
#
|
30
|
+
# require 'r18n-core/translated'
|
31
|
+
#
|
32
|
+
# class Product
|
33
|
+
# include DataMapper::Resource
|
34
|
+
# property :title_ru, String
|
35
|
+
# property :title_en, String
|
36
|
+
# property :desciption_ru, String
|
37
|
+
# property :desciption_en, String
|
38
|
+
#
|
39
|
+
# include R18n::Translated
|
40
|
+
# translations :title, :desciption
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # User know only Russian
|
44
|
+
# R18n.set(R18n::I18n.new('ru'))
|
45
|
+
#
|
46
|
+
# product.title #=> Untranslated
|
47
|
+
#
|
48
|
+
# # Set value to English (default) title
|
49
|
+
# product.title_en = "Anthrax"
|
50
|
+
# product.title #=> "Anthrax"
|
51
|
+
# product.title.locale #=> Locale en (English)
|
52
|
+
#
|
53
|
+
# # Set value to title on user locale (Russian)
|
54
|
+
# product.title = "Сибирская язва"
|
55
|
+
# product.title #=> "Сибирская язва"
|
56
|
+
# product.title.locale #=> Locale ru (Russian)
|
57
|
+
#
|
58
|
+
# product.title_en #=> "Anthrax"
|
59
|
+
# product.title_ru #=> "Сибирская язва"
|
60
|
+
#
|
61
|
+
# Proxy-method support all funtion from I18n: global and type filters,
|
62
|
+
# pluralization, variables. It also return TranslatedString or Untranslated.
|
63
|
+
#
|
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.
|
68
|
+
#
|
69
|
+
# See R18n::Translated::Base for class method documentation.
|
70
|
+
#
|
71
|
+
# == Options
|
72
|
+
# You can set option for proxy-method as Hash with keys;
|
73
|
+
# * +type+ – YAML type for filters. For example, "markdown" or "escape_html".
|
74
|
+
# * +methods+ – manual method map as Hash of locale codes to method names.
|
75
|
+
# * +no_params+ – set it to true if you proxy-method must send it parameters
|
76
|
+
# only to filters.
|
77
|
+
# * +no_write+ – set it to true if you don’t want to create proxy-setters.
|
78
|
+
#
|
79
|
+
# Method +translation+ will be more useful for options:
|
80
|
+
#
|
81
|
+
# translation :title, :methods => {:ru => :russian, :en => :english}
|
82
|
+
module Translated
|
83
|
+
class << self
|
84
|
+
def included(base) #:nodoc:
|
85
|
+
base.send :extend, Base
|
86
|
+
base.instance_variable_set '@unlocalized_getters', {}
|
87
|
+
base.instance_variable_set '@unlocalized_setters', {}
|
88
|
+
base.instance_variable_set '@translation_types', {}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Module with class methods, which be added after R18n::Translated include.
|
93
|
+
module Base
|
94
|
+
# Hash of translation method names to it type for filters.
|
95
|
+
attr_reader :translation_types
|
96
|
+
|
97
|
+
# Add several proxy +methods+. See R18n::Translated for description.
|
98
|
+
# It’s more compact, that +translation+.
|
99
|
+
#
|
100
|
+
# translations :title, :keywords, [:desciption, {:type => 'markdown'}]
|
101
|
+
def translations(*methods)
|
102
|
+
methods.each do |method|
|
103
|
+
translation(*method)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Add proxy-method +name+. See R18n::Translated for description.
|
108
|
+
# It’s more useful to set options.
|
109
|
+
#
|
110
|
+
# translation :desciption, :type => 'markdown'
|
111
|
+
def translation(name, options = {})
|
112
|
+
if options[:methods]
|
113
|
+
@unlocalized_getters[name] = Hash[
|
114
|
+
options[:methods].map { |l, i| [l.to_s, i.to_s] } ]
|
115
|
+
unless options[:no_write]
|
116
|
+
@unlocalized_setters[name] = Hash[
|
117
|
+
options[:methods].map { |l, i| [l.to_s, i.to_s + '='] } ]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
@translation_types[name] = options[:type]
|
122
|
+
call = options[:no_params] ? 'call' : 'call(*params)'
|
123
|
+
|
124
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
125
|
+
def #{name}(*params)
|
126
|
+
path = "\#{self.class.name}##{name}"
|
127
|
+
|
128
|
+
unlocalized = self.class.unlocalized_getters(#{name.inspect})
|
129
|
+
R18n.get.locales.each do |locale|
|
130
|
+
code = locale.code
|
131
|
+
next unless unlocalized.has_key? code
|
132
|
+
result = method(unlocalized[code]).#{call}
|
133
|
+
next unless result
|
134
|
+
|
135
|
+
type = self.class.translation_types[#{name.inspect}]
|
136
|
+
return R18n::Filters.process(result, locale, path, type, params)
|
137
|
+
end
|
138
|
+
|
139
|
+
R18n::Untranslated.new(path, '#{name}')
|
140
|
+
end
|
141
|
+
EOS
|
142
|
+
|
143
|
+
unless options[:no_write]
|
144
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
145
|
+
def #{name}=(*params)
|
146
|
+
unlocalized = self.class.unlocalized_setters(#{name.inspect})
|
147
|
+
R18n.get.locales.each do |locale|
|
148
|
+
code = locale.code
|
149
|
+
next unless unlocalized.has_key? code
|
150
|
+
return method(unlocalized[code]).call(*params)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
EOS
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Return Hash of locale code to getter method for proxy +method+. If you
|
158
|
+
# didn’t set map in +translation+ option +methods+, it will be detect
|
159
|
+
# automatically.
|
160
|
+
def unlocalized_getters(method)
|
161
|
+
matcher = Regexp.new('^' + Regexp.escape(method.to_s) + '_(.*[^=])$')
|
162
|
+
unless @unlocalized_getters.has_key? method
|
163
|
+
@unlocalized_getters[method] = {}
|
164
|
+
self.instance_methods.reject { |i| not i =~ matcher }.each do |i|
|
165
|
+
@unlocalized_getters[method][i.to_s.match(matcher)[1]] = i.to_s
|
166
|
+
end
|
167
|
+
end
|
168
|
+
@unlocalized_getters[method]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Return Hash of locale code to setter method for proxy +method+. If you
|
172
|
+
# didn’t set map in +translation+ option +methods+, it will be detect
|
173
|
+
# automatically.
|
174
|
+
def unlocalized_setters(method)
|
175
|
+
matcher = Regexp.new('^' + Regexp.escape(method.to_s) + '_(.*)=$')
|
176
|
+
unless @unlocalized_setters.has_key? method
|
177
|
+
@unlocalized_setters[method] = {}
|
178
|
+
self.instance_methods.reject { |i| not i =~ matcher }.each do |i|
|
179
|
+
@unlocalized_setters[method][i.to_s.match(matcher)[1]] = i.to_s
|
180
|
+
end
|
181
|
+
end
|
182
|
+
@unlocalized_setters[method]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|