mobility 0.1.10 → 0.1.11
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -1
- data/Gemfile.lock +3 -1
- data/README.md +428 -411
- data/lib/generators/rails/mobility/backend_generators/base.rb +69 -0
- data/lib/generators/rails/mobility/backend_generators/column_backend.rb +15 -0
- data/lib/generators/rails/mobility/backend_generators/table_backend.rb +42 -0
- data/lib/generators/rails/mobility/generators.rb +5 -0
- data/lib/generators/rails/mobility/templates/column_translations.rb +17 -0
- data/lib/generators/rails/mobility/templates/table_migration.rb +17 -0
- data/lib/generators/rails/mobility/templates/table_translations.rb +28 -0
- data/lib/generators/rails/mobility/translations_generator.rb +75 -0
- data/lib/mobility.rb +10 -3
- data/lib/mobility/attributes.rb +4 -4
- data/lib/mobility/backend/active_model/dirty.rb +0 -2
- data/lib/mobility/backend/active_record/column.rb +15 -2
- data/lib/mobility/backend/active_record/table.rb +9 -0
- data/lib/mobility/backend/column.rb +11 -16
- data/lib/mobility/backend/sequel/column.rb +8 -2
- data/lib/mobility/backend/sequel/dirty.rb +0 -2
- data/lib/mobility/backend/table.rb +5 -0
- data/lib/mobility/fallthrough_accessors.rb +2 -3
- data/lib/mobility/instance_methods.rb +5 -4
- data/lib/mobility/rails.rb +1 -2
- data/lib/mobility/version.rb +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db71989aceb090cc6bc7fba2a4bfed78ae0ccc23
|
4
|
+
data.tar.gz: fa3c53b2540e63638c7e35b2fa43731b7c00ed90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cef93cb7bc0a451a3eaf7ea18b284258894a99c5214b5933f9ff54752bf525900736c35850c6a7e3a712e116e5b3c0a721b230eeefff3fbf7cab452ed6daa66
|
7
|
+
data.tar.gz: 0b682ce576e80ba2834f80fe91b19d0027277b06fea27f1fa8eca9e3aa91fc2bba627b0bfea8fe9b495bae5cd19b5ee2eb0b697a6dd592283ecc3e73453ea494
|
data/CHANGELOG.md
CHANGED
@@ -2,9 +2,27 @@
|
|
2
2
|
|
3
3
|
## 0.1
|
4
4
|
|
5
|
+
### 0.1.11
|
6
|
+
* Add backend-specific translations generator (`rails generate
|
7
|
+
mobility:translations`)
|
8
|
+
([9dbe4d](https://github.com/shioyama/mobility/commit/9dbe4d2221f3c97ec265c297ad2be201a5180151),
|
9
|
+
[583a51](https://github.com/shioyama/mobility/commit/583a51c9945615460079a1f81ffbd7a69d91a581),
|
10
|
+
[6b9605](https://github.com/shioyama/mobility/commit/6b9605ed6fa599578fd36065ac17e6b2b93a8378),
|
11
|
+
[e2e807](https://github.com/shioyama/mobility/commit/e2e807494bd1f642c67a0dbd678cea49b16f11b0))
|
12
|
+
* Fix bug with combination of Column backend and fallthrough accessors
|
13
|
+
([212f07](https://github.com/shioyama/mobility/commit/212f078145f613ab85faf7dbf993c7da9a91bcdd))
|
14
|
+
* Raise `InvalidLocale` when getting a locale that is not available
|
15
|
+
([d4f0ee](https://github.com/shioyama/mobility/commit/d4f0ee20d5507ba147f31aa03081f685e31ab46a))
|
16
|
+
* Pass options to backend write from setter
|
17
|
+
([5d224f](https://github.com/shioyama/mobility/commit/5d224fa7bb877d9dc1f6c3983b096b22aeea5bc7))
|
18
|
+
* Correctly include `FallthroughAccessors` module in module, not backend
|
19
|
+
([d9471d](https://github.com/shioyama/mobility/commit/d9471db7ab71766a98e4e411b476d2197fbf7f51))
|
20
|
+
* Handle presence methods in `FallthroughAccessors`
|
21
|
+
([66f630](https://github.com/shioyama/mobility/commit/66f630548c01b8d380c6aeeab4c32b085133c754))
|
22
|
+
|
5
23
|
### 0.1.10
|
6
24
|
* Fix fallback options ([#12](https://github.com/shioyama/mobility/pull/12) and
|
7
|
-
|
25
|
+
[09a163](https://github.com/shioyama/mobility/commit/09a1636bc743633fd13dc6c59ebf1e2366a0e2c4))
|
8
26
|
* Include fallbacks module by default
|
9
27
|
([#13](https://github.com/shioyama/mobility/pull/13/files))
|
10
28
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
mobility (0.1.
|
4
|
+
mobility (0.1.10)
|
5
5
|
i18n (>= 0.6.10)
|
6
6
|
request_store (~> 1.0)
|
7
7
|
|
@@ -68,6 +68,7 @@ GEM
|
|
68
68
|
diff-lcs (>= 1.2.0, < 2.0)
|
69
69
|
rspec-support (~> 3.5.0)
|
70
70
|
rspec-support (3.5.0)
|
71
|
+
sequel (4.42.1)
|
71
72
|
shellany (0.0.1)
|
72
73
|
slop (3.6.0)
|
73
74
|
sqlite3 (1.3.13)
|
@@ -88,6 +89,7 @@ DEPENDENCIES
|
|
88
89
|
rake (~> 10.0)
|
89
90
|
rspec (~> 3.0)
|
90
91
|
rspec-its (~> 1.2.0)
|
92
|
+
sequel (>= 4.0.0, < 5.0)
|
91
93
|
sqlite3
|
92
94
|
yard (~> 0.9.0)
|
93
95
|
|
data/README.md
CHANGED
@@ -1,68 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
[gemnasium]: https://gemnasium.com/shioyama/mobility
|
4
|
-
[codeclimate]: https://codeclimate.com/github/shioyama/mobility
|
5
|
-
[docs]: http://www.rubydoc.info/gems/mobility
|
6
|
-
|
7
|
-
# Mobility
|
1
|
+
Mobility
|
2
|
+
========
|
8
3
|
|
9
4
|
[][gem]
|
10
5
|
[][travis]
|
11
6
|
[][gemnasium]
|
12
7
|
[][codeclimate]
|
13
8
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
[
|
34
|
-
[
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
- a [cache](#cache) to improve read/write performance (included by default)
|
43
|
-
- translation [fallbacks](#fallbacks), in case a translation is missing in a
|
44
|
-
given locale
|
45
|
-
- (for classes that support it) [dirty](#dirty) tracking of changed attributes
|
46
|
-
(`ActiveModel::Dirty` in Rails)
|
47
|
-
- [locale-specific accessors](#locale-accessors) for translated attributes, of
|
48
|
-
the form `<attribute>_<locale>` (similar to
|
49
|
-
[globalize-accessors](https://github.com/globalize/globalize-accessors))
|
9
|
+
[gem]: https://rubygems.org/gems/mobility
|
10
|
+
[travis]: https://travis-ci.org/shioyama/mobility
|
11
|
+
[gemnasium]: https://gemnasium.com/shioyama/mobility
|
12
|
+
[codeclimate]: https://codeclimate.com/github/shioyama/mobility
|
13
|
+
[docs]: http://www.rubydoc.info/gems/mobility
|
14
|
+
[wiki]: https://github.com/shioyama/mobility/wiki
|
15
|
+
|
16
|
+
Mobility is a gem for storing and retrieving translations as attributes on a
|
17
|
+
class. These translations could be the content of blog posts, captions on
|
18
|
+
images, tags on bookmarks, or anything else you might want to store in
|
19
|
+
different languages.
|
20
|
+
|
21
|
+
Storage of translations is handled by customizable "backends" which encapsulate
|
22
|
+
different storage strategies. The default, preferred way to store translations
|
23
|
+
is to put them all in a set of two shared tables, but many alternatives are
|
24
|
+
also supported, including translatable columns (like
|
25
|
+
[Traco](https://github.com/barsoom/traco)) and translation tables (like
|
26
|
+
[Globalize](https://github.com/globalize/globalize)), as well as
|
27
|
+
database-specific storage solutions such as
|
28
|
+
[jsonb](https://www.postgresql.org/docs/current/static/datatype-json.html ) and
|
29
|
+
[Hstore](https://www.postgresql.org/docs/current/static/hstore.html) (for
|
30
|
+
PostgreSQL).
|
31
|
+
|
32
|
+
Mobility is a cross-platform solution, currently supporting both
|
33
|
+
[ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html)
|
34
|
+
and [Sequel](http://sequel.jeremyevans.net/) ORM, with support for other
|
35
|
+
platforms planned.
|
50
36
|
|
51
37
|
For a detailed introduction to Mobility, see [Translating with
|
52
|
-
Mobility](http://dejimata.com/2017/3/3/translating-with-mobility).
|
38
|
+
Mobility](http://dejimata.com/2017/3/3/translating-with-mobility). See also the
|
39
|
+
[Roadmap](https://github.com/shioyama/mobility/wiki/Roadmap) for what's in the
|
40
|
+
works for future releases.
|
53
41
|
|
54
|
-
|
42
|
+
Installation
|
43
|
+
------------
|
55
44
|
|
56
45
|
Add this line to your application's Gemfile:
|
57
46
|
|
58
47
|
```ruby
|
59
|
-
gem 'mobility', '~> 0.1.
|
48
|
+
gem 'mobility', '~> 0.1.11'
|
60
49
|
```
|
61
50
|
|
62
|
-
To translate attributes on a model,
|
63
|
-
|
64
|
-
options, including the backend to use. (See [Defining Backend
|
65
|
-
Attributes](#attributes) below.)
|
51
|
+
To translate attributes on a model, include (or extend) `Mobility`, then call
|
52
|
+
`translates` passing in one or more attributes as well as a hash of options.
|
66
53
|
|
67
54
|
### ActiveRecord (Rails)
|
68
55
|
|
@@ -74,59 +61,32 @@ the [active_record-4.2
|
|
74
61
|
branch](https://github.com/shioyama/mobility/tree/active_record_4.2).)
|
75
62
|
|
76
63
|
If using Mobility in a Rails project, you can run the generator to create an
|
77
|
-
initializer and
|
78
|
-
default
|
64
|
+
initializer and a migration to create shared translation tables for the
|
65
|
+
default `KeyValue` backend:
|
79
66
|
|
80
67
|
```
|
81
68
|
rails generate mobility:install
|
82
69
|
```
|
83
70
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
```
|
88
|
-
rails generate mobility:install --without_tables
|
89
|
-
```
|
71
|
+
(If you do not plan to use the default backend, you may want to use
|
72
|
+
the `--without_tables` option here to skip the migration generation.)
|
90
73
|
|
91
74
|
The generator will create an initializer file `config/initializers/mobility.rb`
|
92
|
-
with the
|
93
|
-
|
94
|
-
```
|
95
|
-
Mobility.config.default_backend = :key_value
|
96
|
-
```
|
97
|
-
|
98
|
-
To set a different default backend, set `default_backend` to another value (see
|
99
|
-
possibilities [below](#backend)). Other configuration options can be set using the
|
100
|
-
`configure` method, see: {Mobility::Configuration} for details.
|
101
|
-
|
102
|
-
To get started quickly, run the generator with tables, and add the following to
|
103
|
-
a class to add translated attributes:
|
75
|
+
with the lines:
|
104
76
|
|
105
77
|
```ruby
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
translates :content, type: :text
|
78
|
+
Mobility.configure do |config|
|
79
|
+
config.default_backend = :key_value
|
80
|
+
config.accessor_method = :translates
|
110
81
|
end
|
111
82
|
```
|
112
83
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
post = Post.create(title: "Mobility")
|
118
|
-
post.title #=> "Mobility"
|
119
|
-
I18n.locale = :ja
|
120
|
-
post.title #=> nil
|
121
|
-
post.title = "モビリティ"
|
122
|
-
post.save
|
123
|
-
post.title #=> "モビリティ"
|
124
|
-
I18n.locale = :en
|
125
|
-
post.title #=> "Mobility"
|
126
|
-
```
|
84
|
+
To use a different default backend, set `default_backend` to another value (see
|
85
|
+
possibilities [below](#backends)). Other configuration options are
|
86
|
+
described in the [API
|
87
|
+
docs](http://www.rubydoc.info/gems/mobility/Mobility/Configuration).
|
127
88
|
|
128
|
-
|
129
|
-
else Mobility can do.
|
89
|
+
See [Getting Started](#quickstart) to get started translating your models.
|
130
90
|
|
131
91
|
### Sequel
|
132
92
|
|
@@ -144,343 +104,348 @@ class Post < ::Sequel::Model
|
|
144
104
|
end
|
145
105
|
```
|
146
106
|
|
147
|
-
Otherwise everything is almost identical to AR, with the exception that there
|
107
|
+
Otherwise everything is (almost) identical to AR, with the exception that there
|
148
108
|
is no equivalent to a Rails generator (so you will need to create the migration
|
149
|
-
for any translation table(s) yourself,
|
150
|
-
|
151
|
-
## Usage
|
109
|
+
for any translation table(s) yourself, using Rails generators as a reference).
|
152
110
|
|
153
|
-
|
111
|
+
The models in examples below all inherit from `ActiveRecord::Base`, but
|
112
|
+
everything works exactly the same if the parent class is `Sequel::Model`.
|
154
113
|
|
155
|
-
|
156
|
-
|
157
|
-
a hash of options.
|
114
|
+
Usage
|
115
|
+
-----
|
158
116
|
|
159
|
-
|
117
|
+
### <a name="quickstart"></a>Getting Started
|
160
118
|
|
161
|
-
|
162
|
-
|
163
|
-
its value is a symbol it will be converted to CamelCase and appended to the
|
164
|
-
`Mobility::Backend` module name to get the backend class (so `key_value` will
|
165
|
-
be converted to {Mobility::Backend::KeyValue}). See the list of [backends](#backend).
|
166
|
-
- **`cache`** (Boolean)<br>
|
167
|
-
Whether to use a [cache](#cache).
|
168
|
-
- **`fallbacks`** (Boolean or Hash)<br>
|
169
|
-
Enable [fallbacks](#fallbacks), and optionally configure them.
|
170
|
-
- **`dirty`** (Boolean)<br>
|
171
|
-
Whether to enable [dirty tracking](#dirty).
|
172
|
-
- **`locale_accessors`** (Boolean or Array)<br>
|
173
|
-
Enable [locale accessors](#locale-accessors) and optionally configure them.
|
174
|
-
|
175
|
-
In addition to these, each backend may have specific configuration options. For
|
176
|
-
example, the default key-value backend, which stores attributes and their
|
177
|
-
translations as key/value pairs on shared tables, has a `type` option which
|
178
|
-
specifies which type of column (string or text) to use for storing
|
179
|
-
translations.
|
180
|
-
|
181
|
-
Here is an example defining three attributes on an ActiveRecord model:
|
119
|
+
Once the install generator has been run to generate translation tables, using
|
120
|
+
Mobility is as easy as adding a few lines to any class you want to translate:
|
182
121
|
|
183
122
|
```ruby
|
184
|
-
class
|
123
|
+
class Word < ActiveRecord::Base
|
185
124
|
include Mobility
|
186
|
-
translates :
|
187
|
-
translates :
|
125
|
+
translates :name, type: :string
|
126
|
+
translates :meaning, type: :text
|
188
127
|
end
|
189
128
|
```
|
190
129
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
default) is disabled for `title` and `author` (but not `content`).
|
195
|
-
[Fallbacks](#fallbacks) are enabled for `content` (but not `title` or
|
196
|
-
`author`).
|
197
|
-
|
198
|
-
Finally, `title` and `author` store their translations as string columns
|
199
|
-
(`type: :string`) whereas `content` stores its values as text columns (`type:
|
200
|
-
:text`). The `type` key is a backend-specific option used by the `KeyValue`
|
201
|
-
backend.
|
202
|
-
|
203
|
-
Note that Mobility will detect the model class and use this to determine which
|
204
|
-
ORM-specific backend to use. In the example above, it will use
|
205
|
-
{Mobility::Backend::ActiveRecord::KeyValue}; if the class were a
|
206
|
-
`Sequel::Model`, it would have used {Mobility::Backend::Sequel::KeyValue}. In
|
207
|
-
general options to configure the backend are ORM-independent.
|
130
|
+
You now have translated attributes `name` (as a string column) and `meaning`
|
131
|
+
(as a text column) on the model `Word`. You can set their values like you
|
132
|
+
would any other attribute:
|
208
133
|
|
209
|
-
### Setting the Locale
|
210
134
|
|
211
|
-
|
212
|
-
|
213
|
-
|
135
|
+
```ruby
|
136
|
+
word = Word.new
|
137
|
+
word.name = "mobility"
|
138
|
+
word.meaning = "(noun): quality of being changeable, adaptable or versatile"
|
139
|
+
word.name
|
140
|
+
#=> "mobility"
|
141
|
+
word.meaning
|
142
|
+
#=> "(noun): quality of being changeable, adaptable or versatile"
|
143
|
+
word.save
|
144
|
+
word = Word.first
|
145
|
+
word.name
|
146
|
+
#=> "mobility"
|
147
|
+
word.meaning
|
148
|
+
#=> "(noun): quality of being changeable, adaptable or versatile"
|
149
|
+
```
|
150
|
+
|
151
|
+
Presence methods are also supported:
|
214
152
|
|
215
153
|
```ruby
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
154
|
+
word.name?
|
155
|
+
#=> true
|
156
|
+
word.name = nil
|
157
|
+
word.name?
|
158
|
+
#=> false
|
159
|
+
word.name = ""
|
160
|
+
word.name?
|
161
|
+
#=> false
|
221
162
|
```
|
222
163
|
|
223
|
-
|
164
|
+
What's different here is that the value of these attributes changes with the
|
165
|
+
value of `I18n.locale`:
|
224
166
|
|
225
167
|
```ruby
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
168
|
+
I18n.locale = :ja
|
169
|
+
word.name
|
170
|
+
#=> nil
|
171
|
+
word.meaning
|
172
|
+
#=> nil
|
231
173
|
```
|
232
174
|
|
233
|
-
|
234
|
-
|
235
|
-
Mobility defines getter, setter, and presence methods for translated attributes
|
236
|
-
on the model class. Regardless of which backend you use to store translations,
|
237
|
-
the basic interface for accessing them is the same.
|
238
|
-
|
239
|
-
Assuming we have a model `Post` as above, we can first set the locale, then
|
240
|
-
create a post with a translated attribute:
|
175
|
+
The `name` and `meaning` of this word are not defined in any locale except
|
176
|
+
English. Let's define them in Japanese and save the model:
|
241
177
|
|
242
178
|
```ruby
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
#=> "
|
247
|
-
|
248
|
-
#=>
|
179
|
+
word.name = "モビリティ"
|
180
|
+
word.meaning = "(名詞):動きやすさ、可動性"
|
181
|
+
word.name
|
182
|
+
#=> "モビリティ"
|
183
|
+
word.meaning
|
184
|
+
#=> "(名詞):動きやすさ、可動性"
|
185
|
+
word.save
|
249
186
|
```
|
250
187
|
|
251
|
-
|
188
|
+
Now our word has names and meanings in two different languages:
|
252
189
|
|
253
190
|
```ruby
|
254
|
-
|
255
|
-
|
256
|
-
|
191
|
+
word = Word.first
|
192
|
+
I18n.locale = :en
|
193
|
+
word.name
|
194
|
+
#=> "mobility"
|
195
|
+
word.meaning
|
196
|
+
#=> "(noun): quality of being changeable, adaptable or versatile"
|
197
|
+
I18n.locale = :ja
|
198
|
+
word.name
|
199
|
+
#=> "モビリティ"
|
200
|
+
word.meaning
|
201
|
+
#=> "(名詞):動きやすさ、可動性"
|
257
202
|
```
|
258
203
|
|
259
|
-
|
204
|
+
Internally, Mobility is mapping the values in different locales to storage
|
205
|
+
locations, usually database columns. By default these values are stored as keys
|
206
|
+
(attribute names) and values (attribute translations) on a set of translation
|
207
|
+
tables, one for strings and one for text columns, but this can be easily
|
208
|
+
changed and/or customized (see the [Backends](#backends) section below).
|
260
209
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
post.title
|
269
|
-
#=> "Mobility(名詞):動きやすさ、可動性"
|
270
|
-
post.title?
|
271
|
-
#=> true
|
272
|
-
```
|
210
|
+
### Getting and Setting Translations
|
211
|
+
|
212
|
+
The easiest way to get or set a translation is to use the getter and setter
|
213
|
+
methods described above (`word.name` and `word.name=`), but you may want to
|
214
|
+
access the value of an attribute in a specific locale, independent of the
|
215
|
+
current value of `I18n.locale` (or `Mobility.locale`). There are a few ways to
|
216
|
+
do this.
|
273
217
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
should not generally be necessary):
|
218
|
+
The first way is to define locale-specific methods, one for each locale you
|
219
|
+
want to access directly on a given attribute. These are called "locale
|
220
|
+
accessors" in Mobility, and they can be defined by passing a `locale_accessors`
|
221
|
+
option when defining translated attributes on the model class:
|
279
222
|
|
280
223
|
```ruby
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
224
|
+
class Word < ActiveRecord::Base
|
225
|
+
include Mobility
|
226
|
+
translates :name, type: :string, locale_accessors: [:en, :ja]
|
227
|
+
end
|
285
228
|
```
|
286
229
|
|
287
|
-
|
288
|
-
|
230
|
+
Since we have enabled locale accessors for English and Japanese, we can access
|
231
|
+
translations for these locales with `name_en` and `name_ja`:
|
289
232
|
|
290
233
|
```ruby
|
291
|
-
|
292
|
-
#=> "
|
293
|
-
|
294
|
-
#=> "
|
234
|
+
word.name_en
|
235
|
+
#=> "mobility"
|
236
|
+
word.name_ja
|
237
|
+
#=> "モビリティ"
|
238
|
+
word.name_en = "foo"
|
239
|
+
word.name
|
240
|
+
#=> "foo"
|
295
241
|
```
|
296
242
|
|
297
|
-
|
243
|
+
Other locales, however, will not work:
|
298
244
|
|
299
245
|
```ruby
|
300
|
-
|
301
|
-
|
302
|
-
post.title
|
303
|
-
#=> "new title"
|
304
|
-
post.title_backend.write(:en, "Mobility (noun): quality of being changeable, adaptable or versatile")
|
305
|
-
post.save
|
306
|
-
post.title
|
307
|
-
#=> "Mobility (noun): quality of being changeable, adaptable or versatile"
|
246
|
+
word.name_ru
|
247
|
+
#=> NoMethodError: undefined method `name_ru' for #<Word id: ... >
|
308
248
|
```
|
309
249
|
|
310
|
-
|
311
|
-
|
312
|
-
shared tables, `mobility_string_translations` and `mobility_text_translations`,
|
313
|
-
depending on the `type` of the attribute (corresponding to the type of column
|
314
|
-
used).
|
250
|
+
To generate methods for all locales in `I18n.available_locales` (at the time
|
251
|
+
the model is first loaded), use `locale_accessors: true`.
|
315
252
|
|
316
|
-
|
317
|
-
|
253
|
+
An alternative to using the `locale_accessors` option is to use the
|
254
|
+
`fallthrough_accessors` option, with `fallthrough_accessors: true`. This uses
|
255
|
+
Ruby's [`method_missing`](http://apidock.com/ruby/BasicObject/method_missing)
|
256
|
+
method to implicitly define the same methods as above, but supporting any
|
257
|
+
locale without any method definitions. (Locale accessors and fallthrough
|
258
|
+
locales can be used together without conflict, with locale accessors taking
|
259
|
+
precedence if defined for a given locale.)
|
318
260
|
|
319
|
-
|
261
|
+
For example, if we define `Word` this way:
|
320
262
|
|
321
|
-
|
263
|
+
```ruby
|
264
|
+
class Word < ActiveRecord::Base
|
265
|
+
include Mobility
|
266
|
+
translates :name, type: :string, fallthrough_accessors: true
|
267
|
+
end
|
268
|
+
```
|
322
269
|
|
323
|
-
|
324
|
-
Store translations as columns on a table with locale as a postfix, of the
|
325
|
-
form `title_en`, `title_fr`, etc. for an attribute `title`.
|
326
|
-
- **`:table`** ({Mobility::Backend::Table})<br>
|
327
|
-
Store translations on a model-specific table, e.g. for a model `Post` with
|
328
|
-
table `posts`, store translations on a table `post_translations`, and join
|
329
|
-
the translation table when fetching translated values.
|
330
|
-
- **`:key_value`** ({Mobility::Backend::KeyValue})<br>
|
331
|
-
Store translations on a shared table of locale/attribute translation pairs,
|
332
|
-
associated through a polymorphic relation with multiple models.
|
333
|
-
- **`:serialized`** ({Mobility::Backend::Serialized})<br>
|
334
|
-
Store translations as serialized YAML or JSON on a text column.
|
335
|
-
- **`:hstore`** ({Mobility::Backend::Hstore})<br>
|
336
|
-
Store translations as values of a hash stored as a PostgreSQL hstore column.
|
337
|
-
- **`:jsonb`** ({Mobility::Backend::Jsonb})<br>
|
338
|
-
Store translations as values of a hash stored as a PostgreSQL jsonb column.
|
270
|
+
... then we can access any locale we want, without specifying them upfront:
|
339
271
|
|
340
|
-
|
341
|
-
|
272
|
+
```ruby
|
273
|
+
word = Word.new
|
274
|
+
word.name_fr = "mobilité"
|
275
|
+
word.name_fr
|
276
|
+
#=> "mobilité"
|
277
|
+
word.name_ja = "モビリティ"
|
278
|
+
word.name_ja
|
279
|
+
#=> "モビリティ"
|
280
|
+
```
|
342
281
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
potentially many different languages, consider `:table`.
|
348
|
-
- For all other cases (many locales, many translated models), or if you're just
|
349
|
-
not sure, the recommended solution is `:key_value` for maximum flexibility
|
350
|
-
and minimum database migrations.
|
282
|
+
(Note however that Mobility will complain if you have
|
283
|
+
`I18n.enforce_available_locales` set to `true` and you try accessing a locale
|
284
|
+
not present in `I18n.available_locales`; set it to `false` if you want to allow
|
285
|
+
*any* locale.)
|
351
286
|
|
287
|
+
Another way to fetch values in a locale is to pass the `locale` option to the
|
288
|
+
getter method, like this:
|
352
289
|
|
353
|
-
|
290
|
+
```ruby
|
291
|
+
word.name(locale: :en)
|
292
|
+
#=> "mobility"
|
293
|
+
word.name(locale: :fr)
|
294
|
+
#=> "mobilité"
|
295
|
+
```
|
354
296
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
used to define such methods on a given class:
|
297
|
+
You can also *set* the value of an attribute this way; however, since the
|
298
|
+
`word.name = <value>` syntax does not accept any options, the only way to do this is to
|
299
|
+
use `send` (this is included mostly for consistency):
|
359
300
|
|
360
301
|
```ruby
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
end
|
302
|
+
word.send(:name=, "mobiliteit", locale: :nl)
|
303
|
+
word.name_nl
|
304
|
+
#=> "mobiliteit"
|
365
305
|
```
|
366
306
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
Since we have enabled locale accessors for English and Japanese, we can access
|
372
|
-
translations for these locales with:
|
307
|
+
Yet another way to get and set translated attributes is to call `read` and
|
308
|
+
`write` on the storage backend, which can be accessed using the method
|
309
|
+
`<attribute>_backend`. Without worrying too much about the details of
|
310
|
+
how this works for now, the syntax for doing this is simple:
|
373
311
|
|
374
312
|
```ruby
|
375
|
-
|
376
|
-
#=> "
|
377
|
-
|
378
|
-
#=> "
|
379
|
-
|
380
|
-
|
313
|
+
word.name_backend.read(:en)
|
314
|
+
#=> "mobility"
|
315
|
+
word.name_backend.read(:nl)
|
316
|
+
#=> "mobiliteit"
|
317
|
+
word.name_backend.write(:en, "foo")
|
318
|
+
word.name_backend.read(:en)
|
381
319
|
#=> "foo"
|
382
320
|
```
|
383
321
|
|
384
|
-
|
385
|
-
|
322
|
+
Internally, all methods for accessing translated attributes ultimately end up
|
323
|
+
reading and writing from the backend instance this way.
|
386
324
|
|
387
|
-
|
388
|
-
`fallthrough_accessors` option (defined in {Mobility::FallthroughAccessors})
|
389
|
-
with `fallthrough_accessors: true`. This uses `method_missing` to implicitly
|
390
|
-
define the same methods as above, but supporting any locale without any method
|
391
|
-
definitions. [Dirty tracking](#dirty) enables fallthrough locales for tracking
|
392
|
-
attribute changes. (Both locale accessors and fallthrough locales can be used
|
393
|
-
together without conflict.)
|
325
|
+
### Setting the Locale
|
394
326
|
|
395
|
-
|
396
|
-
|
327
|
+
It may not always be desirable to use `I18n.locale` to set the locale for
|
328
|
+
content translations. For example, a user whose interface is in English
|
329
|
+
(`I18n.locale` is `:en`) may want to see content in Japanese. If you use
|
330
|
+
`I18n.locale` exclusively for the locale, you will have a hard time showing
|
331
|
+
stored translations in one language while showing the interface in another
|
332
|
+
language.
|
397
333
|
|
398
|
-
|
334
|
+
For these cases, Mobility also has its own locale, which defaults to
|
335
|
+
`I18n.locale` but can be set independently:
|
399
336
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
337
|
+
```ruby
|
338
|
+
I18n.locale = :en
|
339
|
+
Mobility.locale #=> :en
|
340
|
+
Mobility.locale = :fr
|
341
|
+
Mobility.locale #=> :fr
|
342
|
+
I18n.locale #=> :en
|
343
|
+
```
|
404
344
|
|
405
|
-
|
406
|
-
|
407
|
-
backend:
|
345
|
+
To set the Mobility locale in a block, you can use `Mobility.with_locale` (like
|
346
|
+
`I18n.with_locale`):
|
408
347
|
|
409
348
|
```ruby
|
410
|
-
|
411
|
-
|
349
|
+
Mobility.locale = :en
|
350
|
+
Mobility.with_locale(:ja) do
|
351
|
+
Mobility.locale #=> :ja
|
352
|
+
end
|
353
|
+
Mobility.locale #=> :en
|
412
354
|
```
|
413
355
|
|
414
|
-
|
356
|
+
Mobility uses [RequestStore](https://github.com/steveklabnik/request_store) to
|
357
|
+
reset these global variables after every request, so you don't need to worry
|
358
|
+
about thread safety. If you're not using Rails, consult RequestStore's
|
359
|
+
[README](https://github.com/steveklabnik/request_store#no-rails-no-problem) for
|
360
|
+
details on how to configure it for your use case.
|
415
361
|
|
416
362
|
### <a name="fallbacks"></a>Fallbacks
|
417
363
|
|
418
|
-
Mobility offers basic support for translation fallbacks
|
419
|
-
as
|
420
|
-
|
421
|
-
with fallbacks for each locale as an option to the backend:
|
364
|
+
Mobility offers basic support for translation fallbacks. To enable fallbacks,
|
365
|
+
pass a hash with fallbacks for each locale as an option when defining
|
366
|
+
translated attributes on a class:
|
422
367
|
|
423
368
|
```ruby
|
424
|
-
class
|
369
|
+
class Word < ActiveRecord::Base
|
425
370
|
include Mobility
|
426
|
-
translates :
|
371
|
+
translates :name, type: :string, fallbacks: { de: :ja, fr: :ja }
|
372
|
+
translates :meaning, type: :text, fallbacks: { de: :ja, fr: :ja }
|
427
373
|
end
|
428
374
|
```
|
429
375
|
|
430
376
|
Internally, Mobility assigns the fallbacks hash to an instance of
|
431
|
-
`I18n::Locale::Fallbacks.new
|
377
|
+
`I18n::Locale::Fallbacks.new` (this can be customized by setting the
|
378
|
+
`default_fallbacks` configuration option, see the [API documentation on
|
379
|
+
configuration](http://www.rubydoc.info/gems/mobility/Mobility/Configuration)).
|
432
380
|
|
433
|
-
By setting fallbacks for
|
434
|
-
through to the Japanese value if none is present for either of these locales
|
381
|
+
By setting fallbacks for German and French to Japanese, values will fall
|
382
|
+
through to the Japanese value if none is present for either of these locales,
|
383
|
+
but not for other locales:
|
435
384
|
|
436
385
|
```ruby
|
437
|
-
Mobility.locale = :
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
#=> "
|
443
|
-
|
444
|
-
#=> "
|
445
|
-
|
446
|
-
#=> "
|
386
|
+
Mobility.locale = :ja
|
387
|
+
word = Word.create(name: "モビリティ", meaning: "(名詞):動きやすさ、可動性")
|
388
|
+
word.name(locale: :de)
|
389
|
+
#=> "モビリティ"
|
390
|
+
word.meaning(locale: :de)
|
391
|
+
#=> "(名詞):動きやすさ、可動性"
|
392
|
+
word.name(locale: :fr)
|
393
|
+
#=> "モビリティ"
|
394
|
+
word.meaning(locale: :fr)
|
395
|
+
#=> "(名詞):動きやすさ、可動性"
|
396
|
+
word.name(locale: :ru)
|
397
|
+
#=> nil
|
398
|
+
word.meaning(locale: :ru)
|
399
|
+
#=> nil
|
447
400
|
```
|
448
401
|
|
449
402
|
You can optionally disable fallbacks to get the real value for a given locale
|
450
403
|
(for example, to check if a value in a particular locale is set or not) by
|
451
|
-
passing `fallback: false` (
|
452
|
-
the getter method:
|
404
|
+
passing `fallback: false` (*singular*, not plural) to the getter method:
|
453
405
|
|
454
406
|
```ruby
|
455
|
-
|
407
|
+
word.meaning(locale: :de, fallback: false)
|
456
408
|
#=> nil
|
457
|
-
|
409
|
+
word.meaning(locale: :fr, fallback: false)
|
458
410
|
#=> nil
|
411
|
+
word.meaning(locale: :ja, fallback: false)
|
412
|
+
#=> "(名詞):動きやすさ、可動性"
|
459
413
|
```
|
460
414
|
|
461
415
|
You can also set the fallback locales for a single read by passing one or more
|
462
|
-
locales
|
416
|
+
locales:
|
463
417
|
|
464
418
|
```ruby
|
465
|
-
|
466
|
-
|
467
|
-
|
419
|
+
Mobility.with_locale(:fr) do
|
420
|
+
word.meaning = "(nf): aptitude à bouger, à se déplacer, à changer, à évoluer"
|
421
|
+
end
|
422
|
+
word.save
|
423
|
+
word.meaning(locale: :de, fallback: false)
|
468
424
|
#=> nil
|
469
|
-
|
470
|
-
#=> "
|
471
|
-
|
472
|
-
#=> "
|
425
|
+
word.meaning(locale: :de, fallback: :fr)
|
426
|
+
#=> "(nf): aptitude à bouger, à se déplacer, à changer, à évoluer"
|
427
|
+
word.meaning(locale: :de, fallback: [:ja, :fr])
|
428
|
+
#=> "(名詞):動きやすさ、可動性"
|
473
429
|
```
|
474
430
|
|
475
|
-
For more details, see
|
431
|
+
For more details, see the [API documentation on
|
432
|
+
fallbacks](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Fallbacks)
|
433
|
+
and [this article on I18n
|
434
|
+
fallbacks](https://github.com/svenfuchs/i18n/wiki/Fallbacks).
|
476
435
|
|
477
436
|
### <a name="dirty"></a>Dirty Tracking
|
478
437
|
|
479
|
-
Dirty tracking (tracking of changed attributes) can be enabled for models which
|
438
|
+
Dirty tracking (tracking of changed attributes) can be enabled for models which
|
439
|
+
support it. Currently this is models which include
|
440
|
+
[ActiveModel::Dirty](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html)
|
441
|
+
(like `ActiveRecord::Base`) and Sequel models (through the
|
442
|
+
[dirty](http://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/Dirty.html)
|
443
|
+
plugin).
|
480
444
|
|
481
|
-
Enabling dirty tracking is as simple as sending the `dirty: true` option
|
482
|
-
|
483
|
-
(ActiveModel or Sequel); we will describe the
|
445
|
+
Enabling dirty tracking is as simple as sending the `dirty: true` option when
|
446
|
+
defining a translated attribute. The way dirty tracking works is somewhat
|
447
|
+
dependent on the model class (ActiveModel or Sequel); we will describe the
|
448
|
+
ActiveModel implementation here.
|
484
449
|
|
485
450
|
First, enable dirty tracking (note that this is a persisted AR model, although
|
486
451
|
dirty tracking is not specific to AR and works for non-persisted models as well):
|
@@ -488,29 +453,39 @@ dirty tracking is not specific to AR and works for non-persisted models as well)
|
|
488
453
|
```ruby
|
489
454
|
class Post < ActiveRecord::Base
|
490
455
|
include Mobility
|
491
|
-
translates :title, dirty: true
|
456
|
+
translates :title, type: :string, dirty: true
|
492
457
|
end
|
493
458
|
```
|
494
459
|
|
495
|
-
|
460
|
+
Let's assume we start with a post with a title in English and Japanese:
|
461
|
+
|
462
|
+
```ruby
|
463
|
+
post = Post.create(title: "Introducing Mobility")
|
464
|
+
Mobility.with_locale(:ja) { post.title = "モビリティの紹介" }
|
465
|
+
post.save
|
466
|
+
```
|
467
|
+
|
468
|
+
Now let's change the title:
|
496
469
|
|
497
470
|
```ruby
|
498
|
-
post.
|
499
|
-
#=> "Mobility
|
471
|
+
post = Post.first
|
472
|
+
post.title #=> "Introducing Mobility"
|
500
473
|
post.title = "a new title"
|
501
|
-
|
502
|
-
#=> "
|
503
|
-
post.title = "新しいタイトル"
|
474
|
+
Mobility.with_locale(:ja) do
|
475
|
+
post.title #=> "モビリティの紹介"
|
476
|
+
post.title = "新しいタイトル"
|
477
|
+
post.title #=> "新しいタイトル"
|
478
|
+
end
|
504
479
|
```
|
505
480
|
|
506
481
|
Now you can use dirty methods as you would any other (untranslated) attribute:
|
507
482
|
|
508
483
|
```ruby
|
509
484
|
post.title_was
|
510
|
-
#=> "Mobility
|
485
|
+
#=> "Introducing Mobility"
|
511
486
|
Mobility.locale = :ja
|
512
487
|
post.title_was
|
513
|
-
#=> "
|
488
|
+
#=> "モビリティの紹介"
|
514
489
|
post.changed
|
515
490
|
["title_en", "title_ja"]
|
516
491
|
post.save
|
@@ -524,22 +499,41 @@ post.previous_changes
|
|
524
499
|
{
|
525
500
|
"title_en" =>
|
526
501
|
[
|
527
|
-
"Mobility
|
502
|
+
"Introducing Mobility",
|
528
503
|
"a new title"
|
529
504
|
],
|
530
505
|
"title_ja" =>
|
531
506
|
[
|
532
|
-
"
|
507
|
+
"モビリティの紹介",
|
533
508
|
"新しいタイトル"
|
534
509
|
]
|
535
510
|
}
|
536
511
|
```
|
537
512
|
|
538
|
-
|
539
|
-
|
540
|
-
|
513
|
+
Notice that Mobility uses locale suffixes to indicate which locale has changed;
|
514
|
+
dirty tracking is implemented this way to ensure that it is clear what
|
515
|
+
has changed in which locale, avoiding any possible ambiguity.
|
516
|
+
|
517
|
+
For more details, see the [API documentation on dirty
|
518
|
+
tracking](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Dirty).
|
541
519
|
|
542
|
-
|
520
|
+
### Cache
|
521
|
+
|
522
|
+
The Mobility cache caches localized values that have been fetched once so they
|
523
|
+
can be quickly retrieved again. The cache is enabled by default and should
|
524
|
+
generally only be disabled when debugging; this can be done by passing `cache:
|
525
|
+
false` when defining an attribute, like this:
|
526
|
+
|
527
|
+
```ruby
|
528
|
+
class Word < ActiveRecord::Base
|
529
|
+
include Mobility
|
530
|
+
translates :name, type: :string, cache: false
|
531
|
+
end
|
532
|
+
```
|
533
|
+
|
534
|
+
The cache is normally just a hash with locale keys and string (translation)
|
535
|
+
values, but some backends (e.g. KeyValue and Table backends) have slightly more
|
536
|
+
complex implementations.
|
543
537
|
|
544
538
|
### <a name="querying"></a>Querying
|
545
539
|
|
@@ -554,12 +548,12 @@ So assuming a model:
|
|
554
548
|
```ruby
|
555
549
|
class Post < ActiveRecord::Base
|
556
550
|
include Mobility
|
557
|
-
translates :title,
|
558
|
-
translates :content,
|
551
|
+
translates :title, type: :string
|
552
|
+
translates :content, type: :text
|
559
553
|
end
|
560
554
|
```
|
561
555
|
|
562
|
-
we can query for posts with title "foo" and content "bar" just as we would
|
556
|
+
... we can query for posts with title "foo" and content "bar" just as we would
|
563
557
|
query on untranslated attributes, and Mobility will convert the queries to
|
564
558
|
whatever the backend requires to actually return the correct results:
|
565
559
|
|
@@ -571,108 +565,128 @@ results in the SQL:
|
|
571
565
|
|
572
566
|
```sql
|
573
567
|
SELECT "posts".* FROM "posts"
|
574
|
-
|
568
|
+
INNER JOIN "mobility_string_translations" "title_mobility_string_translations"
|
575
569
|
ON "title_mobility_string_translations"."key" = 'title'
|
576
570
|
AND "title_mobility_string_translations"."locale" = 'en'
|
577
571
|
AND "title_mobility_string_translations"."translatable_type" = 'Post'
|
578
572
|
AND "title_mobility_string_translations"."translatable_id" = "posts"."id"
|
579
|
-
|
573
|
+
INNER JOIN "mobility_text_translations" "content_mobility_text_translations"
|
580
574
|
ON "content_mobility_text_translations"."key" = 'content'
|
581
575
|
AND "content_mobility_text_translations"."locale" = 'en'
|
582
576
|
AND "content_mobility_text_translations"."translatable_type" = 'Post'
|
583
577
|
AND "content_mobility_text_translations"."translatable_id" = "posts"."id"
|
584
|
-
|
585
|
-
"title_mobility_string_translations"."value" = 'foo'
|
578
|
+
WHERE "content_mobility_text_translations"."value" = 'bar'
|
579
|
+
AND "title_mobility_string_translations"."value" = 'foo'
|
586
580
|
```
|
587
581
|
|
588
582
|
As can be seen in the query above, behind the scenes Mobility joins two tables,
|
589
583
|
one with string translations and one with text translations, and aliases the
|
590
584
|
joins for each attribute so as to match the particular values passed in to the
|
591
|
-
query. Details of how this is done can be found in
|
592
|
-
|
585
|
+
query. Details of how this is done can be found in the [API documentation for
|
586
|
+
AR query
|
587
|
+
methods](http://www.rubydoc.info/gems/mobility/Mobility/Backend/ActiveRecord/KeyValue/QueryMethods).
|
588
|
+
|
589
|
+
If you would prefer to avoid the `i18n` scope everywhere, define it as a
|
590
|
+
default scope on your model:
|
593
591
|
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
592
|
+
```ruby
|
593
|
+
class Post < ActiveRecord::Base
|
594
|
+
include Mobility
|
595
|
+
translates :title, type: :string
|
596
|
+
translates :content, type: :text
|
597
|
+
default_scope { i18n }
|
598
|
+
end
|
599
|
+
```
|
598
600
|
|
599
|
-
|
600
|
-
{Mobility::Backend::ActiveRecord::QueryMethods} or
|
601
|
-
{Mobility::Backend::Sequel::QueryMethods}.
|
601
|
+
Now translated attributes can be queried just like normal attributes:
|
602
602
|
|
603
|
-
|
603
|
+
```ruby
|
604
|
+
Post.find_by(title: "Introducing Mobility")
|
605
|
+
#=> finds post with English title "Introducing Mobility"
|
606
|
+
```
|
604
607
|
|
605
|
-
|
606
|
-
|
607
|
-
any particular translation solution, so that application designers are free to
|
608
|
-
mix, match and customize strategies to suit their needs.
|
608
|
+
<a name="backends"></a>Backends
|
609
|
+
--------
|
609
610
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
metaprogramming, details of which can be found in the [API
|
614
|
-
documentation][docs]
|
611
|
+
Mobility supports different storage strategies, called "backends". The default
|
612
|
+
backend is the `KeyValue` backend, which stores translations in two tables, by
|
613
|
+
default named `mobility_text_translations` and `mobility_string_translations`.
|
615
614
|
|
616
|
-
|
617
|
-
|
618
|
-
|
615
|
+
You can set the default backend to a different value in the global
|
616
|
+
configuration, or you can set it explicitly when defining a translated
|
617
|
+
attribute, like this:
|
619
618
|
|
620
619
|
```ruby
|
621
|
-
class
|
622
|
-
|
623
|
-
translates :title, backend: :key_value, type: :string
|
624
|
-
translates :content, backend: :column, cache: false
|
625
|
-
translates :author_name, backend: :jsonb
|
620
|
+
class Word < ActiveRecord::Base
|
621
|
+
translates :name, backend: :table
|
626
622
|
end
|
627
623
|
```
|
628
624
|
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
625
|
+
This would set the `name` attribute to use the `Table` backend (see below).
|
626
|
+
The `type` option (`type: :string` or `type: :text`) is missing here because
|
627
|
+
this is an option specific to the KeyValue backend (specifying which shared
|
628
|
+
table to store translations on). Backends have their own specific options; see
|
629
|
+
the API documentation for which options are available for each.
|
634
630
|
|
635
|
-
|
631
|
+
Everything else described above (fallbacks, dirty tracking, locale accessors,
|
632
|
+
caching, querying, etc) is the same regardless of which backend you use.
|
636
633
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
634
|
+
### Table Backend (like Globalize)
|
635
|
+
|
636
|
+
The `Table` backend stores translations as columns on a model-specific table. If
|
637
|
+
your model uses the table `posts`, then by default this backend will store an
|
638
|
+
attribute `title` on a table `post_translations`, and join the table to
|
639
|
+
retrieve the translated value.
|
640
|
+
|
641
|
+
To use the table backend on a model, you will need to first create a
|
642
|
+
translation table for the model, which (with Rails) you can do using the
|
643
|
+
`mobility:translations` generator:
|
644
|
+
|
645
|
+
```
|
646
|
+
rails generate mobility:translations post title:string content:text
|
641
647
|
```
|
642
648
|
|
643
|
-
will
|
649
|
+
This will generate the `post_translations` table with columns `title` and
|
650
|
+
`content`, and all other necessary columns and indices. For more details see
|
651
|
+
the API documentation on the [`Mobility::Backend::Table`
|
652
|
+
class](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Table).
|
653
|
+
|
654
|
+
### Column Backend (like Traco)
|
655
|
+
|
656
|
+
The `Column` backend stores translationsi as columns with locale suffixes on
|
657
|
+
the model table. For an attribute `title`, these would be of the form
|
658
|
+
`title_en`, `title_fr`, etc.
|
659
|
+
|
660
|
+
Use the `mobility:translations` generator to add columns for locales in
|
661
|
+
`I18n.available_locales` to your model:
|
644
662
|
|
645
|
-
```sql
|
646
|
-
SELECT "posts".* FROM "posts"
|
647
|
-
INNER JOIN "mobility_string_translations" "title_mobility_string_translations"
|
648
|
-
ON "title_mobility_string_translations"."key" = 'title'
|
649
|
-
AND "title_mobility_string_translations"."locale" = 'en'
|
650
|
-
AND "title_mobility_string_translations"."translatable_type" = 'Post'
|
651
|
-
AND "title_mobility_string_translations"."translatable_id" = "posts"."id"
|
652
|
-
WHERE (posts.author_name @> ('{"en":"baz"}')::jsonb)
|
653
|
-
AND "posts"."content_en" = 'bar'
|
654
|
-
AND "title_mobility_string_translations"."value" = 'foo'
|
655
663
|
```
|
664
|
+
rails generate mobility:translations post title:string content:text
|
665
|
+
```
|
666
|
+
|
667
|
+
For more details, see the API documentation on the [`Mobility::Backend::Column`
|
668
|
+
class](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Column).
|
656
669
|
|
657
|
-
|
658
|
-
record which satisfies all of them.
|
670
|
+
### PostgreSQL-specific Backends
|
659
671
|
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
672
|
+
Mobility also supports jsonb and Hstore storage options, if you are using
|
673
|
+
PostgreSQL as your database. To use this option, create column(s) on the model
|
674
|
+
table for each translated attribute, and set your backend to `:jsonb` or
|
675
|
+
`:hstore`. Other details are covered in the API documentation
|
676
|
+
([`Mobility::Backend::Jsonb`](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Jsonb)
|
677
|
+
and
|
678
|
+
[`Mobility::Backend::Hstore`](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Hstore)).
|
666
679
|
|
667
|
-
|
680
|
+
Development
|
681
|
+
-----------
|
668
682
|
|
669
683
|
### Custom Backends
|
670
684
|
|
671
685
|
Although Mobility is primarily oriented toward storing ActiveRecord model
|
672
686
|
translations, it can potentially be used to handle storing translations in
|
673
|
-
other formats
|
674
|
-
|
675
|
-
|
687
|
+
other formats. In particular, the features mentioned above (locale accessors,
|
688
|
+
caching, fallbacks, dirty tracking to some degree) are not specific to database
|
689
|
+
storage.
|
676
690
|
|
677
691
|
To use a custom backend, simply pass the name of a class which includes
|
678
692
|
`Mobility::Backend` to `translates`:
|
@@ -689,9 +703,9 @@ class MyClass
|
|
689
703
|
end
|
690
704
|
```
|
691
705
|
|
692
|
-
For details on how to define a backend class, see the
|
693
|
-
|
694
|
-
|
706
|
+
For details on how to define a backend class, see the [API documentation on the
|
707
|
+
`Mobility::Backend`
|
708
|
+
module](http://www.rubydoc.info/gems/mobility/Mobility/Backend).
|
695
709
|
|
696
710
|
### Testing Backends
|
697
711
|
|
@@ -733,11 +747,14 @@ can be changed, see the shared examples for details.
|
|
733
747
|
Backends are also each tested against specialized specs targeted at their
|
734
748
|
particular implementations.
|
735
749
|
|
736
|
-
|
750
|
+
More Information
|
751
|
+
----------------
|
737
752
|
|
738
753
|
- [Github repository](https://www.github.com/shioyama/mobility)
|
739
754
|
- [API documentation][docs]
|
755
|
+
- [Wiki][wiki]
|
740
756
|
|
741
|
-
|
757
|
+
License
|
758
|
+
-------
|
742
759
|
|
743
760
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|