logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/
commit: ed1cfd6f5eee450277f32019dd290f3f48cf258f
parent 8f9a139ba17fa99ca472d15023485290db0e8faf
Author: nicole mikołajczyk <git@mkljczk.pl>
Date:   Thu,  2 Oct 2025 13:36:30 +0200

Support translateLocally translation provider

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>

Diffstat:

Achangelog.d/translation-provider-translatelocally.add1+
Mconfig/description.exs11++++++++++-
Alib/pleroma/language/translation/translate_locally.ex129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/pleroma/language/translation/translate_locally_test.exs59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 199 insertions(+), 1 deletion(-)

diff --git a/changelog.d/translation-provider-translatelocally.add b/changelog.d/translation-provider-translatelocally.add @@ -0,0 +1 @@ +Support translateLocally translation provider diff --git a/config/description.exs b/config/description.exs @@ -3558,7 +3558,8 @@ config :pleroma, :config_description, [ type: :module, suggestions: [ Pleroma.Language.Translation.Deepl, - Pleroma.Language.Translation.Libretranslate + Pleroma.Language.Translation.Libretranslate, + Pleroma.Language.Translation.TranslateLocally ] }, %{ @@ -3588,6 +3589,14 @@ config :pleroma, :config_description, [ label: "LibreTranslate API Key", type: :string, suggestions: ["YOUR_API_KEY"] + }, + %{ + group: {:subgroup, Pleroma.Language.Translation.TranslateLocally}, + key: :intermediate_language, + label: + "translateLocally intermediate language (used when direct source->target model is not available)", + type: :string, + suggestions: ["en"] } ] } diff --git a/lib/pleroma/language/translation/translate_locally.ex b/lib/pleroma/language/translation/translate_locally.ex @@ -0,0 +1,129 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Language.Translation.TranslateLocally do + alias Pleroma.Language.Translation.Provider + + use Provider + + @behaviour Provider + + @name "translateLocally" + + @impl Provider + def missing_dependencies do + if Pleroma.Utils.command_available?("translateLocally") do + [] + else + ["translateLocally"] + end + end + + @impl Provider + def configured?, do: is_map(models()) + + @impl Provider + def translate(content, source_language, target_language) do + model = + models() + |> Map.get(source_language, %{}) + |> Map.get(target_language) + + models = + if model do + [model] + else + [ + models() + |> Map.get(source_language, %{}) + |> Map.get(intermediary_language()), + models() + |> Map.get(intermediary_language(), %{}) + |> Map.get(target_language) + ] + end + + translated_content = + Enum.reduce(models, content, fn model, content -> + text_path = Path.join(System.tmp_dir!(), "translateLocally-#{Ecto.UUID.generate()}") + + File.write(text_path, content) + + translated_content = + case System.cmd("translateLocally", ["-m", model, "-i", text_path, "--html"]) do + {content, _} -> content + _ -> nil + end + + File.rm(text_path) + + translated_content + end) + + {:ok, + %{ + content: translated_content, + detected_source_language: source_language, + provider: @name + }} + end + + @impl Provider + def supported_languages(:source) do + languages = + languages_matrix() + |> elem(1) + |> Map.keys() + + {:ok, languages} + end + + @impl Provider + def supported_languages(:target) do + languages = + languages_matrix() + |> elem(1) + |> Map.values() + |> List.flatten() + |> Enum.uniq() + + {:ok, languages} + end + + @impl Provider + def languages_matrix do + languages = + models() + |> Map.to_list() + |> Enum.map(fn {key, value} -> {key, Map.keys(value)} end) + |> Enum.into(%{}) + + matrix = + if intermediary_language() do + languages + |> Map.to_list() + |> Enum.map(fn {key, value} -> + with_intermediary = + (((value ++ languages[intermediary_language()]) + |> Enum.uniq()) -- + [key]) + |> Enum.sort() + + {key, with_intermediary} + end) + |> Enum.into(%{}) + else + languages + end + + {:ok, matrix} + end + + @impl Provider + def name, do: @name + + defp models, do: Pleroma.Config.get([__MODULE__, :models]) + + defp intermediary_language, do: Pleroma.Config.get([__MODULE__, :intermediary_language]) +end diff --git a/test/pleroma/language/translation/translate_locally_test.exs b/test/pleroma/language/translation/translate_locally_test.exs @@ -0,0 +1,59 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Language.Translation.TranslateLocallyTest do + use Pleroma.DataCase + + alias Pleroma.Language.Translation.TranslateLocally + + @example_models %{ + "de" => %{ + "en" => "de-en-base" + }, + "en" => %{ + "de" => "en-de-base", + "pl" => "en-pl-tiny" + }, + "cs" => %{ + "en" => "cs-en-base" + }, + "pl" => %{ + "en" => "pl-en-tiny" + } + } + + test "it returns languages list" do + clear_config([Pleroma.Language.Translation.TranslateLocally, :models], @example_models) + + assert {:ok, languages} = TranslateLocally.supported_languages(:source) + assert ["cs", "de", "en", "pl"] = languages |> Enum.sort() + end + + describe "it returns languages matrix" do + test "without intermediary language" do + clear_config([Pleroma.Language.Translation.TranslateLocally, :models], @example_models) + + assert {:ok, + %{ + "cs" => ["en"], + "de" => ["en"], + "en" => ["de", "pl"], + "pl" => ["en"] + }} = TranslateLocally.languages_matrix() + end + + test "with intermediary language" do + clear_config([Pleroma.Language.Translation.TranslateLocally, :models], @example_models) + clear_config([Pleroma.Language.Translation.TranslateLocally, :intermediary_language], "en") + + assert {:ok, + %{ + "cs" => ["de", "en", "pl"], + "de" => ["en", "pl"], + "en" => ["de", "pl"], + "pl" => ["de", "en"] + }} = TranslateLocally.languages_matrix() + end + end +end