defmodule Cldr.Language do @moduledoc """ Cldr Languages does provide functionality regarding languages within the data supplied by the Cldr core package. To use the functionality of this module you need to [register it as provider](https://github.com/kipcole9/cldr#providers) in your cldr backend module. ## Example defmodule MyApp.Backend do use Cldr, providers: [Cldr.Language, …], … end ## Interface All functionality will be compiled into `MyApp.Backend.Language`. ### Functions * `available_languages/0` * `available_languages/1` * `known_languages/0` * `known_languages/1` * `to_string/1` * `to_string/2` """ # This is only meant to be called by Cldr @doc false def cldr_backend_provider(config) do module = inspect(__MODULE__) backend = config.backend config = Macro.escape(config) quote location: :keep, bind_quoted: [ module: module, backend: backend, config: config ] do defmodule Language do alias Cldr.LanguageTag alias Cldr.Locale @type styles :: :standard | :short @styles [:standard, :short] # Simpler than unquoting the backend everywhere defp backend, do: unquote(backend) defp get_locale, do: backend().get_locale() defp default_locale, do: backend().default_locale() @doc """ Return all the languages' iso-codes available for a given locale. Defaults to the current locale. ## Example > #{inspect(__MODULE__)}.Language.available_languages("en") ["aa", "ab", "ace", "ach", "ada", "ady", "ae", "aeb", "af", "afh", "agq", "ain", "ak", "akk", "akz", "ale", "aln", "alt", "am", "an", "ang", "anp", "ar", "ar-001", "arc", "arn", "aro", "arp", "arq", "ars", "arw", "ary", "arz", "as", "asa", "ase", "ast", "av", "avk", "awa", "ay", "az", "ba", "bal", "ban", "bar", "bas", "bax", "bbc", "bbj", ...] """ @spec available_languages() :: list(String.t()) | {:error, term()} @spec available_languages(String.t() | LanguageTag.t()) :: list(String.t()) | {:error, term()} def available_languages(locale \\ get_locale()) def available_languages(%LanguageTag{cldr_locale_name: cldr_locale_name}) do available_languages(cldr_locale_name) end @doc """ Return a map of iso-code keyed maps of language names in any available formats for the given locale. Defaults to the current locale. ## Example > #{inspect(__MODULE__)}.Language.known_languages("en") %{"bez" => %{standard: "Bena"}, "lo" => %{standard: "Lao"}, "kha" => %{standard: "Khasi"}, "eo" => %{standard: "Esperanto"}, "rm" => %{standard: "Romansh"}, "ja" => %{standard: "Japanese"}, "sw-CD" => %{standard: "Congo Swahili"}, "pdc" => %{standard: "Pennsylvania German"}, "om" => %{standard: "Oromo"}, "jut" => %{standard: "Jutish"}, "lij" => %{standard: "Ligurian"}, "kut" => %{standard: "Kutenai"}, "vep" => %{standard: "Veps"}, "yao" => %{standard: "Yao"}, "gez" => %{standard: "Geez"}, "cr" => %{standard: "Cree"}, "ne" => %{standard: "Nepali"}, "zbl" => %{standard: "Blissymbols"}, "ae" => %{standard: "Avestan"}, "rof" => %{standard: "Rombo"}, "tkl" => %{standard: "Tokelau"}, "rgn" => %{standard: "Romagnol"}, "el" => %{standard: "Greek"}, "myv" => %{standard: "Erzya"}, "smj" => %{standard: "Lule Sami"}, "fo" => %{standard: "Faroese"}, "ii" => %{standard: "Sichuan Yi"}, "bum" => %{standard: "Bulu"}, "za" => %{standard: "Zhuang"}, "raj" => %{standard: "Rajasthani"}, "mrj" => %{standard: "Western Mari"}, "stq" => %{standard: "Saterland Frisian"}, "hu" => %{standard: "Hungarian"}, "mga" => %{standard: "Middle Irish"}, "bej" => %{standard: "Beja"}, "yue" => %{standard: "Cantonese"}, "xog" => %{standard: "Soga"}, "ttt" => %{standard: "Muslim Tat"}, "uga" => %{standard: "Ugaritic"}, "rup" => %{standard: "Aromanian"}, "crs" => %{standard: "Seselwa Creole French"}, "oc" => %{standard: "Occitan"}, "chp" => %{standard: "Chipewyan"}, "zen" => %{standard: "Zenaga"}, "kmb" => %{standard: "Kimbundu"}, "nr" => %{standard: "South Ndebele"}, "tiv" => %{standard: "Tiv"}, "aln" => %{standard: "Gheg Albanian"}, "sh" => %{standard: "Serbo-Croatian"}, "fil" => %{...}, ...} """ @spec known_languages() :: %{String.t() => %{required(styles()) => String.t()}} | {:error, term()} @spec known_languages(String.t() | LanguageTag.t()) :: %{String.t() => %{required(styles()) => String.t()}} | {:error, term()} def known_languages(locale \\ get_locale()) def known_languages(%LanguageTag{cldr_locale_name: cldr_locale_name}) do known_languages(cldr_locale_name) end # Implement available_locales/known_locales for locale_name <- Cldr.Config.known_locale_names(config) do languages = locale_name |> Cldr.Config.get_locale(config) |> Map.get(:languages) def available_languages(unquote(locale_name)) do unquote(Enum.sort(Map.keys(languages))) end def known_languages(unquote(locale_name)) do unquote(Macro.escape(languages)) end end # Error cases for available_locales/known_locales def available_languages(locale), do: {:error, Locale.locale_error(locale)} def known_languages(locale), do: {:error, Locale.locale_error(locale)} @doc """ Try to translate the given lanuage iso code or language tag. ## Example iex> #{inspect(__MODULE__)}.Language.to_string("eo") {:ok, "Esperanto"} """ @spec to_string(String.t() | LanguageTag.t()) :: {:ok, String.t()} | {:error, term()} @spec to_string(String.t() | LanguageTag.t(), Keyword.t()) :: {:ok, String.t()} | {:error, term()} def to_string(key, options \\ []) do opts = %{} |> merge_style(options[:style]) |> merge_locale(options[:locale]) |> merge_fallback(options[:fallback]) result = to_string_by_locale(key, opts.locale, opts) if result == :error && Map.fetch!(opts, :fallback) do to_string_by_locale(key, default_locale(), opts) else result end end defp to_string_by_locale(key, locale, %{style: style}) do with {:ok, lang} <- locale |> known_languages |> Map.fetch(key) do case Map.fetch(lang, style) do {:ok, _} = val -> val :error -> Map.fetch(lang, :standard) end end end # Merge style into options # Only allow @styles (default to standard) defp merge_style(opts, nil), do: Map.put(opts, :style, :standard) defp merge_style(opts, style) when style in @styles, do: Map.put(opts, :style, style) defp merge_style(opts, style) do msg = "Invalid :style option #{inspect(style)} supplied. " <> "Valid styles are #{inspect(@styles)}." raise ArgumentError, msg end # Merge locale into options # Default to cldr's current locale defp merge_locale(opts, nil), do: Map.put(opts, :locale, get_locale()) defp merge_locale(opts, locale), do: Map.put(opts, :locale, locale) # Merge fallback into options # Only allow booleans (default to false) defp merge_fallback(opts, nil), do: Map.put(opts, :fallback, false) defp merge_fallback(opts, fallback) when is_boolean(fallback), do: Map.put(opts, :fallback, fallback) defp merge_fallback(opts, fallback) do msg = "Invalid :fallback option #{inspect(fallback)} supplied. " <> "Valid fallbacks are #{inspect([true, false])}." raise ArgumentError, msg end end end end end