Permalink
Browse files

add and adapt Globalize2 fallback implementation

  • Loading branch information...
1 parent 32ddc80 commit 1b37a303b27d6222b17162804b06323e5628768f Sven Fuchs committed Jul 12, 2009
@@ -0,0 +1,18 @@
+require 'i18n/backend/simple'
+require 'i18n/locale/fallbacks'
+
+module I18n
+ module Backend
+ module Fallbacks
+ def translate(locale, key, options = {})
+ I18n.fallbacks[locale].each do |fallback|
+ begin
+ result = super(fallback, key, options) and return result
+ rescue I18n::MissingTranslationData
+ end
+ end
+ raise(I18n::MissingTranslationData.new(locale, key, options))
+ end
+ end
+ end
+end
@@ -0,0 +1,75 @@
+require 'i18n/locale/tag'
+
+module I18n
+ @@fallbacks = nil
+
+ class << self
+ # Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
+ def fallbacks
+ @@fallbacks ||= I18n::Locale::Fallbacks.new
+ end
+
+ # Sets the current fallbacks implementation. Use this to set a differen fallbacks implementation.
+ def fallbacks=(fallbacks)
+ @@fallbacks = fallbacks
+ end
+
+ # Locale tag factory. Uses I18n::Locale::Tag by default.
+ def tag(tag)
+ tags.tag(tag.to_sym)
+ end
+
+ def tags
+ @@tags ||= I18n::Locale::Tag
+ end
+
+ def tags=(tag_class)
+ @@tags = tag_class
+ end
+ end
+
+ 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
+ end
+ attr_reader :defaults
+
+ def [](locale)
+ raise InvalidLocale.new(locale) if locale.nil?
+ locale = locale.to_sym
+ has_key?(locale) ? fetch(locale) : store(locale, compute(locale))
+ 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 = I18n.tag(tag).self_and_parents.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
View
@@ -0,0 +1,26 @@
+# for specifications see http://en.wikipedia.org/wiki/IETF_language_tag
+#
+# SimpleParser does not implement advanced usages such as grandfathered tags
+
+require 'i18n/locale/tag/basic'
+require 'i18n/locale/tag/rfc4646'
+
+module I18n
+ module Locale
+ module Tag
+ class << self
+ def implementation
+ @@implementation ||= Rfc4646
+ end
+
+ def implementation=(implementation)
+ @@implementation = implementation
+ end
+
+ def tag(tag)
+ implementation.tag(tag)
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,39 @@
+require 'i18n/locale/tag/parents'
+
+module I18n
+ module Locale
+ module Tag
+ class Basic
+ class << self
+ def tag(tag)
+ new(tag)
+ end
+ end
+
+ include Parents
+
+ attr_reader :tag
+
+ def initialize(*tag)
+ @tag = tag.join('-').to_sym
+ end
+
+ def subtags
+ @subtags = tag.to_s.split('-').map { |subtag| subtag.to_s }
+ end
+
+ def to_sym
+ tag
+ end
+
+ def to_s
+ tag.to_s
+ end
+
+ def to_a
+ subtags
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,22 @@
+module I18n
+ module Locale
+ module Tag
+ module Parents
+ def parent
+ @parent ||= begin
+ segs = to_a.compact
+ segs.length > 1 ? self.class.tag(*segs[0..(segs.length-2)].join('-')) : nil
+ end
+ end
+
+ def self_and_parents
+ @self_and_parents ||= [self] + parents
+ end
+
+ def parents
+ @parents ||= ([parent] + (parent ? parent.parents : [])).compact
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,71 @@
+# for specifications see http://en.wikipedia.org/wiki/IETF_language_tag
+#
+# Rfc4646::Parser does not implement advanced usages such as grandfathered tags
+
+require 'i18n/locale/tag/parents'
+
+module I18n
+ module Locale
+ module Tag
+ RFC4646_SUBTAGS = [ :language, :script, :region, :variant, :extension, :privateuse, :grandfathered ]
+ RFC4646_FORMATS = { :language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase }
+
+ class Rfc4646 < Struct.new(*RFC4646_SUBTAGS)
+ class << self
+ def parser
+ @@parser ||= Rfc4646::Parser
+ end
+
+ def parser=(parser)
+ @@parser = parser
+ end
+
+ def tag(tag)
+ matches = parser.match(tag)
+ new(*matches) if matches
+ end
+ end
+
+ include Parents
+
+ 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
+
+ module Parser
+ 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
+end
@@ -0,0 +1,59 @@
+require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
+require 'i18n/backend/fallbacks'
+
+# class I18nFallbacksBackendApiBasicsTest < Test::Unit::TestCase
+# include Tests::Backend::Simple::Setup::Base
+# include Tests::Backend::Fallbacks::Setup
+# include Tests::Backend::Api::Basics
+# end
+
+class I18nFallbacksBackendApiTranslateTest < Test::Unit::TestCase
+ include Tests::Backend::Simple::Setup::Base
+ include Tests::Backend::Fallbacks::Setup
+ include Tests::Backend::Api::Translation
+end
+
+# class I18nFallbacksBackendApiInterpolateTest < Test::Unit::TestCase
+# include Tests::Backend::Simple::Setup::Base
+# include Tests::Backend::Fallbacks::Setup
+# include Tests::Backend::Api::Interpolation
+# end
+#
+# class I18nFallbacksBackendApiLambdaTest < Test::Unit::TestCase
+# include Tests::Backend::Simple::Setup::Base
+# include Tests::Backend::Fallbacks::Setup
+# include Tests::Backend::Api::Lambda
+# end
+#
+# class I18nFallbacksBackendApiTranslateLinkedTest < Test::Unit::TestCase
+# include Tests::Backend::Simple::Setup::Base
+# include Tests::Backend::Fallbacks::Setup
+# include Tests::Backend::Api::Link
+# end
+#
+# class I18nFallbacksBackendApiPluralizationTest < Test::Unit::TestCase
+# include Tests::Backend::Simple::Setup::Base
+# include Tests::Backend::Fallbacks::Setup
+# include Tests::Backend::Api::Pluralization
+# end
+
+# class I18nFallbacksBackendApiLocalizeDateTest < Test::Unit::TestCase
+# include Tests::Backend::Fallbacks::Setup::Localization
+# include Tests::Backend::Api::Localization::Date
+# end
+#
+# class I18nFallbacksBackendApiLocalizeDateTimeTest < Test::Unit::TestCase
+# include Tests::Backend::Fallbacks::Setup::Localization
+# include Tests::Backend::Api::Localization::DateTime
+# end
+#
+# class I18nFallbacksBackendApiLocalizeTimeTest < Test::Unit::TestCase
+# include Tests::Backend::Fallbacks::Setup::Localization
+# include Tests::Backend::Api::Localization::Time
+# end
+#
+# class I18nFallbacksBackendApiLocalizeLambdaTest < Test::Unit::TestCase
+# include Tests::Backend::Fallbacks::Setup::Localization
+# include Tests::Backend::Api::Localization::Lambda
+# end
+
@@ -0,0 +1,14 @@
+module Tests
+ module Backend
+ module Fallbacks
+ module Setup
+ def setup
+ super
+ class << I18n.backend
+ include I18n::Backend::Fallbacks
+ end
+ end
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 1b37a30

Please sign in to comment.