r18n-core 0.2.3 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/README.rdoc +157 -35
  2. data/base/cs.yml +34 -0
  3. data/base/de.yml +24 -0
  4. data/base/en.yml +24 -0
  5. data/base/fr.yml +24 -0
  6. data/base/pl.yml +24 -0
  7. data/base/ru.yml +30 -0
  8. data/lib/r18n-core/filters.rb +245 -0
  9. data/lib/r18n-core/i18n.rb +57 -40
  10. data/lib/r18n-core/locale.rb +116 -19
  11. data/lib/r18n-core/translated.rb +186 -0
  12. data/lib/r18n-core/translation.rb +33 -73
  13. data/lib/r18n-core/unsupported_locale.rb +27 -9
  14. data/lib/r18n-core/utils.rb +49 -0
  15. data/lib/r18n-core/version.rb +1 -1
  16. data/lib/r18n-core.rb +3 -15
  17. data/locales/cs.rb +23 -0
  18. data/locales/cs.yml +26 -0
  19. data/locales/de.yml +3 -8
  20. data/locales/en-us.rb +8 -0
  21. data/locales/en-us.yml +9 -0
  22. data/locales/en.rb +26 -0
  23. data/locales/en.yml +3 -8
  24. data/locales/eo.yml +3 -8
  25. data/locales/fr.rb +14 -0
  26. data/locales/fr.yml +3 -8
  27. data/locales/kk.yml +3 -8
  28. data/locales/pl.yml +4 -11
  29. data/locales/ru.yml +3 -8
  30. data/spec/filters_spec.rb +167 -0
  31. data/spec/i18n_spec.rb +61 -16
  32. data/spec/locale_spec.rb +46 -19
  33. data/spec/locales/cs_spec.rb +22 -0
  34. data/spec/locales/en-us_spec.rb +14 -0
  35. data/spec/locales/en_spec.rb +14 -0
  36. data/spec/locales/fr_spec.rb +10 -0
  37. data/spec/locales/pl_spec.rb +17 -17
  38. data/spec/locales/ru_spec.rb +2 -2
  39. data/spec/r18n_spec.rb +7 -3
  40. data/spec/spec_helper.rb +2 -0
  41. data/spec/translated_spec.rb +108 -0
  42. data/spec/translation_spec.rb +24 -56
  43. data/spec/translations/extension/{no_TR.yml → no-tr.yml} +0 -0
  44. data/spec/translations/general/en.yml +15 -2
  45. data/spec/translations/general/{no_LC.yml → no-lc.yml} +0 -0
  46. metadata +46 -31
  47. data/locales/en_US.yml +0 -14
@@ -24,25 +24,26 @@ require 'yaml'
24
24
  module R18n
25
25
  # Translation for interface to i18n support. You can load several locales and
26
26
  # if translation willn’t be found in first, r18n will be search it in next.
27
+ # Use R18n::I18n.new to load translations.
27
28
  #
28
29
  # Translation files use YAML format and has name like en.yml (English) or
29
- # en_US.yml (USA English dialect) with language/country code (RFC 3066). In
30
- # translation file you can use strings, numbers, floats (any YAML types),
31
- # procedures (<tt>!!proc</tt>) and pluralizable values (<tt>!!pl</tt>). You
32
- # can use params in string values, which you can replace in program. Just
33
- # write <tt>%1</tt>, <tt>%2</tt>, etc and set it values as method arguments,
34
- # when you will be get value.
30
+ # en-US.yml (USA English dialect) with language/country code (RFC 3066). In
31
+ # translation file you can use strings, numbers, floats (any YAML types)
32
+ # and pluralizable values (<tt>!!pl</tt>). You can use params in string
33
+ # values, which you can replace in program. Just write <tt>%1</tt>,
34
+ # <tt>%2</tt>, etc and set it values as method arguments, when you will be get
35
+ # value.
35
36
  #
