Skip to content

Commit

Permalink
Use github CI and improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
kipcole9 committed Mar 4, 2024
1 parent 03b0d29 commit ac0355f
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 77 deletions.
106 changes: 106 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Elixir CI

# Define workflow that runs when changes are pushed to the
# `main` branch or pushed to a PR branch that targets the `main`
# branch. Change the branch name if your project uses a
# different name for the main branch like "master" or "production".
on:
push:
branches: [ "main" ] # adapt branch for project
pull_request:
branches: [ "main" ] # adapt branch for project

# Sets the ENV `MIX_ENV` to `test` for running tests
env:
MIX_ENV: test

permissions:
contents: read

jobs:
test:
Set up a Postgres DB service. By default, Phoenix applications
use Postgres. This creates a database for running tests.
Additional services can be defined here if required.
services:
db:
image: postgres:12
ports: ['5432:5432']
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
runs-on: ubuntu-latest
name: Test on OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
strategy:
# Specify the OTP and Elixir versions to use when building
# and running the workflow steps.
matrix:
otp: ['25.3.2'] # Define the OTP version [required]
elixir: ['1.14.4'] # Define the elixir version [required]
steps:
# Step: Setup Elixir + Erlang image as the base.
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}

# Step: Check out the code.
- name: Checkout code
uses: actions/checkout@v3

# Step: Define how to cache deps. Restores existing cache if present.
- name: Cache deps
id: cache-deps
uses: actions/cache@v3
env:
cache-name: cache-elixir-deps
with:
path: deps
key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ env.cache-name }}-
# Step: Define how to cache the `_build` directory. After the first run,
# this speeds up tests runs a lot. This includes not re-compiling our
# project's downloaded deps every run.
- name: Cache compiled build
id: cache-build
uses: actions/cache@v3
env:
cache-name: cache-compiled-build
with:
path: _build
key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ env.cache-name }}-
${{ runner.os }}-mix-
# Step: Download project dependencies. If unchanged, uses
# the cached version.
- name: Install dependencies
run: mix deps.get

# Step: Compile the project treating any warnings as errors.
# Customize this step if a different behavior is desired.
- name: Compiles without warnings
run: mix compile --warnings-as-errors

# Step: Check that the checked in code has already been formatted.
# This step fails if something was found unformatted.
# Customize this step as desired.
# - name: Check Formatting
# run: mix format --check-formatted

# Step: Execute the tests.
- name: Run tests
run: mix test

