i18n 0.3.7 → 0.4.0.beta

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of i18n might be problematic. Click here for more details.

@@ -0,0 +1,125 @@
1
+ h1. Changelog
2
+
3
+ h2. master
4
+
5
+ *
6
+
7
+ h2. 0.4.0.beta (2010-04-30)
8
+
9
+ * "Added a KeyValue backend":http://github.com/svenfuchs/i18n/commit/28ca5f53ade7f545f8c0804e93564d4686b416a4
10
+
11
+ * "Added transliteration support":http://github.com/svenfuchs/i18n/commit/928fdb4794959e779e90f360eb01ba043672d8d5
12
+
13
+ * "Create Flatten backend module to aid handling flatten translations":http://github.com/svenfuchs/i18n/commit/2ec9d6998aa8facd7b15a3ef47a96cf2471cd8a1
14
+
15
+ * "Decouple the external separator (used when storing translations) from the internal separator in Fast and ActiveRecord backends":http://github.com/svenfuchs/i18n/commit/274cb4daa0ca5e3b2bd23b45eb7f9fc58f75a79d
16
+
17
+ h2. 0.3.7 (2010-04-17)
18
+
19
+ * "Speed up I18n.normalize_keys by caching reused normalizations and producing less garbage":http://github.com/svenfuchs/i18n/commit/819dac0fea9c29e6545801aa107e63e355728cd4
20
+
21
+ h2. 0.3.6 (2010-03-23)
22
+
23
+ * "Move gettext po parser to lib":http://github.com/svenfuchs/i18n/commit/b2f038663b55727ac2327e6f07a46ba5d69d600c
24
+
25
+ * "Move I18n configuration to I18n.config":http://github.com/svenfuchs/i18n/commit/4a7baea86663ead8c681008c3e80a622f0546b07
26
+
27
+ h2. 0.3.5 (2010-02-26)
28
+
29
+ * "Delegate I18n.normalize_translation_keys to I18n.normalize_keys and deprecate
30
+ the former":http://github.com/svenfuchs/i18n/commit/7284b04d5f5dd9679cb68875515cdd0cdfc96fef
31
+
32
+ h2. 0.3.4 (2010-02-25)
33
+
34
+ * "Rename I18n.normalize_translation_keys to I18n.normalize_keys and finally make it public":http://github.com/svenfuchs/i18n/commit/20b05fe5802df6c90fb70a4e3760b2b851b791b3
35
+
36
+ * "Added CLDR supoprt":http://github.com/svenfuchs/i18n/commit/860eadf671a231e7f5dffb1bb27fa318ff7a8786
37
+
38
+ h2. 0.3.3 (2009-12-29)
39
+
40
+ * "Use lib/i18n/version":http://github.com/svenfuchs/i18n/commit/ff426c8e7a2438b814cb303adadec292dacb752e
41
+
42
+ * "Added a benchmark suite":http://github.com/svenfuchs/i18n/commit/f9b5b9b113097724638bdab96862ffa404e67e70
43
+
44
+ * "Ensure links can be handled recursively":http://github.com/svenfuchs/i18n/commit/2c50bd209f3fc24fe9dfa694c81be64340f09b7d
45
+
46
+ * "Make sure we can lookup false values as translation data":http://github.com/svenfuchs/i18n/commit/561c82ba4b8921d03bfdf56cb2d0c2f287629001
47
+
48
+ * "Added Fast backend module":http://github.com/svenfuchs/i18n/commit/bd2f09f0a251ca793b0e8ecc7e32177a2f091c23
49
+
50
+ * "Added InterpolationCompiler backend module":http://github.com/svenfuchs/i18n/commit/91810887d1abfb28996a9183bc9004678290d28b
51
+
52
+ h2. 0.3.2 (2009-12-12)
53
+
54
+ * "Added Cascade backend":http://github.com/svenfuchs/i18n/commit/8009aef293e9ef8564c9005090d8380feabcaf6f
55
+
56
+ h2. 0.3.1 (2009-12-11)
57
+
58
+ * "Add PoParser to gemspec":http://github.com/svenfuchs/i18n/commit/d6b2763f39c932f66adb039b96882a472f883c51
59
+ * "Enable custom separators for ActiveRecord backend":http://github.com/svenfuchs/i18n/commit/9341d3fcfc951cc31807ba672d2b5d90909ef3e5
60
+ * "Pass interpolation values to interpolation procs":http://github.com/svenfuchs/i18n/commit/39c2ed8fbad645671cd5520ce7ad0aeefe2b0cca
61
+ * "Fix that ngettext supports keys with dots":http://github.com/svenfuchs/i18n/commit/7362a43c34364d500de8899cfcca6bf1a5e6d1c8
62
+
63
+ h2. 0.3.0 (2009-11-30)
64
+
65
+ * "Gettext backend and helpers":http://github.com/svenfuchs/i18n/commit/35a1740d2f10b808548af352006950da4017e374
66
+ * "Metadata module":http://github.com/svenfuchs/i18n/commit/2677208555179b36fcbe958c0e8bc642cf5bc020
67
+ * "Basic ActiveRecord backend":http://github.com/svenfuchs/i18n/commit/786632d0b42de423ecf0969622efc87f1691e2a2
68
+ * "Set encoding to UTF8 for all files":http://github.com/svenfuchs/i18n/commit/9be3d4a311b5bf583eec5d39986176cc40c112f2
69
+ * "Chain backend":http://github.com/svenfuchs/i18n/commit/08259ffb88b3005403648d77bc1cbca0b92f3cf5
70
+ * "Backend/cache implementation":http://github.com/svenfuchs/i18n/commit/e7bf15351cd2e27f5972eb40e65a5dd6f4a0feed
71
+ * "Pluralization module":http://github.com/svenfuchs/i18n/commit/9ca4c9ed52d4706566a6abeb2d78722dcc5d4764
72
+ * "add and adapt Globalize2 fallback implementation":http://github.com/svenfuchs/i18n/commit/1b37a303b27d6222b17162804b06323e5628768f
73
+ * "move Simple backend implementation to a Base backend class and extend Simple from Base.":http://github.com/svenfuchs/i18n/commit/32ddc80a04e6aa247f6d6613bde7f78c73396cb4
74
+
75
+ h2. 0.2.0 (2009-07-12)
76
+
77
+ * "Allow using Ruby 1.9 syntax for string interpolation (API addition)":http://github.com/svenfuchs/i18n/commit/c6e0b06d512f2af57199a843a1d8a40241b32861
78
+ * "Allow configuring the default scope separator, allow to pass a custom scope separator(API addition)":http://github.com/svenfuchs/i18n/commit/5b75bfbc348061adc11e3790187a187275bfd471 (e.g. I18n.t(:'foo|bar', :separator => '|')
79
+ * "Pass :format option to #translate for #localize more useful lambda support":http://github.com/svenfuchs/i18n/commit/e277711b3c844fe7589b8d3f9af0f7d1b969a273
80
+ * "Refactor Simple backend #resolve to #default and #resolve for more consistency. Now allows to pass lambdas as defaults and re-resolve Symbols":http://github.com/svenfuchs/i18n/commit/8c4ce3d923ce5fa73e973fe28217e18165549aba
81
+ * "Add lambda support to #translate (API addition)":http://github.com/svenfuchs/i18n/commit/c90e62d8f7d3d5b78f34cfe328d871b58884f115
82
+ * "Add lambda support to #localize (API addition)":http://github.com/svenfuchs/i18n/commit/9d390afcf33f3f469bb95e6888147152f6cc7442
83
+
84
+ h2. 0.1.3 (2009-02-27)
85
+
86
+ * "Remove unnecessary string encoding handling in the i18n simple backend which made the backend break on Ruby 1.9":http://github.com/svenfuchs/i18n/commit/4c3a970783861a94f2e89f46714fb3434e4f4f8d
87
+
88
+ h2. 0.1.2 (2009-01-09)
89
+
90
+ * "added #available_locales (returns an array of locales for which translations are available)":http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4
91
+ * "flatten load_path before using it so that a nested array of paths won't throw up":http://github.com/svenfuchs/i18n/commit/d473a068a2b90aba98135deb225d6eb6d8104d70
92
+
93
+ h2. 0.1.1 (2008-11-20)
94
+
95
+ * "Use :'en' as a default locale (in favor of :'en-US')":http://github.com/svenfuchs/i18n/commit/c4b10b246aecf7da78cb2568dd0d2ab7e6b8a230
96
+ * "Add #reload! to Simple backend":http://github.com/svenfuchs/i18n/commit/36dd2bd9973b9e1559728749a9daafa44693e964
97
+
98
+ h2. 0.1.0 (2008-10-25)
99
+
100
+ * "Fix Simple backend to distinguish false from nil values":http://github.com/svenfuchs/i18n/commit/39d9a47da14b5f3ba126af48923af8c30e135166
101
+ * "Add #load_path to public api, add initialize to simple backend and remove #load_translations from public api":http://github.com/svenfuchs/i18n/commit/c4c5649e6bc8f020f1aaf5a5470bde048e22c82d
102
+ * "Speed up Backend::Simple#interpolate":http://github.com/svenfuchs/i18n/commit/9e1ac6bf8833304e036323ec9932b9f33c468a35
103
+ * "Remove #populate and #store_translations from public API":http://github.com/svenfuchs/i18n/commit/f4e514a80be7feb509f66824ee311905e2940900
104
+ * "Use :other instead of :many as a plural key":http://github.com/svenfuchs/i18n/commit/0f8f20a2552bf6a2aa758d8fdd62a7154e4a1bf6
105
+ * "Use a class instead of a module for Simple backend":http://github.com/svenfuchs/i18n/commit/08f051aa61320c17debde24a83268bc74e33b995
106
+ * "Make Simple backend #interpolate deal with non-ASCII string encodings":http://github.com/svenfuchs/i18n/commit/d84a3f3f55543c084d5dc5d1fed613b8df148789
107
+ * "Fix default arrays of non-existant keys returning the default array":http://github.com/svenfuchs/i18n/commit/6c04ca86c87f97dc78f07c2a4023644e5ba8b839
108
+
109
+ h2. Initial implementation (June/July 2008)
110
+
111
+ Initial implementation by "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs based on previous discussion/consensus of the rails-i18n team (alphabetical order) and many others:
112
+
113
+ * "Matt Aimonetti":http://railsontherun.com
114
+ * "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs
115
+ * "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
116
+ * "Saimon Moore":http://saimonmoore.net
117
+ * "Stephan Soller":http://www.arkanis-development.de
118
+
119
+ h2. More information
120
+
121
+ * "Homepage":http://rails-i18n.org
122
+ * "Wiki":http://rails-i18n.org/wiki
123
+ * "Mailinglist":http://groups.google.com/group/rails-i18n
124
+ * "About the project/history":http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized
125
+ * "Initial API Intro":http://www.artweb-design.de/2008/7/18/the-ruby-on-rails-i18n-core-api
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 The Ruby I18n team
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,93 @@
1
+ h1. Ruby I18n
2
+
3
+ Ruby Internationalization and localization solution.
4
+
5
+ Features:
6
+
7
+ * translation and localization
8
+ * interpolation of values to translations (Ruby 1.9 compatible syntax)
9
+ * pluralization (CLDR compatible)
10
+ * customizable transliteration to ASCII
11
+ * flexible defaults
12
+ * bulk lookup
13
+ * lambdas as translation data
14
+ * custom key/scope separator
15
+ * custom exception handlers
16
+ * extensible architecture with a swappable backend
17
+
18
+ Pluggable features:
19
+
20
+ * Cache
21
+ * Pluralization: lambda pluralizers stored as translation data
22
+ * Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation)
23
+ * Gettext support
24
+ * Translation metadata
25
+
26
+ Alternative backends:
27
+
28
+ * Chain
29
+ * ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs)
30
+ * KeyValue (uses active_support/json and cannot store procs)
31
+
32
+ For more information and lots of resources see: "http://rails-i18n.org/wiki":http://rails-i18n.org/wiki
33
+
34
+ h2. Installation
35
+
36
+ gem install i18n
37
+
38
+ h3. Installation on Rails < 2.3.5 (deprecated)
39
+
40
+ Up to version 2.3.4 Rails will not accept i18n gems > 0.1.3. There is an unpacked
41
+ gem inside of active_support/lib/vendor which gets loaded unless gem 'i18n', '~> 0.1.3'.
42
+ This requirement is relaxed in "6da03653":http://github.com/rails/rails/commit/6da03653
43
+
44
+ The new i18n gem can be loaded from vendor/plugins like this:
45
+
46
+ def reload_i18n!
47
+ raise "Move to i18n version 0.2.0 or greater" if Rails.version > "2.3.4"
48
+
49
+ $:.grep(/i18n/).each { |path| $:.delete(path) }
50
+ I18n::Backend.send :remove_const, "Simple"
51
+ $: << Rails.root.join('vendor', 'plugins', 'i18n', 'lib').to_s
52
+ end
53
+
54
+ Then you can `reload_i18n!` inside an i18n initializer.
55
+
56
+ h2. Tests
57
+
58
+ You can run tests both with
59
+
60
+ * `rake test` or just `rake`
61
+ * run any test file directly, e.g. `ruby test/api/simple_test.rb`
62
+ * run all tests with `ruby test/all.rb`
63
+
64
+ The structure of the test suite is a bit unusual as it uses modules to reuse
65
+ particular tests in different test cases.
66
+
67
+ The reason for this is that we need to enforce the I18n API across various
68
+ combinations of extensions. E.g. the Simple backend alone needs to support
69
+ the same API as any combination of feature and/or optimization modules included
70
+ to the Simple backend. We test this by reusing the same API defition (implemented
71
+ as test methods) in test cases with different setups.
72
+
73
+ You can find the test cases that enforce the API in test/api. And you can find
74
+ the API definition test methods in test/api/tests.
75
+
76
+ All other test cases (e.g. as defined in test/backend, test/core\_ext) etc.
77
+ follow the usual test setup and should be easy to grok.
78
+
79
+ h2. Authors
80
+
81
+ * "Sven Fuchs":http://www.artweb-design.de
82
+ * "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
83
+ * "Stephan Soller":http://www.arkanis-development.de
84
+ * "Saimon Moore":http://saimonmoore.net
85
+ * "Matt Aimonetti":http://railsontherun.com
86
+
87
+ h2. Contributors
88
+
89
+ http://github.com/svenfuchs/i18n/contributors
90
+
91
+ h2. License
92
+
93
+ MIT License. See the included MIT-LICENSE file.
@@ -244,6 +244,69 @@ module I18n
244
244
  end
