Add Globalize2 so that we get some nice fall backs to other languages when a translation is missing in the requested language.
This commit is contained in:
parent
6ba51da46e
commit
283a3e9ba9
42 changed files with 2670 additions and 0 deletions
102
vendor/plugins/globalize2/lib/globalize/backend/chain.rb
vendored
Normal file
102
vendor/plugins/globalize2/lib/globalize/backend/chain.rb
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
module I18n
|
||||
class << self
|
||||
def chain_backends(*args)
|
||||
self.backend = Globalize::Backend::Chain.new(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Globalize
|
||||
module Backend
|
||||
class Chain
|
||||
def initialize(*args)
|
||||
add(*args) unless args.empty?
|
||||
end
|
||||
|
||||
# Change this to a) accept any number of backends and b) accept classes.
|
||||
# When classes are passed instantiate them and add the instances as backends.
|
||||
# Return the added backends from #add.
|
||||
#
|
||||
# Add an initialize method that accepts the same arguments and passes them
|
||||
# to #add, so we could:
|
||||
# I18n.backend = Globalize::Backend::Chain.new(Globalize::Backend::Foo, Globalize::Backend::Bar)
|
||||
# Globalize::Backend::Chain.new(:foo, :bar)
|
||||
# Globalize.chain_backends :foo, :bar
|
||||
def add(*backends)
|
||||
backends.each do |backend|
|
||||
backend = Globalize::Backend.const_get(backend.to_s.capitalize) if backend.is_a? Symbol
|
||||
backend = backend.new if backend.is_a? Class
|
||||
self.backends << backend
|
||||
end
|
||||
end
|
||||
|
||||
def load_translations(*args)
|
||||
backends.each{|backend| backend.load_translations(*args) }
|
||||
end
|
||||
|
||||
# For defaults:
|
||||
# Never pass any default option to the backends but instead implement our own default
|
||||
# mechanism (e.g. symbols as defaults would need to be passed to the whole chain to
|
||||
# be translated).
|
||||
#
|
||||
# For namespace lookup:
|
||||
# Only return if the result is not a hash OR count is not present, otherwise merge them.
|
||||
# So in effect the count variable would control whether we have a namespace lookup or a
|
||||
# pluralization going on.
|
||||
#
|
||||
# Exceptions:
|
||||
# Make sure that we catch MissingTranslationData exceptions and raise
|
||||
# one in the end when no translation was found at all.
|
||||
#
|
||||
# For bulk translation:
|
||||
# If the key is an array we need to call #translate for each of the
|
||||
# keys and collect the results.
|
||||
|
||||
def translate(locale, key, options = {})
|
||||
raise I18n::InvalidLocale.new(locale) if locale.nil?
|
||||
return key.map{|k| translate locale, k, options } if key.is_a? Array
|
||||
|
||||
default = options.delete(:default)
|
||||
result = backends.inject({}) do |namespace, backend|
|
||||
begin
|
||||
translation = backend.translate(locale.to_sym, key, options)
|
||||
if namespace_lookup?(translation, options)
|
||||
namespace.merge! translation
|
||||
elsif translation
|
||||
return translation
|
||||
end
|
||||
rescue I18n::MissingTranslationData
|
||||
end
|
||||
end
|
||||
result || default(locale, default, options) || raise(I18n::MissingTranslationData.new(locale, key, options))
|
||||
end
|
||||
|
||||
def localize(locale, object, format = :default)
|
||||
backends.each do |backend|
|
||||
result = backend.localize(locale, object, format) and return result
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def backends
|
||||
@backends ||= []
|
||||
end
|
||||
|
||||
def default(locale, default, options = {})
|
||||
case default
|
||||
when String then default
|
||||
when Symbol then translate locale, default, options
|
||||
when Array then default.each do |obj|
|
||||
result = default(locale, obj, options.dup) and return result
|
||||
end and nil
|
||||
end
|
||||
rescue I18n::MissingTranslationData
|
||||
nil
|
||||
end
|
||||
|
||||
def namespace_lookup?(result, options)
|
||||
result.is_a?(Hash) and not options.has_key?(:count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
37
vendor/plugins/globalize2/lib/globalize/backend/pluralizing.rb
vendored
Normal file
37
vendor/plugins/globalize2/lib/globalize/backend/pluralizing.rb
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
require 'i18n/backend/simple'
|
||||
|
||||
module Globalize
|
||||
module Backend
|
||||
class Pluralizing < I18n::Backend::Simple
|
||||
def pluralize(locale, entry, count)
|
||||
return entry unless entry.is_a?(Hash) and count
|
||||
key = :zero if count == 0 && entry.has_key?(:zero)
|
||||
key ||= pluralizer(locale).call(count)
|
||||
raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
|
||||
translation entry[key], :plural_key => key
|
||||
end
|
||||
|
||||
def add_pluralizer(locale, pluralizer)
|
||||
pluralizers[locale.to_sym] = pluralizer
|
||||
end
|
||||
|
||||
def pluralizer(locale)
|
||||
pluralizers[locale.to_sym] || default_pluralizer
|
||||
end
|
||||
|
||||
protected
|
||||
def default_pluralizer
|
||||
pluralizers[:en]
|
||||
end
|
||||
|
||||
def pluralizers
|
||||
@pluralizers ||= { :en => lambda{|n| n == 1 ? :one : :other } }
|
||||
end
|
||||
|
||||
# Overwrite this method to return something other than a String
|
||||
def translation(string, attributes)
|
||||
string
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
60
vendor/plugins/globalize2/lib/globalize/backend/static.rb
vendored
Normal file
60
vendor/plugins/globalize2/lib/globalize/backend/static.rb
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
require 'globalize/backend/pluralizing'
|
||||
require 'globalize/locale/fallbacks'
|
||||
require 'globalize/translation'
|
||||
|
||||
module Globalize
|
||||
module Backend
|
||||
class Static < Pluralizing
|
||||
def initialize(*args)
|
||||
add(*args) unless args.empty?
|
||||
end
|
||||
|
||||
def translate(locale, key, options = {})
|
||||
result, default, fallback = nil, options.delete(:default), nil
|
||||
I18n.fallbacks[locale].each do |fallback|
|
||||
begin
|
||||
result = super(fallback, key, options) and break
|
||||
rescue I18n::MissingTranslationData
|
||||
end
|
||||
end
|
||||
result ||= default locale, default, options
|
||||
|
||||
attrs = {:requested_locale => locale, :locale => fallback, :key => key, :options => options}
|
||||
translation(result, attrs) || raise(I18n::MissingTranslationData.new(locale, key, options))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
alias :orig_interpolate :interpolate unless method_defined? :orig_interpolate
|
||||
def interpolate(locale, string, values = {})
|
||||
result = orig_interpolate(locale, string, values)
|
||||
translation = translation(string)
|
||||
translation.nil? ? result : translation.replace(result)
|
||||
end
|
||||
|
||||
def translation(result, meta = nil)
|
||||
return unless result
|
||||
|
||||
case result
|
||||
when Numeric
|
||||
result
|
||||
when String
|
||||
result = Translation::Static.new(result) unless result.is_a? Translation::Static
|
||||
result.set_meta meta
|
||||
result
|
||||
when Hash
|
||||
Hash[*result.map do |key, value|
|
||||
[key, translation(value, meta)]
|
||||
end.flatten]
|
||||
when Array
|
||||
result.map do |value|
|
||||
translation(value, meta)
|
||||
end
|
||||
else
|
||||
result
|
||||
# raise "unexpected translation type: #{result.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
41
vendor/plugins/globalize2/lib/globalize/i18n/missing_translations_log_handler.rb
vendored
Normal file
41
vendor/plugins/globalize2/lib/globalize/i18n/missing_translations_log_handler.rb
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
# A simple exception handler that behaves like the default exception handler
|
||||
# but additionally logs missing translations to a given log.
|
||||
#
|
||||
# Useful for identifying missing translations during testing.
|
||||
#
|
||||
# E.g.
|
||||
#
|
||||
# require 'globalize/i18n/missing_translations_log_handler
|
||||
# I18n.missing_translations_logger = RAILS_DEFAULT_LOGGER
|
||||
# I18n.exception_handler = :missing_translations_log_handler
|
||||
#
|
||||
# To set up a different log file:
|
||||
#
|
||||
# logger = Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
|
||||
# I18n.missing_translations_logger = logger
|
||||
|
||||
module I18n
|
||||
@@missing_translations_logger = nil
|
||||
|
||||
class << self
|
||||
def missing_translations_logger
|
||||
@@missing_translations_logger ||= begin
|
||||
require 'logger' unless defined?(Logger)
|
||||
Logger.new(STDOUT)
|
||||
end
|
||||
end
|
||||
|
||||
def missing_translations_logger=(logger)
|
||||
@@missing_translations_logger = logger
|
||||
end
|
||||
|
||||
def missing_translations_log_handler(exception, locale, key, options)
|
||||
if MissingTranslationData === exception
|
||||
missing_translations_logger.warn(exception.message)
|
||||
return exception.message
|
||||
else
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
vendor/plugins/globalize2/lib/globalize/i18n/missing_translations_raise_handler.rb
vendored
Normal file
27
vendor/plugins/globalize2/lib/globalize/i18n/missing_translations_raise_handler.rb
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# A simple exception handler that behaves like the default exception handler
|
||||
# but also raises on missing translations.
|
||||
#
|
||||
# Useful for identifying missing translations during testing.
|
||||
#
|
||||
# E.g.
|
||||
#
|
||||
# require 'globalize/i18n/missing_translations_raise_handler
|
||||
# I18n.exception_handler = :missing_translations_raise_handler
|
||||
module I18n
|
||||
class << self
|
||||
def missing_translations_raise_handler(exception, locale, key, options)
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
|
||||
# self.exception_handler = :missing_translations_raise_handler
|
||||
end
|
||||
|
||||
I18n.exception_handler = :missing_translations_raise_handler
|
||||
|
||||
ActionView::Helpers::TranslationHelper.module_eval do
|
||||
def translate(key, options = {})
|
||||
I18n.translate(key, options)
|
||||
end
|
||||
alias :t :translate
|
||||
end
|
63
vendor/plugins/globalize2/lib/globalize/load_path.rb
vendored
Normal file
63
vendor/plugins/globalize2/lib/globalize/load_path.rb
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
# Locale load_path and Locale loading support.
|
||||
#
|
||||
# To use this include the Globalize::LoadPath::I18n module to I18n like this:
|
||||
#
|
||||
# I18n.send :include, Globalize::LoadPath::I18n
|
||||
#
|
||||
# Clients can add load_paths using:
|
||||
#
|
||||
# I18n.load_path.add load_path, 'rb', 'yml' # pass any number of extensions like this
|
||||
# I18n.load_path << 'path/to/dir' # usage without an extension, defaults to 'yml'
|
||||
#
|
||||
# And load locale data using either of:
|
||||
#
|
||||
# I18n.load_locales 'en-US', 'de-DE'
|
||||
# I18n.load_locale 'en-US'
|
||||
#
|
||||
# This will lookup all files named like:
|
||||
#
|
||||
# 'path/to/dir/all.yml'
|
||||
# 'path/to/dir/en-US.yml'
|
||||
# 'path/to/dir/en-US/*.yml'
|
||||
#
|
||||
# The filenames will be passed to I18n.load_translations which delegates to
|
||||
# the backend. So the actual behaviour depends on the implementation of the
|
||||
# backend. I18n::Backend::Simple will be able to read YAML and plain Ruby
|
||||
# files. See the documentation for I18n.load_translations for details.
|
||||
|
||||
module Globalize
|
||||
class LoadPath < Array
|
||||
def extensions
|
||||
@extensions ||= ['rb', 'yml']
|
||||
end
|
||||
attr_writer :extensions
|
||||
|
||||
def locales
|
||||
@locales ||= ['*']
|
||||
end
|
||||
attr_writer :locales
|
||||
|
||||
def <<(path)
|
||||
push path
|
||||
end
|
||||
|
||||
def push(*paths)
|
||||
super(*paths.map{|path| filenames(path) }.flatten.uniq.sort)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def filenames(path)
|
||||
return [path] if File.file? path
|
||||
patterns(path).map{|pattern| Dir[pattern] }
|
||||
end
|
||||
|
||||
def patterns(path)
|
||||
locales.map do |locale|
|
||||
extensions.map do |extension|
|
||||
%W(#{path}/all.#{extension} #{path}/#{locale}.#{extension} #{path}/#{locale}/**/*.#{extension})
|
||||
end
|
||||
end.flatten.uniq
|
||||
end
|
||||
end
|
||||
end
|
63
vendor/plugins/globalize2/lib/globalize/locale/fallbacks.rb
vendored
Normal file
63
vendor/plugins/globalize2/lib/globalize/locale/fallbacks.rb
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
require 'globalize/locale/language_tag'
|
||||
|
||||
module I18n
|
||||
@@fallbacks = nil
|
||||
|
||||
class << self
|
||||
# Returns the current fallbacks. Defaults to +Globalize::Locale::Fallbacks+.
|
||||
def fallbacks
|
||||
@@fallbacks ||= Globalize::Locale::Fallbacks.new
|
||||
end
|
||||
|
||||
# Sets the current fallbacks. Used to set a custom fallbacks instance.
|
||||
def fallbacks=(fallbacks)
|
||||
@@fallbacks = fallbacks
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Globalize
|
||||
module Locale
|
||||
class Fallbacks < Hash
|
||||
def initialize(*defaults)
|
||||
@map = {}
|
||||
map defaults.pop if defaults.last.is_a?(Hash)
|
||||
|
||||
defaults = [I18n.default_locale.to_sym] if defaults.empty?
|
||||
self.defaults = defaults
|
||||
end
|
||||
|
||||
def defaults=(defaults)
|
||||
@defaults = defaults.map{|default| compute(default, false) }.flatten << :root
|
||||
end
|
||||
attr_reader :defaults
|
||||
|
||||
def [](tag)
|
||||
tag = tag.to_sym
|
||||
has_key?(tag) ? fetch(tag) : store(tag, compute(tag))
|
||||
end
|
||||
|
||||
def map(mappings)
|
||||
mappings.each do |from, to|
|
||||
from, to = from.to_sym, Array(to)
|
||||
to.each do |to|
|
||||
@map[from] ||= []
|
||||
@map[from] << to.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def compute(tags, include_defaults = true)
|
||||
result = Array(tags).collect do |tag|
|
||||
tags = LanguageTag::tag(tag.to_sym).parents(true).map! {|t| t.to_sym }
|
||||
tags.each{|tag| tags += compute(@map[tag]) if @map[tag] }
|
||||
tags
|
||||
end.flatten
|
||||
result.push *defaults if include_defaults
|
||||
result.uniq
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
81
vendor/plugins/globalize2/lib/globalize/locale/language_tag.rb
vendored
Normal file
81
vendor/plugins/globalize2/lib/globalize/locale/language_tag.rb
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
# for specifications see http://en.wikipedia.org/wiki/IETF_language_tag
|
||||
#
|
||||
# SimpleParser does not implement advanced usages such as grandfathered tags
|
||||
|
||||
module Globalize
|
||||
module Locale
|
||||
module Rfc4646
|
||||
SUBTAGS = [:language, :script, :region, :variant, :extension, :privateuse, :grandfathered]
|
||||
FORMATS = {:language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase}
|
||||
end
|
||||
|
||||
class LanguageTag < Struct.new(*Rfc4646::SUBTAGS)
|
||||
class << self
|
||||
def parser
|
||||
@@parser ||= SimpleParser
|
||||
end
|
||||
|
||||
def parser=(parser)
|
||||
@@parser = parser
|
||||
end
|
||||
|
||||
def tag(tag)
|
||||
matches = parser.match(tag)
|
||||
new *matches if matches
|
||||
end
|
||||
end
|
||||
|
||||
Rfc4646::FORMATS.each do |name, format|
|
||||
define_method(name) { self[name].send(format) unless self[name].nil? }
|
||||
end
|
||||
|
||||
def to_sym
|
||||
to_s.to_sym
|
||||
end
|
||||
|
||||
def to_s
|
||||
@tag ||= to_a.compact.join("-")
|
||||
end
|
||||
|
||||
def to_a
|
||||
members.collect {|attr| self.send(attr) }
|
||||
end
|
||||
|
||||
def parent
|
||||
segs = to_a.compact
|
||||
segs.length < 2 ? nil : LanguageTag.tag(segs[0..(segs.length-2)].join('-'))
|
||||
end
|
||||
|
||||
def parents(include_self = true)
|
||||
result, parent = [], self.dup
|
||||
result << parent if include_self
|
||||
while parent = parent.parent
|
||||
result << parent
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
module SimpleParser
|
||||
PATTERN = %r{\A(?:
|
||||
([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language
|
||||
(?:-([a-z]{4}))? # script
|
||||
(?:-([a-z]{2}|\d{3}))? # region
|
||||
(?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant
|
||||
(?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension
|
||||
(?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag
|
||||
(x(?:-[0-9a-z]{1,8})+)| # privateuse tag
|
||||
/* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered
|
||||
)\z}xi
|
||||
|
||||
class << self
|
||||
def match(tag)
|
||||
c = PATTERN.match(tag.to_s).captures
|
||||
c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here?
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
38
vendor/plugins/globalize2/lib/globalize/model/active_record.rb
vendored
Normal file
38
vendor/plugins/globalize2/lib/globalize/model/active_record.rb
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
require 'globalize/translation'
|
||||
require 'globalize/locale/fallbacks'
|
||||
require 'globalize/model/active_record/adapter'
|
||||
require 'globalize/model/active_record/translated'
|
||||
|
||||
module Globalize
|
||||
module Model
|
||||
module ActiveRecord
|
||||
class << self
|
||||
def create_proxy_class(klass)
|
||||
Object.const_set "#{klass.name}Translation", Class.new(::ActiveRecord::Base){
|
||||
belongs_to "#{klass.name.underscore}".intern
|
||||
|
||||
def locale
|
||||
read_attribute(:locale).to_sym
|
||||
end
|
||||
|
||||
def locale=(locale)
|
||||
write_attribute(:locale, locale.to_s)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def define_accessors(klass, attr_names)
|
||||
attr_names.each do |attr_name|
|
||||
klass.send :define_method, attr_name, lambda {
|
||||
globalize.fetch self.class.locale, attr_name
|
||||
}
|
||||
klass.send :define_method, "#{attr_name}=", lambda {|val|
|
||||
globalize.stash self.class.locale, attr_name, val
|
||||
self[attr_name] = val
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
96
vendor/plugins/globalize2/lib/globalize/model/active_record/adapter.rb
vendored
Normal file
96
vendor/plugins/globalize2/lib/globalize/model/active_record/adapter.rb
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
module Globalize
|
||||
module Model
|
||||
class AttributeStash < Hash
|
||||
def contains?(locale, attr_name)
|
||||
locale = locale.to_sym
|
||||
self[locale] ||= {}
|
||||
self[locale].has_key? attr_name
|
||||
end
|
||||
|
||||
def read(locale, attr_name)
|
||||
locale = locale.to_sym
|
||||
self[locale] ||= {}
|
||||
self[locale][attr_name]
|
||||
end
|
||||
|
||||
def write(locale, attr_name, value)
|
||||
locale = locale.to_sym
|
||||
self[locale] ||= {}
|
||||
self[locale][attr_name] = value
|
||||
end
|
||||
end
|
||||
|
||||
class Adapter
|
||||
def initialize(record)
|
||||
@record = record
|
||||
|
||||
# TODO what exactly are the roles of cache and stash
|
||||
@cache = AttributeStash.new
|
||||
@stash = AttributeStash.new
|
||||
end
|
||||
|
||||
def fetch(locale, attr_name)
|
||||
# locale = I18n.locale
|
||||
is_cached = @cache.contains?(locale, attr_name)
|
||||
is_cached ? @cache.read(locale, attr_name) : begin
|
||||
value = fetch_attribute locale, attr_name
|
||||
@cache.write locale, attr_name, value if value && value.locale == locale
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
def stash(locale, attr_name, value)
|
||||
@stash.write locale, attr_name, value
|
||||
@cache.write locale, attr_name, value
|
||||
end
|
||||
|
||||
def update_translations!
|
||||
@stash.each do |locale, attrs|
|
||||
translation = @record.globalize_translations.find_or_initialize_by_locale(locale.to_s)
|
||||
attrs.each{|attr_name, value| translation[attr_name] = value }
|
||||
translation.save!
|
||||
end
|
||||
@stash.clear
|
||||
end
|
||||
|
||||
# Clears the cache
|
||||
def clear
|
||||
@cache.clear
|
||||
@stash.clear
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_attribute(locale, attr_name)
|
||||
fallbacks = I18n.fallbacks[locale].map{|tag| tag.to_s}.map(&:to_sym)
|
||||
|
||||
# If the translations were included with
|
||||
# :include => globalize_translations
|
||||
# there is no need to query them again.
|
||||
unless @record.globalize_translations.loaded?
|
||||
translations = @record.globalize_translations.by_locales(fallbacks)
|
||||
else
|
||||
translations = @record.globalize_translations
|
||||
end
|
||||
result, requested_locale = nil, locale
|
||||
|
||||
# Walk through the fallbacks, starting with the current locale itself, and moving
|
||||
# to the next best choice, until we find a match.
|
||||
# Check the @globalize_set_translations cache first to see if we've just changed the
|
||||
# attribute and not saved yet.
|
||||
fallbacks.each do |fallback|
|
||||
# TODO should we be checking stash or just cache?
|
||||
result = @stash.read(fallback, attr_name) || begin
|
||||
translation = translations.detect {|tr| tr.locale == fallback }
|
||||
translation && translation.send(attr_name)
|
||||
end
|
||||
if result
|
||||
locale = fallback
|
||||
break
|
||||
end
|
||||
end
|
||||
result && Translation::Attribute.new(result, :locale => locale, :requested_locale => requested_locale)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
154
vendor/plugins/globalize2/lib/globalize/model/active_record/translated.rb
vendored
Normal file
154
vendor/plugins/globalize2/lib/globalize/model/active_record/translated.rb
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
module Globalize
|
||||
module Model
|
||||
|
||||
class MigrationError < StandardError; end
|
||||
class UntranslatedMigrationField < MigrationError; end
|
||||
class MigrationMissingTranslatedField < MigrationError; end
|
||||
class BadMigrationFieldType < MigrationError; end
|
||||
|
||||
module ActiveRecord
|
||||
module Translated
|
||||
def self.included(base)
|
||||
base.extend ActMethods
|
||||
end
|
||||
|
||||
module ActMethods
|
||||
def translates(*attr_names)
|
||||
options = attr_names.extract_options!
|
||||
options[:translated_attributes] = attr_names
|
||||
|
||||
# Only set up once per class
|
||||
unless included_modules.include? InstanceMethods
|
||||
class_inheritable_accessor :globalize_options, :globalize_proxy
|
||||
|
||||
include InstanceMethods
|
||||
extend ClassMethods
|
||||
|
||||
self.globalize_proxy = Globalize::Model::ActiveRecord.create_proxy_class(self)
|
||||
has_many(
|
||||
:globalize_translations,
|
||||
:class_name => globalize_proxy.name,
|
||||
:extend => Extensions,
|
||||
:dependent => :delete_all,
|
||||
:foreign_key => class_name.foreign_key
|
||||
)
|
||||
|
||||
after_save :update_globalize_record
|
||||
end
|
||||
|
||||
self.globalize_options = options
|
||||
Globalize::Model::ActiveRecord.define_accessors(self, attr_names)
|
||||
|
||||
# Import any callbacks that have been defined by extensions to Globalize2
|
||||
# and run them.
|
||||
extend Callbacks
|
||||
Callbacks.instance_methods.each {|cb| send cb }
|
||||
end
|
||||
|
||||
def locale=(locale)
|
||||
@@locale = locale
|
||||
end
|
||||
|
||||
def locale
|
||||
(defined?(@@locale) && @@locale) || I18n.locale
|
||||
end
|
||||
end
|
||||
|
||||
# Dummy Callbacks module. Extensions to Globalize2 can insert methods into here
|
||||
# and they'll be called at the end of the translates class method.
|
||||
module Callbacks
|
||||
end
|
||||
|
||||
# Extension to the has_many :globalize_translations association
|
||||
module Extensions
|
||||
def by_locales(locales)
|
||||
find :all, :conditions => { :locale => locales.map(&:to_s) }
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def method_missing(method, *args)
|
||||
if method.to_s =~ /^find_by_(\w+)$/ && globalize_options[:translated_attributes].include?($1.to_sym)
|
||||
find(:first, :joins => :globalize_translations,
|
||||
:conditions => [ "#{i18n_attr($1)} = ? AND #{i18n_attr('locale')} IN (?)",
|
||||
args.first,I18n.fallbacks[I18n.locale].map{|tag| tag.to_s}])
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def create_translation_table!(fields)
|
||||
translated_fields = self.globalize_options[:translated_attributes]
|
||||
translated_fields.each do |f|
|
||||
raise MigrationMissingTranslatedField, "Missing translated field #{f}" unless fields[f]
|
||||
end
|
||||
fields.each do |name, type|
|
||||
unless translated_fields.member? name
|
||||
raise UntranslatedMigrationField, "Can't migrate untranslated field: #{name}"
|
||||
end
|
||||
unless [ :string, :text ].member? type
|
||||
raise BadMigrationFieldType, "Bad field type for #{name}, should be :string or :text"
|
||||
end
|
||||
end
|
||||
translation_table_name = self.name.underscore + '_translations'
|
||||
self.connection.create_table(translation_table_name) do |t|
|
||||
t.references self.table_name.singularize
|
||||
t.string :locale
|
||||
fields.each do |name, type|
|
||||
t.column name, type
|
||||
end
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
|
||||
def drop_translation_table!
|
||||
translation_table_name = self.name.underscore + '_translations'
|
||||
self.connection.drop_table translation_table_name
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def i18n_attr(attribute_name)
|
||||
self.base_class.name.underscore + "_translations.#{attribute_name}"
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def reload(options = nil)
|
||||
globalize.clear
|
||||
|
||||
# clear all globalized attributes
|
||||
# TODO what's the best way to handle this?
|
||||
self.class.globalize_options[:translated_attributes].each do |attr|
|
||||
@attributes.delete attr.to_s
|
||||
end
|
||||
|
||||
super options
|
||||
end
|
||||
|
||||
def globalize
|
||||
@globalize ||= Adapter.new self
|
||||
end
|
||||
|
||||
def update_globalize_record
|
||||
globalize.update_translations!
|
||||
end
|
||||
|
||||
def translated_locales
|
||||
globalize_translations.scoped(:select => 'DISTINCT locale').map {|gt| gt.locale.to_sym }
|
||||
end
|
||||
|
||||
def set_translations options
|
||||
options.keys.each do |key|
|
||||
|
||||
translation = globalize_translations.find_by_locale(key.to_s) ||
|
||||
globalize_translations.build(:locale => key.to_s)
|
||||
translation.update_attributes!(options[key])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
32
vendor/plugins/globalize2/lib/globalize/translation.rb
vendored
Normal file
32
vendor/plugins/globalize2/lib/globalize/translation.rb
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
module Globalize
|
||||
# Translations are simple value objects that carry some context information
|
||||
# alongside the actual translation string.
|
||||
|
||||
class Translation < String
|
||||
class Attribute < Translation
|
||||
attr_accessor :requested_locale, :locale, :key
|
||||
end
|
||||
|
||||
class Static < Translation
|
||||
attr_accessor :requested_locale, :locale, :key, :options, :plural_key, :original
|
||||
|
||||
def initialize(string, meta = nil)
|
||||
self.original = string
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(string, meta = nil)
|
||||
set_meta meta
|
||||
super string
|
||||
end
|
||||
|
||||
def fallback?
|
||||
locale.to_sym != requested_locale.to_sym
|
||||
end
|
||||
|
||||
def set_meta(meta)
|
||||
meta.each {|name, value| send :"#{name}=", value } if meta
|
||||
end
|
||||
end
|
||||
end
|
3
vendor/plugins/globalize2/lib/locale/root.yml
vendored
Normal file
3
vendor/plugins/globalize2/lib/locale/root.yml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
root:
|
||||
bidi:
|
||||
direction: left-to-right
|
40
vendor/plugins/globalize2/lib/rails_edge_load_path_patch.rb
vendored
Normal file
40
vendor/plugins/globalize2/lib/rails_edge_load_path_patch.rb
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
module I18n
|
||||
@@load_path = nil
|
||||
@@default_locale = :'en-US'
|
||||
|
||||
class << self
|
||||
def load_path
|
||||
@@load_path ||= []
|
||||
end
|
||||
|
||||
def load_path=(load_path)
|
||||
@@load_path = load_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
I18n::Backend::Simple.module_eval do
|
||||
def initialized?
|
||||
@initialized ||= false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def init_translations
|
||||
load_translations(*I18n.load_path)
|
||||
@initialized = true
|
||||
end
|
||||
|
||||
def lookup(locale, key, scope = [])
|
||||
return unless key
|
||||
init_translations unless initialized?
|
||||
keys = I18n.send :normalize_translation_keys, locale, key, scope
|
||||
keys.inject(translations){|result, k| result[k.to_sym] or return nil }
|
||||
end
|
||||
end
|
||||
|
||||
rails_dir = File.expand_path "#{File.dirname(__FILE__)}/../../../rails/"
|
||||
paths = %w(actionpack/lib/action_view/locale/en-US.yml
|
||||
activerecord/lib/active_record/locale/en-US.yml
|
||||
activesupport/lib/active_support/locale/en-US.yml)
|
||||
paths.each{|path| I18n.load_path << "#{rails_dir}/#{path}" }
|
Loading…
Add table
Add a link
Reference in a new issue