36
- # If in your system procedures in translations willn’t be secure (user can
37
- # upoad or edit it) set <tt>R18n::Translation.call_proc</tt> to false.
37
+ # You can use filters for some YAML type or for all strings. See R18n::Filters
38
+ # for details.
38
39
  #
39
40
  # To get translation value use method with same name. If translation name
40
41
  # is equal with Object methods (+new+, +to_s+, +methods+) use
41
42
  # <tt>[name, params…]</tt>. If you want to get pluralizable value, just set
42
43
  # value for pluralization in first argument of method. See samples below.
43
44
  #
44
- # Translated strings will have +locale+ methods, which return Locale or it
45
- # code, if locale file isn’t exists.
45
+ # Translated strings will have +locale+ methods, which return Locale or
46
+ # UnsupportedLocale, if locale file isn’t exists.
46
47
  #
47
48
  # R18n contain translations for common words (such as “OK”, “Cancel”, etc)
48
49
  # for most supported locales. See <tt>base/</tt> dir.
@@ -66,24 +67,21 @@ module R18n
66
67
  # 1: one comment
67
68
  # n: %1 comments
68
69
  #
69
- # sum: !!proc |x, y| "is #{x + y}"
70
- #
71
70
  # example.rb
72
71
  #
73
- # i18n = R18n::Translation.load(['ru', 'en'], 'translations/')
72
+ # locales = [R18n::Locale.load('ru'), R18n::Locale.load('en')]
73
+ # i18n = R18n::Translation.load(locales, 'translations/')
74
74
  # i18n.one #=> "Один"
75
75
  # i18n.two #=> "Two"
76
76
  #
77
- # i18n.two.locale['code'] #=> "en"
78
- # i18n.two.locale['direction'] #=> "ltr"
77
+ # i18n.two.locale.code #=> "en"
78
+ # i18n.two.locale.ltr? #=> "ltr"
79
79
  #
80
80
  # i18n.entry.between(2, 3) #=> "between 2 and 3"
81
81
  # i18n['methods', 'object'] #=> "Is object method"
82
82
  #
83
83
  # i18n.comments(0) #=> "no comments"
84
84
  # i18n.comments(10) #=> "10 comments"
85
- #
86
- # i18n.sum(2, 3) #=> "is 5"
87
85
  #
88
86
  # i18n.yes #=> "Yes"
89
87
  # i18n.ok #=> "OK"
@@ -97,8 +95,6 @@ module R18n
97
95
  class Translation
98
96
  @@extension_translations = [
99
97
  Pathname(__FILE__).dirname.expand_path + '../../base']
100
-
101
- @@call_proc = true
102
98
 
103
99
  # Get dirs with extension translations. If application translations with
104
100
  # same locale isn’t exists, extension file willn’t be used.
@@ -119,41 +115,31 @@ module R18n
119
115
  end
120
116
  end
121
117
 
122
- # Load all available translations for +locales+. +Locales+ may be a string
123
- # with one user locale or array with many. +Dirs+ may be an array or
124
- # a string with path to dir with translations.
118
+ # Load all available translations for +locales+. +Locales+ must be Locale or
119
+ # UnsupportedLocale instance or an array them. +Dirs+ may be an array or a
120
+ # string with path to dir with translations.
121
+ #
122
+ # To load translations use R18n::I18n.new method, which is more usable.
125
123
  def self.load(locales, dirs)
126
- locales = Array(locales) & self.available(dirs)
127
124
  dirs = @@extension_translations + Array(dirs)
128
125
 
126
+ available = self.available(dirs)
129
127
  translations = []
130
- locales.map! do |locale|
128
+ locales.each do |locale|
129
+ next unless available.include? locale.code.downcase
131
130
  translation = {}
132
131
  dirs.each do |dir|
133
- file = File.join(dir, "#{locale}.yml")
132
+ file = File.join(dir, "#{locale.code.downcase}.yml")
134
133
  if File.exists? file
135
134
  Utils.deep_merge! translation, YAML::load_file(file)
136
135
  end
137
136
  end
138
137
  translations << translation
