Packages
corex
0.1.0-rc.0
0.1.2
0.1.1
0.1.0
0.1.0-rc.1
0.1.0-rc.0
0.1.0-beta.5
0.1.0-beta.4
0.1.0-beta.3
0.1.0-beta.2
0.1.0-beta.1
0.1.0-alpha.33
0.1.0-alpha.32
0.1.0-alpha.31
0.1.0-alpha.30
0.1.0-alpha.29
0.1.0-alpha.28
0.1.0-alpha.27
0.1.0-alpha.26
0.1.0-alpha.25
0.1.0-alpha.24
0.1.0-alpha.23
0.1.0-alpha.22
0.1.0-alpha.21
0.1.0-alpha.20
0.1.0-alpha.19
0.1.0-alpha.18
0.1.0-alpha.17
0.1.0-alpha.16
0.1.0-alpha.15
0.1.0-alpha.14
0.1.0-alpha.13
0.1.0-alpha.12
0.1.0-alpha.11
0.1.0-alpha.10
0.1.0-alpha.9
0.1.0-alpha.8
0.1.0-alpha.7
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
Accessible and unstyled UI components library written in Elixir and TypeScript that integrates Zag.js state machines into the Phoenix Framework.
Current section
Files
Jump to
Current section
Files
lib/corex/translation.ex
defmodule Corex.Translation do
@moduledoc false
defmacro __using__(opts) do
caller = __CALLER__.module
fields = Keyword.fetch!(opts, :fields)
map_merge? = Keyword.get(opts, :map_merge, false)
camel_keys = Keyword.get(opts, :camel_keys, [])
field_atoms = Keyword.keys(fields)
default_map_ast =
{:%, [],
[caller, {:%{}, [], Enum.map(fields, fn {field, text} -> {field, gettext_ast(text)} end)}]}
merge_map_ast =
{:%, [],
[
caller,
{:%{}, [],
Enum.map(field_atoms, fn field ->
{field,
quote do
Corex.Translation.take(partial.unquote(field), default.unquote(field))
end}
end)}
]}
type_ast =
{:%, [],
[
caller,
{:%{}, [], Enum.map(field_atoms, fn field -> {field, quote(do: String.t())} end)}
]}
resolve_map =
if map_merge? do
quote do
def resolve(map) when is_map(map) do
sm = Map.new(map, fn {k, v} -> {to_string(k), v} end)
partial =
struct!(
unquote(caller),
Enum.map(unquote(field_atoms), fn field ->
{field, Map.get(sm, Atom.to_string(field))}
end)
)
merge(partial, default())
end
end
else
[]
end
camel_ast =
if camel_keys == [] do
[]
else
entries =
Enum.map(camel_keys, fn {field, camel_key} ->
{camel_key, quote(do: t.unquote(field))}
end)
[
quote do
def to_camel_map(%unquote(caller){} = t) do
%{unquote_splicing(entries)}
end
end
]
end
quote do
defstruct unquote(Enum.map(field_atoms, &{&1, nil}))
@type t :: unquote(type_ast)
def resolve(nil), do: default()
def resolve(%unquote(caller){} = partial), do: merge(partial, default())
unquote(resolve_map)
defp default do
unquote(default_map_ast)
end
defp merge(%unquote(caller){} = partial, %unquote(caller){} = default) do
unquote(merge_map_ast)
end
unquote(camel_ast)
end
end
defp gettext_ast({:gettext, _, [text]}), do: gettext_ast(text)
defp gettext_ast(text) when is_binary(text) do
case Regex.scan(~r/%\{([a-zA-Z0-9_]+)\}/, text) do
[] ->
quote(do: Corex.Gettext.gettext(unquote(text)))
matches ->
opts =
matches
|> Enum.map_join(", ", fn [_, name] -> "#{name}: \"%{#{name}}\"" end)
Code.string_to_quoted!(
"Corex.Gettext.gettext(#{inspect(text)}, #{opts})",
existing_atoms: :all
)
end
end
def take(nil, default), do: default
def take("", default), do: default
def take(value, _default), do: value
@doc "Merges optional legacy attribute overrides into a resolved translation struct."
def resolve_with_overrides(module, partial, overrides \\ %{})
def resolve_with_overrides(module, partial, overrides) when is_map(overrides) do
partial
|> module.resolve()
|> apply_overrides(overrides)
end
defp apply_overrides(struct, overrides) do
Enum.reduce(overrides, struct, &apply_override/2)
end
defp apply_override({_key, value}, acc) when value in [nil, ""], do: acc
defp apply_override({key, value}, acc) do
field = override_field_key(key)
case Map.fetch(acc, field) do
{:ok, _} -> %{acc | field => value}
:error -> acc
end
end
defp override_field_key(key) when is_atom(key), do: key
defp override_field_key(key) when is_binary(key) do
String.to_existing_atom(key)
end
end