-
Notifications
You must be signed in to change notification settings - Fork 87
KeyValue Backend
The KeyValue backend stores translations on a set of two shared tables, one for string-valued translations and one for text-valued translations. These tables are generated when you run the default install generator (rails generate mobility:install
) and then migrate your database (rake db:migrate
).
A detailed description of the backend is described as "Strategy #3" in this blog post.
The tables generated by the default migration look like this:
create_table "mobility_string_translations", force: :cascade do |t|
t.string "locale"
t.string "key"
t.string "value"
t.integer "translatable_id"
t.string "translatable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["translatable_id", "translatable_type", "key"], name: "index_mobility_string_translations_on_translatable_attribute"
t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_string_translations_on_keys", unique: true
t.index ["translatable_type", "key", "value", "locale"], name: "index_mobility_string_translations_on_query_keys"
end
create_table "mobility_text_translations", force: :cascade do |t|
t.string "locale"
t.string "key"
t.text "value"
t.integer "translatable_id"
t.string "translatable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["translatable_id", "translatable_type", "key"], name: "index_mobility_text_translations_on_translatable_attribute"
t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_text_translations_on_keys", unique: true
end
Both tables have a translatable_type
string column, because these will be used in a polymorphic relationship with translated models.
To understand how this works, suppose we have a model Post
:
class Post < ActiveRecord::Base
include Mobility
translates :title, backend: :key_value, type: :string
translates :content, backend: :key_value, type: :text
end
The backend will create associations on the class roughly resulting in:
class Post < ActiveRecord::Base
has_many :mobility_string_translations, ->{ where key: [:title] },
as: :translatable,
class_name: Mobility::ActiveRecord::StringTranslation,
dependent: :destroy,
inverse_of: :translatable,
autosave: true
has_many :mobility_text_translations, ->{ where key: [:content] },
as: :translatable,
class_name: Mobility::ActiveRecord::TextTranslation,
dependent: :destroy,
inverse_of: :translatable,
autosave: true
end
So the backend adds associations for each type
, scoped to only include translations whose keys are :title
and :content
, respectively.
The classes Mobility::ActiveRecord::StringTranslation
and Mobility::ActiveRecord::TextTranslation
both inherit from Mobility::ActiveRecord::Translation
, which is an abstract class:
module Mobility
module ActiveRecord
class Translation < ::ActiveRecord::Base
self.abstract_class = true
belongs_to :translatable, polymorphic: true
validates :key, presence: true, uniqueness: { scope: [:translatable_id, :translatable_type, :locale] }
validates :translatable, presence: true
validates :locale, presence: true
end
end
end
Since the class itself is abstract, it has no table; the table is set in the descendants Mobility::ActiveRecord::StringTranslation
and Mobility::ActiveRecord::TextTranslation
to mobility_string_translations
and mobility_text_translations
, respectively. The different tables is actually the only difference between these classes, and the only difference between the tables is that their value
columns are of different types (string/text), and that the string table has one additional index on value.