mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-04-24 13:57:23 -04:00
Expose translation service availability
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
557a7d736a
commit
90f91168f7
7 changed files with 233 additions and 4 deletions
|
@ -3523,5 +3523,18 @@ config :pleroma, :config_description, [
|
|||
suggestion: [100_000]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: Pleroma.Translation,
|
||||
type: :group,
|
||||
description: "Translation providers",
|
||||
children: [
|
||||
%{
|
||||
key: Pleroma.Translation,
|
||||
type: :service,
|
||||
suggestions: [Pleroma.Translation.DeepL, Pleroma.Translation.LibreTranslate]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
54
lib/pleroma/translation.ex
Normal file
54
lib/pleroma/translation.ex
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Translation do
|
||||
@cache_ttl 86_400_000
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
def configured? do
|
||||
service = get_service()
|
||||
|
||||
!!service and service.configured?
|
||||
end
|
||||
|
||||
def translate(text, source_language, target_language) do
|
||||
cache_key = get_cache_key(text, source_language, target_language)
|
||||
|
||||
case @cachex.get(:translations_cache, cache_key) do
|
||||
{:ok, nil} ->
|
||||
service = get_service()
|
||||
|
||||
result =
|
||||
if !service or !service.configured? do
|
||||
{:error, :not_found}
|
||||
else
|
||||
service.translate(text, source_language, target_language)
|
||||
end
|
||||
|
||||
store_result(result, cache_key)
|
||||
|
||||
result
|
||||
|
||||
{:ok, result} ->
|
||||
{:ok, result}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp get_service, do: Pleroma.Config.get([__MODULE__, :service])
|
||||
|
||||
defp get_cache_key(text, source_language, target_language) do
|
||||
"#{source_language}/#{target_language}/#{content_hash(text)}"
|
||||
end
|
||||
|
||||
defp store_result({:ok, result}, cache_key) do
|
||||
@cachex.put(:translations_cache, cache_key, result, ttl: @cache_ttl)
|
||||
end
|
||||
|
||||
defp store_result(_, _), do: nil
|
||||
|
||||
defp content_hash(text), do: :crypto.hash(:sha256, text) |> Base.encode64()
|
||||
end
|
75
lib/pleroma/translation/deepl.ex
Normal file
75
lib/pleroma/translation/deepl.ex
Normal file
|
@ -0,0 +1,75 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Translation.DeepL do
|
||||
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||
|
||||
alias Pleroma.Translation.Service
|
||||
|
||||
@behaviour Service
|
||||
|
||||
@impl Service
|
||||
def configured? do
|
||||
not_empty_string(get_plan()) and not_empty_string(get_api_key())
|
||||
end
|
||||
|
||||
@impl Service
|
||||
def translate(content, source_language, target_language) do
|
||||
endpoint = endpoint_url()
|
||||
|
||||
case Pleroma.HTTP.post(
|
||||
endpoint <>
|
||||
"?" <>
|
||||
URI.encode_query(%{
|
||||
text: content,
|
||||
source_lang: source_language |> String.upcase(),
|
||||
target_lang: target_language,
|
||||
tag_handling: "html"
|
||||
}),
|
||||
"",
|
||||
[
|
||||
{"Content-Type", "application/x-www-form-urlencoded"},
|
||||
{"Authorization", "DeepL-Auth-Key #{get_api_key()}"}
|
||||
]
|
||||
) do
|
||||
{:ok, %{status: 429}} ->
|
||||
{:error, :too_many_requests}
|
||||
|
||||
{:ok, %{status: 456}} ->
|
||||
{:error, :quota_exceeded}
|
||||
|
||||
{:ok, %{status: 200} = res} ->
|
||||
%{
|
||||
"translations" => [
|
||||
%{"text" => content, "detected_source_language" => detected_source_language}
|
||||
]
|
||||
} = Jason.decode!(res.body)
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
content: content,
|
||||
detected_source_language: detected_source_language,
|
||||
provider: "DeepL"
|
||||
}}
|
||||
|
||||
_ ->
|
||||
{:error, :internal_server_error}
|
||||
end
|
||||
end
|
||||
|
||||
defp endpoint_url do
|
||||
case get_plan() do
|
||||
"free" -> "https://api-free.deepl.com/v2/translate"
|
||||
_ -> "https://api.deepl.com/v2/translate"
|
||||
end
|
||||
end
|
||||
|
||||
defp get_plan do
|
||||
Pleroma.Config.get([__MODULE__, :plan])
|
||||
end
|
||||
|
||||
defp get_api_key do
|
||||
Pleroma.Config.get([__MODULE__, :api_key])
|
||||
end
|
||||
end
|
66
lib/pleroma/translation/libretranslate.ex
Normal file
66
lib/pleroma/translation/libretranslate.ex
Normal file
|
@ -0,0 +1,66 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Translation.LibreTranslate do
|
||||
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||
|
||||
alias Pleroma.Translation.Service
|
||||
|
||||
@behaviour Service
|
||||
|
||||
@impl Service
|
||||
def configured?, do: not_empty_string(get_base_url())
|
||||
|
||||
@impl Service
|
||||
def translate(content, source_language, target_language) do
|
||||
endpoint = endpoint_url()
|
||||
|
||||
case Pleroma.HTTP.post(
|
||||
endpoint,
|
||||
Jason.encode!(%{
|
||||
q: content,
|
||||
source: source_language |> String.upcase(),
|
||||
target: target_language,
|
||||
format: "html",
|
||||
api_key: get_api_key()
|
||||
}),
|
||||
[
|
||||
{"Content-Type", "application/json"}
|
||||
]
|
||||
) do
|
||||
{:ok, %{status: 429}} ->
|
||||
{:error, :too_many_requests}
|
||||
|
||||
{:ok, %{status: 403}} ->
|
||||
{:error, :quota_exceeded}
|
||||
|
||||
{:ok, %{status: 200} = res} ->
|
||||
%{
|
||||
"translatedText" => content
|
||||
} = Jason.decode!(res.body)
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
content: content,
|
||||
detected_source_language: source_language,
|
||||
provider: "LibreTranslate"
|
||||
}}
|
||||
|
||||
_ ->
|
||||
{:error, :internal_server_error}
|
||||
end
|
||||
end
|
||||
|
||||
defp endpoint_url do
|
||||
get_base_url() <> "/translate"
|
||||
end
|
||||
|
||||
defp get_base_url do
|
||||
Pleroma.Config.get([__MODULE__, :base_url])
|
||||
end
|
||||
|
||||
defp get_api_key do
|
||||
Pleroma.Config.get([__MODULE__, :api_key], "")
|
||||
end
|
||||
end
|
20
lib/pleroma/translation/service.ex
Normal file
20
lib/pleroma/translation/service.ex
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Translation.Service do
|
||||
@callback configured?() :: boolean()
|
||||
|
||||
@callback translate(
|
||||
content :: String.t(),
|
||||
source_language :: String.t(),
|
||||
target_language :: String.t()
|
||||
) ::
|
||||
{:ok,
|
||||
%{
|
||||
content: String.t(),
|
||||
detected_source_language: String.t(),
|
||||
provider: String.t()
|
||||
}}
|
||||
| {:error, atom()}
|
||||
end
|
|
@ -433,9 +433,9 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
required: false
|
||||
),
|
||||
responses: %{
|
||||
200 => Operation.response("Translation", "application/json", translation())
|
||||
400 => Operation.response("Error", "application/json", ApiError)
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
200 => Operation.response("Translation", "application/json", translation()),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError),
|
||||
503 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,7 +202,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
},
|
||||
vapid: %{
|
||||
public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||
}
|
||||
},
|
||||
translation: %{enabled: Pleroma.Translation.configured?}
|
||||
})
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue