allow listing languages supported by a given translator
This commit is contained in:
parent
25111bb407
commit
3580b78105
9 changed files with 198 additions and 2 deletions
|
@ -21,6 +21,24 @@ defp tier do
|
|||
Config.get([:deepl, :tier])
|
||||
end
|
||||
|
||||
@impl Pleroma.Akkoma.Translator
|
||||
def languages do
|
||||
with {:ok, %{status: 200} = response} <- do_languages(),
|
||||
{:ok, body} <- Jason.decode(response.body) do
|
||||
resp =
|
||||
Enum.map(body, fn %{"language" => code, "name" => name} -> %{code: code, name: name} end)
|
||||
|
||||
{:ok, resp}
|
||||
else
|
||||
{:ok, %{status: status} = response} ->
|
||||
Logger.warning("DeepL: Request rejected: #{inspect(response)}")
|
||||
{:error, "DeepL request failed (code #{status})"}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Pleroma.Akkoma.Translator
|
||||
def translate(string, to_language) do
|
||||
with {:ok, %{status: 200} = response} <- do_request(api_key(), tier(), string, to_language),
|
||||
|
@ -45,7 +63,8 @@ defp do_request(api_key, tier, string, to_language) do
|
|||
URI.encode_query(
|
||||
%{
|
||||
text: string,
|
||||
target_lang: to_language
|
||||
target_lang: to_language,
|
||||
tag_handling: "html"
|
||||
},
|
||||
:rfc3986
|
||||
),
|
||||
|
@ -55,4 +74,13 @@ defp do_request(api_key, tier, string, to_language) do
|
|||
]
|
||||
)
|
||||
end
|
||||
|
||||
defp do_languages() do
|
||||
HTTP.get(
|
||||
base_url(tier()) <> "languages?type=target",
|
||||
[
|
||||
{"authorization", "DeepL-Auth-Key #{api_key()}"}
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,22 @@ defp url do
|
|||
Config.get([:libre_translate, :url])
|
||||
end
|
||||
|
||||
@impl Pleroma.Akkoma.Translator
|
||||
def languages do
|
||||
with {:ok, %{status: 200} = response} <- do_languages(),
|
||||
{:ok, body} <- Jason.decode(response.body) do
|
||||
resp = Enum.map(body, fn %{"code" => code, "name" => name} -> %{code: code, name: name} end)
|
||||
{:ok, resp}
|
||||
else
|
||||
{:ok, %{status: status} = response} ->
|
||||
Logger.warning("LibreTranslate: Request rejected: #{inspect(response)}")
|
||||
{:error, "LibreTranslate request failed (code #{status})"}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Pleroma.Akkoma.Translator
|
||||
def translate(string, to_language) do
|
||||
with {:ok, %{status: 200} = response} <- do_request(string, to_language),
|
||||
|
@ -48,4 +64,11 @@ defp do_request(string, to_language) do
|
|||
]
|
||||
)
|
||||
end
|
||||
|
||||
defp do_languages() do
|
||||
url = URI.parse(url())
|
||||
url = %{url | path: "/languages"}
|
||||
|
||||
HTTP.get(to_string(url))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
defmodule Pleroma.Akkoma.Translator do
|
||||
@callback translate(String.t(), String.t()) :: {:ok, String.t(), String.t()} | {:error, any()}
|
||||
@callback languages() :: {:ok, [%{name: String.t(), code: String.t()}]} | {:error, any()}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
defmodule Pleroma.Web.AkkomaAPI.TranslationController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{@unauthenticated_access | scopes: ["read:statuses"]}
|
||||
when action in [
|
||||
:languages
|
||||
]
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TranslationOperation
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
@doc "GET /api/v1/akkoma/translation/languages"
|
||||
def languages(conn, _params) do
|
||||
with {:ok, languages} <- get_languages() do
|
||||
conn
|
||||
|> json(languages)
|
||||
else
|
||||
e -> IO.inspect(e)
|
||||
end
|
||||
end
|
||||
|
||||
defp get_languages do
|
||||
module = Pleroma.Config.get([:translator, :module])
|
||||
|
||||
@cachex.fetch!(:translations_cache, "languages:#{module}}", fn _ ->
|
||||
with {:ok, languages} <- module.languages() do
|
||||
{:ok, languages}
|
||||
else
|
||||
{:error, err} -> {:ignore, {:error, err}}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
41
lib/pleroma/web/api_spec/operations/translate_operation.ex
Normal file
41
lib/pleroma/web/api_spec/operations/translate_operation.ex
Normal file
|
@ -0,0 +1,41 @@
|
|||
defmodule Pleroma.Web.ApiSpec.TranslationOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
@spec open_api_operation(atom) :: Operation.t()
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
@spec languages_operation() :: Operation.t()
|
||||
def languages_operation() do
|
||||
%Operation{
|
||||
tags: ["Retrieve status translation"],
|
||||
summary: "Translate status",
|
||||
description: "View the translation of a given status",
|
||||
operationId: "AkkomaAPI.TranslationController.languages",
|
||||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Translation", "application/json", languages_schema())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp languages_schema do
|
||||
%Schema{
|
||||
type: "array",
|
||||
items: %Schema{
|
||||
type: "object",
|
||||
properties: %{
|
||||
code: %Schema{
|
||||
type: "string"
|
||||
},
|
||||
name: %Schema{
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
|
@ -450,7 +450,7 @@ def translate(%{assigns: %{user: user}} = conn, %{id: id, language: language}) d
|
|||
end
|
||||
|
||||
defp fetch_or_translate(status_id, text, language, translation_module) do
|
||||
@cachex.fetch!(:user_cache, "translations:#{status_id}:#{language}", fn _ ->
|
||||
@cachex.fetch!(:translations_cache, "translations:#{status_id}:#{language}", fn _ ->
|
||||
value = translation_module.translate(text, language)
|
||||
|
||||
with {:ok, _, _} <- value do
|
||||
|
|
|
@ -462,6 +462,11 @@ defmodule Pleroma.Web.Router do
|
|||
put("/statuses/:id/emoji_reactions/:emoji", EmojiReactionController, :create)
|
||||
end
|
||||
|
||||
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
|
||||
pipe_through(:authenticated_api)
|
||||
get("/translation/languages", TranslationController, :languages)
|
||||
end
|
||||
|
||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
pipe_through(:authenticated_api)
|
||||
|
||||
|
|
|
@ -8,6 +8,36 @@ defmodule Pleroma.Akkoma.Translators.DeepLTest do
|
|||
clear_config([:deepl, :api_key], "deepl_api_key")
|
||||
end
|
||||
|
||||
test "should list supported languages" do
|
||||
clear_config([:deepl, :tier], :free)
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :get, url: "https://api-free.deepl.com/v2/languages?type=target"} = env ->
|
||||
auth_header = Enum.find(env.headers, fn {k, _v} -> k == "authorization" end)
|
||||
assert {"authorization", "DeepL-Auth-Key deepl_api_key"} = auth_header
|
||||
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
Jason.encode!([
|
||||
%{
|
||||
"language" => "BG",
|
||||
"name" => "Bulgarian",
|
||||
"supports_formality" => false
|
||||
},
|
||||
%{
|
||||
"language" => "CS",
|
||||
"name" => "Czech",
|
||||
"supports_formality" => false
|
||||
}
|
||||
])
|
||||
}
|
||||
end)
|
||||
|
||||
assert {:ok, [%{code: "BG", name: "Bulgarian"}, %{code: "CS", name: "Czech"}]} =
|
||||
DeepL.languages()
|
||||
end
|
||||
|
||||
test "should work with the free tier" do
|
||||
clear_config([:deepl, :tier], :free)
|
||||
|
||||
|
|
|
@ -8,6 +8,31 @@ defmodule Pleroma.Akkoma.Translators.LibreTranslateTest do
|
|||
clear_config([:libre_translate, :url], "http://libre.translate/translate")
|
||||
end
|
||||
|
||||
test "should list supported languages" do
|
||||
clear_config([:deepl, :tier], :free)
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :get, url: "http://libre.translate/languages"} = _ ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
Jason.encode!([
|
||||
%{
|
||||
"code" => "en",
|
||||
"name" => "English"
|
||||
},
|
||||
%{
|
||||
"code" => "ar",
|
||||
"name" => "Arabic"
|
||||
}
|
||||
])
|
||||
}
|
||||
end)
|
||||
|
||||
assert {:ok, [%{code: "en", name: "English"}, %{code: "ar", name: "Arabic"}]} =
|
||||
LibreTranslate.languages()
|
||||
end
|
||||
|
||||
test "should work without an API key" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :post, url: "http://libre.translate/translate"} = env ->
|
||||
|
|
Loading…
Reference in a new issue