Mobility
Pluggable Ruby translation framework
Install / Use
/learn @shioyama/MobilityREADME
Mobility
This is the readme for version 1.x of Mobility. If you are using an earlier version (0.8.x or earlier), you probably want the readme on the 0-8 branch.
Mobility is a gem for storing and retrieving translations as attributes on a class. These translations could be the content of blog posts, captions on images, tags on bookmarks, or anything else you might want to store in different languages. For examples of what Mobility can do, see the <a href="#companies-using-mobility">Companies using Mobility</a> section below.
Storage of translations is handled by customizable "backends" which encapsulate different storage strategies. The default way to store translations is to put them all in a set of two shared tables, but many alternatives are also supported, including translatable columns and model translation tables, as well as database-specific storage solutions such as json/jsonb and Hstore (for PostgreSQL).
Mobility is a cross-platform solution, currently supporting both ActiveRecord and Sequel ORM, with support for other platforms planned.
For a detailed introduction to Mobility, see Translating with Mobility. See also my talk at RubyConf 2018, Building Generic Software, where I explain the thinking behind Mobility's design.
If you're coming from Globalize, be sure to also read the Migrating from Globalize section of the wiki.
Installation
Add this line to your application's Gemfile:
gem 'mobility', '~> 1.3.2'
ActiveRecord (Rails)
Requirements:
- ActiveRecord >= 7.0
To translate attributes on a model, extend Mobility, then call translates
passing in one or more attributes as well as a hash of options (see below).
If using Mobility in a Rails project, you can run the generator to create an
initializer and a migration to create shared translation tables for the
default KeyValue backend:
rails generate mobility:install
(If you do not plan to use the default backend, you may want to use
the --without_tables option here to skip the migration generation.)
The generator will create an initializer file config/initializers/mobility.rb
which looks something like this:
Mobility.configure do
# PLUGINS
plugins do
backend :key_value
active_record
reader
writer
# ...
end
end
Each method call inside the block passed to plugins declares a plugin, along
with an optional default. To use a different default backend, you can
change the default passed to the backend plugin, like this:
Mobility.configure do
# PLUGINS
plugins do
- backend :key_value
+ backend :table
See other possible backends in the backends section.
You can also set defaults for backend-specific options. Below, we set the
default type option for the KeyValue backend to :string.
Mobility.configure do
# PLUGINS
plugins do
- backend :key_value
+ backend :key_value, type: :string
end
end
We will assume the configuration above in the examples that follow.
See Getting Started to get started translating your models.
Sequel
Requirements:
- Sequel >= 4.0
When configuring Mobility, ensure that you include the sequel plugin:
plugins do
backend :key_value
- active_record
+ sequel
You can extend Mobility just like in ActiveRecord, or you can use the
mobility plugin, which does the same thing:
class Word < ::Sequel::Model
plugin :mobility
translates :name, :meaning
end
Otherwise everything is (almost) identical to AR, with the exception that there is no equivalent to a Rails generator, so you will need to create the migration for any translation table(s) yourself, using Rails generators as a reference.
The models in examples below all inherit from ApplicationRecord, but
everything works exactly the same if the parent class is Sequel::Model.
Usage
<a name="quickstart"></a>Getting Started
Once the install generator has been run to generate translation tables, using
Mobility is as easy as adding a few lines to any class you want to translate.
Simply pass one or more attribute names to the translates method with a hash
of options, like this:
class Word < ApplicationRecord
extend Mobility
translates :name, :meaning
end
Note: When using the KeyValue backend, use the options hash to pass each attribute's type:
class Word < ApplicationRecord
extend Mobility
translates :name, type: :string
translates :meaning, type: :text
end
This is important because this is how Mobility knows to which of the two translation tables it should save your translation.
You now have translated attributes name and meaning on the model Word.
You can set their values like you would any other attribute:
word = Word.new
word.name = "mobility"
word.meaning = "(noun): quality of being changeable, adaptable or versatile"
word.name
#=> "mobility"
word.meaning
#=> "(noun): quality of being changeable, adaptable or versatile"
word.save
word = Word.first
word.name
#=> "mobility"
word.meaning
#=> "(noun): quality of being changeable, adaptable or versatile"
Presence methods are also supported:
word.name?
#=> true
word.name = nil
word.name?
#=> false
word.name = ""
word.name?
#=> false
What's different here is that the value of these attributes changes with the
value of I18n.locale:
I18n.locale = :ja
word.name
#=> nil
word.meaning
#=> nil
The name and meaning of this word are not defined in any locale except
English. Let's define them in Japanese and save the model:
word.name = "モビリティ"
word.meaning = "(名詞):動きやすさ、可動性"
word.name
#=> "モビリティ"
word.meaning
#=> "(名詞):動きやすさ、可動性"
word.save
Now our word has names and meanings in two different languages:
word = Word.first
I18n.locale = :en
word.name
#=> "mobility"
word.meaning
#=> "(noun): quality of being changeable, adaptable or versatile"
I18n.locale = :ja
word.name
#=> "モビリティ"
word.meaning
#=> "(名詞):動きやすさ、可動性"
Internally, Mobility is mapping the values in different locales to storage locations, usually database columns. By default these values are stored as keys (attribute names) and values (attribute translations) on a set of translation tables, one for strings and one for text columns, but this can be easily changed and/or customized (see the Backends section below).
<a name="getset"></a> Getting and Setting Translations
The easiest way to get or set a translation is to use the getter and setter
methods described above (word.name and word.name=), enabled by including
the reader and writer plugins.
You may also want to access the value of an attribute in a specific locale,
independent of the current value of I18n.locale (or Mobility.locale). There
are a few ways to do this.
The first way is to define locale-specific methods, one for each locale you
want to access directly on a given attribute. These are called "locale
accessors" in Mobility, and can be enabled by including the locale_accessors
plugin, with a default set of accessors:
plugins do
# ...
+ locale_accessors [:en, :ja]
You can also override this default from translates in any model:
class Word < ApplicationRecord
extend Mobility
translates :name, locale_accessors: [:en, :ja]
end
Since we have enabled locale accessors for English and Japanese, we can access
translations for these locales with name_en and name_ja:
word.name_en
#=> "mobility"
word.name_ja
#=> "モビリティ"
word.name_en = "foo"
word.name
#=> "foo"
Other locales, however, will not work:
word.name_ru
#=> NoMethodError: undefined method `name_ru' for #<Word id: ... >
With no plugin option (or a default of true), Mobility generates methods for
all locales in I18n.available_locales at the time the model is first loaded.
An alternative to using the locale_accessors plugin is to use the
fallthrough_accessors plugin. This uses Ruby's
method_missing method
to implicitly define the same methods as above, but supporting any locale
without any method definitions. (Locale accessors and fallthrough locales can
be used together without conflict, with locale accessors taking precedence if
defined for a given locale.)
Ensure the plugin is enabled:
plugins do
# ...
+ fallthrough_accessors
... then we can access any locale we want, without specifying them upfront:
word = Word.new
word.name_fr = "mobilité"
word.name_fr
#=> "mobilité"
word.name_ja = "モビリティ"
word.name_ja
#=> "モビリティ"
(Note however that Mobility will complain if you have
I18n.enforce_available_locales set to true and you try accessing a locale
not
Related Skills
feishu-drive
344.1k|
things-mac
344.1kManage Things 3 via the `things` CLI on macOS (add/update projects+todos via URL scheme; read/search/list from the local Things database)
clawhub
344.1kUse the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com
postkit
PostgreSQL-native identity, configuration, metering, and job queues. SQL functions that work with any language or driver
