-
Notifications
You must be signed in to change notification settings - Fork 82
Table Backend
The Table backend stores translation in a model-specific table. For a model Post
with a table posts
, the translation table would be post_translations
. The translation table has columns for every translated attribute, as well as a column for the locale and a foreign key pointing to the model table (post_id
).
A detailed description of this backend is provided as "Strategy #2" in this blog post.
Unlike the default KeyValue backend, for the Table backend you will need to generate and run a migration for each translated model. Mobility has a built-in generator for this. If your default backend is set to :table
, and you want to translate a model Post
to add translated columns title
(string) and content
(text), here is how you would do it:
rails generate mobility:translations post title:string content:text
(Pass --backend=table
as an option if your default backend is something else.)
This will generate a migration to create the following table:
create_table "post_translations", force: :cascade do |t|
t.string "title"
t.text "content"
t.string "locale", null: false
t.integer "post_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["locale"], name: "index_post_translations_on_locale"
t.index ["post_id", "locale"], name: "index_post_translations_on_post_id_and_locale", unique: true
t.index ["post_id"], name: "index_post_translations_on_post_id"
end
As can be seen, the translations table has two attribute columns, title
and content
, as well as a locale
column, timestamps, and a reference to the post (post_id
), as well as some indices to speed up translation retrieval. Note that the index on post_id
and locale
is unique, since normally you would never have more than one translation for a given locale on a given record.
In order to manage translations on a model, Mobility subclasses the Mobility::ActiveRecord::ModelTranslation
(or Mobility::Sequel::ModelTranslation
for Sequel) abstract class and assigns it to a class with a name Post::Translation
(for a model Post
). It then defines two associations: a has_many
association from a model to its translations, and an inverse belongs_to
association from the translation class back to the model, named translated_model
.
The result looks something like this:
class Post < ApplicationRecord
has_many :translations,
class_name: Post::Translation,
foreign_key: :post_id,
dependent: :destroy,
autosave: true,
inverse_of: :translated_model
# ...
end
and
class Post::Translation < ApplicationRecord
belongs_to :post,
class_name: Post,
foreign_key: :post_id,
inverse_of: :translations,
touch: true
# ...
end
When you get a value with post.title
, the backend does roughly the following to get the value:
locale = Mobility.locale
translation = translations.find { |t| t.locale == locale.to_s }
translation ||= translations.build(locale: locale)
translation
So, Mobility looks for a matching translation, and if one does not exist, it builds on with the current locale. This is very similar to how the KeyValue backend fetches translations on a polymorphic association.
(coming soon)