245
245
  alias :t! :translate!
246
246
 
247
+ # Transliterates UTF-8 characters to ASCII. By default this method will
248
+ # transliterate only Latin strings to an ASCII approximation:
249
+ #
250
+ # I18N.transliterate("Ærøskøbing")
251
+ # # => "AEroskobing"
252
+ #
253
+ # I18N.transliterate("日本語")
254
+ # # => "???"
255
+ #
256
+ # It's also possible to add support for per-locale transliterations. I18N
257
+ # expects transliteration rules to be stored at
258
+ # <tt>i18n.transliterate.rule</tt>.
259
+ #
260
+ # Transliteration rules can either be a Hash or a Proc. Procs must accept a
261
+ # single string argument. Hash rules inherit the default transliteration
262
+ # rules, while Procs do not.
263
+ #
264
+ # *Examples*
265
+ #
266
+ # Setting a Hash in <locale>.yml:
267
+ #
268
+ # i18n:
269
+ # transliterate:
270
+ # rule:
271
+ # ü: "ue"
272
+ # ö: "oe"
273
+ #
274
+ # Setting a Hash using Ruby:
275
+ #
276
+ # store_translations(:de, :i18n => {
277
+ # :transliterate => {
278
+ # :rule => {
279
+ # "ü" => "ue",
280
+ # "ö" => "oe"
281
+ # }
282
+ # }
283
+ # )
284
+ #
285
+ # Setting a Proc:
286
+ #
287
+ # translit = lambda {|string| MyTransliterator.transliterate(string) }
288
+ # store_translations(:xx, :i18n => {:transliterate => {:rule => translit})
289
+ #
290
+ # Transliterating strings:
291
+ #
292
+ # I18N.locale = :en
293
+ # I18N.transliterate("Jürgen") # => "Jurgen"
294
+ # I18N.locale = :de
295
+ # I18N.transliterate("Jürgen") # => "Juergen"
296
+ # I18N.transliterate("Jürgen", :locale => :en) # => "Jurgen"
297
+ # I18N.transliterate("Jürgen", :locale => :de) # => "Juergen"
298
+ def transliterate(*args)
299
+ options = args.pop if args.last.is_a?(Hash)
300
+ key = args.shift
301
+ locale = options && options.delete(:locale) || config.locale
302
+ raises = options && options.delete(:raise)
303
+ replacement = options && options.delete(:replacement)
304
+ config.backend.transliterate(locale, key, replacement)
305
+ rescue I18n::ArgumentError => exception
306
+ raise exception if raises
307
+ handle_exception(exception, locale, key, options)
308
+ end
309
+
247
310
  # Localizes certain objects, such as dates and numbers to local formatting.
