@@ -22,7 +22,7 @@ use the `Trans.Translator` component without those dependencies though.
2222- PostgreSQL 9.4 or higher (since ` Trans ` leverages the JSONB datatype)
2323
2424Support for MySQL JSON type (introduced in MySQL 5.7) will come also, but right
25- now it is not yet implemented.
25+ now it is not yet implemented at the database adapter level .
2626
2727## Why Trans?
2828
@@ -46,9 +46,9 @@ translatable schema has an extra column that contains all of its translations.
4646This approach drastically reduces the number of required JOINs when filtering or
4747fetching records.
4848
49- Trans is lightweight and modularized. The main functionality is provided by the
50- ` Trans.Translator ` and the ` Trans.QueryBuilder ` modules, while the ` Trans ` module
51- simplifies the calls to translator and query builder functions from a schema .
49+ ` Trans ` is lightweight and modularized. The ` Trans ` module provides metadata
50+ that is used by the ` Trans.Translator ` and ` Trans.QueryBuilder ` modules, which
51+ implement the main functionality of this library .
5252
5353## Making a schema translatable
5454
@@ -84,6 +84,22 @@ defmodule Article do
8484end
8585```
8686
87+ Then we must use ` Trans ` from our schema module to indicate which fields will
88+ be translated.
89+
90+ ``` elixir
91+ defmodule Article do
92+ use Ecto .Schema
93+ use Trans , translates: [:title , :body ]
94+
95+ schema " articles" do
96+ field :title , :string
97+ field :body , :string
98+ field :translations , :map
99+ end
100+ end
101+ ```
102+
87103## Storing translations
88104
89105Translations are stored as a map of maps in the * translation container* of our
@@ -111,45 +127,44 @@ iex> article = Repo.insert!(changeset)
111127## Filtering queries by translations
112128
113129We may want to fetch articles that are translated into a certain language. To
114- do this we may use the ` with_translations /3` function of the ` Trans.QueryBuilder `
115- module:
130+ do this we use the ` Trans.QueryBuilder.translated /3` macro, which generates the
131+ required SQL fragment for us.
116132
117133``` elixir
118- iex> Article
119- .. .> |> Trans .QueryBuilder .with_translations (:es )
120- .. .> |> Repo .all
121- [debug] SELECT a0." id" , a0." title" , a0." body" , a0." translations"
122- FROM " articles" AS a0
123- WHERE ((a0." translations" - >> $1 ) is not null) [" es" ]
124- [debug] OK query= 4 .7ms queue= 0 .1ms
134+ iex> Repo .all (from a in Article ,
135+ .. .> where: not is_nil (translated (Article , a, :es )))
136+ # SELECT a0."id", a0."title", a0."body", a0."translations"
137+ # FROM "articles" AS a0
138+ # WHERE (NOT ((a0."translations"->"es") IS NULL))
125139```
126140
127141We can also get more specific and fetch only those articles for which their
128- Spanish title contains the word "Trans ".
142+ Spanish title matches "Elixir ".
129143
130144``` elixir
131- iex> Article
132- .. .> |> Trans .QueryBuilder .with_translation (:es , :title , " Trans" )
133- .. .> |> Repo .all
134- [debug] SELECT a0." id" , a0." title" , a0." body" , a0." translations"
135- FROM " articles" AS a0
136- WHERE (a0." translations" - > $1 - >> $2 = $3 ) [" es" , " title" , " Trans" ]
137- [debug] OK query= 2 .6ms queue= 0 .1ms
145+ iex> Repo .all (from a in Article ,
146+ .. .> where: translated (Article , a.title, :es ) == " Elixir" )
147+ # SELECT a0."id", a0."title", a0."body", a0."translations"
148+ # FROM "articles" AS a0
149+ # WHERE ((a0."translations"->"fr"->>"title") = "Elixir")
138150```
139151
140- By default ` Trans ` looks for an exact match when we add a condition. We may
141- also perform a LIKE or ILIKE comparison and use wilcards like this:
152+ The SQL fragment generated by the ` Trans.QueryBuilder.translated/3 ` macro is
153+ compatible with the rest of functions and macros provided by ` Ecto.Query ` and
154+ ` Ecto.Query.Api ` .
142155
143156``` elixir
144- iex> Article
145- .. .> |> Trans .QueryBuilder .with_translation (:es , :title , " %Trans%" , type: :like )
146- .. .> |> Repo .all
147- [debug] SELECT a0." id" , a0." title" , a0." body" , a0." translations"
148- FROM " articles" AS a0
149- WHERE (a0." translations" - > $1 - >> $2 LIKE $3 ) [" es" , " title" , " %Trans%" ]
150- [debug] OK query= 2 .1ms queue= 0 .1ms
157+ iex> Repo .all (from a in Article ,
158+ .. .> where: ilike (translated (Article , a.body, :es ), " %elixir%" ))
159+ # SELECT a0."id", a0."title", a0."body", a0."translations"
160+ # FROM "articles" AS a0
161+ # WHERE ((a0."translations"->"es"->>"body") ILIKE "%elixir%")
151162```
152163
164+ More complex queries such as adding conditions to joined schemas can be easily
165+ generated in the same way. Take a look at the documentation and tests for more
166+ examples.
167+
153168## Obtaining translations from a struct
154169
155170In those examples we will be referring to this article:
@@ -171,20 +186,20 @@ iex> article = %Article{
171186.. .> }
172187```
173188
174- Once we have already loaded a struct, we may use the ` Trans.Translator.translate/4 `
175- function to easily access a translation for a certain field.
189+ Once we have already loaded a struct, we may use the ` Trans.Translator.translate/3 `
190+ function to easily access a translation of a certain field.
176191
177192``` elixir
178- iex> Article . translate (article, :es , :body )
193+ iex> Trans . Translator . translate (article, :title , :es )
179194" Cómo escribir un corrector ortográfico"
180195```
181196
182- The ` Trans.Translator.translate/4 ` function also provides a fallback mechanism
197+ The ` Trans.Translator.translate/3 ` function also provides a fallback mechanism
183198that activates when the required translation does not exist:
184199
185200``` elixir
186- iex> Article . translate (article, :de , :title )
187- " How to Write a Spelling Corrector" # Fallback to untranslated value
201+ iex> Trans . Translator . translate (article, :title , :de )
202+ " How to Write a Spelling Corrector"
188203```
189204
190205## Using a different * translation container*
@@ -193,42 +208,13 @@ In the previous examples we have used `translations` as the name of the
193208* translation container* and ` Trans ` looks automatically for translations into this
194209field.
195210
196- We can also give the * translation container* a different name:
197-
198- ``` elixir
199- defmodule Article do
200- use Ecto .Schema
201-
202- schema " articles" do
203- field :title , :string
204- field :body , :string
205- field :article_translations , :map # this is our translation container
206- end
207- end
208- ```
209-
210- We can call the same functions as in previous examples, but we have to specify
211- the name of the * translation container* to override the default:
212-
213- ``` elixir
214- iex> Article
215- .. .> |> Trans .QueryBuilder .with_translation (:es , :title , " Trans" , container: :article_translations )
216- .. .> |> Repo .all
217- [debug] SELECT a0." id" , a0." title" , a0." body" , a0." translations"
218- FROM " articles" AS a0
219- WHERE (a0." article_translations" - > $1 - >> $2 = $3 ) [" es" , " title" , " Trans" ]
220- [debug] OK query= 2 .6ms queue= 0 .1ms
221- ```
222-
223- Having to specify the name of the * translation container* everytime is error
224- prone and can become tiresome. Instead we can use the ` Trans ` module in our
225- schema and have this option specified automatically for us:
211+ We can also give the * translation container* a different name, for example
212+ ** article_translations** :
226213
227214``` elixir
228215defmodule Article do
229216 use Ecto .Schema
230- use Trans , defaults: [container: :article_translations ],
231- translates: [:title , :body ]
217+ use Trans , translates: [:title , :body ], container: :article_translations
232218
233219 schema " articles" do
234220 field :title , :string
@@ -238,14 +224,13 @@ defmodule Article do
238224end
239225```
240226
241- Now we can do:
227+ We can call the same functions as in previous examples and both ` Trans.Translator `
228+ and ` Trans.QueryBuilder ` will automatically look for translations in the correct field.
242229
243230``` elixir
244- iex> Article
245- .. .> |> Article .with_translation (:es , :title , " Trans" )
246- .. .> |> Repo .all
247- [debug] SELECT a0." id" , a0." title" , a0." body" , a0." translations"
248- FROM " articles" AS a0
249- WHERE (a0." article_translations" - > $1 - >> $2 = $3 ) [" es" , " title" , " Trans" ]
250- [debug] OK query= 2 .6ms queue= 0 .1ms
251- ```
231+ iex> Repo .all (from a in Article ,
232+ .. .> where: not is_nil (translated (Article , a, :es )))
233+ # SELECT a0."id", a0."title", a0."body", a0."article_translations"
234+ # FROM "articles" AS a0
235+ # WHERE (NOT ((a0."article_translations"->"es") IS NULL))
236+ ```
0 commit comments