diff --git a/lib/adapter.ex b/lib/adapter.ex
index c8b0823..f7e2884 100644
--- a/lib/adapter.ex
+++ b/lib/adapter.ex
@@ -1,6 +1,15 @@
defmodule Kanta.DeepL.Adapter do
@moduledoc """
DeepL API adapter
+
+ We use XML tag handling to avoid translating the content of curly brackets.
+ These curly brackets are used by gettext for variable substitution.
+ They look like this:
+
+ ```
+ price = Money.new(1000, "EUR")
+ gettext("Price: %{price}", price: price)
+ ```
"""
use Tesla
@@ -13,29 +22,34 @@ defmodule Kanta.DeepL.Adapter do
plug(Tesla.Middleware.JSON)
- def request_translation(source_lang, target_lang, text) do
- post("/v2/translate", %{
- source_lang: source_lang,
- target_lang: target_lang,
- text: [text]
- })
- |> case do
- {:ok, %Tesla.Env{body: %{"translations" => translations}}} -> {:ok, translations}
- {_, %Tesla.Env{body: body, status: status}} -> {:error, status, body}
- error -> {:error, error}
- end
+ def request_translation(source_lang, target_lang, text, context_name) do
+ request_translations(source_lang, target_lang, [text], context_name)
end
- def request_translations(source_lang, target_lang, texts) do
+ def request_translations(source_lang, target_lang, texts, context_name) do
+ texts = Enum.map(texts, &replace_curly_brackets_with_xml_tags/1)
+
post("/v2/translate", %{
source_lang: source_lang,
target_lang: target_lang,
+ context: context_name,
+ tag_handling: "xml",
+ ignore_tags: ["gettext_variable"],
text: texts
})
|> case do
- {:ok, %Tesla.Env{body: %{"translations" => translations}}} -> {:ok, translations}
- {_, %Tesla.Env{body: body, status: status}} -> {:error, status, body}
- error -> {:error, error}
+ {:ok, %Tesla.Env{body: %{"translations" => translations}}} ->
+ translations
+ |> Enum.map(fn translation ->
+ Map.update!(translation, "text", &replace_xml_tags_with_curly_brackets/1)
+ end)
+ |> then(&{:ok, &1})
+
+ {_, %Tesla.Env{body: body, status: status}} ->
+ {:error, status, body}
+
+ error ->
+ {:error, error}
end
end
@@ -54,4 +68,12 @@ defmodule Kanta.DeepL.Adapter do
{_, config} -> Keyword.get(config, :api_key)
end
end
+
+ defp replace_curly_brackets_with_xml_tags(text) do
+ Regex.replace(~r/%{(.*)}/, text, fn _, x -> "#{x}" end)
+ end
+
+ defp replace_xml_tags_with_curly_brackets(text) do
+ Regex.replace(~r/(.*)<\/gettext_variable>/, text, fn _, x -> "%{#{x}}" end)
+ end
end
diff --git a/lib/form_component.ex b/lib/form_component.ex
index 107c60e..aff0fb8 100644
--- a/lib/form_component.ex
+++ b/lib/form_component.ex
@@ -108,11 +108,8 @@ defmodule Kanta.DeepL.Plugin.FormComponent do
%{"translated_text" => translated},
%{assigns: %{message: %Message{message_type: :singular}}} = socket
) do
- locale = socket.assigns.locale
translation = socket.assigns.translation
-
Translations.update_singular_translation(translation, %{"translated_text" => translated})
-
{:noreply, socket}
end
@@ -121,11 +118,8 @@ defmodule Kanta.DeepL.Plugin.FormComponent do
%{"translated_text" => translated},
%{assigns: %{message: %Message{message_type: :plural}}} = socket
) do
- locale = socket.assigns.locale
translation = socket.assigns.translation
-
Translations.update_plural_translation(translation, %{"translated_text" => translated})
-
{:noreply, socket}
end
@@ -140,7 +134,8 @@ defmodule Kanta.DeepL.Plugin.FormComponent do
case Adapter.request_translation(
source_locale,
String.upcase(translate_locale.iso639_code),
- source_text
+ source_text,
+ if(message.context, do: message.context.name)
) do
{:ok, translations} ->
%{"text" => translated_text} = List.first(translations)
diff --git a/lib/translate_all_component.ex b/lib/translate_all_component.ex
index 797e074..030bc57 100644
--- a/lib/translate_all_component.ex
+++ b/lib/translate_all_component.ex
@@ -24,14 +24,14 @@ defmodule Kanta.DeepL.Plugin.TranslateAllComponent do
singular_messages =
Translations.list_singular_translations(
filter: %{locale_id: socket.assigns.locale_id, translated_text: :is_null},
- preloads: [:message],
+ preloads: [message: :context],
skip_pagination: true
)
plural_messages =
Translations.list_plural_translations(
filter: %{locale_id: socket.assigns.locale_id, translated_text: :is_null},
- preloads: [:message],
+ preloads: [message: :context],
skip_pagination: true
)
@@ -50,18 +50,23 @@ defmodule Kanta.DeepL.Plugin.TranslateAllComponent do
{:noreply, socket}
end
- defp translate_all(messages, locale_code, update_fn) do
- messages
- |> Enum.chunk_every(@deepl_limit)
- |> Enum.map(&translate_batch(&1, locale_code, update_fn))
+ defp translate_all(translations, locale_code, update_fn) do
+ translations
+ |> Enum.group_by(&if &1.message.context, do: &1.message.context.name)
+ |> Enum.reduce([], fn {context_name, same_context_translations}, acc ->
+ same_context_translations
+ |> Enum.chunk_every(@deepl_limit)
+ |> Enum.map(&translate_batch(&1, locale_code, update_fn, context_name))
+ |> then(&Kernel.++(acc, &1))
+ end)
end
- defp translate_batch(batch, locale_code, update_fn) do
+ defp translate_batch(batch, locale_code, update_fn, context_name) do
source_lang = nil
target_lang = String.upcase(locale_code)
texts = Enum.map(batch, & &1.message.msgid)
- case Adapter.request_translations(source_lang, target_lang, texts) do
+ case Adapter.request_translations(source_lang, target_lang, texts, context_name) do
{:ok, translations} ->
batch
|> Enum.zip(translations)