248
311
  def localize(object, options = {})
249
312
  locale = options.delete(:locale) || config.locale
@@ -257,9 +320,12 @@ module I18n
257
320
  # keys are Symbols.
258
321
  def normalize_keys(locale, key, scope, separator = nil)
259
322
  separator ||= I18n.default_separator
260
- normalize_key(locale, separator) +
261
- normalize_key(scope, separator) +
262
- normalize_key(key, separator)
323
+
324
+ keys = []
325
+ keys.concat normalize_key(locale, separator)
326
+ keys.concat normalize_key(scope, separator)
327
+ keys.concat normalize_key(key, separator)
328
+ keys
263
329
  end
264
330
 
265
331
  # making these private until Ruby 1.9.2 can send to protected methods again
@@ -308,28 +374,20 @@ module I18n
308
374
  end
309
375
 
310
376
  def normalize_key(key, separator)
311
- normalized_key_cache(separator)[key] ||=
377
+ normalized_key_cache[separator][key] ||=
312
378
  case key
313
379
  when Array
314
380
  key.map { |k| normalize_key(k, separator) }.flatten
315
- when nil
316
- []
317
381
  else
318
- key = key.to_s
319
- if key == ''
320
- []
321
- elsif key.include?(separator)
322
- keys = key.split(separator) - ['']
323
- keys.map { |k| k.to_sym }
324
- else
325
- [key.to_sym]
326
- end
382
+ keys = key.to_s.split(separator)
383
+ keys.delete('')
384
+ keys.map!{ |k| k.to_sym }
385
+ keys
327
386
  end
328
387
  end
329
388
 
330
- def normalized_key_cache(separator)
389
+ def normalized_key_cache
331
390
  @normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
