Skip to content

Commit

Permalink
Feature: Added basic ActiveRecord backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Theo Cushion authored and Sven Fuchs committed Nov 13, 2009
1 parent 0e7546d commit 786632d
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 0 deletions.
1 change: 1 addition & 0 deletions i18n.gemspec
Expand Up @@ -12,6 +12,7 @@ Gem::Specification.new do |s|
s.extra_rdoc_files = [
"README.textile"
]
s.add_dependency('ruby2ruby', '1.2.2')
s.files = [
"CHANGELOG.textile",
"MIT-LICENSE",
Expand Down
1 change: 1 addition & 0 deletions lib/i18n.rb
Expand Up @@ -10,6 +10,7 @@
require 'i18n/backend/simple'
require 'i18n/exceptions'
require 'i18n/string'
require 'i18n/hash'

module I18n
@@backend = nil
Expand Down
45 changes: 45 additions & 0 deletions lib/i18n/backend/active_record.rb
@@ -0,0 +1,45 @@
require 'i18n/backend/base'
require 'i18n/backend/active_record/translation'
require 'i18n/hash'

module I18n
module Backend
class ActiveRecord < Base

def reload!
end

def store_translations(locale, data)
data.unwind.each{|key,v|
Translation.create(:locale => locale.to_s, :key => key, :value => v)
}
end

def available_locales
Translation.find(:all, :select => 'DISTINCT locale').map{|t| t.locale}
end

protected

def lookup(locale, key, scope = [], separator = I18n.default_separator)
return unless key
separator ||= "."
flat_key = (Array(scope) + Array(key)).join( separator )

result = Translation.locale(locale).key(flat_key).find(:first)
return result.value if result
results = Translation.locale(locale).keys(flat_key, separator)
if results.empty?
return nil
else
chop_range = (flat_key.size + separator.size)..-1
return results.inject({}){|hash,r|
hash[r.key.slice( chop_range )] = hash[r.value]
hash
}.wind
end
end

end
end
end
42 changes: 42 additions & 0 deletions lib/i18n/backend/active_record/translation.rb
@@ -0,0 +1,42 @@
require 'active_record'
require 'ruby2ruby'
require 'parse_tree'
require 'parse_tree_extensions'

class Translation < ActiveRecord::Base

attr_protected :proc
serialize :value

named_scope :locale, lambda {|locale|
{ :conditions => {:locale => locale }}
}

named_scope :key, lambda { |key|
{ :conditions => {:key => key} }
}

named_scope :keys, lambda { |key, separator|
separator ||= I18n.default_separator
{ :conditions => "key LIKE '#{key}#{separator}%'" }
}

def value=(v)
case v
when Proc
write_attribute(:value, v.to_ruby)
write_attribute(:proc, true)
else
write_attribute(:value, v)
end
end

def value
if proc
Kernel.eval read_attribute( :value )
else
read_attribute( :value )
end
end

end
37 changes: 37 additions & 0 deletions lib/i18n/hash.rb
@@ -0,0 +1,37 @@
class Hash

# >> {"a"=>{"b"=>{"c"=>"foo", "d"=>"bar"}, "c"=>"j"}, "q"=>"asd"}.unwind
# => {"a.c"=>"j", "a.b.d"=>"bar", "q"=>"asd", "a.b.c"=>"foo"}
def unwind(separator = ".", key = nil, start = {})
self.inject(start){|hash,k|
expanded_key = [key, k[0]].compact.join( separator )
if k[1].is_a? Hash
k[1].unwind(separator, expanded_key, hash)
else
hash[ expanded_key ] = k[1]
end
hash
}
end

# >> {"a.b.c" => "foo", "a.b.d" => "bar", "a.c" => "j", "q" => "asd"}.wind
# => {"a"=>{"b"=>{"c"=>"foo", "d"=>"bar"}, "c"=>"j"}, "q"=>"asd"}
def wind(separator = ".", key = nil, start = {})
wound = Hash.new
self.each {|key, value|
keys = key.split( separator )
index = 0
keys.inject(wound){|h,v|
index += 1
if index >= keys.size
h[v.to_sym] = value
break
else
h[v.to_sym] ||= {}
end
}
}
wound
end

end
64 changes: 64 additions & 0 deletions test/backend/active_record/api_test.rb
@@ -0,0 +1,64 @@
# encoding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
require 'i18n/backend/active_record'

ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
ActiveRecord::Schema.define(:version => 1) do
create_table :translations do |t|
t.string :locale
t.string :key
t.string :value
t.boolean :proc
end
end

class I18nActiveRecordBackendApiBasicsTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Base
include Tests::Backend::Api::Basics
end

class I18nActiveRecordBackendApiTranslateTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Base
include Tests::Backend::Api::Translation
end

class I18nActiveRecordBackendApiInterpolateTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Base
include Tests::Backend::Api::Interpolation
end

class I18nActiveRecordBackendApiLambdaTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Base
include Tests::Backend::Api::Lambda
end

class I18nActiveRecordBackendApiTranslateLinkedTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Base
include Tests::Backend::Api::Link
end

class I18nActiveRecordBackendApiPluralizationTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Base
include Tests::Backend::Api::Pluralization
end

class I18nActiveRecordBackendApiLocalizeDateTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Localization
include Tests::Backend::Api::Localization::Date
end

class I18nActiveRecordBackendApiLocalizeDateTimeTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Localization
include Tests::Backend::Api::Localization::DateTime
end

class I18nActiveRecordBackendApiLocalizeTimeTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Localization
include Tests::Backend::Api::Localization::Time
end

class I18nActiveRecordBackendApiLocalizeLambdaTest < Test::Unit::TestCase
include Tests::Backend::ActiveRecord::Setup::Localization
include Tests::Backend::Api::Localization::Lambda
end

145 changes: 145 additions & 0 deletions test/backend/active_record/setup.rb
@@ -0,0 +1,145 @@
module Tests
module Backend
module ActiveRecord
module Setup
module Base
def setup
super
I18n.locale = nil
I18n.default_locale = :en
I18n.backend = I18n::Backend::ActiveRecord.new
backend_store_translations :en, :foo => {:bar => 'bar', :baz => 'baz'}
end

def teardown
super
Translation.destroy_all
I18n.backend = nil
end
end

module Localization
include Base

def setup
super
setup_datetime_translations
setup_datetime_lambda_translations
@old_timezone, ENV['TZ'] = ENV['TZ'], 'UTC'
end

def teardown
super
@old_timezone ? ENV['TZ'] = @old_timezone : ENV.delete('TZ')
end

def setup_datetime_translations
backend_store_translations :de, {
:date => {
:formats => {
:default => "%d.%m.%Y",
:short => "%d. %b",
:long => "%d. %B %Y",
:long_ordinalized => lambda { |date, options|
tz = " (#{options[:timezone]})" if options[:timezone]
"#{date.day}ter %B %Y#{tz}"
}
},
:day_names => %w(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag),
:abbr_day_names => %w(So Mo Di Mi Do Fr Sa),
:month_names => %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember).unshift(nil),
:abbr_month_names => %w(Jan Feb Mar Apr Mai Jun Jul Aug Sep Okt Nov Dez).unshift(nil),
:order => [:day, :month, :year]
},
:time => {
:formats => {
:default => "%a, %d. %b %Y %H:%M:%S %z",
:short => "%d. %b %H:%M",
:long => "%d. %B %Y %H:%M",
:long_ordinalized => lambda { |date, options|
tz = " (#{options[:timezone]})" if options[:timezone]
"#{date.day}ter %B %Y, %H:%M Uhr#{tz}"
}
},
:am => 'am',
:pm => 'pm'
},
:datetime => {
:distance_in_words => {
:half_a_minute => 'half a minute',
:less_than_x_seconds => {
:one => 'less than 1 second',
:other => 'less than {{count}} seconds'
},
:x_seconds => {
:one => '1 second',
:other => '{{count}} seconds'
},
:less_than_x_minutes => {
:one => 'less than a minute',
:other => 'less than {{count}} minutes'
},
:x_minutes => {
:one => '1 minute',
:other => '{{count}} minutes'
},
:about_x_hours => {
:one => 'about 1 hour',
:other => 'about {{count}} hours'
},
:x_days => {
:one => '1 day',
:other => '{{count}} days'
},
:about_x_months => {
:one => 'about 1 month',
:other => 'about {{count}} months'
},
:x_months => {
:one => '1 month',
:other => '{{count}} months'
},
:about_x_years => {
:one => 'about 1 year',
:other => 'about {{count}} year'
},
:over_x_years => {
:one => 'over 1 year',
:other => 'over {{count}} years'
}
}
}
}
end

