Update to rails 2.3.8

Update site to use rails 2.3.8 which also allows us to drop the
globalize2 plugin as the core rails i18n code can now do fallback
and pluralization.
This commit is contained in:
Tom Hughes 2010-05-26 19:54:10 +01:00
parent 27c812505d
commit d516198bbe
47 changed files with 71 additions and 2721 deletions

View file

@ -77,7 +77,7 @@ private
def javascript_strings_for_key(key)
js = ""
value = t(key, :locale => "en")
value = I18n.t(key, :locale => "en")
if value.is_a?(String)
js << "i18n_strings['#{key}'] = '" << escape_javascript(t(key)) << "';\n"

View file

@ -5,7 +5,7 @@
ENV['RAILS_ENV'] ||= 'production'
# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION
RAILS_GEM_VERSION = '2.3.8' unless defined? RAILS_GEM_VERSION
# Set the server URL
SERVER_URL = ENV['OSM_SERVER_URL'] || 'www.openstreetmap.org'
@ -80,8 +80,8 @@ Rails::Initializer.run do |config|
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
config.action_controller.session = {
:session_key => '_osm_session',
:secret => 'd886369b1e709c61d1f9fcb07384a2b96373c83c01bfc98c6611a9fe2b6d0b14215bb360a0154265cccadde5489513f2f9b8d9e7b384a11924f772d2872c2a1f'
:key => '_osm_session',
:secret => 'd886369b1e709c61d1f9fcb07384a2b96373c83c01bfc98c6611a9fe2b6d0b14215bb360a0154265cccadde5489513f2f9b8d9e7b384a11924f772d2872c2a1f'
}
# Use the database for sessions instead of the cookie-based default,

View file

@ -1,11 +1,6 @@
require 'globalize/i18n/missing_translations_log_handler'
I18n.missing_translations_logger = Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
I18n.exception_handler = :missing_translations_log_handler
module I18n
module Backend
class Simple
module Base
protected
alias_method :old_init_translations, :init_translations
@ -18,13 +13,26 @@ module I18n
friendly = translate('en', 'time.formats.friendly')
available_locales.each do |locale|
time_formats = I18n.t('time.formats', :locale => locale)
unless time_formats.has_key?(:friendly)
unless lookup(locale, 'time.formats.friendly')
store_translations(locale, :time => { :formats => { :friendly => friendly } })
end
end
end
end
module PluralizationFallback
def pluralize(locale, entry, count)
super
rescue InvalidPluralizationData => ex
raise ex unless ex.entry.has_key?(:other)
ex.entry[:other]
end
end
end
end
I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
I18n::Backend::Simple.send(:include, I18n::Backend::PluralizationFallback)
I18n.load_path << RAILS_ROOT + "/config/pluralizers.rb"
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)

50
config/pluralizers.rb Normal file
View file

@ -0,0 +1,50 @@
{
:ar => {
:i18n => {
:plural => {
:rule => lambda { |count|
case count
when 1 then :one
when 2 then :two
else case count % 100
when 3..10 then :few
when 11..99 then :many
else :other
end
end
}
}
}
},
:ru => {
:i18n => {
:plural => {
:rule => lambda { |count|
case count % 100
when 11,12,13,14 then :many
else case count % 10
when 1 then :one
when 2,3,4 then :few
when 5,6,7,8,9,0 then :many
else :other
end
end
}
}
}
},
:sl => {
:i18n => {
:plural => {
:rule => lambda { |count|
case count % 100
when 1 then :one
when 2 then :two
when 3,4 then :few
else :other
end
}
}
}
}
}

View file