332
- @normalized_key_cache[separator]
333
391
  end
334
392
  end
335
393
  end
@@ -8,12 +8,14 @@ module I18n
8
8
  autoload :Cldr, 'i18n/backend/cldr'
9
9
  autoload :Fallbacks, 'i18n/backend/fallbacks'
10
10
  autoload :Fast, 'i18n/backend/fast'
11
+ autoload :Flatten, 'i18n/backend/flatten'
11
12
  autoload :Gettext, 'i18n/backend/gettext'
12
13
  autoload :Helpers, 'i18n/backend/helpers'
13
- autoload :Links, 'i18n/backend/links'
14
14
  autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
15
+ autoload :KeyValue, 'i18n/backend/key_value'
15
16
  autoload :Metadata, 'i18n/backend/metadata'
16
17
  autoload :Pluralization, 'i18n/backend/pluralization'
17
18
  autoload :Simple, 'i18n/backend/simple'
19
+ autoload :Transliterator, 'i18n/backend/transliterator'
18
20
  end
19
21
  end
@@ -8,20 +8,11 @@ module I18n
8
8
  autoload :StoreProcs, 'i18n/backend/active_record/store_procs'
9
9
  autoload :Translation, 'i18n/backend/active_record/translation'
10
10
 
11
- include Base, Links
11
+ include Base, Flatten
12
12
 
13
13
  def reload!
14
14
  end
15
15
 
16
- def store_translations(locale, data, options = {})
17
- separator = options[:separator] || I18n.default_separator
18
- wind_keys(data, separator).each do |key, value|
19
- store_link(locale, key, value) if value.is_a?(Symbol)
20
- Translation.locale(locale).lookup(expand_keys(key, separator), separator).delete_all
21
- Translation.create(:locale => locale.to_s, :key => key.to_s, :value => value)
22
- end
23
- end
24
-
25
16
  def available_locales
26
17
  begin
27
18
  Translation.available_locales
@@ -33,32 +24,34 @@ module I18n
33
24
  protected
34
25
 
35
26
  def lookup(locale, key, scope = [], options = {})
36
- return unless key
37
-
38
- separator = options[:separator] || I18n.default_separator
39
-
40
- key = resolve_link(locale, key)
41
- key = (Array(scope) + Array(key)).join(separator)
42
- result = Translation.locale(locale).lookup(key, separator).all
27
+ key = normalize_keys(locale, key, scope, options[:separator])
28
+ result = Translation.locale(locale).lookup(key).all
43
29
 
44
30
  if result.empty?
45
- return nil
31
+ nil
46
32
  elsif result.first.key == key
47
- return result.first.value
33
+ result.first.value
48
34
  else
49
- chop_range = (key.size + separator.size)..-1
35
+ chop_range = (key.size + FLATTEN_SEPARATOR.size)..-1
50
36
  result = result.inject({}) do |hash, r|
51
37
  hash[r.key.slice(chop_range)] = r.value
52
38
  hash
53
39
  end
54
- deep_symbolize_keys(unwind_keys(result, separator))
40
+ deep_symbolize_keys(result)
41
+ end
42
+ end
43
+
44
+ def merge_translations(locale, data, options = {})
45
+ flatten_translations(locale, data).each do |key, value|
46
+ Translation.locale(locale).lookup(expand_keys(key)).delete_all
47
+ Translation.create(:locale => locale.to_s, :key => key.to_s, :value => value)
55
48
  end
56
49
  end
57
50
 
58
51
  # For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz']
59
- def expand_keys(key, separator = I18n.default_separator)
60
- key.to_s.split(separator).inject([]) do |keys, key|
61
- keys << [keys.last, key].compact.join(separator)
52
+ def expand_keys(key)
53
+ key.to_s.split(FLATTEN_SEPARATOR).inject([]) do |keys, key|
54
+ keys << [keys.last, key].compact.join(FLATTEN_SEPARATOR)
62
55
  end
63
56
  end
64
57
  end
@@ -40,7 +40,7 @@ module I18n
40
40
  keys = I18n.normalize_keys(locale, key, scope, separator)[1..-1]
41
41
  key = keys.join(separator || I18n.default_separator)
42
42
 
43
- unless ActiveRecord::Translation.locale(locale).lookup(key, separator).exists?
43
+ unless ActiveRecord::Translation.locale(locale).lookup(key).exists?
44
44
  interpolations = options.reject { |name, value| Base::RESERVED_KEYS.include?(name) }.keys
45
45
  keys = count ? I18n.t('i18n.plural.keys', :locale => locale).map { |k| [key, k].join(separator) } : [key]
46
46
  keys.each { |key| store_default_translation(locale, key, interpolations) }
@@ -55,11 +55,9 @@ module I18n
55
55
 
56
56
  def translate(locale, key, options = {})
57
57
  super
58
-
59
- rescue I18n::MissingTranslationData => e
60
- self.store_default_translations(locale, key, options)
61
-
62
- raise e
58
+ rescue I18n::MissingTranslationData => e
59
+ self.store_default_translations(locale, key, options)
60
+ raise e
63
61
  end
64
62
  end
65
63
  end
@@ -60,9 +60,14 @@ module I18n
60
60
 
61
61
  send scope_method, :lookup, lambda { |keys, *separator|
62
62
  column_name = connection.quote_column_name('key')
63
- keys = Array(keys).map! { |key| key.to_s }
64
- separator = separator.first || I18n.default_separator
65
- namespace = "#{keys.last}#{separator}%"
63
+ keys = Array(keys).map! { |key| key.to_s }
64
+
65
+ unless separator.empty?
66
+ warn "[DEPRECATION] Giving a separator to Translation.lookup is deprecated. " <<
67
+ "You can change the internal separator by overwriting FLATTEN_SEPARATOR."
68
+ end
69
+
70
+ namespace = "#{keys.last}#{I18n::Backend::Flatten::FLATTEN_SEPARATOR}%"
66
71
  { :conditions => ["#{column_name} IN (?) OR #{column_name} LIKE ?", keys, namespace] }
67
72
  }
68
73
 
@@ -30,24 +30,34 @@ module I18n
30
30
  raise InvalidLocale.new(locale) unless locale
31
31
  return key.map { |k| translate(locale, k, options) } if key.is_a?(Array)
32
32
 
33
+ entry = lookup!(locale, key, options)
34
+
33
35
  if options.empty?
