i18n-active_record-backend 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Sven Fuchs <svenfuchs@artweb-design.de>
2
+ Copyright (c) 2013 Endika Gutiérrez <me@endika.net>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
@@ -0,0 +1,61 @@
1
+ h1. I18n::Backend::ActiveRecord
2
+
3
+ This repository contains the I18n ActiveRecord backend and support code that has been extracted from the "I18n gem":http://github.com/svenfuchs/i18n.
4
+
5
+ h2. Installation
6
+
7
+ For Bundler put the following in your Gemfile:
8
+
9
+ <pre>
10
+ gem 'i18n-active_record',
11
+ :git => 'git://github.com/svenfuchs/i18n-active_record.git',
12
+ :branch => 'rails-3.2',
13
+ :require => 'i18n/active_record'
14
+ </pre>
15
+
16
+ This gem can generate a migration to create a database table named @translations@, which will store the localized strings.
17
+
18
+ <pre>
19
+ rails g i18n:active_record:install
20
+ rake db:migrate
21
+ </pre>
22
+
23
+ To load @I18n::Backend::ActiveRecord@ into your Rails application, create a new file in *config/initializers* named *locale.rb*.
24
+
25
+ A simple configuration for your locale.rb could look like this:
26
+
27
+ <pre>
28
+ require 'i18n/backend/active_record'
29
+ I18n.backend = I18n::Backend::ActiveRecord.new
30
+ </pre>
31
+
32
+ A more advanced example (thanks, Moritz), which uses YAML files and ActiveRecord for lookups:
33
+
34
+ <pre>
35
+ require 'i18n/backend/active_record'
36
+ I18n.backend = I18n::Backend::ActiveRecord.new
37
+
38
+ I18n::Backend::ActiveRecord.send(:include, I18n::Backend::Memoize)
39
+ I18n::Backend::ActiveRecord.send(:include, I18n::Backend::Flatten)
40
+ I18n::Backend::Simple.send(:include, I18n::Backend::Memoize)
41
+ I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
42
+
43
+ I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, I18n.backend)
44
+ </pre>
45
+
46
+ h2. Usage
47
+
48
+ You can now use @I18n.t('Your String')@ to lookup translations in the database.
49
+
50
+ h2. Contributing
51
+
52
+ <pre>
53
+ bundle install --gemfile=ci/Gemfile.rails-3.2
54
+ rake
55
+ </pre>
56
+
57
+ h2. Maintainers
58
+
59
+ * "Sven Fuchs":http://github.com/svenfuchs
60
+ * "Jeremy Weiskotten":http://github.com/jeremyw
61
+
@@ -0,0 +1,11 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'lib' << 'test'
6
+ t.pattern = 'test/**/*_test.rb'
7
+ t.verbose = false
8
+ t.warning = false
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gem 'i18n', '~> 0.6.0'
4
+ gem 'activerecord', '~> 3.2.0'
5
+ gem 'activesupport', '~> 3.2.0'
6
+
7
+ gem 'sqlite3'
8
+ gem 'mocha'
@@ -0,0 +1,33 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.2.3)
5
+ activesupport (= 3.2.3)
6
+ builder (~> 3.0.0)
7
+ activerecord (3.2.3)
8
+ activemodel (= 3.2.3)
9
+ activesupport (= 3.2.3)
10
+ arel (~> 3.0.2)
11
+ tzinfo (~> 0.3.29)
12
+ activesupport (3.2.3)
13
+ i18n (~> 0.6)
14
+ multi_json (~> 1.0)
15
+ arel (3.0.2)
16
+ builder (3.0.0)
17
+ i18n (0.6.0)
18
+ metaclass (0.0.1)
19
+ mocha (0.10.5)
20
+ metaclass (~> 0.0.1)
21
+ multi_json (1.2.0)
22
+ sqlite3 (1.3.5)
23
+ tzinfo (0.3.32)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ activerecord (~> 3.2.0)
30
+ activesupport (~> 3.2.0)
31
+ i18n (~> 0.6.0)
32
+ mocha
33
+ sqlite3
@@ -0,0 +1,25 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module I18n
5
+ module ActiveRecord
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ def self.next_migration_number(path)
10
+ unless @prev_migration_nr
11
+ @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
12
+ else
13
+ @prev_migration_nr += 1
14
+ end
15
+ @prev_migration_nr.to_s
16
+ end
17
+
18
+ source_root File.expand_path('../templates', __FILE__)
19
+
20
+ def copy_migrations
21
+ migration_template "migration.rb", "db/migrate/create_translations.rb"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ class CreateTranslations < ActiveRecord::Migration
2
+ def change
3
+ create_table :translations do |t|
4
+ t.string :locale
5
+ t.string :key
6
+ t.text :value
7
+ t.text :interpolations
8
+ t.boolean :is_proc, :default => false
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :translations, [:locale, :key], :unique => true
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ require 'i18n'
@@ -0,0 +1,6 @@
1
+ module I18n
2
+ module ActiveRecord
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
6
+
@@ -0,0 +1,63 @@
1
+ require 'i18n/backend/base'
2
+
3
+ module I18n
4
+ module Backend
5
+ class ActiveRecord
6
+ autoload :Missing, 'i18n/backend/active_record/missing'
7
+ autoload :StoreProcs, 'i18n/backend/active_record/store_procs'
8
+ autoload :Translation, 'i18n/backend/active_record/translation'
9
+
10
+ module Implementation
11
+ include Base, Flatten
12
+
13
+ def available_locales
14
+ Translation.available_locales
15
+ rescue ::ActiveRecord::StatementInvalid
16
+ []
17
+ end
18
+
19
+ def store_translations(locale, data, options = {})
20
+ escape = options.fetch(:escape, true)
21
+ flatten_translations(locale, data, escape, false).each do |key, value|
22
+ Translation.locale(locale).lookup(expand_keys(key)).delete_all
23
+ Translation.create(:locale => locale.to_s, :key => key.to_s, :value => value)
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def lookup(locale, key, scope = [], options = {})
30
+ key = normalize_flat_keys(locale, key, scope, options[:separator])
31
+ result = Translation.locale(locale).lookup(key).all
32
+
33
+ if result.empty?
34
+ nil
35
+ elsif result.first.key == key
36
+ result.first.value
37
+ else
38
+ chop_range = (key.size + FLATTEN_SEPARATOR.size)..-1
39
+ result = result.inject({}) do |hash, r|
40
+ hash[r.key.slice(chop_range)] = r.value
41
+ hash
42
+ end
43
+ result.deep_symbolize_keys
44
+ end
45
+
46
+ rescue ::ActiveRecord::StatementInvalid
47
+ # is the translations table missing?
48
+ nil
49
+ end
50
+
51
+ # For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz']
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)
55
+ end
56
+ end
57
+ end
58
+
59
+ include Implementation
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,74 @@
1
+ # This extension stores translation stub records for missing translations to
2
+ # the database.
3
+ #
4
+ # This is useful if you have a web based translation tool. It will populate
5
+ # the database with untranslated keys as the application is being used. A
6
+ # translator can then go through these and add missing translations.
7
+ #
8
+ # Example usage:
9
+ #
10
+ # I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing)
11
+ # I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
12
+ #
13
+ # Stub records for pluralizations will also be created for each key defined
14
+ # in i18n.plural.keys.
15
+ #
16
+ # For example:
17
+ #
18
+ # # en.yml
19
+ # en:
20
+ # i18n:
21
+ # plural:
22
+ # keys: [:zero, :one, :other]
23
+ #
24
+ # # pl.yml
25
+ # pl:
26
+ # i18n:
27
+ # plural:
28
+ # keys: [:zero, :one, :few, :other]
29
+ #
30
+ # It will also persist interpolation keys in Translation#interpolations so
31
+ # translators will be able to review and use them.
32
+ module I18n
33
+ module Backend
34
+ class ActiveRecord
35
+ module Missing
36
+ include Flatten
37
+
38
+ def store_default_translations(locale, key, options = {})
39
+ count, scope, default, separator = options.values_at(:count, :scope, :default, :separator)
40
+ separator ||= I18n.default_separator
41
+ key = normalize_flat_keys(locale, key, scope, separator)
42
+
43
+ unless ActiveRecord::Translation.locale(locale).lookup(key).exists?
44
+ interpolations = options.keys - I18n::RESERVED_KEYS
45
+ keys = count ? I18n.t('i18n.plural.keys', :locale => locale).map { |k| [key, k].join(FLATTEN_SEPARATOR) } : [key]
46
+ keys.each { |key| store_default_translation(locale, key, interpolations) }
47
+ end
48
+ end
49
+
50
+ def store_default_translation(locale, key, interpolations)
51
+ translation = ActiveRecord::Translation.new :locale => locale.to_s, :key => key
52
+ translation.interpolations = interpolations
53
+ translation.save
54
+ end
55
+
56
+ def translate(locale, key, options = {})
57
+ # Support throw/catch for MissingTranslation (i18n 0.6.0)
58
+ result = catch(:exception) do
59
+ super
60
+ end
61
+ if result.is_a?(I18n::MissingTranslation)
62
+ self.store_default_translations(locale, key, options)
63
+ throw(:exception, result)
64
+ end
65
+ result
66
+ rescue I18n::MissingTranslationData => e
67
+ self.store_default_translations(locale, key, options)
68
+ raise e
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,39 @@
1
+ # This module is intended to be mixed into the ActiveRecord backend to allow
2
+ # storing Ruby Procs as translation values in the database.
3
+ #
4
+ # I18n.backend = I18n::Backend::ActiveRecord.new
5
+ # I18n::Backend::ActiveRecord::Translation.send(:include, I18n::Backend::ActiveRecord::StoreProcs)
6
+ #
7
+ # The StoreProcs module requires the ParseTree and ruby2ruby gems and therefor
8
+ # was extracted from the original backend.
9
+ #
10
+ # ParseTree is not compatible with Ruby 1.9.
11
+
12
+ begin
13
+ require 'ruby2ruby'
14
+ require 'parse_tree'
15
+ require 'parse_tree_extensions'
16
+ rescue LoadError => e
17
+ puts "can't use StoreProcs because: #{e.message}"
18
+ end
19
+
20
+ module I18n
21
+ module Backend
22
+ class ActiveRecord
23
+ module StoreProcs
24
+ def value=(v)
25
+ case v
26
+ when Proc
27
+ write_attribute(:value, v.to_ruby)
28
+ write_attribute(:is_proc, true)
29
+ else
30
+ write_attribute(:value, v)
31
+ end
32
+ end
33
+
34
+ Translation.send(:include, self) if method(:to_s).respond_to?(:to_ruby)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,104 @@
1
+ require 'active_record'
2
+
3
+ module I18n
4
+ module Backend
5
+ # ActiveRecord model used to store actual translations to the database.
6
+ #
7
+ # This model expects a table like the following to be already set up in
8
+ # your the database:
9
+ #
10
+ # create_table :translations do |t|
11
+ # t.string :locale
12
+ # t.string :key
13
+ # t.text :value
14
+ # t.text :interpolations
15
+ # t.boolean :is_proc, :default => false
16
+ # end
17
+ #
18
+ # This model supports to named scopes :locale and :lookup. The :locale
19
+ # scope simply adds a condition for a given locale:
20
+ #
21
+ # I18n::Backend::ActiveRecord::Translation.locale(:en).all
22
+ # # => all translation records that belong to the :en locale
23
+ #
24
+ # The :lookup scope adds a condition for looking up all translations
25
+ # that either start with the given keys (joined by an optionally given
26
+ # separator or I18n.default_separator) or that exactly have this key.
27
+ #
28
+ # # with translations present for :"foo.bar" and :"foo.baz"
29
+ # I18n::Backend::ActiveRecord::Translation.lookup(:foo)
30
+ # # => an array with both translation records :"foo.bar" and :"foo.baz"
31
+ #
32
+ # I18n::Backend::ActiveRecord::Translation.lookup([:foo, :bar])
33
+ # I18n::Backend::ActiveRecord::Translation.lookup(:"foo.bar")
34
+ # # => an array with the translation record :"foo.bar"
35
+ #
36
+ # When the StoreProcs module was mixed into this model then Procs will
37
+ # be stored to the database as Ruby code and evaluated when :value is
38
+ # called.
39
+ #
40
+ # Translation = I18n::Backend::ActiveRecord::Translation
41
+ # Translation.create \
42
+ # :locale => 'en'
43
+ # :key => 'foo'
44
+ # :value => lambda { |key, options| 'FOO' }
45
+ # Translation.find_by_locale_and_key('en', 'foo').value
46
+ # # => 'FOO'
47
+ class ActiveRecord
48
+ class Translation < ::ActiveRecord::Base
49
+ TRUTHY_CHAR = "\001"
50
+ FALSY_CHAR = "\002"
51
+
52
+ self.table_name = 'translations'
53
+ self.logger = nil
54
+ attr_accessible :locale, :key, :value
55
+
56
+ serialize :value
57
+ serialize :interpolations, Array
58
+
59
+ class << self
60
+ def locale(locale)
61
+ scoped conditions: { locale: locale.to_s }
62
+ end
63
+
64
+ def lookup(keys, *separator)
65
+ column_name = connection.quote_column_name('key')
66
+ keys = Array(keys).map(&:to_s)
67
+
68
+ unless separator.empty?
69
+ warn "[DEPRECATION] Giving a separator to Translation.lookup is deprecated. " <<
70
+ "You can change the internal separator by overwriting FLATTEN_SEPARATOR."
71
+ end
72
+
73
+ namespace = "#{keys.last}#{I18n::Backend::Flatten::FLATTEN_SEPARATOR}%"
74
+ scoped conditions: ["#{column_name} IN (?) OR #{column_name} LIKE ?", keys, namespace]
75
+ end
76
+
77
+ def available_locales
78
+ Translation.pluck('DISTINCT locale').map(&:to_sym)
79
+ end
80
+ end
81
+
82
+ def interpolates?(key)
83
+ self.interpolations.include?(key) if self.interpolations
84
+ end
85
+
86
+ def value
87
+ value = read_attribute(:value)
88
+ return Kernel.eval(value) if is_proc?
89
+ return false if value == FALSY_CHAR
90
+ return true if value == TRUTHY_CHAR
91
+ value
92
+ end
93
+
94
+ def value=(value)
95
+ value = FALSY_CHAR if value === false
96
+ value = TRUTHY_CHAR if value === true
97
+
98
+ write_attribute(:value, value)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ class I18nBackendActiveRecordTest < Test::Unit::TestCase
4
+ def setup
5
+ I18n.backend = I18n::Backend::ActiveRecord.new
6
+ store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' })
7
+ end
8
+
9
+ def teardown
10
+ I18n::Backend::ActiveRecord::Translation.destroy_all
11
+ super
12
+ end
13
+
14
+ test "store_translations does not allow ambiguous keys (1)" do
15
+ I18n::Backend::ActiveRecord::Translation.delete_all
16
+ I18n.backend.store_translations(:en, :foo => 'foo')
17
+ I18n.backend.store_translations(:en, :foo => { :bar => 'bar' })
18
+ I18n.backend.store_translations(:en, :foo => { :baz => 'baz' })
19
+
20
+ translations = I18n::Backend::ActiveRecord::Translation.locale(:en).lookup('foo').all
21
+ assert_equal %w(bar baz), translations.map(&:value)
22
+
23
+ assert_equal({ :bar => 'bar', :baz => 'baz' }, I18n.t(:foo))
24
+ end
25
+
26
+ test "store_translations does not allow ambiguous keys (2)" do
27
+ I18n::Backend::ActiveRecord::Translation.delete_all
28
+ I18n.backend.store_translations(:en, :foo => { :bar => 'bar' })
29
+ I18n.backend.store_translations(:en, :foo => { :baz => 'baz' })
30
+ I18n.backend.store_translations(:en, :foo => 'foo')
31
+
32
+ translations = I18n::Backend::ActiveRecord::Translation.locale(:en).lookup('foo').all
33
+ assert_equal %w(foo), translations.map(&:value)
34
+
35
+ assert_equal 'foo', I18n.t(:foo)
36
+ end
37
+
38
+ test "can store translations with keys that are translations containing special chars" do
39
+ I18n.backend.store_translations(:es, :"Pagina's" => "Pagina's" )
40
+ assert_equal "Pagina's", I18n.t(:"Pagina's", :locale => :es)
41
+ end
42
+
43
+ with_mocha do
44
+ test "missing translations table does not cause an error in #available_locales" do
45
+ I18n::Backend::ActiveRecord::Translation.expects(:available_locales).raises(::ActiveRecord::StatementInvalid)
46
+ assert_equal [], I18n.backend.available_locales
47
+ end
48
+ end
49
+
50
+ def test_expand_keys
51
+ assert_equal %w(foo foo.bar foo.bar.baz), I18n.backend.send(:expand_keys, :'foo.bar.baz')
52
+ end
53
+ end if defined?(ActiveRecord)
54
+
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ class I18nActiveRecordApiTest < Test::Unit::TestCase
4
+ def setup
5
+ I18n.backend = I18n::Backend::ActiveRecord.new
6
+ super
7
+ end
8
+
9
+ # def self.can_store_procs?
10
+ # I18n::Backend::ActiveRecord.included_modules.include?(I18n::Backend::ActiveRecord::StoreProcs)
11
+ # end
12
+ #
13
+ # include I18n::Tests::Basics
14
+ # include I18n::Tests::Defaults
15
+ # include I18n::Tests::Interpolation
16
+ # include I18n::Tests::Link
17
+ # include I18n::Tests::Lookup
18
+ # include I18n::Tests::Pluralization
19
+ # include I18n::Tests::Procs if can_store_procs?
20
+ #
21
+ # include I18n::Tests::Localization::Date
22
+ # include I18n::Tests::Localization::DateTime
23
+ # include I18n::Tests::Localization::Time
24
+ # include I18n::Tests::Localization::Procs if can_store_procs?
25
+
26
+ test "make sure we use an ActiveRecord backend" do
27
+ assert_equal I18n::Backend::ActiveRecord, I18n.backend.class
28
+ end
29
+ end if defined?(ActiveRecord)
30
+
@@ -0,0 +1,71 @@
1
+ require 'test_helper'
2
+
3
+ class I18nActiveRecordMissingTest < Test::Unit::TestCase
4
+ class Backend < I18n::Backend::ActiveRecord
5
+ include I18n::Backend::ActiveRecord::Missing
6
+ end
7
+
8
+ def setup
9
+ I18n.backend.store_translations(:en, :bar => 'Bar', :i18n => { :plural => { :keys => [:zero, :one, :other] } })
10
+ I18n.backend = I18n::Backend::Chain.new(Backend.new, I18n.backend)
11
+ I18n::Backend::ActiveRecord::Translation.delete_all
12
+ end
13
+
14
+ test "can persist interpolations" do
15
+ translation = I18n::Backend::ActiveRecord::Translation.new(:key => 'foo', :value => 'bar', :locale => :en)
16
+ translation.interpolations = %w(count name)
17
+ translation.save
18
+ assert translation.valid?
19
+ end
20
+
21
+ test "lookup persists the key" do
22
+ I18n.t('foo.bar.baz')
23
+ assert_equal 1, I18n::Backend::ActiveRecord::Translation.count
24
+ assert I18n::Backend::ActiveRecord::Translation.locale(:en).find_by_key('foo.bar.baz')
25
+ end
26
+
27
+ test "lookup does not persist the key twice" do
28
+ 2.times { I18n.t('foo.bar.baz') }
29
+ assert_equal 1, I18n::Backend::ActiveRecord::Translation.count
30
+ assert I18n::Backend::ActiveRecord::Translation.locale(:en).find_by_key('foo.bar.baz')
31
+ end
32
+
33
+ test "lookup persists interpolation keys when looked up directly" do
34
+ I18n.t('foo.bar.baz', :cow => "lucy" ) # creates stub translation.
35
+ translation_stub = I18n::Backend::ActiveRecord::Translation.locale(:en).lookup('foo.bar.baz').first
36
+ assert translation_stub.interpolates?(:cow)
37
+ end
38
+
39
+ test "creates one stub per pluralization" do
40
+ I18n.t('foo', :count => 999)
41
+ translations = I18n::Backend::ActiveRecord::Translation.locale(:en).find_all_by_key %w{ foo.zero foo.one foo.other }
42
+ assert_equal 3, translations.length
43
+ end
44
+
45
+ test "creates no stub for base key in pluralization" do
46
+ I18n.t('foo', :count => 999)
47
+ assert_equal 3, I18n::Backend::ActiveRecord::Translation.locale(:en).lookup("foo").count
48
+ assert !I18n::Backend::ActiveRecord::Translation.locale(:en).find_by_key("foo")
49
+ end
50
+
51
+ test "creates a stub when a custom separator is used" do
52
+ I18n.t('foo|baz', :separator => '|')
53
+ I18n::Backend::ActiveRecord::Translation.locale(:en).lookup("foo.baz").first.update_attributes!(:value => 'baz!')
54
+ assert_equal 'baz!', I18n.t('foo|baz', :separator => '|')
55
+ end
56
+
57
+ test "creates a stub per pluralization when a custom separator is used" do
58
+ I18n.t('foo|bar', :count => 999, :separator => '|')
59
+ translations = I18n::Backend::ActiveRecord::Translation.locale(:en).find_all_by_key %w{ foo.bar.zero foo.bar.one foo.bar.other }
60
+ assert_equal 3, translations.length
61
+ end
62
+
63
+ test "creates a stub when a custom separator is used and the key contains the flatten separator (a dot character)" do
64
+ key = 'foo|baz.zab'
65
+ I18n.t(key, :separator => '|')
66
+ I18n::Backend::ActiveRecord::Translation.locale(:en).lookup("foo.baz\001zab").first.update_attributes!(:value => 'baz!')
67
+ assert_equal 'baz!', I18n.t(key, :separator => '|')
68
+ end
69
+
70
+ end if defined?(ActiveRecord)
71
+
@@ -0,0 +1,71 @@
1
+ $KCODE = 'u' if RUBY_VERSION <= '1.9'
2
+
3
+ require 'rubygems'
4
+ require 'test/unit'
5
+ require 'test_setup'
6
+
7
+ # Do not load the i18n gem from libraries like active_support.
8
+ #
9
+ # This is required for testing against Rails 2.3 because active_support/vendor.rb#24 tries
10
+ # to load I18n using the gem method. Instead, we want to test the local library of course.
11
+ alias :gem_for_ruby_19 :gem # for 1.9. gives a super ugly seg fault otherwise
12
+ def gem(gem_name, *version_requirements)
13
+ puts("skipping loading the i18n gem ...") and return if gem_name == 'i18n'
14
+ super(gem_name, *version_requirements)
15
+ end
16
+
17
+
18
+ I18n::Tests.parse_options!
19
+
20
+ require 'bundler/setup'
21
+ require 'i18n/active_record'
22
+ require 'i18n/tests'
23
+ require 'mocha'
24
+ I18n::Tests.setup_active_record
25
+
26
+ class Test::Unit::TestCase
27
+ def self.test(name, &block)
28
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
29
+ defined = instance_method(test_name) rescue false
30
+ raise "#{test_name} is already defined in #{self}" if defined
31
+ if block_given?
32
+ define_method(test_name, &block)
33
+ else
34
+ define_method(test_name) do
35
+ flunk "No implementation provided for #{name}"
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.with_mocha
41
+ yield if Object.respond_to?(:expects)
42
+ end
43
+
44
+ def teardown
45
+ I18n.locale = nil
46
+ I18n.default_locale = :en
47
+ I18n.load_path = []
48
+ I18n.available_locales = nil
49
+ I18n.backend = nil
50
+ end
51
+
52
+ def translations
53
+ I18n.backend.instance_variable_get(:@translations)
54
+ end
55
+
56
+ def store_translations(*args)
57
+ data = args.pop
58
+ locale = args.pop || :en
59
+ I18n.backend.store_translations(locale, data)
60
+ end
61
+
62
+ def locales_dir
63
+ File.dirname(__FILE__) + '/test_data/locales'
64
+ end
65
+ end
66
+
67
+ Object.class_eval do
68
+ def meta_class
69
+ class << self; self; end
70
+ end
71
+ end unless Object.method_defined?(:meta_class)
@@ -0,0 +1,72 @@
1
+ require 'optparse'
2
+
3
+ module I18n
4
+ module Tests
5
+ class << self
6
+ def options
7
+ @options ||= { :with => [], :adapter => 'sqlite3' }
8
+ end
9
+
10
+ def parse_options!
11
+ OptionParser.new do |o|
12
+ o.on('-w', '--with DEPENDENCIES', 'Define dependencies') do |dep|
13
+ options[:with] = dep.split(',').map { |group| group.to_sym }
14
+ end
15
+ end.parse!
16
+
17
+ options[:with].each do |dep|
18
+ case dep
19
+ when :sqlite3, :mysql, :postgres
20
+ @options[:adapter] = dep
21
+ end
22
+ end
23
+
24
+ ENV['BUNDLE_GEMFILE'] ||= 'ci/Gemfile.rails-3.2'
25
+ end
26
+
27
+ def setup_active_record
28
+ begin
29
+ require 'active_record'
30
+ ActiveRecord::Base.connection
31
+ true
32
+ rescue LoadError => e
33
+ puts "can't use ActiveRecord backend because: #{e.message}"
34
+ rescue ActiveRecord::ConnectionNotEstablished
35
+ require 'i18n/backend/active_record'
36
+ require 'i18n/backend/active_record/store_procs'
37
+ connect_active_record
38
+ true
39
+ end
40
+ end
41
+
42
+ def connect_active_record
43
+ connect_adapter
44
+ ActiveRecord::Migration.verbose = false
45
+ ActiveRecord::Schema.define(:version => 1) do
46
+ create_table :translations, :force => true do |t|
47
+ t.string :locale
48
+ t.string :key
49
+ t.text :value
50
+ t.text :interpolations
51
+ t.boolean :is_proc, :default => false
52
+ end
53
+ add_index :translations, [:locale, :key], :unique => true
54
+ end
55
+ end
56
+
57
+ def connect_adapter
58
+ case options[:adapter].to_sym
59
+ when :sqlite3
60
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
61
+ when :mysql
62
+ # CREATE DATABASE i18n_unittest;
63
+ # CREATE USER 'i18n'@'localhost' IDENTIFIED BY '';
64
+ # GRANT ALL PRIVILEGES ON i18n_unittest.* to 'i18n'@'localhost';
65
+ ActiveRecord::Base.establish_connection(:adapter => "mysql", :database => "i18n_unittest", :username => "i18n", :password => "", :host => "localhost")
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: i18n-active_record-backend
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sven Fuchs
9
+ - Endika Gutiérrez
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-02-04 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: i18n
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 0.6.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: 0.6.0
31
+ description: I18n ActiveRecord backend. Allows to store translations in a database
32
+ using ActiveRecord, e.g. for providing a web-interface for managing translations.
33
+ email: me@endika.net
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - ci/Gemfile.rails-3.2
39
+ - ci/Gemfile.rails-3.2.lock
40
+ - lib/generators/i18n/active_record/install/install_generator.rb
41
+ - lib/generators/i18n/active_record/install/templates/migration.rb
42
+ - lib/i18n/active_record/version.rb
43
+ - lib/i18n/active_record.rb
44
+ - lib/i18n/backend/active_record/missing.rb
45
+ - lib/i18n/backend/active_record/store_procs.rb
46
+ - lib/i18n/backend/active_record/translation.rb
47
+ - lib/i18n/backend/active_record.rb
48
+ - test/active_record_test.rb
49
+ - test/api_test.rb
50
+ - test/missing_test.rb
51
+ - test/test_helper.rb
52
+ - test/test_setup.rb
53
+ - MIT-LICENSE
54
+ - README.textile
55
+ - Rakefile
56
+ homepage: https://github.com/endSly/i18n-active_record-backend
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project: ! '[none]'
76
+ rubygems_version: 1.8.24
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: I18n ActiveRecord backend
80
+ test_files: []