139
-
140
- Locale.load(locale)
141
138
  end
142
139
 
143
140
  self.new(locales, translations)
144
141
  end
145
142
 
146
- # Is procedures in translations will be call. Set to false if user can
147
- # upload or edit translations.
148
- def self.call_proc=(call)
149
- @@call_proc = call
150
- end
151
-
152
- # Is procedures in translations will be call
153
- def self.call_proc
154
- @@call_proc
155
- end
156
-
157
143
  # Create translation hash for +path+ with messages in +translations+ for
158
144
  # +locales+.
159
145
  #
@@ -184,49 +170,23 @@ module R18n
184
170
 
185
171
  @translations.each_with_index do |translation, i|
186
172
  result = translation[name]
187
- next if result.nil?
188
-
189
- if result.is_a? YAML::PrivateType
190
- case result.type_id
191
- when 'proc'
192
- if @@call_proc
193
- return eval("proc {#{result.value}}").call(*params)
194
- else
195
- return result.value
196
- end
197
- when 'pl'
198
- locale = @locales[i]
199
-
200
- type = locale.pluralize(params.first)
201
- if params.first.is_a? Float
202
- params[0] = locale.format_float(params.first)
203
- elsif params.first.is_a? Integer
204
- params[0] = locale.format_integer(params.first)
205
- end
206
-
207
- type = 'n' if not result.value.include? type
208
- result = result.value[type]
209
- else
210
- return result
211
- end
212
- end
173
+ next unless result
213
174
 
214
- if result.is_a? String
215
- result = result.clone
216
- params.each_with_index do |param, i|
217
- result.gsub! "%#{i+1}", param.to_s
218
- end
219
- return TranslatedString.new(result, @locales[i], path)
220
- elsif result.is_a? Hash
175
+ if result.is_a? Hash
221
176
  return self.class.new(@locales, @translations.map { |i|
222
177
  i[name] or {}
223
178
  }, path)
179
+ elsif result.is_a? YAML::PrivateType
180
+ type = result.type_id
181
+ result = result.value
224
182
  else
225
- return result.clone
183
+ type = nil
226
184
  end
185
+
186
+ return Filters.process(result, @locales[i], path, type, params)
227
187
  end
228
188
 
229
- return Untranslated.new(path, name)
189
+ Untranslated.new(path, name)
230
190
  end
231
191
  end
232
192
  end
@@ -22,38 +22,56 @@ module R18n
22
22
  # Locale without information file. Contain only it code, empty title and data
23
23
  # from default locale.
24
24
  class UnsupportedLocale
25
+ # Locale, to get data and pluralization for unsupported locale.
26
+ attr_accessor :base
27
+
25
28
  # Create object for unsupported locale with +code+ and load other locale
26
- # data from default locale.
27
- def initialize(code)
28
- @@default_locale ||= Locale.load(I18n.default)
29
+ # data from +base+ locale.
30
+ def initialize(code, base = nil)
29
31
  @code = code
32
+ @base = Locale.load(I18n.default) if @code != I18n.default
33
+ end
34
+
35
+ # Is locale has information file. In this class always return false.
36
+ def supported?
37
+ false
30
38
  end
31
39
 
32
- # Human readable locale code and title
40
+ # Human readable locale code and title.
33
41
  def inspect
34
42
  "Unsupported locale #{@code}"
35
43
  end
36
44
 
37
- # Get information about locale
45
+ # Get information about locale.
38
46
  def [](name)
39
47
  case name
40
48
  when 'code'
41
49
  @code
42
50
  when 'title'
43
- ''
51
+ @code
44
52
  else
45
- @@default_locale[name]
53
+ @base[name]
46
54
  end
47
55
  end
56
+
57
+ # Locale RFC 3066 code.
58
+ def code
59
+ @code
60
+ end
61
+
62
+ # Locale code as title.
63
+ def title
64
+ @code
65
+ end
48
66
 
49
67
  # Is another locale has same code
50
68
  def ==(locale)