34
- entry = resolve(locale, key, lookup(locale, key), options)
36
+ entry = resolve(locale, key, entry, options)
35
37
  raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil?
36
38
  else
37
- count, scope, default = options.values_at(:count, :scope, :default)
39
+ count, default = options.values_at(:count, :default)
38
40
  values = options.reject { |name, value| RESERVED_KEYS.include?(name) }
39
41
 
40
- entry = lookup(locale, key, scope, options)
41
42
  entry = entry.nil? && default ? default(locale, key, default, options) : resolve(locale, key, entry, options)
42
43
  raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil?
43
44
 
44
- entry = pluralize(locale, entry, count) if count
45
- entry = interpolate(locale, entry, values) if values
45
+ entry = pluralize(locale, entry, count) if count
46
+ entry = interpolate(locale, entry, values)
46
47
  end
47
48
 
48
49
  entry
49
50
  end
50
51
 
52
+ # Given a locale and a UTF-8 string, return the locale's ASCII
53
+ # approximation for the string.
54
+ def transliterate(locale, string, replacement = nil)
55
+ @transliterators ||= {}
56
+ @transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule',
57
+ :locale => locale, :resolve => false, :default => {})
58
+ @transliterators[locale].transliterate(string, replacement)
59
+ end
60
+
51
61
  # Acts the same as +strftime+, but uses a localized version of the
52
62
  # format string. Takes a key from the date/time formats translations as
53
63
  # a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
@@ -103,20 +113,27 @@ module I18n
103
113
  @translations ||= {}
104
114
  end
105
115
 
116
+ # Check if the key is valid and then initialize the translation and
117
+ # trigger the default lookup behavior.
118
+ def lookup!(locale, key, options)
119
+ return unless key
120
+ init_translations unless initialized?
121
+ lookup(locale, key, options[:scope], options)
122
+ end
123
+
106
124
  # Looks up a translation from the translations hash. Returns nil if
107
125
  # eiher key is nil, or locale, scope or key do not exist as a key in the
108
126
  # nested translations hash. Splits keys or scopes containing dots
109
127
  # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
110
128
  # <tt>%w(currency format)</tt>.
111
129
  def lookup(locale, key, scope = [], options = {})
112
- return unless key
113
- init_translations unless initialized?
114
130
  keys = I18n.normalize_keys(locale, key, scope, options[:separator])
131
+
115
132
  keys.inject(translations) do |result, key|
116
133
  key = key.to_sym
117
134
  return nil unless result.is_a?(Hash) && result.has_key?(key)
118
135
  result = result[key]
