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.
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: ","