51
- @code == locale['code']
69
+ @code.downcase == locale.code.downcase
52
70
  end
53
71
 
54
72
  # Proxy to default locale object.
55
73
  def method_missing(name, *params)
56
- @@default_locale.send(name, *params)
74
+ @base.send(name, *params)
57
75
  end
58
76
  end
59
77
  end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Common methods for i18n support.
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
+ # Common methods for another R18n code.
22
+ module R18n
23
+ module Utils
24
+ # Recursively hash merge.
25
+ def self.deep_merge!(a, b)
26
+ b.each_pair do |name, value|
27
+ if a[name].is_a? Hash
28
+ self.deep_merge!(a[name], value)
29
+ else
30
+ a[name] = value
31
+ end
32
+ end
33
+ a
34
+ end
35
+
36
+ # Convert Time to Date. Backport from Ruby 1.9.
37
+ def self.to_date(time)
38
+ jd = Date.send(:civil_to_jd, time.year, time.mon, time.mday, Date::ITALY)
39
+ Date.new!(Date.send(:jd_to_ajd, jd, 0, 0), 0, Date::ITALY)
40
+ end
41
+
42
+ HTML_ENTRIES = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;' }
43
+
44
+ # Escape HTML entries (<, >, &). Copy from HAML helper.
45
+ def self.escape_html(content)
46
+ content.to_s.gsub(/[><&]/) { |s| HTML_ENTRIES[s] }
47
+ end
48
+ end
49
+ end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module R18n
3
- VERSION = '0.2.3' unless defined? R18n::VERSION
3
+ VERSION = '0.3' unless defined? R18n::VERSION
4
4
  end
data/lib/r18n-core.rb CHANGED
@@ -24,10 +24,12 @@ require 'pathname'
24
24
 
25
25
  dir = Pathname(__FILE__).dirname.expand_path + 'r18n-core'
26
26
  require dir + 'version'
27
+ require dir + 'utils'
27
28
  require dir + 'locale'
28
29
  require dir + 'unsupported_locale'
29
30
  require dir + 'translated_string'
30
31
  require dir + 'untranslated'
32
+ require dir + 'filters'
31
33
  require dir + 'translation'
32
34
  require dir + 'i18n'
33
35
 
@@ -61,19 +63,5 @@ module R18n
61
63
  attr_accessor :untranslated
62
64
  end
63
65
 
64
- self.untranslated = '%1'
65
-
66
- module Utils
67
- # Recursively hash merge.
68
- def self.deep_merge!(a, b)
69
- b.each_pair do |name, value|
70
- if a[name].is_a? Hash
71
- self.deep_merge!(a[name], value)
72
- else
73
- a[name] = value
74
- end
75
- end
76
- a
77
- end
78
- end
66
+ self.untranslated = '%2[%3]'
79
67
  end
data/locales/cs.rb ADDED
@@ -0,0 +1,23 @@
1
+ module R18n
2
+ module Locales
3
+ class Cs < R18n::Locale
4
+ def pluralize(n)
5
+ return 0 if n == 0
6
+ # 0 - 0
7
+ # 1 - 1
8
+ # 2..4 - 2
9
+ # n
10
+
11
+ case n
12
+ when 1
13
+ 1
14
+ when 2..4
15
+ 2
16
+ else
17
+ 'n'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
data/locales/cs.yml ADDED
@@ -0,0 +1,26 @@
1
+ title: Český
2
+ code: cs
3
+ sublocales: [cz, sk]
4
+ direction: ltr
5
+
6
+ week:
7
+ start: monday
8
+ days: [Neděle, Pondělí, Úterý, Středa, Čtvrtek, Pátek, Sobota]
9
+ abbrs: [Ne, Po, Út, St, Čt, Pá, So]
10
+
11
+ months:
12
+ names: [Ledna, Února, Března, Dubna, Května, Června, Července, Srpna, Září, Října, Listopadu, Prosince]
13
+ abbrs: [Led, Úno, Bře, Dub, Kvě, Čer, Čvc, Srp, Zář, Říj, Lis, Pro]
14
+ standalone: [Leden, Únor, Březen, Duben, Květen, Červen, Červenec, Srpen, Září, Říjen, Listopad, Prosinec]
15
+
16
+ time:
17
+ am: dop.
18
+ pm: odp.
19
+ time: " %H:%M"
20
+ date: "%d.%m.%Y"
21
+ full: "%e %B"
22
+ year: "_ %Y"
23
+
24
+ numbers:
25
+ decimal_separator: ","
26
+ group_delimiter: " "
data/locales/de.yml CHANGED
@@ -16,15 +16,10 @@ months:
16
16
  time:
17
17
  am: vormittags
18
18
  pm: nachmittags
19
-
20
- formats:
21
- time: "%H:%M"
19
+ time: " %H:%M"
22
20
  date: "%d.%m.%Y"
23
- short_date: "%d %b"
24
- long_date: "%d %B %Y"
25
- datetime: "%a %d %b %Y %H:%M:%S %Z"
26
- short_datetime: "%d %b %H:%M"
27
- long_datetime: "%d %B %Y %H:%M"
21
+ full: "%e %B %Y"
22
+ year: "_ %Y"
28
23
 
29
24
  numbers:
30
25
  decimal_separator: ","
data/locales/en-us.rb ADDED
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__), 'en')
2
+
3
+ module R18n
4
+ module Locales
5
+ class EnUs < En
6
+ end
7
+ end
8
+ end
data/locales/en-us.yml ADDED
@@ -0,0 +1,9 @@
1
+ include: en
2
+
3
+ code: en-US
4
+ title: English (US)
5
+ sublocales: [en]
6
+
7
+ time:
8
+ date: "%m/%d/%Y"
9
+ full: "%B %e"
data/locales/en.rb ADDED
@@ -0,0 +1,26 @@
1
+ module R18n
2
+ module Locales
3
+ class En < R18n::Locale
4
+ def ordinalize(n)
5
+ if (11..13).include?(n % 100)
6
+ "#{n}th"
7
+ else
8
+ case n % 10
9
+ when 1; "#{n}st"
10
+ when 2; "#{n}nd"
11
+ when 3; "#{n}rd"
12
+ else "#{n}th"
13
+ end
14
+ end
15
+ end
16
+
17
+ def format_date_full(i18n, date, year = true)
18
+ format = @data['time']['full']
19
+ if year
20
+ format = @data['time']['year'].sub('_', format)
21
+ end
22
+ strftime(date, format.sub('%e', ordinalize(date.mday)))
23
+ end
24
+ end
25
+ end
26
+ end
data/locales/en.yml CHANGED
@@ -16,15 +16,10 @@ months:
16
16
  time:
17
17
  am: AM
18
18
  pm: PM
19
-
20
- formats:
21
- time: "%H:%M"
19
+ time: " %H:%M"
22
20
  date: "%d/%m/%Y"
23
- short_date: "%d %b"
24
- long_date: "%d %B, %Y"
25
- datetime: "%a %d %b %H:%M:%S %Z %Y"
26
- short_datetime: "%d %b %H:%M"
27
- long_datetime: "%d %B, %Y %H:%M"
21
+ full: "%e of %B"
22
+ year: "_, %Y"
28
23
 
29
24
  numbers:
30
25
  decimal_separator: "."
data/locales/eo.yml CHANGED
@@ -16,15 +16,10 @@ months:
16
16
  time:
17
17
  am: AM
18
18
  pm: PM
19
-
20
- formats:
21
- time: "%H:%M"
19
+ time: " %H:%M"
22
20
  date: "%Y-%m-%d"
23
- short_date: "%d %b"
24
- long_date: "la %d-a de %B de %Y"
25
- datetime: "%a, %d %b %Y, %H:%M:%S %Z"
26
- short_datetime: "%d %b, %H:%M"
27
- long_datetime: "la %d-a de %B de %Y, %H:%M"
21
+ full: "la %e-a de %B"
22
+ year: "_ de %Y"
28
23
 