119
- result = resolve(locale, key, result, options) if result.is_a?(Symbol)
136
+ result = resolve(locale, key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
120
137
  String === result ? result.dup : result
121
138
  end
122
139
  end
@@ -249,17 +266,9 @@ module I18n
249
266
  def merge_translations(locale, data, options = {})
250
267
  locale = locale.to_sym
251
268
  translations[locale] ||= {}
252
- separator = options[:separator] || I18n.default_separator
253
- data = unwind_keys(data, separator)
254
- data = deep_symbolize_keys(data)
255
269
 
256
- # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
257
- merger = proc do |key, v1, v2|
258
- # TODO should probably be:
259
- # raise TypeError.new("can't merge #{v1.inspect} and #{v2.inspect}") unless Hash === v1 && Hash === v2
260
- Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : (v2 || v1)
261
- end
262
- translations[locale].merge!(data, &merger)
270
+ data = deep_symbolize_keys(data)
271
+ deep_merge_hash!(translations[locale], data)
263
272
  end
264
273
  end
265
274
  end
@@ -36,7 +36,6 @@ module I18n
36
36
  module Backend
37
37
  module Cascade
38
38
  def lookup(locale, key, scope = [], options = {})
39
- return unless key
40
39
  return super unless cascade = options[:cascade]
41
40
 
42
41
  separator = options[:separator] || I18n.default_separator
@@ -12,57 +12,50 @@
12
12
  module I18n
13
13
  module Backend
14
14
  module Fast
15
- include Links
15
+ include Flatten
16
16
 
17
- def reset_flattened_translations!
18
- @flattened_translations = nil
19
- end
20
-
21
- def flattened_translations
22
- @flattened_translations ||= flatten_translations(translations)
23
- end
24
-
25
- def merge_translations(locale, data, options = {})
26
- super
27
- reset_flattened_translations!
28
- end
29
-
30
- def init_translations
17
+ # Overwrite reload! to also clean up flattened translations.
18
+ def reload!
31
19
  super
32
20
  reset_flattened_translations!
33
21
  end
34
22
 
35
23
  protected
36
- def flatten_translations(translations)
37
- # don't flatten locale roots
38
- translations.inject({}) do |result, (locale, translations)|
39
- result[locale] = wind_keys(translations, nil, true)
40
- result[locale].each do |key, value|
41
- store_link(locale, key, value) if value.is_a?(Symbol)
42
- end
43
- result
44
- end
24
+
25
+ # Generate flattened translations after translations are initialized.
26
+ def init_translations
27
+ super
28
+ flattened_translations
45
29
  end
46
30
 
47
31
  def lookup(locale, key, scope = nil, options = {})
48
- return unless key
49
- init_translations unless initialized?
32
+ locale = locale.to_sym
33
+ return nil unless flattened_translations[locale]
50
34
 
51
- return nil unless flattened_translations.has_key?(locale.to_sym)
35
+ key = normalize_keys(locale, key, scope, options[:separator]).to_sym
36
+ flattened_translations[locale][key]
37
+ end
38
+
39
+ # Store flattened translations in a variable.
40
+ def flattened_translations
41
+ @flattened_translations ||=
42
+ translations.inject({}) do |result, (locale, data)|
43
+ result[locale] = flatten_translations(locale, data, true)
44
+ result
45
+ end
46
+ end
52
47
 
53
- separator = options[:separator]
54
- if separator && I18n.default_separator != separator
55
- key = cleanup_non_standard_separator(key, separator)
56
- scope = Array(scope).map{|k| cleanup_non_standard_separator(k, separator)} if scope
57
- end
58
-
59
- key = resolve_link(locale, key)
60
- key = (Array(scope) + [key]).join(I18n.default_separator) if scope
61
- flattened_translations[locale.to_sym][key.to_sym]
48
+ # Clean up flattened translations variable. Should be called whenever
49
+ # the internal hash is changed.
50
+ def reset_flattened_translations!
51
+ @flattened_translations = nil
62
52
  end
63
53
 
64
- def cleanup_non_standard_separator(key, user_separator)
65
- escape_default_separator(key).tr(user_separator, I18n.default_separator)
54
+ # Overwrite merge_translations to clean up the internal hash so added
55
+ # translations are also cached.
56
+ def merge_translations(locale, data, options = {})
57
+ super
58
+ reset_flattened_translations!
66
59
  end
67
60
  end
68
61
  end
@@ -0,0 +1,100 @@
1
+ module I18n
2
+ module Backend
3
+ # This module contains several helpers to assist flattening translations.
4
+ # You may want to flatten translations for:
5
+ #
6
+ # 1) speed up lookups, as in the Fast backend;
7
+ # 2) In case you want to store translations in a data store, as in ActiveRecord backend;
8
+ #
9
+ # You can check both backends above for some examples.
10
+ # This module also keeps all links in a hash so they can be properly resolved when flattened.
11
+ module Flatten
12
+ SEPARATOR_ESCAPE_CHAR = "\001"
13
+ FLATTEN_SEPARATOR = "."
14
+
15
+ # Store flattened links.
16
+ def links
17
+ @links ||= Hash.new { |h,k| h[k] = {} }
18
+ end
19
+
20
+ # Flatten keys for nested Hashes by chaining up keys:
21
+ #
22
+ # >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind
23
+ # => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }
24
+ #
25
+ def flatten_keys(hash, prev_key = nil, &block)
26
+ hash.each_pair do |key, value|
27
+ key = escape_default_separator(key)
28
+ curr_key = [prev_key, key].compact.join(FLATTEN_SEPARATOR).to_sym
29
+ yield curr_key, value
30
+ flatten_keys(value, curr_key, &block) if value.is_a?(Hash)
31
+ end
32
+ end
33
+
34
+ # Receives a hash of translations (where the key is a locale and
35
+ # the value is another hash) and return a hash with all
36
+ # translations flattened.
37
+ #
38
+ # Nested hashes are included in the flattened hash just if subtree
39
+ # is true and Symbols are automatically stored as links.
40
+ def flatten_translations(locale, data, subtree=false)
41
+ hash = {}
42
+ flatten_keys(data) do |key, value|
43
+ if value.is_a?(Hash)
44
+ hash[key] = value if subtree
45
+ else
46
+ store_link(locale, key, value) if value.is_a?(Symbol)
47
+ hash[key] = value
48
+ end
49
+ end
50
+ hash
51
+ end
52
+
53
+ # normalize_keys the flatten way. This method is significantly faster
54
+ # and creates way less objects than the one at I18n.normalize_keys.
55
+ # It also handles escaping the translation keys.
56
+ def normalize_keys(locale, key, scope, separator)
57
+ keys = [scope, key].flatten.compact
58
+ separator ||= I18n.default_separator
59
+
60
+ if separator != FLATTEN_SEPARATOR
61
+ keys.map! do |k|
62
+ k.to_s.tr("#{FLATTEN_SEPARATOR}#{separator}",
63
+ "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}")
64
+ end
65
+ end
66
+
67
+ resolve_link(locale, keys.join("."))
68
+ end
69
+
70
+ protected
71
+
72
+ def store_link(locale, key, link)
73
+ links[locale.to_sym][key.to_s] = link.to_s
74
+ end
75
+
76
+ def resolve_link(locale, key)
77
+ key, locale = key.to_s, locale.to_sym
78
+ links = self.links[locale]
79
+
80
+ if links.key?(key)
81
+ links[key]
82
+ elsif link = find_link(locale, key)
83
+ store_link(locale, key, key.gsub(*link))
84
+ else
85
+ key
86
+ end
87
+ end
88
+
89
+ def find_link(locale, key)
90
+ links[locale].each do |from, to|
91
+ return [from, to] if key[0, from.length] == from
92
+ end && nil
93
+ end
94
+
95
+ def escape_default_separator(key)
96
+ key.to_s.tr(FLATTEN_SEPARATOR, SEPARATOR_ESCAPE_CHAR)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -1,8 +1,6 @@
1
1
  module I18n
2
2
  module Backend
3
3
  module Helpers
4
- SEPARATOR_ESCAPE_CHAR = "\001"
5
-
6
4
  # Return a new hash with all keys and nested keys converted to symbols.
7
5
  def deep_symbolize_keys(hash)
8
6
  hash.inject({}) { |result, (key, value)|
@@ -12,57 +10,16 @@ module I18n
12
10
  }
13
11
  end
14
12
 
15
- # Flatten keys for nested Hashes by chaining up keys using the separator
16
- # >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind
17
- # => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }
18
- def wind_keys(hash, separator = nil, subtree = false, prev_key = nil, result = {}, orig_hash=hash)
19
- separator ||= I18n.default_separator
20
-
21
- hash.each_pair do |key, value|
22
- key = escape_default_separator(key, separator)
23
- curr_key = [prev_key, key].compact.join(separator).to_sym
24
-
25
- if value.is_a?(Hash)
26
- result[curr_key] = value if subtree
27
- wind_keys(value, separator, subtree, curr_key, result, orig_hash)
28
- else
29
- result[unescape_default_separator(curr_key)] = value
30
- end
31
- end
32
-
33
- result
34
- end
35
-
36
- def escape_default_separator(key, separator=nil)
37
- key.to_s.tr(separator || I18n.default_separator, SEPARATOR_ESCAPE_CHAR)
38
- end
39
-
40
- def unescape_default_separator(key, separator=nil)
41
- key.to_s.tr(SEPARATOR_ESCAPE_CHAR, separator || I18n.default_separator).to_sym
13
+ # deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
14
+ MERGER = proc do |key, v1, v2|
15
+ # TODO should probably be:
16
+ # raise TypeError.new("can't merge #{v1.inspect} and #{v2.inspect}") unless Hash === v1 && Hash === v2
17
+ Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : (v2 || v1)
42
18
  end
43
19
 
44
- # Expand keys chained by the the given separator through nested Hashes
45
- # >> { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }.unwind
46
- # => { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}
47
- def unwind_keys(hash, separator = ".")
48
- result = {}
49
- hash.each do |key, value|
50
- keys = key.to_s.split(separator)
51
- curr = result
52
- curr = curr[keys.shift] ||= {} while keys.size > 1
53
- curr[keys.shift] = value
54
- end
55
- result
20
+ def deep_merge_hash!(hash, data)
21
+ hash.merge!(data, &MERGER)
56
22
  end
57
-
58
- # # Flatten the given array once
59
- # def flatten_once(array)
60
- # result = []
61
- # for element in array # a little faster than each
62
- # result.push(*element)
63
- # end
64
- # result
65
- # end
66
23
  end
67
24
  end
68
25
  end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+ require 'i18n/backend/base'
3
+ require 'active_support/json'
4
+
5
+ module I18n
6
+ module Backend
7
+ # This is a basic backend for key value stores. It receives on
8
+ # initialization the store, which should respond to three methods:
9
+ #
10
+ # * store#[](key) - Used to get a value
11
+ # * store#[]=(key, value) - Used to set a value
12
+ # * store#keys - Used to get all keys
13
+ #
14
+ # Since these stores only supports string, all values are converted
15
+ # to JSON before being stored, allowing it to also store booleans,
16
+ # hashes and arrays. However, this store does not support Procs.
17
+ #
18
+ # As the ActiveRecord backend, Symbols are just supported when loading
19
+ # translations from the filesystem or through explicit store translations.
20
+ #
21
+ # Also, avoid calling I18n.available_locales since it's a somehow
22
+ # expensive operation in most stores.
23
+ #
24
+ # == Example
25
+ #
26
+ # To setup I18n to use TokyoCabinet in memory is quite straightforward:
27
+ #
28
+ # require 'rufus/tokyo/cabinet' # gem install rufus-tokyo
29
+ # I18n.backend = I18n::Backend::KeyValue.new(Rufus::Tokyo::Cabinet.new('*'))
30
+ #
31
+ # == Subtrees
32
+ #
33
+ # In most backends, you are allowed to retrieve part of a translation tree:
34
+ #
35
+ # I18n.backend.store_translations :en, :foo => { :bar => :baz }
36
+ # I18n.t "foo" #=> { :bar => :baz }
37
+ #
38
+ # This backend supports this feature by default, but it slows down the storage
39
+ # of new data considerably and makes hard to delete entries. That said, you are
40
+ # allowed to disable the storage of subtrees on initialization:
41
+ #
42
+ # I18n::Backend::KeyValue.new(@store, false)
43
+ #
44
+ class KeyValue
45
+ attr_accessor :store
46
+
47
+ include Base, Flatten
48
+
49
+ def initialize(store, subtrees=true)
50
+ @store, @subtrees = store, subtrees
51
+ end
52
+
53
+ # Mute reload! since we really don't want to clean the database.
54
+ def reload!
55
+ end
56
+
57
+ def available_locales
58
+ locales = @store.keys.map { |k| k =~ /\./; $` }
59
+ locales.uniq!
60
+ locales.compact!
61
+ locales.map! { |k| k.to_sym }
62
+ locales
63
+ end
64
+
65
+ protected
66
+
67
+ def lookup(locale, key, scope = [], options = {})
68
+ key = normalize_keys(locale, key, scope, options[:separator])
69
+ value = @store["#{locale}.#{key}"]
70
+ value = ActiveSupport::JSON.decode(value) if value
71
+ value.is_a?(Hash) ? deep_symbolize_keys(value) : value
72
+ end
73
+
74
+ def merge_translations(locale, data, options = {})
75
+ flatten_translations(locale, data, @subtrees).each do |key, value|
76
+ key = "#{locale}.#{key}"
77
+
78
+ case value
79
+ when Hash
80
+ if @subtrees && (old_value = @store[key])
81
+ old_value = ActiveSupport::JSON.decode(old_value)
82
+ value = deep_symbolize_keys(old_value).merge(value) if old_value.is_a?(Hash)
83
+ end
84
+ when Proc
85
+ raise "Key-value stores cannot handle procs"
86
+ when Symbol
87
+ value = nil
88
+ end
89
+
90
+ @store[key] = ActiveSupport::JSON.encode(value) unless value.nil?
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -13,9 +13,7 @@
13
13
  # into the Simple backend class - or whatever other backend you are using:
14
14
  #
15
15
  # I18n::Backend::Simple.send(:include, I18n::Backend::Metadata)
16
-
17
- require 'i18n/core_ext/object/meta_class'
18
-
16
+ #
19
17
  module I18n
20
18
  module Backend
21
19
  module Metadata
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+ module I18n
3
+ module Backend
4
+ module Transliterator
5
+
6
+ DEFAULT_REPLACEMENT_CHAR = "?"
7
+
8
+ # Get a transliterator instance.
9
+ def self.get(rule = nil)
10
+ if !rule || rule.kind_of?(Hash)
11
+ HashTransliterator.new(rule)
12
+ elsif rule.kind_of? Proc
13
+ ProcTransliterator.new(rule)
14
+ else
15
+ raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash."
16
+ end
17
+ end
18
+
19
+ # A transliterator which accepts a Proc as its transliteration rule.
20
+ class ProcTransliterator
21
+
22
+ def initialize(rule)
23
+ @rule = rule
24
+ end
25
+
26
+ def transliterate(string, replacement = nil)
27
+ @rule.call(string)
28
+ end
29
+
30
+ end
31
+
32
+ # A transliterator which accepts a Hash of characters as its translation
33
+ # rule.
34
+ class HashTransliterator
35
+
36
+ DEFAULT_APPROXIMATIONS = {
37
+ "À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
38
+ "Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
39
+ "Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
40
+ "Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
41
+ "Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
42
+ "ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
43
+ "ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
44
+ "ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
45
+ "ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
46
+ "Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
47
+ "ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
48
+ "Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
49
+ "ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
50
+ "Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
51
+ "ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
52
+ "Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
53
+ "ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
54
+ "ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
55
+ "Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
56
+ "ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
57
+ "Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
58
+ "œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
59
+ "Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
60
+ "š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
61
+ "Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
62
+ "ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
63
+ "Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
64
+ "Ž"=>"Z", "ž"=>"z"
65
+ }
66
+
67
+ def initialize(rule = nil)
68
+ @rule = rule
69
+ add DEFAULT_APPROXIMATIONS
70
+ add rule if rule
71
+ end
72
+
73
+ def transliterate(string, replacement = nil)
74
+ string.gsub(/[^\x00-\x7f]/u) do |char|
75
+ approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def approximations
82
+ @approximations ||= {}
83
+ end
84
+
85
+ # Add transliteration rules to the approximations hash.
86
+ def add(hash)
87
+ hash.keys.each {|key| hash[key.to_s] = hash.delete(key).to_s}
88
+ approximations.merge! hash
89
+ end
90
+
91
+ end
92
+ end
93
+ end
94
+ end
@@ -5,6 +5,8 @@ module I18n
5
5
  PLURAL_SEPARATOR = "\001"
6
6
  CONTEXT_SEPARATOR = "\004"
7
7
 
8
+ autoload :Helpers, 'i18n/gettext/helpers'
9
+
8
10
  @@plural_keys = { :en => [:one, :other] }
9
11
 
10
12
  class << self
@@ -2,12 +2,12 @@
2
2
  require 'i18n/gettext'
3
3
 
4
4
  module I18n
5
- module Helpers
5
+ module Gettext
6
6
  # Implements classical Gettext style accessors. To use this include the
7
7
  # module to the global namespace or wherever you want to use it.
8
8
  #
9
9
  # include I18n::Helpers::Gettext
10
- module Gettext
10
+ module Helpers
11
11
  def gettext(msgid, options = {})
12
12
  I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options))
13
13
  end
@@ -28,7 +28,7 @@
28
28
  #
29
29
  # I18n.default_locale = :"en-US"
30
30
  # I18n.fallbacks = I18n::Fallbacks.new(:"de-AT" => :"de-DE")
31
- # I18n.fallbacks[:"de-AT"] # => [:"de-AT", :"de-DE", :de, :"en-US", :en] # TODO does not seem to work this way?
31
+ # I18n.fallbacks[:"de-AT"] # => [:"de-AT", :"de-DE", :de, :"en-US", :en]
32
32
  #
33
33
  # # using a custom locale as default fallback locale
34
34
  #
@@ -55,10 +55,10 @@
55
55
  module I18n
56
56
  module Locale
57
57
  class Fallbacks < Hash
58
- def initialize(*args)
58
+ def initialize(*mappings)
59
59
  @map = {}
60
- map(args.pop) if args.last.is_a?(Hash) # TODO allow mappings anywhere in the args array
61
- self.defaults = args.empty? ? [I18n.default_locale.to_sym] : args
60
+ map(mappings.pop) if mappings.last.is_a?(Hash)
61
+ self.defaults = mappings.empty? ? [I18n.default_locale.to_sym] : mappings
62
62
  end
63
63
 
64
64
  def defaults=(defaults)
@@ -1,3 +1,3 @@
1
1
  module I18n
2
- VERSION = "0.3.7"
3
- end
2
+ VERSION = "0.4.0.beta"
3
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ prerelease: true
5
5
  segments:
6
6
  - 0
7
- - 3
8
- - 7
9
- version: 0.3.7
7
+ - 4
8
+ - 0
9
+ - beta
10
+ version: 0.4.0.beta
10
11
  platform: ruby
11
12
  authors:
12
13
  - Sven Fuchs
@@ -18,7 +19,7 @@ autorequire:
18
19
  bindir: bin
19
20
  cert_chain: []
20
21
 
21
- date: 2010-04-17 00:00:00 +02:00
22
+ date: 2010-04-30 00:00:00 +02:00
22
23
  default_executable:
23
24
  dependencies: []
24
25
 
@@ -44,22 +45,22 @@ files:
44
45
  - lib/i18n/backend/cldr.rb
45
46
  - lib/i18n/backend/fallbacks.rb
46
47
  - lib/i18n/backend/fast.rb
48
+ - lib/i18n/backend/flatten.rb
47
49
  - lib/i18n/backend/gettext.rb
48
50
  - lib/i18n/backend/helpers.rb
49
51
  - lib/i18n/backend/interpolation_compiler.rb
50
- - lib/i18n/backend/links.rb
52
+ - lib/i18n/backend/key_value.rb
51
53
  - lib/i18n/backend/metadata.rb
52
54
  - lib/i18n/backend/pluralization.rb
53
55
  - lib/i18n/backend/simple.rb
56
+ - lib/i18n/backend/transliterator.rb
54
57
  - lib/i18n/core_ext/hash/except.rb
55
58
  - lib/i18n/core_ext/hash/slice.rb
56
- - lib/i18n/core_ext/object/meta_class.rb
57
59
  - lib/i18n/core_ext/string/interpolate.rb
58
60
  - lib/i18n/exceptions.rb
59
61
  - lib/i18n/gettext.rb
62
+ - lib/i18n/gettext/helpers.rb
60
63
  - lib/i18n/gettext/po_parser.rb
61
- - lib/i18n/helpers.rb
62
- - lib/i18n/helpers/gettext.rb
63
64
  - lib/i18n/locale.rb
64
65
  - lib/i18n/locale/fallbacks.rb
65
66
  - lib/i18n/locale/tag.rb
@@ -67,6 +68,9 @@ files:
67
68
  - lib/i18n/locale/tag/rfc4646.rb
68
69
  - lib/i18n/locale/tag/simple.rb
69
70
  - lib/i18n/version.rb
71
+ - README.textile
72
+ - MIT-LICENSE
73
+ - CHANGELOG.textile
70
74
  has_rdoc: true
71
75
  homepage: http://github.com/svenfuchs/i18n
72
76
  licenses: []
@@ -1,34 +0,0 @@
1
- module I18n
2
- module Backend
3
- module Links
4
- protected
5
- def links(locale)
6
- @links ||= {}
7
- @links[locale.to_sym] ||= {}
8
- end
9
-
10
- def store_link(locale, key, link)
11
- links(locale)[key.to_s] = link.to_s
12
- end
13
-
14
- def resolve_link(locale, key)
15
- key = key.to_s
16
- links = self.links(locale)
17
-
18
- if links.key?(key)
19
- links[key]
20
- elsif link = find_link(locale, key)
21
- store_link(locale, key, key.gsub(*link))
22
- else
23
- key
24
- end
25
- end
26
-
27
- def find_link(locale, key)
28
- links(locale).each do |from, to|
29
- return [from, to] if key[0, from.length] == from
30
- end && nil
31
- end
32
- end
33
- end
34
- end
@@ -1,5 +0,0 @@
1
- Object.class_eval do
2
- def meta_class
3
- class << self; self; end
4
- end
5
- end unless Object.method_defined?(:meta_class)
@@ -1,5 +0,0 @@
1
- module I18n
2
- module Helpers
3
- autoload :Gettext, 'i18n/helpers/gettext'
4
- end
5
- end