@ -1,4 +0,0 @@
doc
spec/spec/db/*
vendor
NOTES

View file

@ -1,21 +0,0 @@
The MIT License
Copyright (c) 2008, 2009 Joshua Harvey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,202 +0,0 @@
h1. Globalize2
Globalize2 is the successor of Globalize for Rails.
It is compatible with and builds on the new "I18n api in Ruby on Rails":http://rails-i18n.org. and adds model translations as well as a bunch of other useful features, such as Locale fallbacks (RFC4647 compliant) and automatic loading of Locale data from defined directory/file locations.
Globalize2 is much more lightweight and modular than its predecessor was. Content translations in Globalize2 use default ActiveRecord features and do not limit any functionality any more.
All features and tools in Globalize2 are implemented in the most unobstrusive and loosely-coupled way possible, so you can pick whatever features or tools you need for your application and combine them with other tools from other libraries or plugins.
h2. Requirements
Rails 2.2 (currently Rails edge)
h2. Installation
To install Globalize2 with its default setup just use:
<pre><code>
script/plugin install git://github.com/joshmh/globalize2.git
</code></pre>
This will:
* activate model translations
* set I18n.load_path to an instance of Globalize::LoadPath
* set I18n.backend to an instance of Globalize::Backend::Static
h2. Configuration
You might want to add additional configuration to an initializer, e.g. config/initializers/globalize.rb
h2. Model translations
Model translations (or content translations) allow you to translate your models' attribute values. E.g.
<pre><code>
class Post < ActiveRecord::Base
translates :title, :text
end
</code></pre>
Allows you to values for the attributes :title and :text per locale:
<pre><code>
I18n.locale = :en
post.title # Globalize2 rocks!
I18n.locale = :he
post.title # גלובאלייז2 שולט!
</code></pre>
In order to make this work, you'll need to add the appropriate translation tables. Globalize2 comes with a handy helper method to help you do this. It's called @create_translation_table!@. Here's an example:
<pre><code>
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.timestamps
end
Post.create_translation_table! :title => :string, :text => :text
end
def self.down
drop_table :posts
Post.drop_translation_table!
end
end
</code></pre>
Note that the ActiveRecord model @Post@ must already exist and have a @translates@ directive listing the translated fields.
h2. Globalize::Backend::Static
Globalize2 ships with a Static backend that builds on the Simple backend from the I18n library (which is shipped with Rails) and adds the following features:
* It uses locale fallbacks when looking up translation data.
* It returns an instance of Globalize::Translation::Static instead of a plain Ruby String as a translation.
* It allows to hook in custom pluralization logic as lambdas.
h2. Custom pluralization logic
The Simple backend has its pluralization algorithm baked in hardcoded. This algorithm is only suitable for English and other languages that have the same pluralization rules. It is not suitable for, e.g., Czech though.
To add custom pluralization logic to Globalize' Static backend you can do something like this:
<pre><code>
@backend.add_pluralizer :cz, lambda{|c|
c == 1 ? :one : (2..4).include?(c) ? :few : :other
}
</code></pre>
h2. Locale Fallbacks
Globalize2 ships with a Locale fallback tool which extends the I18n module to hold a fallbacks instance which is set to an instance of Globalize::Locale::Fallbacks by default but can be swapped with a different implementation.
Globalize2 fallbacks will compute a number of other locales for a given locale. For example:
<pre><code>
I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :"en-US", :en]
</code></pre>
Globalize2 fallbacks always fall back to
* all parents of a given locale (e.g. :es for :"es-MX"),
* then to the fallbacks' default locales and all of their parents and
* finally to the :root locale.
The default locales are set to [:"en-US"] by default but can be set to something else. The root locale is a concept borrowed from "CLDR":http://unicode.org and makes sense for storing common locale data which works as a last default fallback (e.g. "ltr" for bidi directions).
One can additionally add any number of additional fallback locales manually. These will be added before the default locales to the fallback chain. For example:
<pre><code>
fb = I18n.fallbacks
fb.map :ca => :"es-ES"
fb[:ca] # => [:ca, :"es-ES", :es, :"en-US", :en]
fb.map :"ar-PS" => :"he-IL"
fb[:"ar-PS"] # => [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en]
fb[:"ar-EG"] # => [:"ar-EG", :ar, :"en-US", :en]
fb.map :sms => [:"se-FI", :"fi-FI"]
fb[:sms] # => [:sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en]
</code></pre>
h2. Globalize::LoadPath
Globalize2 replaces the plain Ruby array that is set to I18n.load_path by default through an instance of Globalize::LoadPath.
This object can be populated with both paths to files and directories. If a path to a directory is added to it it will look up all locale data files present in that directory enforcing the following convention:
<pre><code>
I18n.load_path << "#{RAILS_ROOT}/lib/locales"
# will load all the following files if present:
lib/locales/all.yml
lib/locales/fr.yml
lib/locales/fr/*.yaml
lib/locales/ru.yml
lib/locales/ru/*.yaml
...
</code></pre>
One can also specify which locales are used. By default this is set to "*" meaning that files for all locales are added. To define that only files for the locale :es are added one can specify:
<pre><code>
I18n.load_path.locales = [:es]
</code></pre>
One can also specify which file extensions are used. By default this is set to ['rb', 'yml'] so plain Ruby and YAML files are added if found. To define that only *.sql files are added one can specify:
<pre><code>
I18n.load_path.extensions = ['sql']
</code></pre>
Note that Globalize::LoadPath "expands" a directory to its contained file paths immediately when you add it to the load_path. Thus, if you change the locales or extensions settings in the middle of your application the change won't be applied to already added file paths.
h2. Globalize::Translation classes
Globalize2's Static backend as well as Globalize2 model translations return instances of Globalize::Translation classes (instead of plain Ruby Strings). These are simple and lightweight value objects that carry some additional meta data about the translation and how it was looked up.
Model translations return instances of Globalize::Translation::Attribute, the Static backend returns instances of Globalize::Translation::Static.
For example:
<pre><code>
I18n.locale = :de
# Translation::Attribute
title = Post.first.title # assuming that no translation can be found:
title.locale # => :en
title.requested_locale # => :de
title.fallback? # => true
# Translation::Static
rails = I18n.t :rails # assuming that no translation can be found:
rails.locale # => :en
rails.requested_locale # => :de
rails.fallback? # => true
rails.options # returns the options passed to #t
rails.plural_key # returns the plural_key (e.g. :one, :other)
rails.original # returns the original translation with no values
# interpolated to it (e.g. "Hi {{name}}!")
</code></pre>
h2. Missing Translations Log Handler
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

View file

@ -1,25 +0,0 @@
class ActsAsTaggableMigration < ActiveRecord::Migration
def self.up
create_table :globalize_translations do |t|
t.string :locale, :null => false
t.string :key, :null => false
t.string :translation
t.timestamps
end
# TODO: FINISH DOING MIGRATION -- stopped in the middle
create_table :globalize_translations_map do |t|
t.string :key, :null => false
t.integer :translation_id, :null => false
end
add_index :taggings, :tag_id
add_index :taggings, [:taggable_id, :taggable_type]
end
def self.down
drop_table :globalize_translations
drop_table :tags
end
end

View file

@ -1,10 +0,0 @@
require 'rails_edge_load_path_patch.rb' unless I18n.respond_to?(:load_path)
ActiveRecord::Base.send :include, Globalize::Model::ActiveRecord::Translated
I18n.backend = Globalize::Backend::Static.new
I18n.load_path = Globalize::LoadPath.new I18n.load_path
I18n.load_path << "#{File.dirname(__FILE__)}/lib/locale"
I18n.load_path << "#{RAILS_ROOT}/lib/locale"

View file

@ -1,102 +0,0 @@
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

View file

@ -1,75 +0,0 @@
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, entry)
key = :other unless entry.has_key?(key)
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 { |count, entry|
case count
when 1 then :one
else :other
end
},
:ar => lambda { |count, entry|
case count
when 1 then :one
when 2 then :two
else case count % 100
when 3..10 then :few
when 11..99 then :many
else :other
end
end
},
:ru => lambda { |count, entry|
case count % 100
when 11,12,13,14 then :many
else case count % 10
when 1 then :one
when 2,3,4 then :few
when 5,6,7,8,9,0 then :many
else :other
end
end
},
:sl => lambda { |count, entry|
case count % 100
when 1 then :one
when 2 then :two
when 3,4 then :few
else :other
end
}
}
end
# Overwrite this method to return something other than a String
def translation(string, attributes)
string
end
end
end
end

View file

@ -1,60 +0,0 @@
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

View file

@ -1,41 +0,0 @@
# 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

View file

@ -1,27 +0,0 @@
# 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

View file

@ -1,63 +0,0 @@
# 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

View file

@ -1,63 +0,0 @@
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

View file

@ -1,81 +0,0 @@
# 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

View file

@ -1,38 +0,0 @@
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

View file

@ -1,96 +0,0 @@
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

View file

@ -1,154 +0,0 @@
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

View file

@ -1,32 +0,0 @@
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

View file

@ -1,3 +0,0 @@
root:
bidi:
direction: left-to-right

View file

@ -1,40 +0,0 @@
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}" }

View file

@ -1,51 +0,0 @@
Stopped DB Backend in the middle, here's where we left off:
h1. Some Notes
* Started doing the migration generator in generators/db_backend.rb
* Translation keys will be in dotted string format
* Question: Do we need a plural_key column, or can we build it in to the dotted key?
* We will probably have to code the following methods from scratch, to optimize db calls:
** translate
** localize
** pluralize
* We should refactor @interpolation@ code so that it can be included into backend code without inheriting SimpleBackend
** Rationale: interpolation is something done entirely after a string is fetched from the data store
** Alternately, it could be done from within the I18n module
h1. Schema
There will be two db tables.
# globalize_translations will have: locale, key, translation, created_at, updated_at.
# globalize_translations_map will have: key, translation_id.
globalize_translations_map will let us easily fetch entire sub-trees of namespaces.
However, this table may not be necessary, as it may be feasible to just use key LIKE "some.namespace.%".
h1. Caching
We'll almost certainly want to implement caching in the backend. Should probably be a customized
implementation based on the Rails caching mechanism, to support memcached, etc.
h1. Queries
We'll want to pull in lots of stuff at once and return a single translation based on some
quick Ruby selection. The query will look something like this:
<pre>
<code>
SELECT * FROM globalize_translations
WHERE locale in (<fallbacks>) AND
key IN (key, default_key)
</code>
</pre>
The Ruby code would then pick the first translation that satisfies a fallback, in fallback order.
Of course, the records with the supplied key would take precedence of those with the default key.
h1. Misc
We should revisit the :zero plural code. On the one hand it's certainly useful for
many apps in many languages. On the other hand it's not mentioned in CLDR, and not a real
concept in language pluralization. Right now, I'm feeling it's still a good idea to keep it in.

View file

@ -1,175 +0,0 @@
require File.join( File.dirname(__FILE__), '..', 'test_helper' )
require 'globalize/backend/chain'
module Globalize
module Backend
class Dummy
def translate(locale, key, options = {})
end
end
end
end
class ChainedTest < ActiveSupport::TestCase
test "instantiates a chained backend and sets test as backend" do
assert_nothing_raised { I18n.chain_backends }
assert_instance_of Globalize::Backend::Chain, I18n.backend
end
test "passes all given arguments to the chained backends #initialize method" do
Globalize::Backend::Chain.expects(:new).with(:spec, :simple)
I18n.chain_backends :spec, :simple
end
test "passes all given arguments to #add assuming that they are backends" do
# no idea how to spec that
end
end
class AddChainedTest < ActiveSupport::TestCase
def setup
I18n.backend = Globalize::Backend::Chain.new
end
test "accepts an instance of a backend" do
assert_nothing_raised { I18n.backend.add Globalize::Backend::Dummy.new }
assert_instance_of Globalize::Backend::Dummy, I18n.backend.send(:backends).first
end
test "accepts a class and instantiates the backend" do
assert_nothing_raised { I18n.backend.add Globalize::Backend::Dummy }
assert_instance_of Globalize::Backend::Dummy, I18n.backend.send(:backends).first
end
test "accepts a symbol, constantizes test as a backend class and instantiates the backend" do
assert_nothing_raised { I18n.backend.add :dummy }
assert_instance_of Globalize::Backend::Dummy, I18n.backend.send(:backends).first
end
test "accepts any number of backend instances, classes or symbols" do
assert_nothing_raised { I18n.backend.add Globalize::Backend::Dummy.new, Globalize::Backend::Dummy, :dummy }
assert_instance_of Globalize::Backend::Dummy, I18n.backend.send(:backends).first
assert_equal [ Globalize::Backend::Dummy, Globalize::Backend::Dummy, Globalize::Backend::Dummy ],
I18n.backend.send(:backends).map{|backend| backend.class }
end
end
class TranslateChainedTest < ActiveSupport::TestCase
def setup
I18n.locale = :en
I18n.backend = Globalize::Backend::Chain.new
@first_backend = I18n::Backend::Simple.new
@last_backend = I18n::Backend::Simple.new
I18n.backend.add @first_backend
I18n.backend.add @last_backend
end
test "delegates #translate to all backends in the order they were added" do
@first_backend.expects(:translate).with(:en, :foo, {})
@last_backend.expects(:translate).with(:en, :foo, {})
I18n.translate :foo
end
test "returns the result from #translate from the first backend if test's not nil" do
@first_backend.store_translations :en, {:foo => 'foo from first backend'}
@last_backend.store_translations :en, {:foo => 'foo from last backend'}
result = I18n.translate :foo
assert_equal 'foo from first backend', result
end
test "returns the result from #translate from the second backend if the first one returned nil" do
@first_backend.store_translations :en, {}
@last_backend.store_translations :en, {:foo => 'foo from last backend'}
result = I18n.translate :foo
assert_equal 'foo from last backend', result
end
test "looks up a namespace from all backends and merges them (if a result is a hash and no count option is present)" do
@first_backend.store_translations :en, {:foo => {:bar => 'bar from first backend'}}
@last_backend.store_translations :en, {:foo => {:baz => 'baz from last backend'}}
result = I18n.translate :foo
assert_equal( {:bar => 'bar from first backend', :baz => 'baz from last backend'}, result )
end
test "raises a MissingTranslationData exception if no translation was found" do
assert_raise( I18n::MissingTranslationData ) { I18n.translate :not_here, :raise => true }
end
test "raises an InvalidLocale exception if the locale is nil" do
assert_raise( I18n::InvalidLocale ) { Globalize::Backend::Chain.new.translate nil, :foo }
end
test "bulk translates a number of keys from different backends" do
@first_backend.store_translations :en, {:foo => 'foo from first backend'}
@last_backend.store_translations :en, {:bar => 'bar from last backend'}
result = I18n.translate [:foo, :bar]
assert_equal( ['foo from first backend', 'bar from last backend'], result )
end
test "still calls #translate on all the backends" do
@last_backend.expects :translate
I18n.translate :not_here, :default => 'default'
end
test "returns a given default string when no backend returns a translation" do
result = I18n.translate :not_here, :default => 'default'
assert_equal 'default', result
end
end
class CustomLocalizeBackend < I18n::Backend::Simple
def localize(locale, object, format = :default)
"result from custom localize backend" if locale == 'custom'
end
end
class LocalizeChainedTest < ActiveSupport::TestCase
def setup
I18n.locale = :en
I18n.backend = Globalize::Backend::Chain.new
@first_backend = CustomLocalizeBackend.new
@last_backend = I18n::Backend::Simple.new
I18n.backend.add @first_backend
I18n.backend.add @last_backend
@time = Time.now
end
test "delegates #localize to all backends in the order they were added" do
@first_backend.expects(:localize).with(:en, @time, :default)
@last_backend.expects(:localize).with(:en, @time, :default)
I18n.localize @time
end
test "returns the result from #localize from the first backend if test's not nil" do
@last_backend.expects(:localize).never
result = I18n.localize @time, :locale => 'custom'
assert_equal 'result from custom localize backend', result
end
test "returns the result from #localize from the second backend if the first one returned nil" do
@last_backend.expects(:localize).returns "value from last backend"
result = I18n.localize @time
assert_equal 'value from last backend', result
end
end
class NamespaceChainedTest < ActiveSupport::TestCase
def setup
@backend = Globalize::Backend::Chain.new
end
test "returns false if the given result is not a Hash" do
assert !@backend.send(:namespace_lookup?, 'foo', {})
end
test "returns false if a count option is present" do
assert !@backend.send(:namespace_lookup?, {:foo => 'foo'}, {:count => 1})
end
test "returns true if the given result is a Hash AND no count option is present" do
assert @backend.send(:namespace_lookup?, {:foo => 'foo'}, {})
end
end

View file

@ -1,63 +0,0 @@
require File.join( File.dirname(__FILE__), '..', 'test_helper' )
require 'globalize/backend/pluralizing'
class PluralizingTest < ActiveSupport::TestCase
def setup
@backend = Globalize::Backend::Pluralizing.new
@cz_pluralizer = lambda{|c| c == 1 ? :one : (2..4).include?(c) ? :few : :other }
end
test "#pluralizer returns the pluralizer for a given locale if defined" do
assert_instance_of Proc, @backend.pluralizer(:en)
end
test "#pluralizer returns the default pluralizer if no pluralizer is defined for the given locale" do
assert_equal @backend.pluralizer(:en), @backend.pluralizer(:de)
end
test "#add_pluralizer allows to store a pluralizer per locale" do
assert_nothing_raised { @backend.add_pluralizer(:cz, @cz_pluralizer) }
assert_equal @cz_pluralizer, @backend.pluralizer(:cz)
end
end
class PluralizePluralizingTest < ActiveSupport::TestCase
def setup
@backend = Globalize::Backend::Pluralizing.new
@cz_pluralizer = lambda{|c| c == 1 ? :one : (2..4).include?(c) ? :few : :other }
@backend.store_translations :en, :foo => {:one => 'one en foo', :other => 'many en foos'}
@backend.store_translations :cz, :foo => {:one => 'one cz foo', :few => 'few cz foos', :other => 'many cz foos'}
end
test "looks up the :one translation when count is 1" do
assert_equal 'one en foo', @backend.translate(:en, :foo, :count => 1)
end
test "looks up the :other translation when count is 2" do
assert_equal 'many en foos', @backend.translate(:en, :foo, :count => 2)
end
end
class CzPluralizingTest < ActiveSupport::TestCase
def setup
@backend = Globalize::Backend::Pluralizing.new
@cz_pluralizer = lambda{|c| c == 1 ? :one : (2..4).include?(c) ? :few : :other }
@backend.store_translations :en, :foo => {:one => 'one en foo', :other => 'many en foos'}
@backend.store_translations :cz, :foo => {:one => 'one cz foo', :few => 'few cz foos', :other => 'many cz foos'}
@backend.add_pluralizer(:cz, @cz_pluralizer)
end
test "looks up the :one translation when count is 1 (:cz)" do
assert_equal 'one cz foo', @backend.translate(:cz, :foo, :count => 1)
end
test "looks up the :few translation when count is 2 (:cz)" do
assert_equal 'few cz foos', @backend.translate(:cz, :foo, :count => 2)
end
test "looks up the :other translation when count is 5 (:cz)" do
assert_equal 'many cz foos', @backend.translate(:cz, :foo, :count => 5)
end
end

View file

@ -1,143 +0,0 @@
require File.join( File.dirname(__FILE__), '..', 'test_helper' )
require 'globalize/backend/static'
require 'globalize/translation'
require 'action_view'
include ActionView::Helpers::NumberHelper
I18n.locale = :'en-US' # Need to set this, since I18n defaults to 'en'
class StaticTest < ActiveSupport::TestCase
def setup
I18n.backend = Globalize::Backend::Static.new
translations = {:"en-US" => {:foo => "foo in en-US", :boz => 'boz', :buz => {:bum => 'bum'}},
:"en" => {:bar => "bar in en"},
:"de-DE" => {:baz => "baz in de-DE"},
:"de" => {:boo => "boo in de", :number => { :currency => { :format => { :unit => '€', :format => '%n %u'}}}}}
translations.each do |locale, data|
I18n.backend.store_translations locale, data
end
I18n.fallbacks.map :"de-DE" => :"en-US", :he => :en
end
test "returns an instance of Translation:Static" do
translation = I18n.translate :foo
assert_instance_of Globalize::Translation::Static, translation
end
test "returns the translation in en-US if present" do
assert_equal "foo in en-US", I18n.translate(:foo, :locale => :"en-US")
end
test "returns the translation in en if en-US is not present" do
assert_equal "bar in en", I18n.translate(:bar, :locale => :"en-US")
end
test "returns the translation in de-DE if present" do
assert_equal "baz in de-DE", I18n.translate(:baz, :locale => :"de-DE")
end
test "returns the translation in de if de-DE is not present" do
assert_equal "boo in de", I18n.translate(:boo, :locale => :"de-DE")
end
test "returns the translation in en-US if none of de-DE and de are present" do
assert_equal "foo in en-US", I18n.translate(:foo, :locale => :"de-DE")
end
test "returns the translation in en if none of de-DE, de and en-US are present" do
assert_equal "bar in en", I18n.translate(:bar, :locale => :"de-DE")
end
test "returns the translation in en if none in he is present" do
assert_equal "bar in en", I18n.translate(:bar, :locale => :he)
end
test "returns the given default String when the key is not present for any locale" do
assert_equal "default", I18n.translate(:missing, :default => "default")
end
test "returns the fallback translation for the key if present for a fallback locale" do
I18n.backend.store_translations :de, :non_default => "non_default in de"
assert_equal "non_default in de", I18n.translate(:non_default, :default => "default", :locale => :"de-DE")
end
test "returns an array of translations" do
assert_instance_of Array, I18n.translate([:foo, :boz])
end
test "returns an array of instances of Translation::Static" do
assert_equal [Globalize::Translation::Static], I18n.translate([:foo, :boz]).map(&:class).uniq
end
test "returns a hash of translations" do
assert_instance_of Hash, I18n.translate(:"buz")
end
test "returns an array of translations 2" do
assert_equal [Globalize::Translation::Static], I18n.translate(:"buz").values.map(&:class)
end
test "returns currency properly formated" do
currency = number_to_currency(10)
assert_equal "$10.00", currency
end
test "returns currency properly formated for locale" do
currency = number_to_currency(10, :locale => :'de')
assert_equal "10.000 €", currency
end
test "returns currency properly formated from parameters" do
currency = number_to_currency(10, :format => "%n %u", :unit => '€')
assert_equal "10.00 €", currency
end
test "makes sure interpolation does not break even with False as string" do
assert_equal "translation missing: en, support, array, skip_last_comma", I18n.translate(:"support.array.skip_last_comma")
end
end
class TranslationStaticTest < ActiveSupport::TestCase
def setup
I18n.backend = Globalize::Backend::Static.new
translations = {
:greeting => "Hi {{name}}",
:messages => { :one => "You have one message.", :other => "You have {{count}} messages."}
}
I18n.backend.store_translations :"en", translations
end
def greeting
I18n.translate :greeting, :locale => :"en-US", :name => "Joshua"
end
test "stores the actual locale" do
assert_equal :en, greeting.locale
end
test "stores the requested locale" do
assert_equal :'en-US', greeting.requested_locale
end
test "stores the requested key" do
assert_equal :greeting, greeting.key
end
test "stores the options given to #translate" do
assert_equal( {:name => "Joshua"}, greeting.options )
end
test "stores the original translation before test was interpolated" do
assert_equal "Hi {{name}}", greeting.original
end
test "stores the plural_key :one if pluralized as such" do
message = I18n.translate :messages, :locale => :"en-US", :count => 1
assert_equal :one, message.plural_key
end
test "stores the plural_key :other if pluralized as such" do
messages = I18n.translate :messages, :locale => :"en-US", :count => 2
assert_equal :other, messages.plural_key
end
end

View file

@ -1,2 +0,0 @@
en-US:
from-all-file: From the "all" file.

View file

@ -1,2 +0,0 @@
de-DE:
from-locale-file: Aus der Locale Datei.

View file

@ -1,2 +0,0 @@
en-US:
from-locale-file: From the locale file.

View file

@ -1,2 +0,0 @@
en-US:
from-locale-dir: From the locale directory.

View file

@ -1,2 +0,0 @@
fi-FI:
from-locale-dir: Locale hakemistosta.

View file

@ -1,11 +0,0 @@
# This schema creates tables without columns for the translated fields
ActiveRecord::Schema.define do
create_table :blogs, :force => true do |t|
t.string :name
end
create_table :posts, :force => true do |t|
t.references :blog
end
end

View file

@ -1,24 +0,0 @@
class Post < ActiveRecord::Base
translates :subject, :content
validates_presence_of :subject
end
class Blog < ActiveRecord::Base
has_many :posts, :order => 'id ASC'
end
class Parent < ActiveRecord::Base
translates :content
end
class Child < Parent
end
class Comment < ActiveRecord::Base
validates_presence_of :content
belongs_to :post
end
class TranslatedComment < Comment
translates :content
end

View file

@ -1,39 +0,0 @@
ActiveRecord::Schema.define do
create_table :blogs, :force => true do |t|
t.string :description
end
create_table :posts, :force => true do |t|
t.references :blog
end
create_table :post_translations, :force => true do |t|
t.string :locale
t.references :post
t.string :subject
t.text :content
end
create_table :parents, :force => true do |t|
end
create_table :parent_translations, :force => true do |t|
t.string :locale
t.references :parent
t.text :content
t.string :type
end
create_table :comments, :force => true do |t|
t.references :post
end
create_table :translated_comment_translations, :force => true do |t|
t.string :locale
t.references :comment
t.string :subject
t.text :content
end
end

View file

@ -1,36 +0,0 @@
require File.join( File.dirname(__FILE__), '..', 'test_helper' )
require 'globalize/i18n/missing_translations_log_handler'
class MissingTranslationsTest < ActiveSupport::TestCase
test "defines I18n.missing_translations_logger accessor" do
assert I18n.respond_to?(:missing_translations_logger)
end
test "defines I18n.missing_translations_logger= writer" do
assert I18n.respond_to?(:missing_translations_logger=)
end
end
class TestLogger < String
def warn(msg) self.concat msg; end
end
class LogMissingTranslationsTest < ActiveSupport::TestCase
def setup
@locale, @key, @options = :en, :foo, {}
@exception = I18n::MissingTranslationData.new(@locale, @key, @options)
@logger = TestLogger.new
I18n.missing_translations_logger = @logger
end
test "still returns the exception message for MissingTranslationData exceptions" do
result = I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
assert_equal 'translation missing: en, foo', result
end
test "logs the missing translation to I18n.missing_translations_logger" do
I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
assert_equal 'translation missing: en, foo', @logger
end
end

View file

@ -1,49 +0,0 @@
require File.join( File.dirname(__FILE__), 'test_helper' )
require 'globalize/load_path'
class LoadPathTest < ActiveSupport::TestCase
def setup
@plugin_dir = "#{File.dirname(__FILE__)}/.."
@locale_dir = "#{File.dirname(__FILE__)}/data/locale"
@load_path = Globalize::LoadPath.new
end
test "returns glob patterns for all locales and ruby + yaml files by default" do
patterns = %w(locales/all.rb
locales/*.rb
locales/*/**/*.rb
locales/all.yml
locales/*.yml
locales/*/**/*.yml)
assert_equal patterns, @load_path.send(:patterns, 'locales')
end
test "returns the glob patterns for registered locales and extensions" do
@load_path.locales = [:en, :de]
@load_path.extensions = [:sql]
patterns = %w(locales/all.sql
locales/en.sql
locales/en/**/*.sql
locales/de.sql
locales/de/**/*.sql)
assert_equal patterns, @load_path.send(:patterns, 'locales')
end
test "expands paths using yml as a default file extension" do
@load_path << @locale_dir
expected = %w(all.yml de-DE.yml en-US.yml en-US/module.yml fi-FI/module.yml root.yml)
assert_equal expected, @load_path.map{|path| path.sub("#{@locale_dir}\/", '')}
end
test "appends new paths to the collection so earlier collected paths preceed later collected ones" do
@load_path.locales = [:root]
@load_path << "#{@plugin_dir}/lib/locale"
@load_path << @locale_dir
expected = %W(#{@plugin_dir}/lib/locale/root.yml
#{@locale_dir}/all.yml
#{@locale_dir}/root.yml)
assert_equal expected, @load_path
end
end

View file

@ -1,154 +0,0 @@
require File.join( File.dirname(__FILE__), '..', 'test_helper' )
require 'globalize/locale/fallbacks'
include Globalize::Locale
I18n.default_locale = :'en-US' # This has to be set explicitly, no longer default for I18n
class FallbacksTest < ActiveSupport::TestCase
def setup
I18n.fallbacks = Fallbacks.new
end
def teardown
I18n.default_locale = :'en-US'
end
test "#[] caches computed results" do
I18n.fallbacks['en']
assert_equal( { :en => [:en, :"en-US", :root] }, I18n.fallbacks )
end
test "#defaults always reflect the I18n.default_locale if no default has been set manually" do
I18n.default_locale = :'en-US'
assert_equal( [:'en-US', :en, :root], I18n.fallbacks.defaults )
end
test "#defaults always reflect a manually passed default locale if any" do
I18n.fallbacks = Fallbacks.new(:'fi-FI')
assert_equal( [:'fi-FI', :fi, :root], I18n.fallbacks.defaults )
I18n.default_locale = :'de-DE'
assert_equal( [:'fi-FI', :fi, :root], I18n.fallbacks.defaults )
end
test "#defaults allows to set multiple defaults" do
I18n.fallbacks = Fallbacks.new(:'fi-FI', :'se-FI')
assert_equal( [:'fi-FI', :fi, :'se-FI', :se, :root], I18n.fallbacks.defaults )
end
end
class NoMappingFallbacksTest < ActiveSupport::TestCase
def setup
@fallbacks = Fallbacks.new(:'en-US')
end
test "returns [:es, :en-US, :root] for :es" do
assert_equal [:es, :"en-US", :en, :root], @fallbacks[:es]
end
test "returns [:es-ES, :es, :en-US, :root] for :es-ES" do
assert_equal [:"es-ES", :es, :"en-US", :en, :root], @fallbacks[:"es-ES"]
end
test "returns [:es-MX, :es, :en-US, :root] for :es-MX" do
assert_equal [:"es-MX", :es, :"en-US", :en, :root], @fallbacks[:"es-MX"]
end
test "returns [:es-Latn-ES, :es-Latn, :es, :en-US, :root] for :es-Latn-ES" do
assert_equal [:"es-Latn-ES", :"es-Latn", :es, :"en-US", :en, :root], @fallbacks[:'es-Latn-ES']
end
test "returns [:en, :en-US, :root] for :en" do
assert_equal [:en, :"en-US", :root], @fallbacks[:en]
end
test "returns [:en-US, :en, :root] for :en-US (special case: locale == default)" do
assert_equal [:"en-US", :en, :root], @fallbacks[:"en-US"]
end
end
class CaMappingFallbacksTest < ActiveSupport::TestCase
# Most people who speak Catalan also live in Spain, so test is safe to assume
# that they also speak Spanish as spoken in Spain.
def setup
@fallbacks = Fallbacks.new(:'en-US')
@fallbacks.map :ca => :"es-ES"
end
test "returns [:ca, :es-ES, :es, :en-US, :root] for :ca" do
assert_equal [:ca, :"es-ES", :es, :"en-US", :en, :root], @fallbacks[:ca]
end
test "returns [:ca-ES, :ca, :es-ES, :es, :en-US, :root] for :ca-ES" do
assert_equal [:"ca-ES", :ca, :"es-ES", :es, :"en-US", :en, :root], @fallbacks[:"ca-ES"]
end
end
class ArMappingFallbacksTest < ActiveSupport::TestCase
# People who speak Arabic as spoken in Palestine often times also speak
# Hebrew as spoken in Israel. However test is in no way safe to assume that
# everybody who speaks Arabic also speaks Hebrew.
def setup
@fallbacks = Fallbacks.new(:'en-US')
@fallbacks.map :"ar-PS" => :"he-IL"
end
test "returns [:ar, :en-US, :root] for :ar" do
assert_equal [:ar, :"en-US", :en, :root], @fallbacks[:ar]
end
test "returns [:ar-EG, :ar, :en-US, :root] for :ar-EG" do
assert_equal [:"ar-EG", :ar, :"en-US", :en, :root], @fallbacks[:"ar-EG"]
end
test "returns [:ar-PS, :ar, :he-IL, :he, :en-US, :root] for :ar-PS" do
assert_equal [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en, :root], @fallbacks[:"ar-PS"]
end
end
class SmsMappingFallbacksTest < ActiveSupport::TestCase
# Sami people live in several scandinavian countries. In Finnland many people
# know Swedish and Finnish. Thus, test can be assumed that Sami living in
# Finnland also speak Swedish and Finnish.
def setup
@fallbacks = Fallbacks.new(:'en-US')
@fallbacks.map :sms => [:"se-FI", :"fi-FI"]
end
test "returns [:sms-FI, :sms, :se-FI, :se, :fi-FI, :fi, :en-US, :root] for :sms-FI" do
assert_equal [:"sms-FI", :sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en, :root], @fallbacks[:"sms-FI"]
end
end
class DeAtMappingFallbacksTest < ActiveSupport::TestCase
def setup
@fallbacks = Fallbacks.new(:'en-US')
@fallbacks.map :"de-AT" => :"de-DE"
end
test "returns [:de, :en-US, :root] for de" do
assert_equal [:de, :"en-US", :en, :root], @fallbacks[:"de"]
end
test "returns [:de-DE, :de, :en-US, :root] for de-DE" do
assert_equal [:"de-DE", :de, :"en-US", :en, :root], @fallbacks[:"de-DE"]
end
test "returns [:de-AT, :de, :de-DE, :en-US, :root] for de-AT" do
assert_equal [:"de-AT", :de, :"de-DE", :"en-US", :en, :root], @fallbacks[:"de-AT"]
end
end
class DeMappingFallbacksTest < ActiveSupport::TestCase
def setup
@fallbacks = Fallbacks.new(:'en-US')
@fallbacks.map :de => :en, :he => :en
end
test "returns [:de, :en, :root] for :de" do
assert_equal [:de, :en, :"en-US", :root], @fallbacks[:de]
end
test "returns [:he, :en, :root] for :de" do
assert_equal [:he, :en, :"en-US", :root], @fallbacks[:he]
end
end

View file

@ -1,130 +0,0 @@
require File.join( File.dirname(__FILE__), '..', 'test_helper' )
require 'globalize/locale/language_tag'
include Globalize::Locale
class LanguageTagTest < ActiveSupport::TestCase
test "given a valid tag 'de' returns an LanguageTag from #tag" do
assert_instance_of LanguageTag, LanguageTag.tag('de')
end
test "given a valid tag 'de' returns an array of subtags" do
assert_equal ['de', nil, nil, nil, nil, nil, nil], LanguageTag::SimpleParser.match('de')
end
test "given a valid tag 'de-DE' returns an array of subtags" do
assert_equal ['de', nil, 'DE', nil, nil, nil, nil], LanguageTag::SimpleParser.match('de-DE')
end
test "given a valid lowercase tag 'de-latn-de-variant-x-phonebk' returns an array of subtags" do
assert_equal ['de', 'latn', 'de', 'variant', nil, 'x-phonebk', nil],
LanguageTag::SimpleParser.match('de-latn-de-variant-x-phonebk')
end
test "given a valid uppercase tag 'DE-LATN-DE-VARIANT-X-PHONEBK' returns an array of subtags" do
assert_equal ['DE', 'LATN', 'DE', 'VARIANT', nil, 'X-PHONEBK', nil],
LanguageTag::SimpleParser.match('DE-LATN-DE-VARIANT-X-PHONEBK')
end
test "given an invalid tag 'a-DE' test returns false" do
assert !LanguageTag::SimpleParser.match('a-DE')
end
test "given an invalid tag 'de-419-DE' test returns false" do
assert !LanguageTag::SimpleParser.match('de-419-DE')
end
end
class DeLatnLanguageTagTest < ActiveSupport::TestCase
def setup
subtags = %w(de Latn DE variant a-ext x-phonebk i-klingon)
@tag = LanguageTag.new *subtags
end
test "returns 'de' as the language subtag in lowercase" do
assert_equal 'de', @tag.language
end
test "returns 'Latn' as the script subtag in titlecase" do
assert_equal 'Latn', @tag.script
end
test "returns 'DE' as the region subtag in uppercase" do
assert_equal 'DE', @tag.region
end
test "returns 'variant' as the variant subtag in lowercase" do
assert_equal 'variant', @tag.variant
end
test "returns 'a-ext' as the extension subtag" do
assert_equal 'a-ext', @tag.extension
end
test "returns 'x-phonebk' as the privateuse subtag" do
assert_equal 'x-phonebk', @tag.privateuse
end
test "returns 'i-klingon' as the grandfathered subtag" do
assert_equal 'i-klingon', @tag.grandfathered
end
test "returns a formatted tag string from #to_s" do
assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon', @tag.to_s
end
test "returns an array containing the formatted subtags from #to_a" do
assert_equal %w(de Latn DE variant a-ext x-phonebk i-klingon), @tag.to_a
end
end
class InheritanceLanguageTagTest < ActiveSupport::TestCase
test "returns 'de-Latn-DE-variant-a-ext-x-phonebk' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do
tag = LanguageTag.new *%w(de Latn DE variant a-ext x-phonebk i-klingon)
assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk', tag.parent.to_s
end
test "returns 'de-Latn-DE-variant-a-ext' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk'" do
tag = LanguageTag.new *%w(de Latn DE variant a-ext x-phonebk)
assert_equal 'de-Latn-DE-variant-a-ext', tag.parent.to_s
end
test "returns 'de-Latn-DE-variant' as the parent of 'de-Latn-DE-variant-a-ext'" do
tag = LanguageTag.new *%w(de Latn DE variant a-ext)
assert_equal 'de-Latn-DE-variant', tag.parent.to_s
end
test "returns 'de-Latn-DE' as the parent of 'de-Latn-DE-variant'" do
tag = LanguageTag.new *%w(de Latn DE variant)
assert_equal 'de-Latn-DE', tag.parent.to_s
end
test "returns 'de-Latn' as the parent of 'de-Latn-DE'" do
tag = LanguageTag.new *%w(de Latn DE)
assert_equal 'de-Latn', tag.parent.to_s
end
test "returns 'de' as the parent of 'de-Latn'" do
tag = LanguageTag.new *%w(de Latn)
assert_equal 'de', tag.parent.to_s
end
# TODO RFC4647 says: "If no language tag matches the request, the "default" value is returned."
# where should we set the default language?
# test "returns '' as the parent of 'de'" do
# tag = LanguageTag.new *%w(de)
# assert_equal '', tag.parent.to_s
# end
test "returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do
parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon
de-Latn-DE-variant-a-ext-x-phonebk
de-Latn-DE-variant-a-ext
de-Latn-DE-variant
de-Latn-DE
de-Latn
de)
tag = LanguageTag.new *%w(de Latn DE variant a-ext x-phonebk i-klingon)
assert_equal parents, tag.parents.map{|tag| tag.to_s}
end
end

View file

@ -1,73 +0,0 @@
require File.join( File.dirname(__FILE__), '..', '..', 'test_helper' )
require 'active_record'
require 'globalize/model/active_record'
# Hook up model translation
ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
# Load Post model
require File.join( File.dirname(__FILE__), '..', '..', 'data', 'post' )
class MigrationTest < ActiveSupport::TestCase
def setup
reset_db! File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'no_globalize_schema.rb'))
end
test 'globalize table added' do
assert !Post.connection.table_exists?( :post_translations )
Post.create_translation_table! :subject => :string, :content => :text
assert Post.connection.table_exists?( :post_translations )
columns = Post.connection.columns( :post_translations )
assert locale = columns.detect {|c| c.name == 'locale' }
assert_equal :string, locale.type
assert subject = columns.detect {|c| c.name == 'subject' }
assert_equal :string, subject.type
assert content = columns.detect {|c| c.name == 'content' }
assert_equal :text, content.type
assert post_id = columns.detect {|c| c.name == 'post_id' }
assert_equal :integer, post_id.type
assert created_at = columns.detect {|c| c.name == 'created_at' }
assert_equal :datetime, created_at.type
assert updated_at = columns.detect {|c| c.name == 'updated_at' }
assert_equal :datetime, updated_at.type
end
test 'globalize table dropped' do
assert !Post.connection.table_exists?( :post_translations )
Post.create_translation_table! :subject => :string, :content => :text
assert Post.connection.table_exists?( :post_translations )
Post.drop_translation_table!
assert !Post.connection.table_exists?( :post_translations )
end
test 'exception on untranslated field inputs' do
assert_raise Globalize::Model::UntranslatedMigrationField do
Post.create_translation_table! :subject => :string, :content => :text, :bogus => :string
end
end
test 'exception on missing field inputs' do
assert_raise Globalize::Model::MigrationMissingTranslatedField do
Post.create_translation_table! :content => :text
end
end
test 'exception on bad input type' do
assert_raise Globalize::Model::BadMigrationFieldType do
Post.create_translation_table! :subject => :string, :content => :integer
end
end
test 'create_translation_table! should not be called on non-translated models' do
assert_raise NoMethodError do
Blog.create_translation_table! :name => :string
end
end
test 'drop_translation_table! should not be called on non-translated models' do
assert_raise NoMethodError do
Blog.drop_translation_table!
end
end
end

