-
Notifications
You must be signed in to change notification settings - Fork 82
Migrating from Globalize
Mobility's behaviour and interface is largely the same as Globalize, so migrating is straightforward. Note that there are some minor differences, so having a comprehensive test suite for your existing application is important.
Change your Gemfile to remove globalize and add mobility
-gem 'globalize'
+gem 'mobility'
Run rails generate mobility:install
to create the initializer.
Edit config/initializers/mobility.rb
and change the default backend to table
and turn on dirty tracking. ("Table" is the name of the backend where translations are stored in another table, equivalent to what Globalize does. See the wiki page on the Table backend for details.)
Although not required, it is also recommended to enable locale accessors for use with the dirty plugin, and set I18n.available_locales
.
Mobility.configure do |config|
+ config.default_backend = :table
- config.default_backend = :key_value
config.accessor_method = :translates
config.query_method = :i18n
+ config.default_options[:dirty] = true
+ config.default_options[:locale_accessors] = true # recommended
end
Extend Mobility from every model which you plan to translate before calling translates
, e.g.:
class Post < ApplicationRecord
+ extend Mobility
translates :title, :content
# ...
end
Alternatively, you can just extend Mobility from ApplicationRecord
(but extending each model separately is preferred):
class ApplicationRecord < ActiveRecord::Base
+ extend Mobility
self.abstract_class = true
end
If you were using fallbacks, also turn them on in mobility:
Mobility.configure do |config|
config.default_backend = :table
config.accessor_method = :translates
config.query_method = :i18n
config.default_options[:dirty] = true
+ config.default_options[:fallbacks] = { en: :ja, ja: :en }
end
Note that fallbacks (like all options) can be customized for each model, by passing the fallbacks
option to translates
. The default_options
above just sets the default in case none is specified in the model.
Mobility supports tracking changed (translated) attributes like Globalize. To enable this, enable the dirty
option in your mobility initializer (as mentioned above).
Mobility.configure do |config|
config.default_backend = :table
config.accessor_method = :translates
config.query_method = :i18n
+ config.default_options[:dirty] = true
end
There is a subtle difference in how Mobility handles dirty attributes. In Globalize, a change to the title
attribute in any locale will be tracked as a change to title
. Mobility does things differently: changes to an attribute in a given locale will be tracked with a suffix containing the locale, so that you can see changes to multiple locales at once.
post.changed
["title_en", "title_ja"]
Again, like fallbacks, you can enable or disable dirty tracking on each model if you like by passing the dirty
option to translates
.
Mobility supports having locale specific accessors (e.g., title_en
or title_ja
) like the globalize-accessors gem does.
To use this, first remove globalize-accessors from your Gemfile
-gem 'globalize-accessors'
Then turn on the locale_accessors
plugin in your mobility initializer:
Mobility.configure do |config|
config.default_backend = :table
config.accessor_method = :translates
config.query_method = :i18n
config.default_options[:dirty] = true
+ config.default_options[:locale_accessors] = true
end
This will define locale accessor methods for all locales in I18n.available_locales
.
Also be sure to remove any globalize_accessors
from your models. Locale accessors can be customized per model using the locale_accessors
option to translates
.
class Post < ApplicationRecord
translates :title, locale_accessors: [:en, :fr, :de] # overrides default
end
The association from model to translations is the same as Globalize, a has_many
relation (by default) named translations
:
post = Post.first
post.translations #=> returns post translations
(You can actually change the name of the association to something else with the association_name
option to translates
.)
However, whereas the inverse association in Globalize is called globalized_model
, it is called translated_model
in Mobility:
translation = post.translations.first
translation.translated_model #=> returns the post
This means, among other things, that if you are using fixtures, you'll need to change globalized_model
everywhere to translated_model
.
Mobility does not have migration helper methods like Post.create_translation_table!
to create new translation tables for translated models. Instead, there are rails generators for this.
If you have a model Post
and want to add translations for attributes title
(string) and content
(text), you would do this with:
rails generate mobility:translations post title:string content:text
Mobility supports querying on translated attributes like Globalize. However, unlike Globalize, this is not enabled by default. To query on translated attributes, you need to use the i18n
scope (the name of this scope can be customized using the query_method
configuration option):
Post.i18n.where(title: "foo")
Post.i18n.find_by(title: "foo")
Post.i18n.find_by_title("foo")
Although it is not recommended, you can make this query scope enabled by default using a default_scope
, like this, in your model class:
default_scope { i18n }
Then you can query on translated attributes like with Globalize, without any extra scope:
Post.where(title: "foo")
Post.find_by(title: "foo")
Post.find_by_title("foo")
To enable the i18n
by default on all models, put the default scope above in ApplicationRecord
.
Note that Mobility does not currently support querying with fallbacks like Globalize does, although this is in the roadmap. It also does not support ordering on translated attributes (there is an issue to add support for this). These are the only issues remaining before Mobility can claim full feature parity with Globalize.
Mobility has a uniqueness validator like Globalize, however unlike Globalize it does not use monkey-patching. This means that in order for it to work, you need to call validates
after you extend Mobility, like this:
class Post < ApplicationRecord
extend Mobility
translates :title
validates :title, uniqueness: true
end
Also, uniqueness validation in Mobility does not currently support case-sensitivity. If you try to validate with case sensitivity enabled Mobility will issue a warning, and case sensitive validation will be used.
Mobility does not support interpolation like Globalize does, but you can get the same effect of code like this (in Globalize):
greeter.greeting(name: 'Chris')
with the slightly more verbose:
I18n.interpolate(greeter.greeting, name: 'Chris')
See #162 for more discussion on this.
If you have any code which currently compares the value of a translation locale, like this (from RefineryCMS):
def translated_to_default_locale?
persisted? && translations.any? { |t| t.locale == Refinery::I18n.default_frontend_locale}
end
... you will need to change it to convert the translation locale to a symbol, like this:
def translated_to_default_locale?
persisted? && translations.any? { |t| t.locale.to_sym == Refinery::I18n.default_frontend_locale}
end
This is because unlike Globalize, Mobility does not override the locale
method on the translation class to convert the value to a symbol.
- FriendlyId: friendly_id-mobility
See the Table Backend section of the wiki.