defmodule Backpex do
@moduledoc """
Backpex provides an easy way to manage existing resources in your application.
"""
@doc false
defmacro __(msg, live_resource \\ nil) do
{msg, opts} =
case msg do
{msg, opts} -> {msg, opts}
msg -> {msg, Macro.escape(%{})}
end
quote do
use Gettext, backend: Backpex.Gettext
# mark translation for extraction
Gettext.Macros.dgettext_noop("backpex", unquote(msg))
Backpex.translate({unquote(msg), unquote(opts)}, unquote(live_resource))
end
end
@doc """
Translates a text with the configured translator_function. If a live_resource is given, it calls the LiveResource's translate callback.
## Examples
Backpex.translate("Hello")
Backpex.translate({"Hello %{name}", %{name: "World"}})
Backpex.translate("Welcome", MyApp.LiveResource)
"""
def translate(msg, live_resource \\ nil)
def translate(msg, live_resource) when is_binary(msg) do
translate({msg, %{}}, live_resource)
end
def translate({msg, opts}, nil = _live_resource) do
translate_func = translate_func(:general)
translate_func.({msg, opts})
end
def translate({msg, opts}, live_resource) do
live_resource.translate({msg, opts})
end
@doc """
Translates an error text with the configured error_translator_function.
## Examples
Backpex.translate_error("can't be blank")
Backpex.translate_error({"must be greater than %{number}", %{number: 0}})
"""
def translate_error(msg) when is_binary(msg) do
translate_error({msg, %{}})
end
def translate_error({msg, opts}) do
translate_func = translate_func(:error)
translate_func.({msg, opts})
end
defp translate_func(type) when type in [:general, :error] do
key =
case type do
:error -> :error_translator_function
:general -> :translator_function
end
case Application.get_env(:backpex, key) do
{module, function} ->
&apply(module, function, [&1])
nil ->
IO.warn("""
Backpex can't find the #{inspect(key)} configuration in your config.
Hint: you can set up a `translator_function` and `error_translator_function` to route all strings to your application helpers:
config :backpex,
translator_function: {MyAppWeb.CoreComponents, :translate_backpex},
error_translator_function: {MyAppWeb.CoreComponents, :translate_error}
A fallback message translator will be used.
""")
&default_translate/1
end
end
defp default_translate({msg, opts}) do
Enum.reduce(opts, msg, fn {key, value}, acc ->
try do
String.replace(acc, "%{#{key}}", to_string(value))
rescue
e ->
IO.warn(
"""
The fallback message translator of Backpex could not handle replacing the given value.
Given value: #{inspect(value)}
Exception: #{Exception.message(e)}
""",
__STACKTRACE__
)
"invalid value"
end
end)
end
end