defmodule EctoGettext do @moduledoc """ EctoGettext - library for localization Ecto validation errors with using Gettext ### Example: import EctoGettext localize_validations(MyApp.Gettext, errors) """ @doc """ Get a list of errors and return a new list with translated errors """ @spec localize_validations(module, list) :: list def localize_validations(module, errors) do errors |> Enum.map(fn elem -> localize(module, elem) end) end @spec localize(module, tuple) :: {String.t, String.t} defp localize(module, error) do {attr, message} = error {set_attr(module, attr), set_message(module, message)} end @spec numbers() :: list defp numbers, do: ~w(0 1 2 3 4 5 6 7 8 9) @spec set_attr(module, atom) :: String.t defp set_attr(module, attr) do attr = attr |> Atom.to_string |> String.replace("_", " ") |> String.capitalize Gettext.dgettext(module, "attributes", attr) end @spec set_message(module, String.t) :: String.t defp set_message(module, message) do message = String.downcase(message) if parse_number(message) do cond do String.starts_with?(message, "should be") -> Gettext.dgettext(module, "errors", format_message(message, :should), count: parse_number(message)) String.starts_with?(message, "must be") -> Gettext.dgettext(module, "errors", format_message(message, :must), count: parse_number(message)) true -> Gettext.dgettext(module, "errors", message) end else Gettext.dgettext(module, "errors", message) end end @spec basic_formatting(String.t) :: list defp basic_formatting(message) do message |> String.graphemes |> Enum.filter(fn elem -> !Enum.member?(numbers, elem) end) |> Enum.join |> String.split(" ", trim: true) end @spec format_message(String.t, atom) :: String.t defp format_message(message, type) when type == :should do message = basic_formatting(message) message = case Enum.count(message) do 5 -> message |> List.insert_at(4, "%{count}") 3 -> message |> List.insert_at(2, "%{count}") end Enum.join(message, " ") end defp format_message(message, type) when type == :must do message = basic_formatting(message) message |> List.insert_at(Enum.count(message), "%{count}") |> Enum.join(" ") end @spec parse_number(String.t) :: integer | nil defp parse_number(message) do message = message |> String.graphemes |> Enum.filter(fn elem -> Enum.member?(numbers, elem) end) |> Enum.join case message do "" -> nil _ -> String.to_integer(message) end end end