def setup_datetime_lambda_translations
backend_store_translations 'ru', {
:date => {
:'day_names' => lambda { |key, options|
(options[:format] =~ /^%A/) ?
%w(Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота) :
%w(воскресенье понедельник вторник среда четверг пятница суббота)
},
:'abbr_day_names' => %w(Вс Пн Вт Ср Чт Пт Сб),
:'month_names' => lambda { |key, options|
(options[:format] =~ /(%d|%e)(\s*)?(%B)/) ?
%w(января февраля марта апреля мая июня июля августа сентября октября ноября декабря).unshift(nil) :
%w(Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь).unshift(nil)
},
:'abbr_month_names' => lambda { |key, options|
(options[:format] =~ /(%d|%e)(\s*)(%b)/) ?
%w(янв. февр. марта апр. мая июня июля авг. сент. окт. нояб. дек.).unshift(nil) :
%w(янв. февр. март апр. май июнь июль авг. сент. окт. нояб. дек.).unshift(nil)
},
},
:time => {
:am => "утра",
:pm => "вечера"
}
}
end
end
end
end
end
end
1 change: 1 addition & 0 deletions test/test_helper.rb
Expand Up @@ -11,6 +11,7 @@

require File.dirname(__FILE__) + '/with_options'
require File.dirname(__FILE__) + '/backend/simple/setup'
require File.dirname(__FILE__) + '/backend/active_record/setup'

Dir[File.dirname(__FILE__) + '/api/**/*.rb'].each do |filename|
require filename
Expand Down

0 comments on commit 786632d

Please sign in to comment.