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/README.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
R18n is a i18n tool to translate your Ruby application in several languages.
|
4
4
|
|
5
|
-
Use <tt>
|
5
|
+
Use <tt>sinatra-r18n</tt> or teamon’s +merb_i18n+ to localize Web applications and
|
6
6
|
<tt>r18n-desktop</tt> to localize desktop application.
|
7
7
|
|
8
8
|
== Features
|
@@ -15,36 +15,14 @@ Use <tt>merb_r18n</tt> to localize Merb Web application and
|
|
15
15
|
month and week days name and give other locale information.
|
16
16
|
* It has translation for commons words, like “OK”, “Cancel”, etc.
|
17
17
|
* It storage translation in rich YAML format. You can put procedures and
|
18
|
-
pluralization (“1 comment”, “5 comments”)
|
18
|
+
pluralization (“1 comment”, “5 comments”) or create you own filters.
|
19
19
|
* It can translate Web and desktop applications.
|
20
20
|
|
21
21
|
== Usage
|
22
22
|
|
23
|
-
=== Locale
|
24
|
-
All supported locales are storage in R18n gem at +locales+ dir. If you want to
|
25
|
-
add your locale, please write me to andrey@sitnik.ru.
|
26
|
-
|
27
|
-
To get information about locale create R18n::Locale instance:
|
28
|
-
|
29
|
-
locale = R18n::Locale.load('en')
|
30
|
-
|
31
|
-
You can get from locale:
|
32
|
-
* Locale title and RFC 3066 code:
|
33
|
-
|
34
|
-
locale['title'] #=> "English"
|
35
|
-
locale['code'] #=> "en"
|
36
|
-
|
37
|
-
* Language direction (left to right, or right to left for Arabic and Hebrew):
|
38
|
-
|
39
|
-
locale['direction'] #=> "ltr"
|
40
|
-
|
41
|
-
* Week start day ("sunday" or "monday")
|
42
|
-
|
43
|
-
locale['week']['start'] #=> "sunday"
|
44
|
-
|
45
23
|
=== Translation
|
46
24
|
Translation files use YAML format and has name like en.yml (English) or
|
47
|
-
|
25
|
+
en-us.yml (USA English dialect) with language/country code (RFC 3066).
|
48
26
|
|
49
27
|
In translation you can use:
|
50
28
|
* Strings
|
@@ -58,8 +36,9 @@ In translation you can use:
|
|
58
36
|
0: No robots
|
59
37
|
1: One robot
|
60
38
|
n: %1 robots
|
61
|
-
*
|
62
|
-
|
39
|
+
* Filters
|
40
|
+
filtered: !!custom_type
|
41
|
+
This content will be processed by filter
|
63
42
|
|
64
43
|
To get translated string use method with key name or square brackets [] for
|
65
44
|
keys, which is same with Object methods (+class+, +inspect+, etc):
|
@@ -105,6 +84,77 @@ See <tt>base/</tt> in dir in gem.
|
|
105
84
|
i18n.cancel #=> "Cancel"
|
106
85
|
i18n.delete #=> "Delete"
|
107
86
|
|
87
|
+
=== Filters
|
88
|
+
You can also add you own filter for translations: escape HTML entries, convert
|
89
|
+
from Markdown syntax, etc.
|
90
|
+
|
91
|
+
R18n::Filters.add('custom_type', :filter_name) do |content, config, replace|
|
92
|
+
content.gsub(' ', replace)
|
93
|
+
end
|
94
|
+
R18n::Filters.add('custom_type') do |content, config, replace|
|
95
|
+
content + '!'
|
96
|
+
end
|
97
|
+
|
98
|
+
i18n.filtered('_') #=> "This_content_will_be_processed_by_filter!"
|
99
|
+
|
100
|
+
You can also add global filters for all translated strings:
|
101
|
+
|
102
|
+
R18n::Filters.add(String, :capitalize_ruby) do |content, config|
|
103
|
+
content.gsub(/ruby/i) { |i| i.swapcase }
|
104
|
+
end
|
105
|
+
|
106
|
+
==== HTML Escape
|
107
|
+
R18n contain 2 filters to escape HTML entries: by YAML type and global. If you
|
108
|
+
need to escape HTML in some translations, just set <tt>!!escape</tt> YAML type:
|
109
|
+
|
110
|
+
greater: !!escape
|
111
|
+
1 < 2 is true
|
112
|
+
|
113
|
+
i18n.greater #=> "1 < 2 is true"
|
114
|
+
|
115
|
+
If you develop web application and want to escape HTML in all translations, just
|
116
|
+
activate global escape filter:
|
117
|
+
|
118
|
+
R18n::Filters.on(:global_escape_html)
|
119
|
+
|
120
|
+
If you enable global HTML escape, you may use <tt>!!html</tt> YAML type to
|
121
|
+
disable escaping in some special value.
|
122
|
+
|
123
|
+
warning: !!html
|
124
|
+
<b>Warning</b>
|
125
|
+
|
126
|
+
R18n::Filters.on(:global_escape_html)
|
127
|
+
i18n.warning #=> "<b>Warning</b>"
|
128
|
+
|
129
|
+
==== Markdown
|
130
|
+
To use Markdown in your translations you must install maruku gem:
|
131
|
+
|
132
|
+
hi: !!markdown
|
133
|
+
**Hi**, people!
|
134
|
+
|
135
|
+
i18n.hi #=> "<p><strong>Hi</strong>, people!</p>"
|
136
|
+
|
137
|
+
|
138
|
+
==== Textile
|
139
|
+
To use Textile in your translations you must install RedCloth gem:
|
140
|
+
|
141
|
+
alarm: !!textile
|
142
|
+
It will delete _all_ users!
|
143
|
+
|
144
|
+
i18n.alarm #=> "<p>It will delete <em>all</em> users!</p>"
|
145
|
+
|
146
|
+
==== Lambdas
|
147
|
+
You can use lambdas in your translations.
|
148
|
+
|
149
|
+
sum: !!proc |x, y| x + y
|
150
|
+
|
151
|
+
i18n.sum(1, 2) #=> 3
|
152
|
+
|
153
|
+
If it isn’t secure in your application (for example, user can change
|
154
|
+
translations), you can disable it:
|
155
|
+
|
156
|
+
R18n::Filters.off(:procedure)
|
157
|
+
|
108
158
|
=== Localization
|
109
159
|
You can print number and float according to the rules of the user locale:
|
110
160
|
|
@@ -116,18 +166,90 @@ non-break thin spaces (for locale, which use it as digit separator).
|
|
116
166
|
You can translate months and week days names in Time, Date and DateTime by
|
117
167
|
+strftime+ method:
|
118
168
|
|
119
|
-
i18n.l Time.now,
|
169
|
+
i18n.l Time.now, '%B' #=> "September"
|
170
|
+
|
171
|
+
R18n has some time formats for locales: <tt>:human</tt>, <tt>:full</tt> and
|
172
|
+
<tt>:standard</tt> (by default):
|
173
|
+
|
174
|
+
i18n.l Time.now, :human #=> "now"
|
175
|
+
i18n.l Time.now, :full #=> "August 9th, 2009 21:47"
|
176
|
+
i18n.l Time.now #=> "08/09/2009 21:41"
|
177
|
+
i18n.l Time.now.to_date #=> "08/09/2009"
|
178
|
+
|
179
|
+
=== Model
|
180
|
+
You can add i18n support to any classes, including ORM models:
|
181
|
+
|
182
|
+
require 'r18n-core/translated'
|
183
|
+
|
184
|
+
class Product
|
185
|
+
include DataMapper::Resource
|
186
|
+
property :title_ru, String
|
187
|
+
property :title_en, String
|
188
|
+
|
189
|
+
include R18n::Translated
|
190
|
+
translations :title
|
191
|
+
end
|
192
|
+
|
193
|
+
# For example, user know only Russian
|
194
|
+
|
195
|
+
# Set value to English (default) title
|
196
|
+
product.title_en = "Anthrax"
|
197
|
+
product.title #=> "Anthrax"
|
198
|
+
|
199
|
+
# Set value to title on user locale (Russian)
|
200
|
+
product.title = "Сибирская язва"
|
201
|
+
product.title #=> "Сибирская язва"
|
202
|
+
|
203
|
+
product.title_en #=> "Anthrax"
|
204
|
+
product.title_ru #=> "Сибирская язва"
|
205
|
+
|
206
|
+
See R18n::Translated for documentation.
|
120
207
|
|
121
|
-
|
122
|
-
|
123
|
-
|
208
|
+
=== Locale
|
209
|
+
All supported locales are storage in R18n gem at +locales+ dir. If you want to
|
210
|
+
add your locale, please write me to andrey@sitnik.ru.
|
124
211
|
|
125
|
-
|
212
|
+
To get information about locale create R18n::Locale instance:
|
213
|
+
|
214
|
+
locale = R18n::Locale.load('en')
|
126
215
|
|
127
|
-
|
128
|
-
|
216
|
+
You can get from locale:
|
217
|
+
* Locale title and RFC 3066 code:
|
218
|
+
|
219
|
+
locale.title #=> "English"
|
220
|
+
locale.code #=> "en"
|
221
|
+
|
222
|
+
* Language direction (left to right, or right to left for Arabic and Hebrew):
|
223
|
+
|
224
|
+
locale.ltr? #=> true
|
225
|
+
|
226
|
+
* Week start day ("sunday" or "monday"):
|
227
|
+
|
228
|
+
locale['week']['start'] #=> "sunday"
|
129
229
|
|
130
|
-
|
230
|
+
== Add locale
|
231
|
+
If R18n hasn’t locale file for your language, please add it. It’s very simple:
|
232
|
+
* Create in locales/ file _code_.yml for your language and describe locale.
|
233
|
+
Just copy from another locale and change different values.
|
234
|
+
* If your language is dialect or base on another (as American English (en-US)
|
235
|
+
base on English (en)) write <tt>include: _base_locale_</tt> and similar
|
236
|
+
values can be deleted.
|
237
|
+
* If in your country people mostly know another language (like in exUSSR
|
238
|
+
countries people know Russian), write
|
239
|
+
<tt>sublocales: [_another_locale_, en]</tt>. Else write only:
|
240
|
+
<tt>sublocales: [en]</tt>. For dialect put base locale to +sublocales+ too.
|
241
|
+
* Create in base/ file _code_.yml for your language and translate base messages.
|
242
|
+
Just copy file from language, which you know, and rewrite values.
|
243
|
+
* If you language need some special logic (for example, different pluralization
|
244
|
+
or time formatters) you can change Locale class. Create in locales/ file
|
245
|
+
_code_.rb and write R18n::Locales::_Code_ class, which must extend
|
246
|
+
R18n::Locale.
|
247
|
+
* Push files by GitHub (http://github.com/ai/r18n) or just write e-mail with
|
248
|
+
this files to me (andrey@sitnik.ru).
|
249
|
+
|
250
|
+
_Code_ is RFC 3066 code for your language (for example, “en” for English and
|
251
|
+
“fr_CA” for Canadian French). You can send to my e-mail any questions (on
|
252
|
+
http://sitnik.ru you find another contact addresses).
|
131
253
|
|
132
254
|
== License
|
133
255
|
R18n is licensed under the GNU Lesser General Public License version 3.
|
data/base/cs.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
ok: OK
|
2
|
+
save: Uložit
|
3
|
+
cancel: Zrušit
|
4
|
+
yes: Ano
|
5
|
+
no: Ne
|
6
|
+
exit: Konec
|
7
|
+
delete: Smazat
|
8
|
+
|
9
|
+
human_time:
|
10
|
+
after_days: !!pl
|
11
|
+
1: za %1 den
|
12
|
+
2: za %1 dny
|
13
|
+
n: za %1 dnů
|
14
|
+
tomorrow: zítra
|
15
|
+
after_hours: !!pl
|
16
|
+
1: za %1 hodinu
|
17
|
+
2: za %1 hodiny
|
18
|
+
n: za %1 hodin
|
19
|
+
after_minutes: !!pl
|
20
|
+
1: za %1 minutu
|
21
|
+
2: za %1 minuty
|
22
|
+
n: za %1 minut
|
23
|
+
now: teď
|
24
|
+
today: dnes
|
25
|
+
minutes_ago: !!pl
|
26
|
+
1: před %1 minutou
|
27
|
+
n: před %1 minutami
|
28
|
+
hours_ago: !!pl
|
29
|
+
1: před %1 hodinou
|
30
|
+
n: před %1 hodinami
|
31
|
+
yesterday: včera
|
32
|
+
days_ago: !!pl
|
33
|
+
1: před %1 dnem
|
34
|
+
n: před %1 dny
|
data/base/de.yml
CHANGED
@@ -5,3 +5,27 @@ yes: Ja
|
|
5
5
|
no: Nein
|
6
6
|
exit: Ausgang
|
7
7
|
delete: Löschen
|
8
|
+
|
9
|
+
human_time:
|
10
|
+
after_days: !!pl
|
11
|
+
1: nach %1 Tag
|
12
|
+
n: nach %1 Tagen
|
13
|
+
tomorrow: morgen
|
14
|
+
after_hours: !!pl
|
15
|
+
1: nach %1 Stunde
|
16
|
+
n: nach %1 Stunden
|
17
|
+
after_minutes: !!pl
|
18
|
+
1: nach %1 Minute
|
19
|
+
n: nach %1 Minuten
|
20
|
+
now: jetzt
|
21
|
+
today: heute
|
22
|
+
minutes_ago: !!pl
|
23
|
+
1: %1 Minute vor
|
24
|
+
n: %1 Minuten vor
|
25
|
+
hours_ago: !!pl
|
26
|
+
1: %1 Stunde vor
|
27
|
+
n: %1 Stunden vor
|
28
|
+
yesterday: gestern
|
29
|
+
days_ago: !!pl
|
30
|
+
1: %1 Tag vor
|
31
|
+
n: %1 Tagen vor
|
data/base/en.yml
CHANGED
@@ -5,3 +5,27 @@ yes: Yes
|
|
5
5
|
no: No
|
6
6
|
exit: Exit
|
7
7
|
delete: Delete
|
8
|
+
|
9
|
+
human_time:
|
10
|
+
after_days: !!pl
|
11
|
+
1: after %1 day
|
12
|
+
n: after %1 days
|
13
|
+
tomorrow: tomorrow
|
14
|
+
after_hours: !!pl
|
15
|
+
1: after %1 hour
|
16
|
+
n: after %1 hours
|
17
|
+
after_minutes: !!pl
|
18
|
+
1: after %1 minute
|
19
|
+
n: after %1 minutes
|
20
|
+
now: now
|
21
|
+
today: today
|
22
|
+
minutes_ago: !!pl
|
23
|
+
1: %1 minute ago
|
24
|
+
n: %1 minutes ago
|
25
|
+
hours_ago: !!pl
|
26
|
+
1: %1 hour ago
|
27
|
+
n: %1 hours ago
|
28
|
+
yesterday: yesterday
|
29
|
+
days_ago: !!pl
|
30
|
+
1: %1 day ago
|
31
|
+
n: %1 days ago
|
data/base/fr.yml
CHANGED
@@ -5,3 +5,27 @@ yes: Oui
|
|
5
5
|
no: Non
|
6
6
|
exit: Sortie
|
7
7
|
delete: Supprimer
|
8
|
+
|
9
|
+
human_time:
|
10
|
+
after_days: !!pl
|
11
|
+
1: dans une jour
|
12
|
+
n: dans %1 jours
|
13
|
+
tomorrow: demain
|
14
|
+
after_hours: !!pl
|
15
|
+
1: dans une heure
|
16
|
+
n: dans %1 heures
|
17
|
+
after_minutes: !!pl
|
18
|
+
1: dans une minute
|
19
|
+
n: dans %1 minutes
|
20
|
+
now: maintenant
|
21
|
+
today: "aujourd'hui"
|
22
|
+
minutes_ago: !!pl
|
23
|
+
1: il ya une minute
|
24
|
+
n: il ya %1 minutes
|
25
|
+
hours_ago: !!pl
|
26
|
+
1: il ya une heure
|
27
|
+
n: il ya %1 heures
|
28
|
+
yesterday: hier
|
29
|
+
days_ago: !!pl
|
30
|
+
1: il ya une jour
|
31
|
+
n: il ya %1 jours
|
data/base/pl.yml
CHANGED
@@ -5,3 +5,27 @@ yes: Tak
|
|
5
5
|
no: Nie
|
6
6
|
exit: Wyjście
|
7
7
|
delete: Usuń
|
8
|
+
|
9
|
+
human_time:
|
10
|
+
after_days: !!pl
|
11
|
+
1: po %1 dniu
|
12
|
+
n: po %1 dniach
|
13
|
+
tomorrow: jutro
|
14
|
+
after_hours: !!pl
|
15
|
+
1: po %1 godzinie
|
16
|
+
n: po %1 godzinach
|
17
|
+
after_minutes: !!pl
|
18
|
+
1: po %1 minucie
|
19
|
+
n: po %1 minutach
|
20
|
+
now: teraz
|
21
|
+
today: dzisiaj
|
22
|
+
minutes_ago: !!pl
|
23
|
+
1: %1 minutę temu
|
24
|
+
n: %1 minut temu
|
25
|
+
hours_ago: !!pl
|
26
|
+
1: %1 godzinę temu
|
27
|
+
n: %1 godzin temu
|
28
|
+
yesterday: wczoraj
|
29
|
+
days_ago: !!pl
|
30
|
+
1: %1 dzień temu
|
31
|
+
n: %1 dni temu
|
data/base/ru.yml
CHANGED
@@ -5,3 +5,33 @@ yes: Да
|
|
5
5
|
no: Нет
|
6
6
|
exit: Выход
|
7
7
|
delete: Удалить
|
8
|
+
|
9
|
+
human_time:
|
10
|
+
after_days: !!pl
|
11
|
+
1: через %1 день
|
12
|
+
2: через %1 дня
|
13
|
+
n: через %1 дней
|
14
|
+
tomorrow: завтра
|
15
|
+
after_hours: !!pl
|
16
|
+
1: через %1 час
|
17
|
+
2: через %1 часа
|
18
|
+
n: через %1 часов
|
19
|
+
after_minutes: !!pl
|
20
|
+
1: через %1 минуту
|
21
|
+
2: через %1 минуты
|
22
|
+
n: через %1 минут
|
23
|
+
now: сейчас
|
24
|
+
today: сегодня
|
25
|
+
minutes_ago: !!pl
|
26
|
+
1: %1 минуту назад
|
27
|
+
2: %1 минуты назад
|
28
|
+
n: %1 минут назад
|
29
|
+
hours_ago: !!pl
|
30
|
+
1: %1 час назад
|
31
|
+
2: %1 часа назад
|
32
|
+
n: %1 часов назад
|
33
|
+
yesterday: вчера
|
34
|
+
days_ago: !!pl
|
35
|
+
1: %1 день назад
|
36
|
+
2: %1 дня назад
|
37
|
+
n: %1 дней назад
|
@@ -0,0 +1,245 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
=begin
|
3
|
+
Filters for translations content.
|
4
|
+
|
5
|
+
Copyright (C) 2009 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
|
+
require 'rubygems'
|
22
|
+
|
23
|
+
module R18n
|
24
|
+
# Filter is a way, to process translations: escape HTML entries, convert from
|
25
|
+
# Markdown syntax, etc.
|
26
|
+
#
|
27
|
+
# In translation file filtered content must be marked by YAML type:
|
28
|
+
#
|
29
|
+
# filtered: !!custom_type
|
30
|
+
# This content will be processed by filter
|
31
|
+
#
|
32
|
+
# Filter function will be receive filtered content as first argument, struct
|
33
|
+
# with filter config as second and filter parameters as next arguments.
|
34
|
+
#
|
35
|
+
# R18n::Filters.add('custom_type', :no_space) do |content, config, replace|
|
36
|
+
# content.gsub(' ', replace)
|
37
|
+
# end
|
38
|
+
# R18n::Filters.add('custom_type') do |content, config, replace|
|
39
|
+
# content + '!'
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# i18n.filtered('_') #=> "This_content_will_be_processed_by_filter!"
|
43
|
+
#
|
44
|
+
# Use String class as type to add global filter for all translated strings:
|
45
|
+
#
|
46
|
+
# R18n::Filters.add(String, :escape_html) do |content, config, params|
|
47
|
+
# escape_html(content)
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Filter config contain two parameters: translation locale and path. But it is
|
51
|
+
# OpenStruct and you can add you own parameter to cross-filter communications:
|
52
|
+
#
|
53
|
+
# R18n::Filters.add(String, :hide_truth) do |content, config|
|
54
|
+
# return content if config.censorship_check
|
55
|
+
#
|
56
|
+
# if content.scan(CENSORSHIP_WORDS[config.locale]).empty?
|
57
|
+
# content
|
58
|
+
# else
|
59
|
+
# "Error in #{config.path}"
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# R18n::Filters.add('passed', :show_lie) do |content, config|
|
64
|
+
# config.censorship_check = true
|
65
|
+
# content
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# You can disable, enabled and delete filters:
|
69
|
+
#
|
70
|
+
# R18n::Filters.off(:no_space))
|
71
|
+
# i18n.filtered('_') #=> "This content will be processed by filter!"
|
72
|
+
# R18n::Filters.on(:no_space)
|
73
|
+
# i18n.filtered('_') #=> "This_content_will_be_processed_by_filter!"
|
74
|
+
# R18n::Filters.delete(:no_space)
|
75
|
+
module Filters
|
76
|
+
Filter = Struct.new(:name, :type, :block, :enabled) do
|
77
|
+
def call(*params)
|
78
|
+
block.call(*params)
|
79
|
+
end
|
80
|
+
|
81
|
+
def enabled?
|
82
|
+
enabled
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class << self
|
87
|
+
# Hash of filter names to Filters.
|
88
|
+
def defined
|
89
|
+
@defined ||= {}
|
90
|
+
end
|
91
|
+
|
92
|
+
# Hash of types to enabled Filters.
|
93
|
+
def enabled
|
94
|
+
@enabled ||= Hash.new([])
|
95
|
+
end
|
96
|
+
|
97
|
+
# Hash of types to all Filters.
|
98
|
+
def by_type
|
99
|
+
@by_type ||= Hash.new([])
|
100
|
+
end
|
101
|
+
|
102
|
+
# Process +translation+ by global filters and filters for special +type+.
|
103
|
+
def process(translation, locale, path, type, params)
|
104
|
+
case translation
|
105
|
+
when Numeric, NilClass, FalseClass, TrueClass, Symbol
|
106
|
+
else
|
107
|
+
translation = translation.clone
|
108
|
+
end
|
109
|
+
config = OpenStruct.new(:locale => locale, :path => path)
|
110
|
+
|
111
|
+
if type
|
112
|
+
filters = Filters.enabled[type]
|
113
|
+
filters.each { |f| translation = f.call(translation, config, *params)}
|
114
|
+
end
|
115
|
+
|
116
|
+
if translation.is_a? String
|
117
|
+
Filters.enabled[String].each do |f|
|
118
|
+
translation = f.call(translation, config, *params)
|
119
|
+
end
|
120
|
+
return TranslatedString.new(translation, locale, path)
|
121
|
+
else
|
122
|
+
translation
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Add new filter for +type+ with +name+ and return filter object. You
|
127
|
+
# can use String class as +type+ to add global filter for all translated
|
128
|
+
# string.
|
129
|
+
#
|
130
|
+
# Several filters for same type will be call consecutively, but you can
|
131
|
+
# set +position+ in call list.
|
132
|
+
#
|
133
|
+
# Filter content will be sent to +block+ as first argument, struct with
|
134
|
+
# config as second and filters parameters will be in next arguments.
|
135
|
+
def add(type, name = nil, position = nil, &block)
|
136
|
+
unless name
|
137
|
+
@last_auto_name ||= 0
|
138
|
+
begin
|
139
|
+
@last_auto_name += 1
|
140
|
+
name = @last_auto_name
|
141
|
+
end while defined.has_key? name
|
142
|
+
else
|
143
|
+
delete(name)
|
144
|
+
end
|
145
|
+
|
146
|
+
filter = Filter.new(name, type, block, true)
|
147
|
+
defined[name] = filter
|
148
|
+
|
149
|
+
unless enabled.has_key? type
|
150
|
+
enabled[type] = []
|
151
|
+
by_type[type] = []
|
152
|
+
end
|
153
|
+
if position
|
154
|
+
enabled[type].insert(position, filter)
|
155
|
+
by_type[type].insert(position, filter)
|
156
|
+
else
|
157
|
+
enabled[type] << filter
|
158
|
+
by_type[type] << defined[name]
|
159
|
+
end
|
160
|
+
|
161
|
+
filter
|
162
|
+
end
|
163
|
+
|
164
|
+
# Delete +filter+ by name or Filter object.
|
165
|
+
def delete(filter)
|
166
|
+
filter = defined[filter] unless filter.is_a? Filter
|
167
|
+
return unless filter
|
168
|
+
|
169
|
+
defined.delete(filter.name)
|
170
|
+
by_type[filter.type].delete(filter)
|
171
|
+
enabled[filter.type].delete(filter)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Disable +filter+ by name or Filter object.
|
175
|
+
def off(filter)
|
176
|
+
filter = defined[filter] unless filter.is_a? Filter
|
177
|
+
return unless filter
|
178
|
+
|
179
|
+
filter.enabled = false
|
180
|
+
enabled[filter.type].delete(filter)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Turn on disabled +filter+ by name or Filter object.
|
184
|
+
def on(filter)
|
185
|
+
filter = defined[filter] unless filter.is_a? Filter
|
186
|
+
return unless filter
|
187
|
+
|
188
|
+
filter.enabled = true
|
189
|
+
enabled[filter.type] = by_type[filter.type].reject { |i| !i.enabled? }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
Filters.add('proc', :procedure) do |content, config, *params|
|
195
|
+
eval("proc { #{content} }").call(*params)
|
196
|
+
end
|
197
|
+
|
198
|
+
Filters.add('pl', :pluralization) do |content, config, param|
|
199
|
+
type = config.locale.pluralize(param)
|
200
|
+
type = 'n' if not content.include? type
|
201
|
+
content[type]
|
202
|
+
end
|
203
|
+
|
204
|
+
Filters.add(String, :variables) do |content, config, *params|
|
205
|
+
content = content.clone
|
206
|
+
params.each_with_index do |param, i|
|
207
|
+
if param.is_a? Float
|
208
|
+
param = config.locale.format_float(param)
|
209
|
+
elsif param.is_a? Integer
|
210
|
+
param = config.locale.format_integer(param)
|
211
|
+
end
|
212
|
+
content.gsub! "%#{i+1}", param.to_s
|
213
|
+
end
|
214
|
+
content
|
215
|
+
end
|
216
|
+
|
217
|
+
Filters.add('escape', :escape_html) do |content, config|
|
218
|
+
config.dont_escape_html = true
|
219
|
+
Utils.escape_html(content)
|
220
|
+
end
|
221
|
+
|
222
|
+
Filters.add('html', :dont_escape_html) do |content, config|
|
223
|
+
config.dont_escape_html = true
|
224
|
+
content
|
225
|
+
end
|
226
|
+
|
227
|
+
Filters.add(String, :global_escape_html) do |content, config|
|
228
|
+
if config.dont_escape_html
|
229
|
+
content
|
230
|
+
else
|
231
|
+
Utils.escape_html(content)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
Filters.off(:global_escape_html)
|
235
|
+
|
236
|
+
Filters.add('markdown', :maruku) do |content, config|
|
237
|
+
require 'maruku'
|
238
|
+
::Maruku.new(content).to_html
|
239
|
+
end
|
240
|
+
|
241
|
+
Filters.add('textile', :redcloth) do |content, config|
|
242
|
+
require 'redcloth'
|
243
|
+
::RedCloth.new(content).to_html
|
244
|
+
end
|
245
|
+
end
|