Add translation module for Argos Translate
Argos Translate is a Python module for translation and can be used as a command line tool. This is also the engine for LibreTranslate, for which we already have a module. Here we can use the engine irectly from our server without doing requests to a third party or having to install our own LibreTranslate webservice. One thing that's currently still missing from ArgosTranslate is auto-detection of languages.
This commit is contained in:
parent
dcac8adb3d
commit
f027db9654
4 changed files with 160 additions and 0 deletions
|
@ -882,6 +882,11 @@
|
|||
url: "http://127.0.0.1:5000",
|
||||
api_key: nil
|
||||
|
||||
config :pleroma, :argos_translate,
|
||||
command_argos_translate: "argos-translate",
|
||||
command_argospm: "argospm",
|
||||
default_language: "en"
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -1140,3 +1140,9 @@ Translations are available at `/api/v1/statuses/:id/translations/:language`, whe
|
|||
|
||||
- `:url` - URL of LibreTranslate instance
|
||||
- `:api_key` - API key for LibreTranslate
|
||||
|
||||
### `:argos_translate`
|
||||
|
||||
- `:command_argos_translate` - command for `argos-translate`. Can be the command if it's in your PATH, or the full path to the file (default: `argos-translate`).
|
||||
- `:command_argospm` - command for `argospm`. Can be the command if it's in your PATH, or the full path to the file (default: `argospm`).
|
||||
- `:default_language` - When no language is provided to translate from, this language will be used. Must be a two letter langage code from a language you have installed (default: `en`).
|
||||
|
|
83
lib/pleroma/akkoma/translators/argos_translate.ex
Normal file
83
lib/pleroma/akkoma/translators/argos_translate.ex
Normal file
|
@ -0,0 +1,83 @@
|
|||
defmodule Pleroma.Akkoma.Translators.ArgosTranslate do
|
||||
@behaviour Pleroma.Akkoma.Translator
|
||||
|
||||
alias Pleroma.Config
|
||||
|
||||
defp argos_translate do
|
||||
Config.get([:argos_translate, :command_argos_translate])
|
||||
end
|
||||
|
||||
defp argospm do
|
||||
Config.get([:argos_translate, :command_argospm])
|
||||
end
|
||||
|
||||
defp default_language do
|
||||
Config.get([:argos_translate, :default_language])
|
||||
end
|
||||
|
||||
defp safe_languages() do
|
||||
try do
|
||||
System.cmd(argospm(), ["list"], stderr_to_stdout: true, parallelism: true)
|
||||
rescue
|
||||
_ -> {"Command #{argospm()} not found", 1}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Pleroma.Akkoma.Translator
|
||||
def languages do
|
||||
with {response, 0} <- safe_languages() do
|
||||
langs =
|
||||
response
|
||||
|> String.split("\n", trim: true)
|
||||
|> Enum.map(fn
|
||||
"translate-" <> l -> String.split(l, "_")
|
||||
_ -> ""
|
||||
end)
|
||||
|
||||
source_langs =
|
||||
langs
|
||||
|> Enum.map(fn [l, _] -> %{code: l, name: l} end)
|
||||
|> Enum.uniq()
|
||||
|
||||
dest_langs =
|
||||
langs
|
||||
|> Enum.map(fn [_, l] -> %{code: l, name: l} end)
|
||||
|> Enum.uniq()
|
||||
|
||||
{:ok, source_langs, dest_langs}
|
||||
else
|
||||
{response, _} -> {:error, "ArgosTranslate failed to fetch languages (#{response})"}
|
||||
end
|
||||
end
|
||||
|
||||
defp safe_translate(string, from_language, to_language) do
|
||||
try do
|
||||
System.cmd(
|
||||
argos_translate(),
|
||||
["--from-lang", from_language, "--to-lang", to_language, string],
|
||||
stderr_to_stdout: true,
|
||||
parallelism: true
|
||||
)
|
||||
rescue
|
||||
_ -> {"Command #{argos_translate()} not found", 1}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Pleroma.Akkoma.Translator
|
||||
def translate(string, from_language, to_language) do
|
||||
# Akkoma's Pleroma-fe expects us to detect the source language automatically.
|
||||
# Argos-translate doesn't have that option (yet?)
|
||||
# see <https://github.com/argosopentech/argos-translate/issues/9>
|
||||
# For now we choose a default source language from settings.
|
||||
# Afterwards people get the option to overwrite the source language from a dropdown.
|
||||
from_language = from_language || default_language()
|
||||
to_language = to_language || default_language()
|
||||
|
||||
with {translated, 0} <-
|
||||
safe_translate(string, from_language, to_language) do
|
||||
{:ok, from_language, translated}
|
||||
else
|
||||
{response, _} -> {:error, "ArgosTranslate failed to translate (#{response})"}
|
||||
end
|
||||
end
|
||||
end
|
66
test/pleroma/akkoma/translators/argos_translate_test.exs
Normal file
66
test/pleroma/akkoma/translators/argos_translate_test.exs
Normal file
|
@ -0,0 +1,66 @@
|
|||
defmodule Pleroma.Akkoma.Translators.ArgosTranslateTest do
|
||||
alias Pleroma.Akkoma.Translators.ArgosTranslate
|
||||
|
||||
import Mock
|
||||
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
setup do
|
||||
clear_config([:argos_translate, :command_argos_translate], "argos-translate_test")
|
||||
clear_config([:argos_translate, :command_argospm], "argospm_test")
|
||||
end
|
||||
|
||||
test "it lists available languages" do
|
||||
languages =
|
||||
with_mock System, [:passthrough],
|
||||
cmd: fn "argospm_test", ["list"], _ ->
|
||||
{"translate-nl_en\ntranslate-en_nl\ntranslate-ja_en\n", 0}
|
||||
end do
|
||||
ArgosTranslate.languages()
|
||||
end
|
||||
|
||||
assert {:ok, source_langs, dest_langs} = languages
|
||||
|
||||
assert [%{code: "en", name: "en"}, %{code: "ja", name: "ja"}, %{code: "nl", name: "nl"}] =
|
||||
source_langs |> Enum.sort()
|
||||
|
||||
assert [%{code: "en", name: "en"}, %{code: "nl", name: "nl"}] = dest_langs |> Enum.sort()
|
||||
end
|
||||
|
||||
test "it translates from default language when no language is set" do
|
||||
translation_response =
|
||||
with_mock System, [:passthrough],
|
||||
cmd: fn "argos-translate_test", ["--from-lang", "en", "--to-lang", "fr", "blabla"], _ ->
|
||||
{"yadayada", 0}
|
||||
end do
|
||||
ArgosTranslate.translate("blabla", nil, "fr")
|
||||
end
|
||||
|
||||
assert {:ok, "en", "yadayada"} = translation_response
|
||||
end
|
||||
|
||||
test "it translates from the provided language" do
|
||||
translation_response =
|
||||
with_mock System, [:passthrough],
|
||||
cmd: fn "argos-translate_test", ["--from-lang", "nl", "--to-lang", "en", "blabla"], _ ->
|
||||
{"yadayada", 0}
|
||||
end do
|
||||
ArgosTranslate.translate("blabla", "nl", "en")
|
||||
end
|
||||
|
||||
assert {:ok, "nl", "yadayada"} = translation_response
|
||||
end
|
||||
|
||||
test "it returns a proper error when the executable can't be found" do
|
||||
non_existing_command = "sfqsfgqsefd"
|
||||
clear_config([:argos_translate, :command_argos_translate], non_existing_command)
|
||||
clear_config([:argos_translate, :command_argospm], non_existing_command)
|
||||
|
||||
assert nil == System.find_executable(non_existing_command)
|
||||
|
||||
assert {:error, "ArgosTranslate failed to fetch languages" <> _} = ArgosTranslate.languages()
|
||||
|
||||
assert {:error, "ArgosTranslate failed to translate" <> _} =
|
||||
ArgosTranslate.translate("blabla", "nl", "en")
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue