commit f3b057a7be666f9f026581c424511c0199eecd17 Author: Danylo Negriienko Date: Sun Jun 7 17:00:56 2020 +0300 Initial commit diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dab8143 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +localizator-*.tar + +/config/*.secret.exs + +# Ignore vscode config. +/.vscode/ \ No newline at end of file diff --git a/.iex.exs b/.iex.exs new file mode 100644 index 0000000..61bd50e --- /dev/null +++ b/.iex.exs @@ -0,0 +1,6 @@ +alias Localizator.Parser +alias Localizator.Parser.{Base, JSON} +alias Localizator.Translator +alias Localizator.Translator.{Yandex, Direction} +alias Localizator.Translitor +alias Localizator.Translitor.{UK, BE, RU} diff --git a/README.md b/README.md new file mode 100644 index 0000000..c115ed7 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Localizator + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `localizator` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:localizator, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at [https://hexdocs.pm/localizator](https://hexdocs.pm/localizator). + diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..0df6813 --- /dev/null +++ b/config/config.exs @@ -0,0 +1,19 @@ +use Mix.Config + +# Configures Elixir's Logger +config :logger, :console, + format: "$time $metadata[$level] $message\n", + level: :info, + metadata: [:request_id] + +config :localizator, + default_language: "en", + translators: [Localizator.Translator.Yandex], + translitors: [ + Localizator.Translitor.BE, + Localizator.Translitor.RU, + Localizator.Translitor.UK + ], + parsers: [Localizator.Parser.YAML, Localizator.Parser.JSON] + +import_config "#{Mix.env()}.exs" diff --git a/config/dev.exs b/config/dev.exs new file mode 100644 index 0000000..d3a1175 --- /dev/null +++ b/config/dev.exs @@ -0,0 +1,3 @@ +import Config + +import_config "dev.secret.exs" diff --git a/config/prod.exs b/config/prod.exs new file mode 100644 index 0000000..becde76 --- /dev/null +++ b/config/prod.exs @@ -0,0 +1 @@ +import Config diff --git a/config/test.exs b/config/test.exs new file mode 100644 index 0000000..becde76 --- /dev/null +++ b/config/test.exs @@ -0,0 +1 @@ +import Config diff --git a/lib/commons/commons.ex b/lib/commons/commons.ex new file mode 100644 index 0000000..5feed6e --- /dev/null +++ b/lib/commons/commons.ex @@ -0,0 +1,18 @@ +defmodule Localizator.Commons do + def struct_from_map(a_map, as: a_struct) do + # Find the keys within the map + keys = + Map.keys(a_struct) + |> Enum.filter(fn x -> x != :__struct__ end) + + # Process map, checking for both string / atom keys + processed_map = + for key <- keys, into: %{} do + value = Map.get(a_map, key) || Map.get(a_map, to_string(key)) + {key, value} + end + + a_struct = Map.merge(a_struct, processed_map) + a_struct + end +end diff --git a/lib/localizator.ex b/lib/localizator.ex new file mode 100644 index 0000000..1e2b8a6 --- /dev/null +++ b/lib/localizator.ex @@ -0,0 +1,18 @@ +defmodule Localizator do + @moduledoc """ + Documentation for `Localizator`. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Localizator.hello() + :world + + """ + def hello do + :world + end +end diff --git a/lib/parser/base.ex b/lib/parser/base.ex new file mode 100644 index 0000000..ec95503 --- /dev/null +++ b/lib/parser/base.ex @@ -0,0 +1,20 @@ +defmodule Localizator.Parser.Base do + @typedoc """ + Text contents of the file for parse in Map + """ + @type contents :: String.t() + + @typedoc """ + Data in Map format for encoding to text contents of the file + """ + @type data :: map + + @typedoc """ + List of the file extensions for parser + """ + @type extensions :: [String.t()] + + @callback parse(contents) :: {:ok, data} | {:error, any} + @callback generate(data) :: {:ok, contents} | {:error, any} + @callback extensions() :: {:ok, extensions} +end diff --git a/lib/parser/json.ex b/lib/parser/json.ex new file mode 100644 index 0000000..d45ba15 --- /dev/null +++ b/lib/parser/json.ex @@ -0,0 +1,25 @@ +defmodule Localizator.Parser.JSON do + @extensions ["json"] + + @type contents :: String.t() + @type data :: map + @type extensions :: [String.t()] + + @behaviour Localizator.Parser.Base + + @impl true + @spec parse(contents) :: {:ok, data} | {:error, atom | Jason.DecodeError.t()} + def parse(contents) do + Jason.decode(contents) + end + + @impl true + @spec generate(data) :: {:ok, contents} | {:error, any} + def generate(data) do + Jason.encode(data) + end + + @impl true + @spec extensions() :: {:ok, extensions} + def extensions(), do: {:ok, @extensions} +end diff --git a/lib/parser/yaml.ex b/lib/parser/yaml.ex new file mode 100644 index 0000000..8910c4f --- /dev/null +++ b/lib/parser/yaml.ex @@ -0,0 +1,42 @@ +defmodule Localizator.Parser.YAML do + @indent " " + @extensions ["yml", "yaml"] + + @type contents :: String.t() + @type data :: map + @type extensions :: [String.t()] + + @behaviour Localizator.Parser.Base + + @impl true + @spec parse(contents) :: {:ok, data} | {:error, atom | Jason.DecodeError.t()} + def parse(contents) do + YamlElixir.read_from_string(contents) + end + + @impl true + @spec generate(data) :: {:ok, contents} | {:error, any} + def generate(data) do + {:ok, to_yaml(data)} + end + + defp to_yaml(data, indentation \\ "") do + data + |> Map.keys() + |> Enum.map(fn key -> + Map.fetch!(data, key) + |> value_to_yaml(key, indentation) + end) + |> Enum.join("\n") + end + + defp value_to_yaml(value, key, indentation) when is_number(value) or is_bitstring(value), + do: "#{indentation}#{key}: #{value}" + + defp value_to_yaml(value, key, indentation) when is_map(value), + do: "#{indentation}#{key}:\n#{to_yaml(value, "#{indentation}#{@indent}")}" + + @impl true + @spec extensions() :: {:ok, extensions} + def extensions(), do: {:ok, @extensions} +end diff --git a/lib/translator/base.ex b/lib/translator/base.ex new file mode 100644 index 0000000..a6c725a --- /dev/null +++ b/lib/translator/base.ex @@ -0,0 +1,22 @@ +defmodule Localizator.Translator.Base do + @typedoc """ + Plain Text + """ + @type text :: String.t() + + @typedoc """ + Locale + """ + @type locale :: String.t() + @type from :: locale + @type to :: locale + + @typedoc """ + Error Message + """ + @type message :: String.t() + + @callback detect(text) :: {:ok, locale} | {:error, message} + @callback translate(text, to) :: {:ok, text} | {:error, message} + @callback translate(text, to, from) :: {:ok, text} | {:error, message} +end diff --git a/lib/translator/direction/direction.ex b/lib/translator/direction/direction.ex new file mode 100644 index 0000000..efbaccd --- /dev/null +++ b/lib/translator/direction/direction.ex @@ -0,0 +1,31 @@ +defmodule Localizator.Translator.Direction do + alias Localizator.Commons + + defstruct [:from, :to] + + @typedoc """ + Locale + """ + @type locale :: String.t() | Atom.t() + @type from :: locale + @type from_may_be_nil :: from | nil + @type to :: locale + @type direction :: {from, to} | to | %{from: from, to: to} | %{to: to} + @type direction_struct :: %{to: to, from: from_may_be_nil} + + @spec get(direction) :: direction_struct + def get(direction) do + direction_map = + case direction do + [from: from, to: to] -> %{to: "#{to}", from: "#{from}"} + [from, to] -> %{to: "#{to}", from: "#{from}"} + {from, to} -> %{to: "#{to}", from: "#{from}"} + %{from: from, to: to} -> %{to: "#{to}", from: "#{from}"} + %{to: to} -> %{to: "#{to}", from: nil} + [to: to] -> %{to: "#{to}", from: nil} + to -> %{to: "#{to}", from: nil} + end + + Commons.struct_from_map(direction_map, as: %__MODULE__{}) + end +end diff --git a/lib/translator/translator.ex b/lib/translator/translator.ex new file mode 100644 index 0000000..fc73964 --- /dev/null +++ b/lib/translator/translator.ex @@ -0,0 +1,47 @@ +defmodule Localizator.Translator do + alias Localizator.Translator.Direction + + @typedoc """ + Locale + """ + @type locale :: String.t() + @type from :: locale + @type from_may_be_nil :: from | nil + @type to :: locale + @type direction :: {from, to} | to | %{from: from, to: to} | %{to: to} + @type direction_map :: %{to: to, from: from_may_be_nil} + + @typedoc """ + Translation service + """ + @type translator :: Atom.t() + + @typedoc """ + Translation services list + """ + @type translators :: [translator] + + @typedoc """ + Translation resource + """ + @type resource :: String.t() | Map.t() | List.t() + @type source :: resource + @type result :: resource + + @typedoc """ + Error Message + """ + @type message :: String.t() + + @spec list() :: translators + def list(), do: Application.get_env(:localizator, :translators) + + @spec default() :: translator + def default(), do: list() |> List.first() + + @spec translate(source, direction, translator) :: {:ok, result} | {:error, message} + def translate(source, direction, translator \\ default()) do + map = Direction.get(direction) + translator.translate(source, map.to, map.from) + end +end diff --git a/lib/translator/yandex.ex b/lib/translator/yandex.ex new file mode 100644 index 0000000..d17051e --- /dev/null +++ b/lib/translator/yandex.ex @@ -0,0 +1,40 @@ +defmodule Localizator.Translator.Yandex do + @type text :: String.t() + @type locale :: String.t() + @type from :: locale + @type to :: locale + @type message :: String.t() + + @behaviour Localizator.Translator.Base + + @impl true + @spec detect(text) :: {:ok, locale} | {:error, message} + def detect(text) when is_bitstring(text) do + case YandexTranslate.detect(text) do + %{languageCode: locale} -> {:ok, locale} + %{} -> {:error, "Couldn't detect language"} + end + end + + @impl true + @spec translate(text, to) :: {:ok, text} | {:error, message} + def translate(text, to, from \\ nil) + + def translate(text, to, from) when is_bitstring(text) and is_bitstring(to) do + translator_response = + case from do + nil -> YandexTranslate.translate(text, to) + _ -> YandexTranslate.translate(text, to, from) + end + + case translator_response do + %{translations: translations} -> + translations + |> List.first() + |> Map.fetch(:text) + + %{message: message} -> + {:error, message} + end + end +end diff --git a/lib/translitor/base.ex b/lib/translitor/base.ex new file mode 100644 index 0000000..29fdb5f --- /dev/null +++ b/lib/translitor/base.ex @@ -0,0 +1,28 @@ +defmodule Localizator.Translitor.Base do + @moduledoc """ + Base translit module. Describing behaviour for transliteration modules + """ + + @typedoc """ + Plain Text + """ + @type text :: String.t() + + @typedoc """ + Translited Text + """ + @type translited :: String.t() + + @typedoc """ + Locale + """ + @type locale :: String.t() + + @typedoc """ + Error Message + """ + @type message :: String.t() + + @callback convert(text) :: {:ok, translited} | {:error, message} + @callback locale() :: {:ok, locale} +end diff --git a/lib/translitor/be.ex b/lib/translitor/be.ex new file mode 100644 index 0000000..4b1ba32 --- /dev/null +++ b/lib/translitor/be.ex @@ -0,0 +1,146 @@ +defmodule Localizator.Translitor.BE do + @moduledoc """ + Translit module for Belorussian language. + """ + @locale "be" + @table %{ + "А" => "A", + "Б" => "B", + "В" => "V", + "Г" => "G", + "Д" => "D", + "Ж" => "Zh", + "З" => "Z", + "И" => "I", + "І" => "I", + "Й" => "J", + "К" => "K", + "Л" => "L", + "М" => "M", + "Н" => "N", + "О" => "O", + "П" => "P", + "Р" => "R", + "С" => "S", + "Т" => "T", + "У" => "U", + "Ф" => "F", + "Х" => "Kh", + "Ц" => "Ts", + "Ч" => "Ch", + "Ш" => "Sh", + "Щ" => "Shch", + "Ы" => "Y", + "Э" => "E", + "Ь" => "", + "Ъ" => "", + "Ў" => "W", + "а" => "a", + "б" => "b", + "в" => "v", + "г" => "g", + "д" => "d", + "ж" => "zh", + "з" => "z", + "и" => "i", + "і" => "i", + "й" => "j", + "к" => "k", + "л" => "l", + "м" => "m", + "н" => "n", + "о" => "o", + "п" => "p", + "р" => "r", + "с" => "s", + "т" => "t", + "у" => "u", + "ф" => "f", + "х" => "kh", + "ц" => "ts", + "ч" => "ch", + "ш" => "sh", + "щ" => "shch", + "ы" => "y", + "э" => "e", + "ў" => "w", + "ь" => "", + "ъ" => "", + "'" => "", + "’" => "" + } + + @typedoc """ + Plain Text + """ + @type text :: String.t() + + @typedoc """ + Locale + """ + @type locale :: String.t() + + @behaviour Localizator.Translitor.Base + + @impl true + @spec locale() :: {:ok, locale} + def locale(), do: @locale + + @doc """ + A belorussian to english transliteration + ## Example + ``elixir + iex> Localizator.Translitor.BE.convert("Ева Салаўёва") + "Jeva Salawjova" + `` + """ + @impl true + @spec convert(text) :: text + def convert(text) when is_bitstring(text) do + text + |> String.graphemes() + |> Enum.reduce([], &convert_letter/2) + |> Enum.join() + end + + # special case for "е", that should be mapped as "ie" + defp convert_letter(letter, acc) + when letter in ~w(Е е Ё ё Ю ю Я я) do + cond do + List.last(acc) in ~w(B V G D Zh Z K L M N P R S T F Kh Ts Ch Sh Shch b v g d zh z k l m n p r s t f kh ts ch sh shch) -> + acc ++ [convert_i(letter)] + + List.last(acc) in (~w(A I J O U Y E W a i j o u y e w) ++ ["", " ", nil]) -> + acc ++ [convert_j(letter)] + end + end + + defp convert_letter(letter, acc) do + acc ++ [convert_letter(letter)] + end + + defp convert_letter(letter) do + case Map.get(@table, letter) do + nil -> letter + latin -> latin + end + end + + defp convert_i("Е"), do: "Ie" + defp convert_i("е"), do: "ie" + defp convert_i("Ё"), do: "Io" + defp convert_i("ё"), do: "io" + defp convert_i("Ю"), do: "Iu" + defp convert_i("ю"), do: "iu" + defp convert_i("Я"), do: "Ia" + defp convert_i("я"), do: "ia" + + defp convert_j("Е"), do: "Je" + defp convert_j("е"), do: "je" + defp convert_j("Ё"), do: "Jo" + defp convert_j("ё"), do: "jo" + defp convert_j("Ю"), do: "Ju" + defp convert_j("ю"), do: "ju" + defp convert_j("Я"), do: "Ja" + defp convert_j("я"), do: "ja" +end diff --git a/lib/translitor/ru.ex b/lib/translitor/ru.ex new file mode 100644 index 0000000..5314ac0 --- /dev/null +++ b/lib/translitor/ru.ex @@ -0,0 +1,114 @@ +defmodule Localizator.Translitor.RU do + @moduledoc """ + Translit module for Russian language. By ICAO Standard (2012—2016) + """ + @locale "ru" + @table %{ + " " => " ", + "А" => "A", + "Б" => "B", + "В" => "V", + "Г" => "G", + "Д" => "D", + "Е" => "E", + "Ё" => "E", + "Ж" => "Zh", + "З" => "Z", + "И" => "I", + "Й" => "I", + "К" => "K", + "Л" => "L", + "М" => "M", + "Н" => "N", + "О" => "O", + "П" => "P", + "Р" => "R", + "С" => "S", + "Т" => "T", + "У" => "U", + "Ф" => "F", + "Х" => "Kh", + "Ц" => "Ts", + "Ч" => "Ch", + "Ш" => "Sh", + "Щ" => "Shch", + "Ъ" => "", + "Ы" => "Y", + "Ь" => "", + "Э" => "E", + "Ю" => "Iu", + "Я" => "Ia", + "а" => "a", + "б" => "b", + "в" => "v", + "г" => "g", + "д" => "d", + "е" => "e", + "ё" => "e", + "ж" => "zh", + "з" => "z", + "и" => "i", + "й" => "i", + "к" => "k", + "л" => "l", + "м" => "m", + "н" => "n", + "о" => "o", + "п" => "p", + "р" => "r", + "с" => "s", + "т" => "t", + "у" => "u", + "ф" => "f", + "х" => "kh", + "ц" => "ts", + "ч" => "ch", + "ш" => "sh", + "щ" => "shch", + "ъ" => "", + "ы" => "y", + "ь" => "", + "э" => "e", + "ю" => "iu", + "я" => "ia" + } + @typedoc """ + Plain Text + """ + @type text :: String.t() + + @typedoc """ + Locale + """ + @type locale :: String.t() + + @behaviour Localizator.Translitor.Base + + @impl true + @spec locale() :: {:ok, locale} + def locale(), do: @locale + + @doc """ + A russian to english transliteration + ## Example + ``elixir + iex> Localizator.Translitor.RU.convert("Роман Шевченко") + "Roman Shevchenko" + `` + """ + @impl true + @spec convert(text) :: text + def convert(text) when is_bitstring(text) do + text + |> String.graphemes() + |> Enum.map(&convert_letter/1) + |> Enum.join() + end + + defp convert_letter(letter) do + case Map.get(@table, letter) do + nil -> letter + latin -> latin + end + end +end diff --git a/lib/translitor/translitor.ex b/lib/translitor/translitor.ex new file mode 100644 index 0000000..48b4785 --- /dev/null +++ b/lib/translitor/translitor.ex @@ -0,0 +1,47 @@ +defmodule Localizator.Translitor do + @typedoc """ + Locale + """ + @type locale :: String.t() + @type to :: locale + + @typedoc """ + Translitor service + """ + @type translitor :: Atom.t() + + @typedoc """ + Transliters list + """ + @type translitors :: [translitor] + + @typedoc """ + Translition resource + """ + @type resource :: String.t() | Map.t() | List.t() + @type source :: resource + @type result :: resource + + @typedoc """ + Error Message + """ + @type message :: String.t() + + @spec list() :: translitors + def list(), do: Application.get_env(:localizator, :translitors) + + defp translitors() do + list() + |> Enum.map(fn translitor -> {translitor.locale(), translitor} end) + |> Map.new() + end + + @spec convert(source, locale) :: {:ok, result} | {:error, message} + def convert(source, locale) do + translitor = + translitors() + |> Map.get(locale) + + translitor.convert(source) + end +end diff --git a/lib/translitor/uk.ex b/lib/translitor/uk.ex new file mode 100644 index 0000000..9fd1a8f --- /dev/null +++ b/lib/translitor/uk.ex @@ -0,0 +1,139 @@ +defmodule Localizator.Translitor.UK do + @moduledoc """ + Translit module for Ukrainian language. + """ + @locale "uk" + @table %{ + "А" => "A", + "Б" => "B", + "В" => "V", + "Г" => "H", + "Ґ" => "G", + "Д" => "D", + "Е" => "E", + "Є" => "Ye", + "Ж" => "Zh", + "З" => "Z", + "И" => "Y", + "І" => "I", + "Ї" => "Yi", + "Й" => "Y", + "К" => "K", + "Л" => "L", + "М" => "M", + "Н" => "N", + "О" => "O", + "П" => "P", + "Р" => "R", + "С" => "S", + "Т" => "T", + "У" => "U", + "Ф" => "F", + "Х" => "Kh", + "Ц" => "Ts", + "Ч" => "Ch", + "Ш" => "Sh", + "Щ" => "Shch", + "Ю" => "Yu", + "Я" => "Ya", + "а" => "a", + "б" => "b", + "в" => "v", + "г" => "h", + "ґ" => "g", + "д" => "d", + "е" => "e", + "є" => "ie", + "ж" => "zh", + "з" => "z", + "и" => "y", + "і" => "i", + "ї" => "i", + "й" => "i", + "к" => "k", + "л" => "l", + "м" => "m", + "н" => "n", + "о" => "o", + "п" => "p", + "р" => "r", + "с" => "s", + "т" => "t", + "у" => "u", + "ф" => "f", + "х" => "kh", + "ц" => "ts", + "ч" => "ch", + "ш" => "sh", + "щ" => "shch", + "ю" => "iu", + "я" => "ia" + } + + @typedoc """ + Plain Text + """ + @type text :: String.t() + + @typedoc """ + Locale + """ + @type locale :: String.t() + + @behaviour Localizator.Translitor.Base + + @impl true + @spec locale() :: {:ok, locale} + def locale(), do: @locale + + @doc """ + A ukrainian to english transliteration + ## Example + ``elixir + iex> Localizator.Translitor.UK.convert("Данило Негрієнко") + "Danylo Nehriienko" + `` + """ + @impl true + @spec convert(text) :: text + def convert(text) when is_binary(text) do + text + |> String.graphemes() + |> Enum.reduce([], &convert_letter/2) + |> Enum.join() + end + + # special case for "Зг", that should be mapped as "zgh" + defp convert_letter(letter, [last_letter] = acc) + when letter in ~w(Г г) and last_letter in ~w(z Z) do + acc ++ [convert_gh(letter)] + end + + # special case for "зг", that should be mapped as "zgh" + defp convert_letter(letter, acc) when letter in ~w(Г г) do + case List.last(acc) do + last_letter when last_letter in ~w(z Z) -> acc ++ [convert_gh(letter)] + _ -> acc ++ [convert_letter(letter)] + end + end + + defp convert_letter(letter, acc) do + acc ++ [convert_letter(letter)] + end + + defp convert_letter(" "), do: " " + defp convert_letter("ь"), do: "" + defp convert_letter("Ь"), do: "" + defp convert_letter("'"), do: "" + defp convert_letter("’"), do: "" + + defp convert_letter(letter) do + case Map.get(@table, letter) do + nil -> letter + latin -> latin + end + end + + defp convert_gh("Г"), do: "Gh" + defp convert_gh("г"), do: "gh" +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..0f5f55c --- /dev/null +++ b/mix.exs @@ -0,0 +1,36 @@ +defmodule Localizator.MixProject do + use Mix.Project + + def project do + [ + app: :localizator, + version: "0.1.0", + elixir: "~> 1.10", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:yandex_translate, "~> 0.4.0"}, + + # Parsers/generators + {:jason, "~> 1.2.1"}, + {:yaml_elixir, "~> 2.4.0"}, + + # Remix for autorestart + {:ex_doc, "~> 0.21.3", only: :dev, runtime: false}, + {:credo, "~> 1.4.0-rc.1", only: :dev, runtime: false}, + {:remix, "~> 0.0.2", only: :dev} + ] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..79fae3a --- /dev/null +++ b/mix.lock @@ -0,0 +1,18 @@ +%{ + "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, + "castore": {:hex, :castore, "0.1.6", "2da0dccb3eacb67841d11790598ff03cd5caee861e01fad61dce1376b5da28e6", [:mix], [], "hexpm", "f874c510b720d31dd6334e9ae5c859a06a3c9e67dfe1a195c512e57588556d3f"}, + "credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"}, + "earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"}, + "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, + "jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"}, + "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"}, + "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"}, + "makeup": {:hex, :makeup, "1.0.2", "0b9f7bfb7a88bed961341b359bc2cc1b233517af891ba4890ec5a580ffe738b4", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "43833299231c6a6983afc75a34e43eeba638521d5527ff89809fa6372424fd7e"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"}, + "mint": {:hex, :mint, "1.0.0", "ca5ab33497ba2bdcc42f6cdd3927420a6159116be87c8173658e93c8746703da", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "b8943ef1e630879538dd6620bfc189d4d75fab3ad39f3fe9c50539879f7efd84"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, + "remix": {:hex, :remix, "0.0.2", "f06115659d8ede8d725fae1708920ef73353a1b39efe6a232d2a38b1f2902109", [:mix], [], "hexpm", "5f5555646ed4fca83fab8620735150aa0bc408c5a17a70d28cfa7086bc6f497c"}, + "yamerl": {:hex, :yamerl, "0.8.0", "8214cfe16bbabe5d1d6c14a14aea11c784b9a21903dd6a7c74f8ce180adae5c7", [:rebar3], [], "hexpm", "010634477bf9c208a0767dcca89116c2442cf0b5e87f9c870f85cd1c3e0c2aab"}, + "yaml_elixir": {:hex, :yaml_elixir, "2.4.0", "2f444abc3c994c902851fde56b6a9cb82895c291c05a0490a289035c2e62ae71", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "4e25a6d5c873e393689c6f1062c5ec90f6cd1be2527b073178ae37eae4c78bee"}, + "yandex_translate": {:hex, :yandex_translate, "0.4.0", "a8851bdd0899d29334c9028aecc70c9921cae5dff86e512e30e72a0bceb6613a", [:mix], [{:castore, "~> 0.1.5", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.2.0", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:mint, "~> 1.0.0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "d4cdb333d8e447d0de20e0314ffc5fdd0a38421800866801007baca9102936f1"}, +} diff --git a/test/data/translitor/be.txt b/test/data/translitor/be.txt new file mode 100644 index 0000000..1c828ed --- /dev/null +++ b/test/data/translitor/be.txt @@ -0,0 +1,66 @@ +Ева - Jeva +Васiльева - Vasiljeva +Васiлёнак - Vasilionak +Ёрш - Jorsh +Вераб’ёў - Vierabjow +Салаўёва - Salawjova +Адъютантов - Adjutantov +Любоў - Liubow +В’юноў - Vjunow +Чарняк - Charniak +Лябецкая - Liabietskaja +Дар’я - Darja +Азаранка - Azaranka +Аліна - Alina +Багушэвіч - Bagushevich +Барыс - Barys +Вольскі - Volski +Віктар - Viktar +Галубовіч - Galubovich +Загорскі - Zagorski +Галіна - Galina +Дубко - Dubko +Дзмітры - Dzmitry +Дзмітрыева - Dzmitryjeva +Емяльянаў - Jemialjanaw +Жылуновіч - Zhylunovich +Жанна - Zhanna +Завальнюк - Zavalniuk +Зарына - Zaryna +Іваноўскі - Ivanowski +Ірына - Iryna +Шаройка - Sharojka +Красько - Krasko +Кацярына - Katsiaryna +Лінкевіч - Linkievich +Людміла - Liudmila +Масько - Masko +Марына - Maryna +Насовіч - Nasovich +Наталля - Natallia +Осцікавіч - Ostsikavich +Пятроўскі - Piatrowski +Павел - Paviel +Рыжонак - Ryzhonak +Раіса - Raisa +Смулькевіч - Smulkievich +Сяргей - Siargiej +Татарчук - Tatarchuk +Тамара - Tamara +Улашчык - Ulashchyk +Уладзімір - Uladzimir +Каўшоў - Kawshow +Станіслаў - Stanislaw +Сафонаў - Safonaw +Фёдар - Fiodar +Харытончык - Kharytonchyk +Цвірка - Tsvirka +Цімафей - Tsimafiej +Чарнoвіч - Charnovich +Часлаў - Chaslaw +Шашковіч - Shashkovich +Кашулёнак - Kashulionak +Кныровіч - Knyrovich +Валеры - Valiery +Эйсмант - Ejsmant +Эрык - Eryk \ No newline at end of file diff --git a/test/data/translitor/ru.txt b/test/data/translitor/ru.txt new file mode 100644 index 0000000..86aeacd --- /dev/null +++ b/test/data/translitor/ru.txt @@ -0,0 +1,2 @@ +Роман - Roman +Шевченко - Shevchenko \ No newline at end of file diff --git a/test/data/translitor/uk.txt b/test/data/translitor/uk.txt new file mode 100644 index 0000000..a0082f3 --- /dev/null +++ b/test/data/translitor/uk.txt @@ -0,0 +1,79 @@ +Розгон - Rozghon +Згурський - Zghurskyi +Алушта - Alushta +Андрій - Andrii +Борщагівка - Borshchahivka +Борисенко - Borysenko +Вінниця - Vinnytsia +Володимир - Volodymyr +Гадяч - Hadiach +Богдан - Bohdan +Згурський - Zghurskyi +Згори - Zghory +Ґалаґан - Galagan +Ґорґани - Gorgany +Донецьк - Donetsk +Дмитро - Dmytro +Рівне - Rivne +Олег - Oleh +Есмань - Esman +Єнакієве - Yenakiieve +Гаєвич - Haievych +Короп'є - Koropie +Житомир - Zhytomyr +Жанна - Zhanna +Жежелів - Zhezheliv +Закарпаття - Zakarpattia +Казимирчук - Kazymyrchuk +Медвин - Medvyn +Михайленко - Mykhailenko +Іванків - Ivankiv +Іващенко - Ivashchenko +Їжакевич - Yizhakevych +Кадиївка - Kadyivka +Мар'їне - Marine +Йосипівна - Yosypivna +Стрий - Stryi +Олексій - Oleksii +Київ - Kyiv +Коваленко - Kovalenko +Лебедин - Lebedyn +Леонід - Leonid +Миколаїв - Mykolaiv +Маринич - Marynych +Ніжин - Nizhyn +Наталія - Nataliia +Одеса - Odesa +Онищенко - Onyshchenko +Полтава - Poltava +Петро - Petro +Розгон - Rozghon +Решетилівка - Reshetylivka +Рибчинський - Rybchynskyi +Суми - Sumy +Соломія - Solomiia +Тернопіль - Ternopil +Троць - Trots +Ужгород - Uzhhorod +Уляна - Uliana +Фастів - Fastiv +Філіпчук - Filipchuk +Харків - Kharkiv +Христина - Khrystyna +Біла Церква - Bila Tserkva +Стеценко - Stetsenko +Чернівці - Chernivtsi +Шевченко - Shevchenko +Шостка - Shostka +Кишеньки - Kyshenky +Щербухи - Shcherbukhy +Гоща - Hoshcha +Гаращенко - Harashchenko +Юрій - Yurii +Корюківка - Koriukivka +Яготин - Yahotyn +Ярошенко - Yaroshenko +Костянтин - Kostiantyn +Знам'янка - Znamianka +Феодосія - Feodosiia +Смартphone - Smartphone \ No newline at end of file diff --git a/test/localizator_test.exs b/test/localizator_test.exs new file mode 100644 index 0000000..4e01f75 --- /dev/null +++ b/test/localizator_test.exs @@ -0,0 +1,8 @@ +defmodule LocalizatorTest do + use ExUnit.Case + doctest Localizator + + test "greets the world" do + assert Localizator.hello() == :world + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/test/translitor_test/be_test.exs b/test/translitor_test/be_test.exs new file mode 100644 index 0000000..ae01476 --- /dev/null +++ b/test/translitor_test/be_test.exs @@ -0,0 +1,22 @@ +defmodule Localizator.TranslitorTest.BETest do + alias Localizator.Translitor.BE + use ExUnit.Case + doctest Localizator.Translitor.BE + + test "Sucess Belorussian translitaration" do + data = file_to_list("test/data/translitor/be.txt") + + Enum.each(data, fn words -> + assert [cyrillic, latin] = String.split(words, " - "), + "Invalid input for tests. Expected two words separated by dash, got: #{words}" + + assert latin == BE.convert(cyrillic) + end) + end + + defp file_to_list(file) do + file + |> File.read!() + |> String.split("\n") + end +end diff --git a/test/translitor_test/ru_test.exs b/test/translitor_test/ru_test.exs new file mode 100644 index 0000000..0f00ccb --- /dev/null +++ b/test/translitor_test/ru_test.exs @@ -0,0 +1,22 @@ +defmodule Localizator.TranslitorTest.RUTest do + alias Localizator.Translitor.RU + use ExUnit.Case + doctest Localizator.Translitor.RU + + test "Sucess Russian translitaration" do + data = file_to_list("test/data/translitor/ru.txt") + + Enum.each(data, fn words -> + assert [cyrillic, latin] = String.split(words, " - "), + "Invalid input for tests. Expected two words separated by dash, got: #{words}" + + assert latin == RU.convert(cyrillic) + end) + end + + defp file_to_list(file) do + file + |> File.read!() + |> String.split("\n") + end +end diff --git a/test/translitor_test/uk_test.exs b/test/translitor_test/uk_test.exs new file mode 100644 index 0000000..8f2e657 --- /dev/null +++ b/test/translitor_test/uk_test.exs @@ -0,0 +1,22 @@ +defmodule Localizator.TranslitorTest.UKTest do + alias Localizator.Translitor.UK + use ExUnit.Case + doctest Localizator.Translitor.UK + + test "Sucess Ukrainian translitaration" do + data = file_to_list("test/data/translitor/uk.txt") + + Enum.each(data, fn words -> + assert [cyrillic, latin] = String.split(words, " - "), + "Invalid input for tests. Expected two words separated by dash, got: #{words}" + + assert latin == UK.convert(cyrillic) + end) + end + + defp file_to_list(file) do + file + |> File.read!() + |> String.split("\n") + end +end