simonmenke-globalize2 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +21 -0
- data/README.textile +202 -0
- data/generators/db_backend.rb +0 -0
- data/generators/templates/db_backend_migration.rb +25 -0
- data/init.rb +1 -0
- data/lib/globalize/backend/chain.rb +102 -0
- data/lib/globalize/backend/pluralizing.rb +37 -0
- data/lib/globalize/backend/static.rb +60 -0
- data/lib/globalize/i18n/missing_translations_log_handler.rb +41 -0
- data/lib/globalize/i18n/missing_translations_raise_handler.rb +27 -0
- data/lib/globalize/load_path.rb +63 -0
- data/lib/globalize/locale/fallbacks.rb +63 -0
- data/lib/globalize/locale/language_tag.rb +81 -0
- data/lib/globalize/model/active_record.rb +38 -0
- data/lib/globalize/model/active_record/adapter.rb +96 -0
- data/lib/globalize/model/active_record/translated.rb +154 -0
- data/lib/globalize/translation.rb +32 -0
- data/lib/locale/root.yml +3 -0
- data/lib/rails_edge_load_path_patch.rb +40 -0
- data/notes.textile +51 -0
- data/rails/init.rb +9 -0
- data/test/backends/chained_test.rb +175 -0
- data/test/backends/pluralizing_test.rb +63 -0
- data/test/backends/static_test.rb +143 -0
- data/test/data/locale/all.yml +2 -0
- data/test/data/locale/de-DE.yml +2 -0
- data/test/data/locale/en-US.yml +2 -0
- data/test/data/locale/en-US/module.yml +2 -0
- data/test/data/locale/fi-FI/module.yml +2 -0
- data/test/data/locale/root.yml +0 -0
- data/test/data/no_globalize_schema.rb +11 -0
- data/test/data/post.rb +24 -0
- data/test/data/schema.rb +39 -0
- data/test/i18n/missing_translations_test.rb +36 -0
- data/test/load_path_test.rb +49 -0
- data/test/locale/fallbacks_test.rb +154 -0
- data/test/locale/language_tag_test.rb +130 -0
- data/test/model/active_record/migration_test.rb +73 -0
- data/test/model/active_record/sti_translated_test.rb +75 -0
- data/test/model/active_record/translated_test.rb +458 -0
- data/test/test_helper.rb +26 -0
- data/test/translation_test.rb +54 -0
- metadata +114 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
# A simple exception handler that behaves like the default exception handler
|
2
|
+
# but additionally logs missing translations to a given log.
|
3
|
+
#
|
4
|
+
# Useful for identifying missing translations during testing.
|
5
|
+
#
|
6
|
+
# E.g.
|
7
|
+
#
|
8
|
+
# require 'globalize/i18n/missing_translations_log_handler
|
9
|
+
# I18n.missing_translations_logger = RAILS_DEFAULT_LOGGER
|
10
|
+
# I18n.exception_handler = :missing_translations_log_handler
|
11
|
+
#
|
12
|
+
# To set up a different log file:
|
13
|
+
#
|
14
|
+
# logger = Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
|
15
|
+
# I18n.missing_translations_logger = logger
|
16
|
+
|
17
|
+
module I18n
|
18
|
+
@@missing_translations_logger = nil
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def missing_translations_logger
|
22
|
+
@@missing_translations_logger ||= begin
|
23
|
+
require 'logger' unless defined?(Logger)
|
24
|
+
Logger.new(STDOUT)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def missing_translations_logger=(logger)
|
29
|
+
@@missing_translations_logger = logger
|
30
|
+
end
|
31
|
+
|
32
|
+
def missing_translations_log_handler(exception, locale, key, options)
|
33
|
+
if MissingTranslationData === exception
|
34
|
+
missing_translations_logger.warn(exception.message)
|
35
|
+
return exception.message
|
36
|
+
else
|
37
|
+
raise exception
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# A simple exception handler that behaves like the default exception handler
|
2
|
+
# but also raises on missing translations.
|
3
|
+
#
|
4
|
+
# Useful for identifying missing translations during testing.
|
5
|
+
#
|
6
|
+
# E.g.
|
7
|
+
#
|
8
|
+
# require 'globalize/i18n/missing_translations_raise_handler
|
9
|
+
# I18n.exception_handler = :missing_translations_raise_handler
|
10
|
+
module I18n
|
11
|
+
class << self
|
12
|
+
def missing_translations_raise_handler(exception, locale, key, options)
|
13
|
+
raise exception
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# self.exception_handler = :missing_translations_raise_handler
|
18
|
+
end
|
19
|
+
|
20
|
+
I18n.exception_handler = :missing_translations_raise_handler
|
21
|
+
|
22
|
+
ActionView::Helpers::TranslationHelper.module_eval do
|
23
|
+
def translate(key, options = {})
|
24
|
+
I18n.translate(key, options)
|
25
|
+
end
|
26
|
+
alias :t :translate
|
27
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Locale load_path and Locale loading support.
|
2
|
+
#
|
3
|
+
# To use this include the Globalize::LoadPath::I18n module to I18n like this:
|
4
|
+
#
|
5
|
+
# I18n.send :include, Globalize::LoadPath::I18n
|
6
|
+
#
|
7
|
+
# Clients can add load_paths using:
|
8
|
+
#
|
9
|
+
# I18n.load_path.add load_path, 'rb', 'yml' # pass any number of extensions like this
|
10
|
+
# I18n.load_path << 'path/to/dir' # usage without an extension, defaults to 'yml'
|
11
|
+
#
|
12
|
+
# And load locale data using either of:
|
13
|
+
#
|
14
|
+
# I18n.load_locales 'en-US', 'de-DE'
|
15
|
+
# I18n.load_locale 'en-US'
|
16
|
+
#
|
17
|
+
# This will lookup all files named like:
|
18
|
+
#
|
19
|
+
# 'path/to/dir/all.yml'
|
20
|
+
# 'path/to/dir/en-US.yml'
|
21
|
+
# 'path/to/dir/en-US/*.yml'
|
22
|
+
#
|
23
|
+
# The filenames will be passed to I18n.load_translations which delegates to
|
24
|
+
# the backend. So the actual behaviour depends on the implementation of the
|
25
|
+
# backend. I18n::Backend::Simple will be able to read YAML and plain Ruby
|
26
|
+
# files. See the documentation for I18n.load_translations for details.
|
27
|
+
|
28
|
+
module Globalize
|
29
|
+
class LoadPath < Array
|
30
|
+
def extensions
|
31
|
+
@extensions ||= ['rb', 'yml']
|
32
|
+
end
|
33
|
+
attr_writer :extensions
|
34
|
+
|
35
|
+
def locales
|
36
|
+
@locales ||= ['*']
|
37
|
+
end
|
38
|
+
attr_writer :locales
|
39
|
+
|
40
|
+
def <<(path)
|
41
|
+
push path
|
42
|
+
end
|
43
|
+
|
44
|
+
def push(*paths)
|
45
|
+
super(*paths.map{|path| filenames(path) }.flatten.uniq.sort)
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def filenames(path)
|
51
|
+
return [path] if File.file? path
|
52
|
+
patterns(path).map{|pattern| Dir[pattern] }
|
53
|
+
end
|
54
|
+
|
55
|
+
def patterns(path)
|
56
|
+
locales.map do |locale|
|
57
|
+
extensions.map do |extension|
|
58
|
+
%W(#{path}/all.#{extension} #{path}/#{locale}.#{extension} #{path}/#{locale}/**/*.#{extension})
|
59
|
+
end
|
60
|
+
end.flatten.uniq
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'globalize/locale/language_tag'
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
@@fallbacks = nil
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Returns the current fallbacks. Defaults to +Globalize::Locale::Fallbacks+.
|
8
|
+
def fallbacks
|
9
|
+
@@fallbacks ||= Globalize::Locale::Fallbacks.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sets the current fallbacks. Used to set a custom fallbacks instance.
|
13
|
+
def fallbacks=(fallbacks)
|
14
|
+
@@fallbacks = fallbacks
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Globalize
|
20
|
+
module Locale
|
21
|
+
class Fallbacks < Hash
|
22
|
+
def initialize(*defaults)
|
23
|
+
@map = {}
|
24
|
+
map defaults.pop if defaults.last.is_a?(Hash)
|
25
|
+
|
26
|
+
defaults = [I18n.default_locale.to_sym] if defaults.empty?
|
27
|
+
self.defaults = defaults
|
28
|
+
end
|
29
|
+
|
30
|
+
def defaults=(defaults)
|
31
|
+
@defaults = defaults.map{|default| compute(default, false) }.flatten << :root
|
32
|
+
end
|
33
|
+
attr_reader :defaults
|
34
|
+
|
35
|
+
def [](tag)
|
36
|
+
tag = tag.to_sym
|
37
|
+
has_key?(tag) ? fetch(tag) : store(tag, compute(tag))
|
38
|
+
end
|
39
|
+
|
40
|
+
def map(mappings)
|
41
|
+
mappings.each do |from, to|
|
42
|
+
from, to = from.to_sym, Array(to)
|
43
|
+
to.each do |to|
|
44
|
+
@map[from] ||= []
|
45
|
+
@map[from] << to.to_sym
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def compute(tags, include_defaults = true)
|
53
|
+
result = Array(tags).collect do |tag|
|
54
|
+
tags = LanguageTag::tag(tag.to_sym).parents(true).map! {|t| t.to_sym }
|
55
|
+
tags.each{|tag| tags += compute(@map[tag]) if @map[tag] }
|
56
|
+
tags
|
57
|
+
end.flatten
|
58
|
+
result.push *defaults if include_defaults
|
59
|
+
result.uniq
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# for specifications see http://en.wikipedia.org/wiki/IETF_language_tag
|
2
|
+
#
|
3
|
+
# SimpleParser does not implement advanced usages such as grandfathered tags
|
4
|
+
|
5
|
+
module Globalize
|
6
|
+
module Locale
|
7
|
+
module Rfc4646
|
8
|
+
SUBTAGS = [:language, :script, :region, :variant, :extension, :privateuse, :grandfathered]
|
9
|
+
FORMATS = {:language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase}
|
10
|
+
end
|
11
|
+
|
12
|
+
class LanguageTag < Struct.new(*Rfc4646::SUBTAGS)
|
13
|
+
class << self
|
14
|
+
def parser
|
15
|
+
@@parser ||= SimpleParser
|
16
|
+
end
|
17
|
+
|
18
|
+
def parser=(parser)
|
19
|
+
@@parser = parser
|
20
|
+
end
|
21
|
+
|
22
|
+
def tag(tag)
|
23
|
+
matches = parser.match(tag)
|
24
|
+
new *matches if matches
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Rfc4646::FORMATS.each do |name, format|
|
29
|
+
define_method(name) { self[name].send(format) unless self[name].nil? }
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_sym
|
33
|
+
to_s.to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
@tag ||= to_a.compact.join("-")
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_a
|
41
|
+
members.collect {|attr| self.send(attr) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def parent
|
45
|
+
segs = to_a.compact
|
46
|
+
segs.length < 2 ? nil : LanguageTag.tag(segs[0..(segs.length-2)].join('-'))
|
47
|
+
end
|
48
|
+
|
49
|
+
def parents(include_self = true)
|
50
|
+
result, parent = [], self.dup
|
51
|
+
result << parent if include_self
|
52
|
+
while parent = parent.parent
|
53
|
+
result << parent
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
module SimpleParser
|
59
|
+
PATTERN = %r{\A(?:
|
60
|
+
([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language
|
61
|
+
(?:-([a-z]{4}))? # script
|
62
|
+
(?:-([a-z]{2}|\d{3}))? # region
|
63
|
+
(?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant
|
64
|
+
(?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension
|
65
|
+
(?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag
|
66
|
+
(x(?:-[0-9a-z]{1,8})+)| # privateuse tag
|
67
|
+
/* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered
|
68
|
+
)\z}xi
|
69
|
+
|
70
|
+
class << self
|
71
|
+
def match(tag)
|
72
|
+
c = PATTERN.match(tag.to_s).captures
|
73
|
+
c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here?
|
74
|
+
rescue
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'globalize/translation'
|
2
|
+
require 'globalize/locale/fallbacks'
|
3
|
+
require 'globalize/model/active_record/adapter'
|
4
|
+
require 'globalize/model/active_record/translated'
|
5
|
+
|
6
|
+
module Globalize
|
7
|
+
module Model
|
8
|
+
module ActiveRecord
|
9
|
+
class << self
|
10
|
+
def create_proxy_class(klass)
|
11
|
+
Object.const_set "#{klass.name}Translation", Class.new(::ActiveRecord::Base){
|
12
|
+
belongs_to "#{klass.name.underscore}".intern
|
13
|
+
|
14
|
+
def locale
|
15
|
+
read_attribute(:locale).to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def locale=(locale)
|
19
|
+
write_attribute(:locale, locale.to_s)
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_accessors(klass, attr_names)
|
25
|
+
attr_names.each do |attr_name|
|
26
|
+
klass.send :define_method, attr_name, lambda {
|
27
|
+
globalize.fetch self.class.locale, attr_name
|
28
|
+
}
|
29
|
+
klass.send :define_method, "#{attr_name}=", lambda {|val|
|
30
|
+
globalize.stash self.class.locale, attr_name, val
|
31
|
+
self[attr_name] = val
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Globalize
|
2
|
+
module Model
|
3
|
+
class AttributeStash < Hash
|
4
|
+
def contains?(locale, attr_name)
|
5
|
+
locale = locale.to_sym
|
6
|
+
self[locale] ||= {}
|
7
|
+
self[locale].has_key? attr_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def read(locale, attr_name)
|
11
|
+
locale = locale.to_sym
|
12
|
+
self[locale] ||= {}
|
13
|
+
self[locale][attr_name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(locale, attr_name, value)
|
17
|
+
locale = locale.to_sym
|
18
|
+
self[locale] ||= {}
|
19
|
+
self[locale][attr_name] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Adapter
|
24
|
+
def initialize(record)
|
25
|
+
@record = record
|
26
|
+
|
27
|
+
# TODO what exactly are the roles of cache and stash
|
28
|
+
@cache = AttributeStash.new
|
29
|
+
@stash = AttributeStash.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch(locale, attr_name)
|
33
|
+
# locale = I18n.locale
|
34
|
+
is_cached = @cache.contains?(locale, attr_name)
|
35
|
+
is_cached ? @cache.read(locale, attr_name) : begin
|
36
|
+
value = fetch_attribute locale, attr_name
|
37
|
+
@cache.write locale, attr_name, value if value && value.locale == locale
|
38
|
+
value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def stash(locale, attr_name, value)
|
43
|
+
@stash.write locale, attr_name, value
|
44
|
+
@cache.write locale, attr_name, value
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_translations!
|
48
|
+
@stash.each do |locale, attrs|
|
49
|
+
translation = @record.globalize_translations.find_or_initialize_by_locale(locale.to_s)
|
50
|
+
attrs.each{|attr_name, value| translation[attr_name] = value }
|
51
|
+
translation.save!
|
52
|
+
end
|
53
|
+
@stash.clear
|
54
|
+
end
|
55
|
+
|
56
|
+
# Clears the cache
|
57
|
+
def clear
|
58
|
+
@cache.clear
|
59
|
+
@stash.clear
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def fetch_attribute(locale, attr_name)
|
65
|
+
fallbacks = I18n.fallbacks[locale].map{|tag| tag.to_s}.map(&:to_sym)
|
66
|
+
|
67
|
+
# If the translations were included with
|
68
|
+
# :include => globalize_translations
|
69
|
+
# there is no need to query them again.
|
70
|
+
unless @record.globalize_translations.loaded?
|
71
|
+
translations = @record.globalize_translations.by_locales(fallbacks)
|
72
|
+
else
|
73
|
+
translations = @record.globalize_translations
|
74
|
+
end
|
75
|
+
result, requested_locale = nil, locale
|
76
|
+
|
77
|
+
# Walk through the fallbacks, starting with the current locale itself, and moving
|
78
|
+
# to the next best choice, until we find a match.
|
79
|
+
# Check the @globalize_set_translations cache first to see if we've just changed the
|
80
|
+
# attribute and not saved yet.
|
81
|
+
fallbacks.each do |fallback|
|
82
|
+
# TODO should we be checking stash or just cache?
|
83
|
+
result = @stash.read(fallback, attr_name) || begin
|
84
|
+
translation = translations.detect {|tr| tr.locale == fallback }
|
85
|
+
translation && translation.send(attr_name)
|
86
|
+
end
|
87
|
+
if result
|
88
|
+
locale = fallback
|
89
|
+
break
|
90
|
+
end
|
91
|
+
end
|
92
|
+
result && Translation::Attribute.new(result, :locale => locale, :requested_locale => requested_locale)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Globalize
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class MigrationError < StandardError; end
|
5
|
+
class UntranslatedMigrationField < MigrationError; end
|
6
|
+
class MigrationMissingTranslatedField < MigrationError; end
|
7
|
+
class BadMigrationFieldType < MigrationError; end
|
8
|
+
|
9
|
+
module ActiveRecord
|
10
|
+
module Translated
|
11
|
+
def self.included(base)
|
12
|
+
base.extend ActMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module ActMethods
|
16
|
+
def translates(*attr_names)
|
17
|
+
options = attr_names.extract_options!
|
18
|
+
options[:translated_attributes] = attr_names
|
19
|
+
|
20
|
+
# Only set up once per class
|
21
|
+
unless included_modules.include? InstanceMethods
|
22
|
+
class_inheritable_accessor :globalize_options, :globalize_proxy
|
23
|
+
|
24
|
+
include InstanceMethods
|
25
|
+
extend ClassMethods
|
26
|
+
|
27
|
+
self.globalize_proxy = Globalize::Model::ActiveRecord.create_proxy_class(self)
|
28
|
+
has_many(
|
29
|
+
:globalize_translations,
|
30
|
+
:class_name => globalize_proxy.name,
|
31
|
+
:extend => Extensions,
|
32
|
+
:dependent => :delete_all,
|
33
|
+
:foreign_key => class_name.foreign_key
|
34
|
+
)
|
35
|
+
|
36
|
+
after_save :update_globalize_record
|
37
|
+
end
|
38
|
+
|
39
|
+
self.globalize_options = options
|
40
|
+
Globalize::Model::ActiveRecord.define_accessors(self, attr_names)
|
41
|
+
|
42
|
+
# Import any callbacks that have been defined by extensions to Globalize2
|
43
|
+
# and run them.
|
44
|
+
extend Callbacks
|
45
|
+
Callbacks.instance_methods.each {|cb| send cb }
|
46
|
+
end
|
47
|
+
|
48
|
+
def locale=(locale)
|
49
|
+
@@locale = locale
|
50
|
+
end
|
51
|
+
|
52
|
+
def locale
|
53
|
+
(defined?(@@locale) && @@locale) || I18n.locale
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Dummy Callbacks module. Extensions to Globalize2 can insert methods into here
|
58
|
+
# and they'll be called at the end of the translates class method.
|
59
|
+
module Callbacks
|
60
|
+
end
|
61
|
+
|
62
|
+
# Extension to the has_many :globalize_translations association
|
63
|
+
module Extensions
|
64
|
+
def by_locales(locales)
|
65
|
+
find :all, :conditions => { :locale => locales.map(&:to_s) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
def method_missing(method, *args)
|
71
|
+
if method.to_s =~ /^find_by_(\w+)$/ && globalize_options[:translated_attributes].include?($1.to_sym)
|
72
|
+
find(:first, :joins => :globalize_translations,
|
73
|
+
:conditions => [ "#{i18n_attr($1)} = ? AND #{i18n_attr('locale')} IN (?)",
|
74
|
+
args.first,I18n.fallbacks[I18n.locale].map{|tag| tag.to_s}])
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_translation_table!(fields)
|
81
|
+
translated_fields = self.globalize_options[:translated_attributes]
|
82
|
+
translated_fields.each do |f|
|
83
|
+
raise MigrationMissingTranslatedField, "Missing translated field #{f}" unless fields[f]
|
84
|
+
end
|
85
|
+
fields.each do |name, type|
|
86
|
+
unless translated_fields.member? name
|
87
|
+
raise UntranslatedMigrationField, "Can't migrate untranslated field: #{name}"
|
88
|
+
end
|
89
|
+
unless [ :string, :text ].member? type
|
90
|
+
raise BadMigrationFieldType, "Bad field type for #{name}, should be :string or :text"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
translation_table_name = self.name.underscore + '_translations'
|
94
|
+
self.connection.create_table(translation_table_name) do |t|
|
95
|
+
t.references self.table_name.singularize
|
96
|
+
t.string :locale
|
97
|
+
fields.each do |name, type|
|
98
|
+
t.column name, type
|
99
|
+
end
|
100
|
+
t.timestamps
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def drop_translation_table!
|
105
|
+
translation_table_name = self.name.underscore + '_translations'
|
106
|
+
self.connection.drop_table translation_table_name
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def i18n_attr(attribute_name)
|
112
|
+
self.base_class.name.underscore + "_translations.#{attribute_name}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
module InstanceMethods
|
117
|
+
def reload(options = nil)
|
118
|
+
globalize.clear
|
119
|
+
|
120
|
+
# clear all globalized attributes
|
121
|
+
# TODO what's the best way to handle this?
|
122
|
+
self.class.globalize_options[:translated_attributes].each do |attr|
|
123
|
+
@attributes.delete attr.to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
super options
|
127
|
+
end
|
128
|
+
|
129
|
+
def globalize
|
130
|
+
@globalize ||= Adapter.new self
|
131
|
+
end
|
132
|
+
|
133
|
+
def update_globalize_record
|
134
|
+
globalize.update_translations!
|
135
|
+
end
|
136
|
+
|
137
|
+
def translated_locales
|
138
|
+
globalize_translations.scoped(:select => 'DISTINCT locale').map {|gt| gt.locale.to_sym }
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_translations options
|
142
|
+
options.keys.each do |key|
|
143
|
+
|
144
|
+
translation = globalize_translations.find_by_locale(key.to_s) ||
|
145
|
+
globalize_translations.build(:locale => key.to_s)
|
146
|
+
translation.update_attributes!(options[key])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|