Localize.Translate.QueryBuilder (Localize Translate v0.1.0)

Copy Markdown View Source

Provides helpers for filtering translations in Ecto.Queries.

This module requires Ecto.SQL to be available during the compilation.

Summary

Functions

Generates a SQL fragment for accessing a translated field in an Ecto.Query.

Generates a SQL fragment for use in an Ecto.Query select clause, aliased to the original field name.

Functions

translated(module, translatable, locale)

(macro)

Generates a SQL fragment for accessing a translated field in an Ecto.Query.

The generated SQL fragment can be coupled with the rest of the functions and operators provided by Ecto.Query and Ecto.Query.API.

Arguments

  • module is the Ecto.Schema module that uses Localize.Translate. Validated at compile time.

  • translatable is either a query binding (such as a) for whole-record access, or a field access expression (such as a.title) for a specific translatable field.

  • locale is either a single locale (atom or string) or a list of locales acting as a fallback chain. May be a literal, a variable, or a runtime expression.

Returns

A fragment/1 AST suitable for use in Ecto.Query clauses. The fragment evaluates to the translated value with fallback to the base column, or NULL when no translation exists for the requested locales.

Safety

This macro emits errors when used with untranslatable schema modules or fields. Errors are emitted during compilation so invalid queries fail before they can run.

Examples

Assuming the Article schema defined in Structured translations:

# Return all articles that have a Spanish translation
from a in Article, where: not is_nil(translated(Article, a, :es))
#=> SELECT a0."id", a0."title", a0."body", a0."translations"
#=> FROM "articles" AS a0
#=> WHERE (NOT (NULLIF((a0."translations"->'es'),'null') IS NULL))

# Query items with a certain translated value
from a in Article, where: translated(Article, a.title, :fr) == "Elixir"
#=> SELECT a0."id", a0."title", a0."body", a0."translations"
#=> FROM "articles" AS a0
#=> WHERE (COALESCE(a0."translations"->$1->>$2, a0."title") = 'Elixir')

# Query items using a case insensitive comparison
from a in Article, where: ilike(translated(Article, a.body, :es), "%elixir%")
#=> SELECT a0."id", a0."title", a0."body", a0."translations"
#=> FROM "articles" AS a0
#=> WHERE (COALESCE(a0."translations"->$1->>$2, a0."body") ILIKE '%elixir%')

Structured translations vs free-form translations

Localize.Translate.QueryBuilder works with both structured translations and free-form translations.

When using structured translations, the translations are saved as an embedded schema. This means that the locale keys will be always present even if there is no translation for that locale. In the database we have a NULL value (nil in Elixir).

# If MyApp.Article uses structured translations
from a in Article, where: not is_nil(translated(Article, a, :es))
#=> SELECT a0."id", a0."title", a0."body", a0."translations"
#=> FROM "articles" AS a0
#=> WHERE (NOT (NULLIF((a0."translations"->'es'),'null') IS NULL))

More complex queries

The translated/3 macro can also be used with relations and joined schemas. For more complex examples take a look at the QueryBuilder tests (the file is located in test/localize/translate/query_builder_test.exs).

translated_as(module, translatable, locale)

(macro)

Generates a SQL fragment for use in an Ecto.Query select clause, aliased to the original field name.

Wraps translated/3 so the returned column carries the base column's name. Ecto can therefore load the translated value directly into the schema struct without further processing or conversion.

Arguments

  • module is the Ecto.Schema module that uses Localize.Translate.

  • translatable is a field access expression such as a.title. The base field name is used as the column alias.

  • locale is either a single locale or a list of locales acting as a fallback chain.

Returns

A fragment/1 AST that selects the translated value aliased to the original field name. See translated/3 for the underlying SQL produced.