defmodule Glific.Flows.Localization do
@moduledoc """
The Localization object which stores all the localizations for all
languages for a flow
"""
alias __MODULE__
use Ecto.Schema
alias Glific.{
Flows.Action,
Flows.Case,
Flows.Category,
Flows.FlowContext,
Settings
}
@type t() :: %__MODULE__{
localizations: map() | nil
}
embedded_schema do
field :localizations, :map
end
@spec add_text(map(), map()) :: map()
defp add_text(map, values) do
if is_nil(values["text"]) do
map
else
Map.put(map, :text, hd(values["text"]))
end
end
@spec add_attachments(map(), map()) :: map()
defp add_attachments(map, values) do
cond do
is_nil(values["attachments"]) ->
map
values["attachments"] == [] ->
map
not is_list(values["attachments"]) ->
map
is_nil(hd(values["attachments"])) ->
map
true ->
case String.split(hd(values["attachments"]), ":", parts: 2) do
[type, url] -> Map.put(map, :attachments, %{type => url})
_ -> map
end
end
end
@spec add_case_arguments(map(), map()) :: map()
defp add_case_arguments(map, values) do
if values["arguments"] in [[""], nil, []],
do: map,
else: Map.put(map, :arguments, values["arguments"])
end
@spec add_category_name(map(), map()) :: map()
defp add_category_name(map, values) do
if values["name"] in ["", nil, []],
do: map,
else: Map.put(map, :name, values["name"])
end
@spec add_template_variables(map(), map()) :: map()
defp add_template_variables(map, values) do
if values["variables"] in ["", nil, []],
do: map,
else: Map.put(map, :variables, values["variables"])
end
# given a json snippet containing all the translation for a specific language
# store them in a uuid map
@spec process_translation(map()) :: map()
defp process_translation(json) when is_nil(json), do: %{}
defp process_translation(json) do
Enum.reduce(
json,
%{},
fn {uuid, values}, acc ->
# We need to think about a better way to do this checking.
# For now, we are just going to check based on the keys in the translations
map =
%{}
|> add_text(values)
|> add_attachments(values)
|> add_case_arguments(values)
|> add_category_name(values)
|> add_template_variables(values)
if values == %{}, do: acc, else: Map.put(acc, uuid, map)
end
)
end
@doc """
Process a json structure from floweditor to the Glific data types
"""
@spec process(map()) :: Localization.t()
def process(json) when is_nil(json) do
language_map = Settings.locale_id_map()
%Localization{localizations: language_map}
end
def process(json) do
language_map = Settings.locale_id_map()
%Localization{
localizations:
json
|> Enum.reduce(
%{},
fn {language, translations}, acc ->
translated = process_translation(translations)
acc
|> Map.put(language, translated)
|> Map.put(language_map[language], translated)
end
)
}
end
@doc """
Given a language id and an action uuid, return the translation if
one exists, else return the original text
"""
@spec get_translation(FlowContext.t(), Action.t(), atom()) :: String.t() | nil | map()
def get_translation(context, action, type \\ :text) do
language_id = context.contact.language_id
element =
context
|> load_localizations()
|> translated_element(language_id, action.uuid, action)
# in some cases we have a localization field, but either the text or the attachment
# is missing and does not have values, in which case, we switch to using the default
# text or attachment from action
if type == :text,
do: Map.get(element, :text, action.text),
else: Map.get(element, :attachments, action.attachments)
end
@doc """
Given a language id and an case uuid, return the translation if
one exists, else return the original text
"""
@spec get_translated_case_arguments(FlowContext.t(), Case.t()) :: any()
def get_translated_case_arguments(context, flow_case) do
language_id = context.contact.language_id
context
|> load_localizations()
|> translated_element(language_id, flow_case.uuid)
|> Map.get(:arguments, flow_case.arguments)
end
@doc """
Given a language id and an category uuid, return the translation if
one exists, else return the original text
"""
@spec get_translated_category_name(FlowContext.t(), Category.t()) :: String.t() | nil
def get_translated_category_name(context, category) do
language_id = context.contact.language_id
context
|> load_localizations()
|> translated_element(language_id, category.uuid)
|> Map.get(:name, category.name)
end
@doc """
Given a language id and an template uuid, return the variable translation if
one exists, else return the original variable
"""
@spec get_translated_template_vars(
FlowContext.t(),
atom | %{:uuid => binary, :variables => any, optional(any) => any}
) :: list() | nil
def get_translated_template_vars(context, template) do
language_id = context.contact.language_id
context
|> load_localizations()
|> translated_element(language_id, template.uuid)
|> Map.get(:variables, template.variables)
end
@spec load_localizations(FlowContext.t()) :: map()
defp load_localizations(context) do
if Ecto.assoc_loaded?(context.flow) and
context.flow.localization != nil,
do: context.flow.localization.localizations,
else: %{}
end
@spec translated_element(map(), integer(), String.t(), Action.t() | map()) :: Action.t() | map()
defp translated_element(localization, language_id, uuid, default \\ %{}) do
if Map.has_key?(localization, language_id) and
Map.has_key?(Map.get(localization, language_id), uuid) do
Map.get(Map.get(localization, language_id), uuid)
else
default
end
end
end