3
votes

I have the following schema in my Phoenix App:

defmodule TattooBackend.Accounts.Account do
  @moduledoc """
  Contains schema and changesets for Account resource.
  """

  use Ecto.Schema

  import Ecto.Changeset

  alias TattooBackend.Accounts.Account

  schema "accounts" do
    field :email, :string
    field :crypted_password, :string
    field :password, :string, virtual: true
    timestamps()
  end

  def changeset(%Account{} = user, attributes \\ %{}) do
    user
    |> cast(attributes, [:email, :password])
    |> validate_required([:email, :password])
    |> validate_length(:password, min: 6)
    |> validate_format(:email, ~r/([\w-\.]+)@((?:[\w]+\.)+)([a-zA-Z]{2,4})/)
    |> unique_constraint(:email, name: "accounts_lower_email_index")
    |> put_password_hash
  end

  defp put_password_hash(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
        put_change(changeset, :crypted_password, Comeonin.Bcrypt.hashpwsalt(pass))
      _ ->
        changeset
    end
  end
end

And I wrote simple test:

defmodule TattooBackend.Accounts.AccountTest do
  use TattooBackend.DataCase, async: true

  alias TattooBackend.Repo
  alias TattooBackend.Accounts.Account

  describe "with invalid attributes" do
    setup do
      changeset = Account.changeset(%Account{})

      {:ok, %{changeset: changeset}}
    end

    test "changeset is inalid", %{changeset: changeset} do
      refute changeset.valid?
    end

    test "changeset have errors", %{changeset: changeset} do
      assert changeset.errors == [
        email:      {"can't be blank", [validation: :required]},
        password:   {"can't be blank", [validation: :required]},
      ]
    end
  end
end

Now my question is how can I translate these Ecto validation messages?

1
There is a message option for pretty much all validation functions which allow you to provide the error's text. Or I am missing something?NoDisplayName
But basic phoenix app contains errors.pot and errors.po files so it should work without passing any custom messages.Mateusz Urbański
Why do you expect Ecto to be aware of how Phoenix handles error messages?Aleksei Matiushkin
I expect that Ecto should have mechanism to translate its error messages to multiple languages. Such a trivial thing but still hard to do it in Elixir...Mateusz Urbański
I would call Ecto.Changeset.traverse_errors and translate error messages to the language of your choice. This topic might also be helpful.Aleksei Matiushkin

1 Answers

3
votes

You can enumerate through your errors and use Gettext to translate them. E.g.

def translate_error(%{errors: errors}=_changeset) do
    Enum.map(errors, fn {field, error} ->
      Atom.to_string(field) <> " " <> translate_error(error)
    end)
end
def translate_error({msg, opts}) do
    case opts[:count] do
      nil -> Gettext.dgettext(TattooBackendWeb.Gettext, "errors", msg, opts)
      count -> Gettext.dngettext(TattooBackendWeb.Gettext, "errors", msg, msg, count, opts)
    end
end

This should print something like:

["email has already been taken", "password can't be blank"]

Hope that helps!