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.
- data/CHANGELOG.textile +125 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +93 -0
- data/lib/i18n.rb +75 -17
- data/lib/i18n/backend.rb +3 -1
- data/lib/i18n/backend/active_record.rb +17 -24
- data/lib/i18n/backend/active_record/missing.rb +4 -6
- data/lib/i18n/backend/active_record/translation.rb +8 -3
- data/lib/i18n/backend/base.rb +27 -18
- data/lib/i18n/backend/cascade.rb +0 -1
- data/lib/i18n/backend/fast.rb +31 -38
- data/lib/i18n/backend/flatten.rb +100 -0
- data/lib/i18n/backend/helpers.rb +7 -50
- data/lib/i18n/backend/key_value.rb +95 -0
- data/lib/i18n/backend/metadata.rb +1 -3
- data/lib/i18n/backend/transliterator.rb +94 -0
- data/lib/i18n/gettext.rb +2 -0
- data/lib/i18n/{helpers/gettext.rb → gettext/helpers.rb} +2 -2
- data/lib/i18n/locale/fallbacks.rb +4 -4
- data/lib/i18n/version.rb +2 -2
- metadata +13 -9
- data/lib/i18n/backend/links.rb +0 -34
- data/lib/i18n/core_ext/object/meta_class.rb +0 -5
- data/lib/i18n/helpers.rb +0 -5
data/CHANGELOG.textile
ADDED
@@ -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
|
data/MIT-LICENSE
ADDED
@@ -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.
|
data/README.textile
ADDED
@@ -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.
|
data/lib/i18n.rb
CHANGED
@@ -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
|
-
|
261
|
-
|
262
|
-
|
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
|
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
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
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
|
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
|
data/lib/i18n/backend.rb
CHANGED
@@ -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,
|
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
|
-
|
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
|
-
|
31
|
+
nil
|
46
32
|
elsif result.first.key == key
|
47
|
-
|
33
|
+
result.first.value
|
48
34
|
else
|
49
|
-
chop_range = (key.size +
|
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(
|
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
|
60
|
-
key.to_s.split(
|
61
|
-
keys << [keys.last, key].compact.join(
|
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
|
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
|
-
|
60
|
-
|
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
|
64
|
-
|
65
|
-
|
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
|
|
data/lib/i18n/backend/base.rb
CHANGED
@@ -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,
|
36
|
+
entry = resolve(locale, key, entry, options)
|
35
37
|
raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil?
|
36
38
|
else
|
37
|
-
count,
|
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)
|
45
|
-
entry = interpolate(locale, entry, 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
|
-
|
257
|
-
|
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
|
data/lib/i18n/backend/cascade.rb
CHANGED
data/lib/i18n/backend/fast.rb
CHANGED
@@ -12,57 +12,50 @@
|
|
12
12
|
module I18n
|
13
13
|
module Backend
|
14
14
|
module Fast
|
15
|
-
include
|
15
|
+
include Flatten
|
16
16
|
|
17
|
-
|
18
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
49
|
-
|
32
|
+
locale = locale.to_sym
|
33
|
+
return nil unless flattened_translations[locale]
|
50
34
|
|
51
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
65
|
-
|
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
|
data/lib/i18n/backend/helpers.rb
CHANGED
@@ -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
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
45
|
-
|
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
|
data/lib/i18n/gettext.rb
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
require 'i18n/gettext'
|
3
3
|
|
4
4
|
module I18n
|
5
|
-
module
|
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
|
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]
|
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(*
|
58
|
+
def initialize(*mappings)
|
59
59
|
@map = {}
|
60
|
-
map(
|
61
|
-
self.defaults =
|
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)
|
data/lib/i18n/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module I18n
|
2
|
-
VERSION = "0.
|
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:
|
4
|
+
prerelease: true
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
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-
|
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/
|
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: []
|
data/lib/i18n/backend/links.rb
DELETED
@@ -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
|