# Step: Execute dialyzer.
- name: Run dialyzer
run: mix dialyzer
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Cldr Trans
![Build Status](http://sweatbox.noexpectations.com.au:8080/buildStatus/icon?job=cldr_trans)
![Build status](https://github.com/elixir-cldr/cldr_trans/actions/workflows/ci.yml/badge.svg)
[![Hex.pm](https://img.shields.io/hexpm/v/ex_cldr_trans.svg)](https://hex.pm/packages/ex_cldr_trans)
[![Hex.pm](https://img.shields.io/hexpm/dw/ex_cldr_trans.svg?)](https://hex.pm/packages/ex_cldr_trans)
[![Hex.pm](https://img.shields.io/hexpm/dt/ex_cldr_trans.svg?)](https://hex.pm/packages/ex_cldr_trans)
Expand All @@ -12,7 +12,7 @@ The package can be installed by adding `ex_cldr_trans` to your list of dependenc
```elixir
def deps do
[
{:ex_cldr_trans, "~> 1.1"}
{:ex_cldr_trans, "~> 1.0"}
]
end
```
Expand Down Expand Up @@ -143,16 +143,16 @@ defmodule MyApp.Article do
schema "articles" do
field :title, :string
field :body, :string
# use the 'translations' macro to set up a map-field with a set of nested
# structs to handle translation values for each configured locale and each
# use the 'translations' macro to set up a map-field with a set of nested
# structs to handle translation values for each configured locale and each
# translatable field
translations :translations
end

def changeset(article, params \\ %{}) do
article
|> cast(params, [:title, :body])
# use 'cast_embed' to handle values for the 'translations' map-field with
# use 'cast_embed' to handle values for the 'translations' map-field with
# a nested changeset
|> cast_embed(:translations, with: &translations_changeset/2)
|> validate_required([:title, :body])
Expand All @@ -163,7 +163,7 @@ defmodule MyApp.Article do
translations
|> cast(params, [])
# use 'cast_embed' to handle values for translated fields for each of the
# configured languages with a changeset defined by the 'translations' macro
# configured languages with a changeset defined by the 'translations' macro
# above
|> cast_embed(:es)
|> cast_embed(:fr)
Expand Down
85 changes: 30 additions & 55 deletions lib/cldr_trans.ex
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
defmodule Cldr.Trans do
@moduledoc """
Manage translations embedded into structs.
Manage translations embedded into translation structs.
Although it can be used with any struct **`Trans` shines when paired with an `Ecto.Schema`**. It
Although it can be used with any struct **`Cldr.Trans` shines when paired with an `Ecto.Schema`**. It
allows you to keep the translations into a field of the schema and avoids requiring extra tables
for translation storage and complex _joins_ when retrieving translations from the database.
`Trans` is split into two main components:
`Cldr.Trans` is split into two main components:
* `Trans.Translator` - provides easy access to struct translations.
* `Trans.QueryBuilder` - provides helpers for querying translations using `Ecto.Query`
* `Cldr.Trans.Translator` - provides easy access to struct translations.
* `Cldr.Trans.QueryBuilder` - provides helpers for querying translations using `Ecto.Query`
(requires `Ecto.SQL`).
When used, `Trans` accepts the following options:
When used, `Cldr.Trans` accepts the following options:
* `:translates` (required) - list of the fields that will be translated.
* `:container` (optional) - name of the field that contains the embedded translations.
Defaults to`:translations`.
* `:default_locale` (optional) - declares the locale of the base untranslated column.
Defaults to the `default_locale` configured for the Cldr backend.
## Structured translations
### Structured translations
Structured translations are the preferred and recommended way of using `Trans`. To use structured
translations **you must define the translations as embedded schemas**:
translations, use the `translations/1` macro when defining a schema. The `translations/1` macro
will define an embedded schema with the name being the parameter passed to the `translations/1`
macro. Within the `:translations` field that is generated, an `embeds_one/2` call is added for
locale configured in the `MyApp.Cldr` backend module. Here's an example schema configuraiton:
defmodule MyApp.Article do
use Ecto.Schema
use MyApp.Cldr.Trans, translates: [:title, :body]
schema "articles" do
field :title, :string
field :body, :string
translations :translations
end
end
When expanded by the Elxir compiler, the example above will look like the following code
(assuming the `MyApp.Cldr` is configured with three locales: `:en`, `:es` and `:fr`). It is
provided here only as an example to show what the `translations/1` macro compiles to.
defmodule MyApp.Article do
use Ecto.Schema
Expand All @@ -50,57 +66,16 @@ defmodule Cldr.Trans do
end
end
Although they required more code than free-form translations, **structured translations provide
some nice benefits** that make them the preferred way of using `Trans`:
* High flexibility when making validations and transformation using the embedded schema's own
changeset.
* Easy to integrate with HTML forms leveraging the capabilities of [Phoenix.HTML.Form.inputs_for/2](https://hexdocs.pm/phoenix_html/Phoenix.HTML.Form.html#module-nested-inputs)
* Easy navegability using the dot notation.
## Free-form translations
Free-form translations were the main way of using `Trans` until the 2.3.0 version. They are still
supported for compatibility with older versions but not recommended for new projects.
To use free-form translations you must define the translations as a map:
defmodule MyApp.Article do
use Ecto.Schema
use MyApp.Cldr.Trans, translates: [:title, :body]
schema "articles" do
field :title, :string
field :body, :string
field :translations, :map
end
end
Although they require less code, **free-form translations provide much less guarantees**:
* There is no way to tell what content and which form will be stored in the translations field.
* Hard to integrate with HTML forms since the Phoenix helpers are not available.
* Difficult navigation requiring the braces notation from the `Access` protocol.
## The translation container
As we have seen in the previous examples, `Trans` automatically stores and looks for translations
in a field called `:translations`. This is known as the **translations container.**
In certain cases you may want to use a different field for storing the translations, this can
be specified when using `Trans` in your module.
# Use the field `:locales` as translation container instead of the default `:translations`
use Trans, translates: [...], container: :locales
## Reflection
### Reflection
Any module that uses `Trans` will have an autogenerated `__trans__` function that can be used for
runtime introspection of the translation metadata.
* `__trans__(:fields)` - Returns the list of translatable fields.
* `__trans__(:container)` - Returns the name of the translation container.
* `__trans__(:default_locale)` - Returns the name of default locale.
* `__trans__(:default_locale)` - Returns the name of default locale. The locale of the fields
in the main schema are considered to be in the language of the default locale.
"""

alias Cldr.Locale
Expand Down
Loading

0 comments on commit ac0355f

Please sign in to comment.