Initial commit
This commit is contained in:
66
lib/microsoft_translator.ex
Executable file
66
lib/microsoft_translator.ex
Executable file
@@ -0,0 +1,66 @@
|
||||
defmodule MicrosoftTranslator do
|
||||
@moduledoc """
|
||||
Basic functions for requests to Yandex Translate API on Yandex Cloud
|
||||
"""
|
||||
|
||||
alias MicrosoftTranslator.Client
|
||||
|
||||
@doc """
|
||||
Retrieves the list of supported languages.
|
||||
|
||||
Return a map with language code (use it for translations) and native language name
|
||||
|
||||
```elixir
|
||||
%{
|
||||
languages: [
|
||||
%{code: "af", "name":"Afrikaans","nativeName":"Afrikaans"},
|
||||
%{code: "ar", "name":"Arabic","nativeName":"العربية"},
|
||||
%{code: "bg", "name":"Bulgarian","nativeName":"Български"},
|
||||
%{code: "bn", "name":"Bangla","nativeName":"বাংলা"},
|
||||
%{code: "bs", "name":"Bosnian","nativeName":"bosanski (latinica)"}
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
"""
|
||||
def languages(), do: Client.call(:languages, %{})
|
||||
|
||||
@doc """
|
||||
Detect the language of the text
|
||||
|
||||
Get text as a string param and return a map with language code
|
||||
|
||||
```elixir
|
||||
MicrosoftTranslator.detect("Криївка")
|
||||
# Response
|
||||
%{languageCode: "uk"}
|
||||
```
|
||||
|
||||
Or get a map with :text and :languageCodeHints (for specify the most likely languages).
|
||||
> In some languages, one and the same word has the same spelling. For example, the English word “hand” is also written as “hand” in German, Swedish, and Dutch. If the text you transmit contains words like this, Translate may detect the source language incorrectly.
|
||||
|
||||
To avoid mistakes, you can use the languageCodeHints field to specify which languages should be given priority when determining the language of the text
|
||||
|
||||
```elixir
|
||||
MicrosoftTranslator.detect(%{text: "Капелюх"})
|
||||
# Response
|
||||
%{languageCode: "uk"}
|
||||
```
|
||||
"""
|
||||
def detect(params) when is_map(params), do: Client.call(:detect, params)
|
||||
def detect(text) when is_binary(text), do: Client.call(:detect, %{text: text})
|
||||
|
||||
def translate(params) when is_map(params), do: Client.call(:translate, params)
|
||||
|
||||
def translate(text, to) when is_binary(text) and is_binary(to),
|
||||
do: Client.call(:translate, %{text: text, to: to})
|
||||
|
||||
def translate(text, to, from)
|
||||
when is_binary(text) and is_binary(to) and is_binary(from),
|
||||
do:
|
||||
Client.call(:translate, %{
|
||||
text: text,
|
||||
to: to,
|
||||
from: from
|
||||
})
|
||||
end
|
||||
12
lib/microsoft_translator/auth/api_key.ex
Normal file
12
lib/microsoft_translator/auth/api_key.ex
Normal file
@@ -0,0 +1,12 @@
|
||||
defmodule MicrosoftTranslator.Auth.ApiKey do
|
||||
def get_auth_headers(%{api_key: api_key, region: region}) do
|
||||
[
|
||||
{"Ocp-Apim-Subscription-Key", api_key},
|
||||
{"Ocp-Apim-Subscription-Region", region}
|
||||
]
|
||||
end
|
||||
|
||||
def get_auth_headers(%{api_key: api_key}) do
|
||||
[{"Ocp-Apim-Subscription-Key", api_key}]
|
||||
end
|
||||
end
|
||||
10
lib/microsoft_translator/auth/auth.ex
Normal file
10
lib/microsoft_translator/auth/auth.ex
Normal file
@@ -0,0 +1,10 @@
|
||||
defmodule MicrosoftTranslator.Auth do
|
||||
alias MicrosoftTranslator.Auth.{ApiKey}
|
||||
|
||||
defp get_config(), do: Application.get_all_env(:microsoft_translator) |> Map.new()
|
||||
|
||||
def authorization_headers(config \\ get_config())
|
||||
|
||||
def authorization_headers(%{api_key: _} = params),
|
||||
do: ApiKey.get_auth_headers(params)
|
||||
end
|
||||
150
lib/microsoft_translator/client.ex
Normal file
150
lib/microsoft_translator/client.ex
Normal file
@@ -0,0 +1,150 @@
|
||||
defmodule MicrosoftTranslator.Client do
|
||||
@empty_state %{data: [], done: false}
|
||||
@base_host "api.cognitive.microsofttranslator.com"
|
||||
@base_path "/"
|
||||
@api_methods %{
|
||||
languages: %{
|
||||
method: "GET",
|
||||
path: "languages"
|
||||
},
|
||||
detect: %{
|
||||
method: "POST",
|
||||
path: "detect"
|
||||
},
|
||||
translate: %{
|
||||
method: "POST",
|
||||
path: "translate"
|
||||
}
|
||||
}
|
||||
@api_default_params %{
|
||||
"api-version" => "3.0"
|
||||
}
|
||||
@availaible_api_methods Map.keys(@api_methods)
|
||||
@default_headers [{"Content-Type", "application/json"}]
|
||||
|
||||
alias MicrosoftTranslator.Auth
|
||||
|
||||
def call(api_method \\ :languages, args \\ %{})
|
||||
when api_method in @availaible_api_methods and is_map(args) do
|
||||
body = generate_body(api_method, args)
|
||||
headers = generate_headers()
|
||||
params = generate_params(api_method, args)
|
||||
|
||||
api_method
|
||||
|> fetch(headers, body, params)
|
||||
|> parse(api_method)
|
||||
end
|
||||
|
||||
defp generate_headers(headers \\ [])
|
||||
|
||||
defp generate_headers(header) when is_tuple(header),
|
||||
do: generate_headers([header])
|
||||
|
||||
defp generate_headers(headers) when is_list(headers),
|
||||
do: @default_headers ++ Auth.authorization_headers() ++ headers
|
||||
|
||||
defp generate_params(:translate, params) do
|
||||
params
|
||||
|> Map.take([:to, :from])
|
||||
|> params_to_string()
|
||||
end
|
||||
|
||||
defp generate_params(api_method, _params) when api_method in [:detect, :languages],
|
||||
do: params_to_string(%{})
|
||||
|
||||
defp params_to_string(params_map) do
|
||||
params_map
|
||||
|> Map.merge(@api_default_params)
|
||||
|> Enum.reduce("", fn {key, value}, acc -> acc <> "&" <> "#{key}" <> "=" <> "#{value}" end)
|
||||
|> String.replace_leading("&", "?")
|
||||
end
|
||||
|
||||
defp generate_body(method, params), do: transform_body(method, params) |> Jason.encode!()
|
||||
|
||||
defp transform_body(:languages, _), do: %{}
|
||||
|
||||
defp transform_body(method, params) when method in [:detect, :translate],
|
||||
do: [%{text: Map.fetch!(params, :text)}]
|
||||
|
||||
defp transform_body(_, params), do: params
|
||||
|
||||
defp parse({:ok, %{data: body}}, api_method) do
|
||||
Jason.decode!(body, keys: :atoms)
|
||||
|> transform_response(api_method)
|
||||
end
|
||||
|
||||
defp transform_response(response, :detect) do
|
||||
result =
|
||||
response
|
||||
|> List.first()
|
||||
|> Map.fetch(:language)
|
||||
|
||||
case result do
|
||||
{:ok, language} -> %{languageCode: language}
|
||||
end
|
||||
end
|
||||
|
||||
defp transform_response(response, :translate) do
|
||||
result =
|
||||
response
|
||||
|> List.first()
|
||||
|> Map.take([:translations])
|
||||
end
|
||||
|
||||
defp transform_response(response, :languages) do
|
||||
%{dictionary: dictionary} = response
|
||||
|
||||
dictionary
|
||||
|> Enum.map(fn {key, %{name: name, nativeName: native, dir: dir}} ->
|
||||
%{code: "#{key}", name: name, nativeName: native, dir: dir}
|
||||
end)
|
||||
end
|
||||
|
||||
def fetch(api_method, headers, body, params) do
|
||||
method = @api_methods[api_method].method
|
||||
path = @base_path <> @api_methods[api_method].path <> params
|
||||
|
||||
with {:ok, conn} <- Mint.HTTP.connect(:https, @base_host, 443),
|
||||
{:ok, conn, _ref} <- Mint.HTTP.request(conn, method, path, headers, body) do
|
||||
handle_response(conn, @empty_state)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_response(conn, state) do
|
||||
receive do
|
||||
message ->
|
||||
case Mint.HTTP.stream(conn, message) do
|
||||
{:ok, conn, responses} ->
|
||||
case Enum.reduce(responses, state, &handle_res/2) do
|
||||
# Loop ends here
|
||||
%{done: true} = state -> {:ok, state}
|
||||
%{done: false} = state -> handle_response(conn, state)
|
||||
end
|
||||
|
||||
{:error, _, reason, _} ->
|
||||
{:error, reason}
|
||||
|
||||
:unknown ->
|
||||
exit({:unexpected, message})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_res({:status, _, status}, state),
|
||||
do: Map.put(state, :status, status)
|
||||
|
||||
defp handle_res({:headers, _, headers}, state),
|
||||
do: Map.put(state, :headers, headers)
|
||||
|
||||
defp handle_res({:data, _, data}, state),
|
||||
do: Map.update!(state, :data, fn acc -> [data | acc] end)
|
||||
|
||||
defp handle_res({:done, _}, state) do
|
||||
Map.update!(state, :data, fn acc ->
|
||||
acc
|
||||
|> Enum.reverse()
|
||||
|> Enum.join("")
|
||||
end)
|
||||
|> Map.put(:done, true)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user