View file

@ -1,75 +0,0 @@
require File.join( File.dirname(__FILE__), '..', '..', 'test_helper' )
require 'active_record'
require 'globalize/model/active_record'
# Hook up model translation
ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
# Load Post model
require File.join( File.dirname(__FILE__), '..', '..', 'data', 'post' )
class StiTranslatedTest < ActiveSupport::TestCase
def setup
I18n.locale = :'en-US'
I18n.fallbacks.clear
reset_db! File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'schema.rb'))
end
def teardown
I18n.fallbacks.clear
end
test "works with simple dynamic finders" do
foo = Child.create :content => 'foo'
Child.create :content => 'bar'
child = Child.find_by_content('foo')
assert_equal foo, child
end
test 'change attribute on globalized model' do
child = Child.create :content => 'foo'
assert_equal [], child.changed
child.content = 'bar'
assert_equal [ 'content' ], child.changed
child.content = 'baz'
assert_member 'content', child.changed
end
test 'change attribute on globalized model after locale switching' do
child = Child.create :content => 'foo'
assert_equal [], child.changed
child.content = 'bar'
I18n.locale = :de
assert_equal [ 'content' ], child.changed
end
test 'fallbacks with lots of locale switching' do
I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
child = Child.create :content => 'foo'
I18n.locale = :'de-DE'
assert_equal 'foo', child.content
I18n.locale = :'en-US'
child.update_attribute :content, 'bar'
I18n.locale = :'de-DE'
assert_equal 'bar', child.content
end
test "saves all locales, even after locale switching" do
child = Child.new :content => 'foo'
I18n.locale = 'de-DE'
child.content = 'bar'
I18n.locale = 'he-IL'
child.content = 'baz'
child.save
I18n.locale = 'en-US'
child = Child.first
assert_equal 'foo', child.content
I18n.locale = 'de-DE'
assert_equal 'bar', child.content
I18n.locale = 'he-IL'
assert_equal 'baz', child.content
end
end

