defmodule IsoLang do
@moduledoc """
Documentation for `IsoLang`.
Provides utilities for dealing with [ISO 639](https://en.wikipedia.org/wiki/ISO_639)
languages.
## See Also
- https://en.wikipedia.org/wiki/IETF_language_tag
- https://tools.ietf.org/search/bcp47
- https://datahub.io/core/language-codes#resource-language-codes-full
"""
use Gettext, otp_app: :iso_lang
@type t :: %__MODULE__{
alpha2: String.t(),
alpha3b: String.t(),
alpha3t: String.t(),
name: String.t(),
native_name: String.t()
}
defstruct alpha2: nil, alpha3b: nil, alpha3t: nil, name: nil, native_name: nil
@doc """
Returns a list of all available ISO language codes.
"""
defdelegate all(opts \\ []), to: IsoLang.Data
@doc """
Gets a single language struct identified by a field.
If the `:by` field is not specified, fields are checked in the following order:
- `:alpha2`
- `:alpha3b`
- `:alpha3t`
- `:name`
## Options
- `:by` specifies which struct field to be used in the search. (optional)
## Examples
iex> IsoLang.get("de")
{:ok, %IsoLang{alpha2: "de", alpha3b: "ger", alpha3t: "deu", name: "German"}}
"""
@spec get(value :: String.t(), opts :: Keyword.t()) :: {:ok, IsoLang.t()} | {:error, any()}
def get(value, opts \\ []) do
key = Keyword.get(opts, :by, nil)
opts
|> all()
|> Enum.find(:not_found, fn
lang when not is_nil(key) -> Map.get(lang, key) == value
%__MODULE__{alpha2: ^value} -> true
%__MODULE__{alpha3b: ^value} -> true
%__MODULE__{alpha3t: ^value} -> true
%__MODULE__{name: ^value} -> true
_ -> false
end)
|> case do
:not_found -> {:error, "Language not found"}
lang -> {:ok, lang}
end
end
@doc """
As `get/2`, but raises on error
"""
def get!(query, opts \\ []) do
case get(query, opts) do
{:ok, lang} -> lang
{:error, error} -> raise error
end
end
@doc """
Searches for matching languages using a case-insensitive query string
## Options
- `:by` specifies which struct field to be used in the search. Default: `:name`
## Examples
iex> IsoLang.find("eng")
{:ok,
[
%IsoLang{alpha2: "bn", alpha3b: "ben", alpha3t: "", name: "Bengali"},
%IsoLang{alpha2: "en", alpha3b: "eng", alpha3t: "", name: "English"}
]}
"""
@spec find(query :: String.t(), opts :: Keyword.t()) :: {:ok, [IsoLang.t()]} | {:error, any()}
def find(query, opts \\ []) do
key = Keyword.get(opts, :by, :name)
{:ok,
opts
|> all()
|> Enum.filter(fn lang ->
query
|> Regex.compile!("i")
|> Regex.match?(
lang
|> Map.fetch!(key)
)
end)}
rescue
e -> {:error, e}
end
@doc """
As `find/2`, but raises on error
"""
def find!(query, opts \\ []) do
case find(query, opts) do
{:ok, lang} -> lang
{:error, error} -> raise error
end
end
end