29
24
  numbers:
30
25
  decimal_separator: "."
data/locales/fr.rb ADDED
@@ -0,0 +1,14 @@
1
+ module R18n
2
+ module Locales
3
+ class Fr < R18n::Locale
4
+ def format_date_full(i18n, date, year = true)
5
+ full = super(i18n, date, year)
6
+ if ' 1' == full[0..1]
7
+ '1er' + full[2..-1]
8
+ else
9
+ full
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
data/locales/fr.yml CHANGED
@@ -16,15 +16,10 @@ months:
16
16
  time:
17
17
  am: AM
18
18
  pm: PM
19
-
20
- formats:
21
- time: "%H:%M"
19
+ time: " %H:%M"
22
20
  date: "%d/%m/%Y"
23
- short_date: "%d %b"
24
- long_date: "%d %B %Y"
25
- datetime: "%a %d %b %Y %H:%M:%S %Z"
26
- short_datetime: "%d %b %H:%M"
27
- long_datetime: "%d %B %Y %H:%M"
21
+ full: "%e %B"
22
+ year: "_ %Y"
28
23
 
29
24
  numbers:
30
25
  decimal_separator: ","
data/locales/kk.yml CHANGED
@@ -16,15 +16,10 @@ months:
16
16
  time:
17
17
  am: " ертеңің"
18
18
  pm: " кештің"
19
-
20
- formats:
21
- time: "%H:%M"
19
+ time: " %H:%M"
22
20
  date: "%Y-%m-%d"
23
- short_date: "%b %d"
24
- long_date: "%Y жылы %B %d-і"
25
- datetime: "%a, %Y ж. %b %d-і, %H:%M:%S %Z"
26
- short_datetime: "%b %d, %H:%M"
27
- long_datetime: "%Y жылы %B %d-і, %H:%M"
21
+ full: "%Y %B %e-і"
22
+ year: "%Y жылы _"
28
23
 
29
24
  numbers:
30
25
  decimal_separator: ","
data/locales/pl.yml CHANGED
@@ -1,10 +1,8 @@
1
1
  title: Polski
2
2
  code: pl
3
- sublocales: []
3
+ sublocales: [en]
4
4
  direction: ltr
5
5
 
6
- pluralization: "n == 0 ? 0 : n == 1 ? 1 : 'n'"
7
-
8
6
  week:
9
7
  start: monday
10
8
  days: [Niedziela, Poniedziałek, Wtorek, Sroda, Czwartek, Piątek, Sobota]
@@ -18,15 +16,10 @@ months:
18
16
  time:
19
17
  am: AM
20
18
  pm: PM
21
-
22
- formats:
23
- time: "%H:%M"
19
+ time: " %H:%M"
24
20
  date: "%d/%m/%Y"
25
- short_date: "%d %b"
26
- long_date: "%d %B, %Y"
27
- datetime: "%a %d %b %H:%M:%S %Z %Y"
28
- short_datetime: "%d %b %H:%M"
29
- long_datetime: "%d %B, %Y %H:%M"
21
+ full: "%e %B"
22
+ year: "_ %Y"
30
23
 
31
24
  numbers:
32
25
  decimal_separator: "."
data/locales/ru.yml CHANGED
@@ -16,15 +16,10 @@ months:
16
16
  time:
17
17
  am: " утра"
18
18
  pm: " вечера"
19
-
20
- formats:
21
- time: "%H:%M"
19
+ time: " %H:%M"
22
20
  date: "%d.%m.%Y"
23
- short_date: "%d %b"
24
- long_date: "%d %B %Y"
25
- datetime: "%a, %d %b %Y, %H:%M:%S %Z"
26
- short_datetime: "%d %b, %H:%M"
27
- long_datetime: "%d %B %Y, %H:%M"
21
+ full: "%e %B"
22
+ year: "_ %Y"
28
23
 
29
24
  numbers:
30
25
  decimal_separator: ","