commit: 28f8bb00d8d6770732a7985aa03d326e601d3694
parent f0eb8e0b0c637104acaf95d8387a0d8f807e964f
Author: marcin mikołajczak <git@mkljczk.pl>
Date: Tue, 8 Nov 2022 23:09:42 +0100
Add supported languages list to /api/v2/instance
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
Diffstat:
9 files changed, 156 insertions(+), 25 deletions(-)
diff --git a/lib/pleroma/language/translation.ex b/lib/pleroma/language/translation.ex
@@ -6,9 +6,9 @@ defmodule Pleroma.Language.Translation do
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
def configured? do
- service = get_service()
+ provider = get_provider()
- !!service and service.configured?
+ !!provider and provider.configured?
end
def translate(text, source_language, target_language) do
@@ -16,13 +16,13 @@ defmodule Pleroma.Language.Translation do
case @cachex.get(:translations_cache, cache_key) do
{:ok, nil} ->
- service = get_service()
+ provider = get_provider()
result =
- if !service or !service.configured? do
+ if !configured?() do
{:error, :not_found}
else
- service.translate(text, source_language, target_language)
+ provider.translate(text, source_language, target_language)
end
store_result(result, cache_key)
@@ -37,7 +37,33 @@ defmodule Pleroma.Language.Translation do
end
end
- defp get_service, do: Pleroma.Config.get([__MODULE__, :provider])
+ def supported_languages(type) when type in [:source, :target] do
+ provider = get_provider()
+
+ cache_key = "#{type}_languages/#{provider.name()}"
+
+ case @cachex.get(:translations_cache, cache_key) do
+ {:ok, nil} ->
+ result =
+ if !configured?() do
+ {:error, :not_found}
+ else
+ provider.supported_languages(type)
+ end
+
+ store_result(result, cache_key)
+
+ result
+
+ {:ok, result} ->
+ {:ok, result}
+
+ {:error, error} ->
+ {:error, error}
+ end
+ end
+
+ defp get_provider, do: Pleroma.Config.get([__MODULE__, :provider])
defp get_cache_key(text, source_language, target_language) do
"#{source_language}/#{target_language}/#{content_hash(text)}"
diff --git a/lib/pleroma/language/translation/deepl.ex b/lib/pleroma/language/translation/deepl.ex
@@ -9,14 +9,17 @@ defmodule Pleroma.Language.Translation.Deepl do
@behaviour Provider
+ @name "DeepL"
+
@impl Provider
- def configured? do
- is_atom(get_base_url()) and not_empty_string(get_api_key())
- end
+ def configured?, do: not_empty_string(base_url()) and not_empty_string(api_key())
@impl Provider
def translate(content, source_language, target_language) do
- endpoint = get_base_url()
+ endpoint =
+ base_url()
+ |> URI.merge("/v2/translate")
+ |> URI.to_string()
case Pleroma.HTTP.post(
endpoint <>
@@ -30,7 +33,7 @@ defmodule Pleroma.Language.Translation.Deepl do
"",
[
{"Content-Type", "application/x-www-form-urlencoded"},
- {"Authorization", "DeepL-Auth-Key #{get_api_key()}"}
+ {"Authorization", "DeepL-Auth-Key #{api_key()}"}
]
) do
{:ok, %{status: 429}} ->
@@ -50,7 +53,7 @@ defmodule Pleroma.Language.Translation.Deepl do
%{
content: content,
detected_source_language: detected_source_language,
- provider: "DeepL"
+ provider: @name
}}
_ ->
@@ -58,11 +61,41 @@ defmodule Pleroma.Language.Translation.Deepl do
end
end
- defp get_base_url do
+ @impl Provider
+ def supported_languages(type) when type in [:source, :target] do
+ endpoint =
+ base_url()
+ |> URI.merge("/v2/languages")
+ |> URI.to_string()
+
+ case Pleroma.HTTP.post(
+ endpoint <> "?" <> URI.encode_query(%{type: type}),
+ "",
+ [
+ {"Content-Type", "application/x-www-form-urlencoded"},
+ {"Authorization", "DeepL-Auth-Key #{api_key()}"}
+ ]
+ ) do
+ {:ok, %{status: 200} = res} ->
+ languages =
+ Jason.decode!(res.body)
+ |> Enum.map(fn %{"language" => language} -> language |> String.downcase() end)
+
+ {:ok, languages}
+
+ _ ->
+ {:error, :internal_server_error}
+ end
+ end
+
+ @impl Provider
+ def name, do: @name
+
+ defp base_url do
Pleroma.Config.get([__MODULE__, :base_url])
end
- defp get_api_key do
+ defp api_key do
Pleroma.Config.get([__MODULE__, :api_key])
end
end
diff --git a/lib/pleroma/language/translation/libretranslate.ex b/lib/pleroma/language/translation/libretranslate.ex
@@ -9,21 +9,21 @@ defmodule Pleroma.Language.Translation.Libretranslate do
@behaviour Provider
+ @name "LibreTranslate"
+
@impl Provider
- def configured?, do: not_empty_string(get_base_url())
+ def configured?, do: not_empty_string(base_url()) and not_empty_string(api_key())
@impl Provider
def translate(content, source_language, target_language) do
- endpoint = endpoint_url()
-
case Pleroma.HTTP.post(
- endpoint,
+ base_url() <> "/translate",
Jason.encode!(%{
q: content,
source: source_language |> String.upcase(),
target: target_language,
format: "html",
- api_key: get_api_key()
+ api_key: api_key()
}),
[
{"Content-Type", "application/json"}
@@ -52,15 +52,29 @@ defmodule Pleroma.Language.Translation.Libretranslate do
end
end
- defp endpoint_url do
- get_base_url() <> "/translate"
+ @impl Provider
+ def supported_languages(_) do
+ case Pleroma.HTTP.get(base_url() <> "/languages") do
+ {:ok, %{status: 200} = res} ->
+ languages =
+ Jason.decode!(res.body)
+ |> Enum.map(fn %{"code" => code} -> code end)
+
+ {:ok, languages}
+
+ _ ->
+ {:error, :internal_server_error}
+ end
end
- defp get_base_url do
+ @impl Provider
+ def name, do: @name
+
+ defp base_url do
Pleroma.Config.get([__MODULE__, :base_url])
end
- defp get_api_key do
+ defp api_key do
Pleroma.Config.get([__MODULE__, :api_key], "")
end
end
diff --git a/lib/pleroma/language/translation/provider.ex b/lib/pleroma/language/translation/provider.ex
@@ -17,4 +17,9 @@ defmodule Pleroma.Language.Translation.Provider do
provider: String.t()
}}
| {:error, atom()}
+
+ @callback supported_languages(type :: :string | :target) ::
+ {:ok, [String.t()]} | {:error, atom()}
+
+ @callback name() :: String.t()
end
diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex
@@ -206,10 +206,32 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
vapid: %{
public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
},
- translation: %{enabled: Pleroma.Language.Translation.configured?()}
+ translation: translation_config()
})
end
+ defp translation_config do
+ enabled = Pleroma.Language.Translation.configured?()
+
+ source_languages =
+ case Pleroma.Language.Translation.supported_languages(:source) do
+ {:ok, languages} -> languages
+ _ -> nil
+ end
+
+ target_languages =
+ case Pleroma.Language.Translation.supported_languages(:target) do
+ {:ok, languages} -> languages
+ _ -> nil
+ end
+
+ %{
+ enabled: enabled,
+ source_languages: source_languages,
+ target_languages: target_languages
+ }
+ end
+
defp pleroma_configuration(instance) do
%{
metadata: %{
diff --git a/test/fixtures/tesla_mock/deepl-languages-list.json b/test/fixtures/tesla_mock/deepl-languages-list.json
@@ -0,0 +1 @@
+[{"language":"BG","name":"Bulgarian","supports_formality":false},{"language":"CS","name":"Czech","supports_formality":false},{"language":"DA","name":"Danish","supports_formality":false},{"language":"DE","name":"German","supports_formality":true},{"language":"EL","name":"Greek","supports_formality":false},{"language":"EN-GB","name":"English (British)","supports_formality":false},{"language":"EN-US","name":"English (American)","supports_formality":false},{"language":"ES","name":"Spanish","supports_formality":true},{"language":"ET","name":"Estonian","supports_formality":false},{"language":"FI","name":"Finnish","supports_formality":false},{"language":"FR","name":"French","supports_formality":true},{"language":"HU","name":"Hungarian","supports_formality":false},{"language":"ID","name":"Indonesian","supports_formality":false},{"language":"IT","name":"Italian","supports_formality":true},{"language":"JA","name":"Japanese","supports_formality":false},{"language":"LT","name":"Lithuanian","supports_formality":false},{"language":"LV","name":"Latvian","supports_formality":false},{"language":"NL","name":"Dutch","supports_formality":true},{"language":"PL","name":"Polish","supports_formality":true},{"language":"PT-BR","name":"Portuguese (Brazilian)","supports_formality":true},{"language":"PT-PT","name":"Portuguese (European)","supports_formality":true},{"language":"RO","name":"Romanian","supports_formality":false},{"language":"RU","name":"Russian","supports_formality":true},{"language":"SK","name":"Slovak","supports_formality":false},{"language":"SL","name":"Slovenian","supports_formality":false},{"language":"SV","name":"Swedish","supports_formality":false},{"language":"TR","name":"Turkish","supports_formality":false},{"language":"UK","name":"Ukrainian","supports_formality":false},{"language":"ZH","name":"Chinese (simplified)","supports_formality":false}]
+\ No newline at end of file
diff --git a/test/pleroma/language/translation/deepl_test.ex b/test/pleroma/language/translation/deepl_test.ex
@@ -24,4 +24,14 @@ defmodule Pleroma.Language.Translation.DeeplTest do
provider: "DeepL"
} = res
end
+
+ test "it returns languages list" do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ clear_config([Pleroma.Language.Translation.Deepl, :base_url], "https://api-free.deepl.com")
+ clear_config([Pleroma.Language.Translation.Deepl, :api_key], "API_KEY")
+
+ assert {:ok, [language | _languages]} = Deepl.supported_languages(:target)
+
+ assert is_binary(language)
+ end
end
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
@@ -1578,6 +1578,15 @@ defmodule HttpRequestMock do
}}
end
+ def post("https://api-free.deepl.com/v2/languages" <> _, _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/deepl-languages-list.json"),
+ headers: [{"content-type", "application/json"}]
+ }}
+ end
+
def post(url, query, body, headers) do
{:error,
"Mock response not implemented for POST #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}
diff --git a/test/support/translation_mock.ex b/test/support/translation_mock.ex
@@ -7,6 +7,8 @@ defmodule TranslationMock do
@behaviour Provider
+ @name "TranslationMock"
+
@impl Provider
def configured?, do: true
@@ -16,7 +18,15 @@ defmodule TranslationMock do
%{
content: content |> String.reverse(),
detected_source_language: source_language,
- provider: "TranslationMock"
+ provider: @name
}}
end
+
+ @impl Provider
+ def supported_languages(_) do
+ ["en", "pl"]
+ end
+
+ @impl Provider
+ def name, do: @name
end