View file

@ -1,458 +0,0 @@
require File.join( File.dirname(__FILE__), '..', '..', 'test_helper' )
require 'active_record'
require 'globalize/model/active_record'
# Hook up model translation
ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
# Load Post model
require File.join( File.dirname(__FILE__), '..', '..', 'data', 'post' )
class TranslatedTest < ActiveSupport::TestCase
def setup
I18n.locale = :'en-US'
I18n.fallbacks.clear
reset_db! File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'schema.rb'))
ActiveRecord::Base.locale = nil
end
def teardown
I18n.fallbacks.clear
end
test "modifiying translated fields" do
post = Post.create :subject => 'foo'
assert_equal 'foo', post.subject
post.subject = 'bar'
assert_equal 'bar', post.subject
end
test "modifiying translated fields while switching locales" do
post = Post.create :subject => 'foo'
assert_equal 'foo', post.subject
I18n.locale = :'de-DE'
post.subject = 'bar'
assert_equal 'bar', post.subject
I18n.locale = :'en-US'
assert_equal 'foo', post.subject
I18n.locale = :'de-DE'
post.subject = 'bar'
end
test "has post_translations" do
post = Post.create
assert_nothing_raised { post.globalize_translations }
end
test "has German post_translations" do
I18n.locale = :de
post = Post.create :subject => 'foo'
assert_equal 1, post.globalize_translations.size
I18n.locale = :en
assert_equal 1, post.globalize_translations.size
end
test "returns the value passed to :subject" do
post = Post.new
assert_equal 'foo', (post.subject = 'foo')
end
test "translates subject and content into en-US" do
post = Post.create :subject => 'foo', :content => 'bar'
assert_equal 'foo', post.subject
assert_equal 'bar', post.content
assert post.save
post.reload
assert_equal 'foo', post.subject
assert_equal 'bar', post.content
end
test "finds a German post" do
post = Post.create :subject => 'foo (en)', :content => 'bar'
I18n.locale = 'de-DE'
post = Post.first
post.subject = 'baz (de)'
post.save
assert_equal 'baz (de)', Post.first.subject
I18n.locale = :'en-US'
assert_equal 'foo (en)', Post.first.subject
end
test "saves an English post and loads test correctly" do
assert_nil Post.first
post = Post.create :subject => 'foo', :content => 'bar'
assert post.save
post = Post.first
assert_equal 'foo', post.subject
assert_equal 'bar', post.content
end
test "updates an attribute" do
post = Post.create :subject => 'foo', :content => 'bar'
post.update_attribute :subject, 'baz'
assert_equal 'baz', Post.first.subject
end
test "update_attributes failure" do
post = Post.create :subject => 'foo', :content => 'bar'
assert !post.update_attributes( { :subject => '' } )
assert_nil post.reload.attributes['subject']
assert_equal 'foo', post.subject
end
test "validates presence of :subject" do
post = Post.new
assert !post.save
post = Post.new :subject => 'foo'
assert post.save
end
test "returns the value for the correct locale, after locale switching" do
post = Post.create :subject => 'foo'
I18n.locale = 'de-DE'
post.subject = 'bar'
post.save
I18n.locale = 'en-US'
post = Post.first
assert_equal 'foo', post.subject
I18n.locale = 'de-DE'
assert_equal 'bar', post.subject
end
test "keeping one field in new locale when other field is changed" do
I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
post = Post.create :subject => 'foo'
I18n.locale = 'de-DE'
post.content = 'bar'
assert_equal 'foo', post.subject
end
test "modifying non-required field in a new locale" do
I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
post = Post.create :subject => 'foo'
I18n.locale = 'de-DE'
post.content = 'bar'
assert post.save
end
test "returns the value for the correct locale, after locale switching, without saving" do
post = Post.create :subject => 'foo'
I18n.locale = 'de-DE'
post.subject = 'bar'
I18n.locale = 'en-US'
assert_equal 'foo', post.subject
I18n.locale = 'de-DE'
assert_equal 'bar', post.subject
end
test "saves all locales, even after locale switching" do
post = Post.new :subject => 'foo'
I18n.locale = 'de-DE'
post.subject = 'bar'
I18n.locale = 'he-IL'
post.subject = 'baz'
post.save
I18n.locale = 'en-US'
post = Post.first
assert_equal 'foo', post.subject
I18n.locale = 'de-DE'
assert_equal 'bar', post.subject
I18n.locale = 'he-IL'
assert_equal 'baz', post.subject
end
test "resolves a simple fallback" do
I18n.locale = 'de-DE'
post = Post.create :subject => 'foo'
I18n.locale = 'de'
post.subject = 'baz'
post.content = 'bar'
post.save
I18n.locale = 'de-DE'
assert_equal 'foo', post.subject
assert_equal 'bar', post.content
end
test "resolves a simple fallback without reloading" do
I18n.locale = 'de-DE'
post = Post.new :subject => 'foo'
I18n.locale = 'de'
post.subject = 'baz'
post.content = 'bar'
I18n.locale = 'de-DE'
assert_equal 'foo', post.subject
assert_equal 'bar', post.content
end
test "resolves a complex fallback without reloading" do
I18n.fallbacks.map 'de' => %w(en he)
I18n.locale = 'de'
post = Post.new
I18n.locale = 'en'
post.subject = 'foo'
I18n.locale = 'he'
post.subject = 'baz'
post.content = 'bar'
I18n.locale = 'de'
assert_equal 'foo', post.subject
assert_equal 'bar', post.content
end
test "returns nil if no translations are found" do
post = Post.new :subject => 'foo'
assert_equal 'foo', post.subject
assert_nil post.content
end
test "returns nil if no translations are found; reloaded" do
post = Post.create :subject => 'foo'
post = Post.first
assert_equal 'foo', post.subject
assert_nil post.content
end
test "works with associations" do
blog = Blog.create
post1 = blog.posts.create :subject => 'foo'
I18n.locale = 'de-DE'
post2 = blog.posts.create :subject => 'bar'
assert_equal 2, blog.posts.size
I18n.locale = 'en-US'
assert_equal 'foo', blog.posts.first.subject
assert_nil blog.posts.last.subject
I18n.locale = 'de-DE'
assert_equal 'bar', blog.posts.last.subject
end
test "works with simple dynamic finders" do
foo = Post.create :subject => 'foo'
Post.create :subject => 'bar'
post = Post.find_by_subject('foo')
assert_equal foo, post
end
test 'change attribute on globalized model' do
post = Post.create :subject => 'foo', :content => 'bar'
assert_equal [], post.changed
post.subject = 'baz'
assert_equal [ 'subject' ], post.changed
post.content = 'quux'
assert_member 'subject', post.changed
assert_member 'content', post.changed
end
test 'change attribute on globalized model after locale switching' do
post = Post.create :subject => 'foo', :content => 'bar'
assert_equal [], post.changed
post.subject = 'baz'
I18n.locale = :de
assert_equal [ 'subject' ], post.changed
end
test 'fallbacks with lots of locale switching' do
I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
post = Post.create :subject => 'foo'
I18n.locale = :'de-DE'
assert_equal 'foo', post.subject
I18n.locale = :'en-US'
post.update_attribute :subject, 'bar'
I18n.locale = :'de-DE'
assert_equal 'bar', post.subject
end
test 'reload' do
post = Post.create :subject => 'foo', :content => 'bar'
post.subject = 'baz'
assert_equal 'foo', post.reload.subject
end
test 'complex writing and stashing' do
post = Post.create :subject => 'foo', :content => 'bar'
post.subject = nil
assert_nil post.subject
assert !post.valid?
end
test 'translated class locale setting' do
assert ActiveRecord::Base.respond_to?(:locale)
assert_equal :'en-US', I18n.locale
assert_equal :'en-US', ActiveRecord::Base.locale
I18n.locale = :de
assert_equal :de, I18n.locale
assert_equal :de, ActiveRecord::Base.locale
ActiveRecord::Base.locale = :es
assert_equal :de, I18n.locale
assert_equal :es, ActiveRecord::Base.locale
I18n.locale = :fr
assert_equal :fr, I18n.locale
assert_equal :es, ActiveRecord::Base.locale
end
test "untranslated class responds to locale" do
assert Blog.respond_to?(:locale)
end
test "to ensure locales in different classes are the same" do
ActiveRecord::Base.locale = :de
assert_equal :de, ActiveRecord::Base.locale
assert_equal :de, Parent.locale
Parent.locale = :es
assert_equal :es, ActiveRecord::Base.locale
assert_equal :es, Parent.locale
end
test "attribute saving goes by content locale and not global locale" do
ActiveRecord::Base.locale = :de
assert_equal :'en-US', I18n.locale
Post.create :subject => 'foo'
assert_equal :de, Post.first.globalize_translations.first.locale
end
test "attribute loading goes by content locale and not global locale" do
post = Post.create :subject => 'foo'
assert_equal :'en-US', ActiveRecord::Base.locale
ActiveRecord::Base.locale = :de
assert_equal :'en-US', I18n.locale
post.update_attribute :subject, 'foo [de]'
assert_equal 'foo [de]', Post.first.subject
ActiveRecord::Base.locale = :'en-US'
assert_equal 'foo', Post.first.subject
end
test "access content locale before setting" do
Globalize::Model::ActiveRecord::Translated::ActMethods.class_eval "remove_class_variable(:@@locale)"
assert_nothing_raised { ActiveRecord::Base.locale }
end
test "translated_locales" do
Post.locale = :de
post = Post.create :subject => 'foo'
Post.locale = :es
post.update_attribute :subject, 'bar'
Post.locale = :fr
post.update_attribute :subject, 'baz'
assert_equal [ :de, :es, :fr ], post.translated_locales
assert_equal [ :de, :es, :fr ], Post.first.translated_locales
end
test "including globalize_translations" do
I18n.locale = :de
Post.create :subject => "Foo1", :content => "Bar1"
Post.create :subject => "Foo2", :content => "Bar2"
class << Post
def tranlsations_included
self.all(:include => :globalize_translations)
end
end
default = Post.all.map {|x| [x.subject, x.content]}
with_include = Post.tranlsations_included.map {|x| [x.subject, x.content]}
assert_equal default, with_include
end
test "setting multiple translations at once with options hash" do
Post.locale = :de
post = Post.create :subject => "foo1", :content => "foo1"
Post.locale = :en
post.update_attributes( :subject => "bar1", :content => "bar1" )
options = { :de => {:subject => "foo2", :content => "foo2"},
:en => {:subject => "bar2", :content => "bar2"} }
post.set_translations options
post.reload
assert ["bar2", "bar2"], [post.subject, post.content]
Post.locale = :de
assert ["foo2", "foo2"], [post.subject, post.content]
end
test "setting only one translation with set_translations" do
Post.locale = :de
post = Post.create :subject => "foo1", :content => "foo1"
Post.locale = :en
post.update_attributes( :subject => "bar1", :content => "bar1" )
options = { :en => {:subject => "bar2", :content => "bar2"} }
post.set_translations options
post.reload
assert ["bar2", "bar2"], [post.subject, post.content]
Post.locale = :de
assert ["foo1", "foo1"], [post.subject, post.content]
end
test "setting only selected attributes with set_translations" do
Post.locale = :de
post = Post.create :subject => "foo1", :content => "foo1"
Post.locale = :en
post.update_attributes( :subject => "bar1", :content => "bar1" )
options = { :de => {:content => "foo2"}, :en => {:subject => "bar2"} }
post.set_translations options
post.reload
assert ["bar2", "bar1"], [post.subject, post.content]
Post.locale = :de
assert ["foo1", "foo2"], [post.subject, post.content]
end
test "setting invalid attributes raises ArgumentError" do
Post.locale = :de
post = Post.create :subject => "foo1", :content => "foo1"
Post.locale = :en
post.update_attributes( :subject => "bar1", :content => "bar1" )
options = { :de => {:fake => "foo2"} }
exception = assert_raise(ActiveRecord::UnknownAttributeError) do
post.set_translations options
end
assert_equal "unknown attribute: fake", exception.message
end
test "reload accepting find options" do
p = Post.create :subject => "Foo", :content => "Bar"
assert p.reload(:readonly => true, :lock => true)
assert_raise(ArgumentError) { p.reload(:foo => :bar) }
end
test "dependent destroy of translation" do
p = Post.create :subject => "Foo", :content => "Bar"
assert_equal 1, PostTranslation.count
p.destroy
assert_equal 0, PostTranslation.count
end
test "translating subclass of untranslated comment model" do
translated_comment = TranslatedComment.create(:post => @post)
assert_nothing_raised { translated_comment.globalize_translations }
end
test "modifiying translated comments works as expected" do
I18n.locale = :en
translated_comment = TranslatedComment.create(:post => @post, :content => 'foo')
assert_equal 'foo', translated_comment.content
I18n.locale = :de
translated_comment.content = 'bar'
assert translated_comment.save
assert_equal 'bar', translated_comment.content
I18n.locale = :en
assert_equal 'foo', translated_comment.content
assert_equal 2, translated_comment.globalize_translations.size
end
end
# TODO should validate_presence_of take fallbacks into account? maybe we need
# an extra validation call, or more options for validate_presence_of.
# TODO error checking for fields that exist in main table, don't exist in
# proxy table, aren't strings or text
#
# TODO allow finding by translated attributes in conditions?
# TODO generate advanced dynamic finders?

View file

@ -1,26 +0,0 @@
require 'rubygems'
require 'test/unit'
require 'active_support'
require 'active_support/test_case'
require 'mocha'
$LOAD_PATH << File.expand_path( File.dirname(__FILE__) + '/../lib' )
class ActiveSupport::TestCase
def reset_db!( schema_path )
::ActiveRecord::Migration.verbose = false # Quiet down the migration engine
::ActiveRecord::Base.establish_connection({
:adapter => 'sqlite3',
:dbfile => ':memory:'
})
::ActiveRecord::Base.silence do
load schema_path
end
end
def assert_member(item, arr)
assert_block "Item #{item} is not in array #{arr}" do
arr.member? item
end
end
end

View file

@ -1,54 +0,0 @@
require File.join( File.dirname(__FILE__), 'test_helper' )
require 'globalize/translation'
class TranslationTest < ActiveSupport::TestCase
include Globalize
def setup
@translation = Translation::Static.new 'foo', :locale => :'en-US'
end
test "responds to fallback?" do
assert @translation.respond_to?( :fallback? )
end
test "returns true when :locale and :requested_locale are not equal" do
@translation.requested_locale = :'de-DE'
assert @translation.fallback?
end
test "returns false when :locale and :requested_locale are equal" do
@translation.requested_locale = :'en-US'
assert !@translation.fallback?
end
test "has the attribute :locale" do
assert @translation.respond_to?( :locale )
end
test "has the attribute :requested_locale" do
assert @translation.respond_to?( :requested_locale )
end
test "has the attribute :options" do
assert @translation.respond_to?( :options )
end
test "has the attribute :plural_key" do
assert @translation.respond_to?( :plural_key )
end
test "has the attribute :original" do
assert @translation.respond_to?( :original )
end
test "Translation::Attribute has the attribute :locale" do
translation = Translation::Attribute.new 'foo'
assert translation.respond_to?( :locale )
end
test "Translation::Attribute has the attribute :requested_locale" do
translation = Translation::Attribute.new 'foo'
assert translation.respond_to?( :requested_locale )
end
end