lib/cldr/plug/plug_accept_language.ex

if Code.ensure_loaded?(Plug) do
  defmodule Cldr.Plug.AcceptLanguage do
    @moduledoc """
    Parses the accept-language header if one is available and sets
    `conn.private[:cldr_locale]` accordingly.  The locale can
    be later retrieved by `Cldr.Plug.AcceptLanguage.get_cldr_locale/1`

    ## Options

    * `:cldr_backend` is any backend module. The default
      is `Cldr.default_backend/0`. If no `:cldr_backend`
      option is provided and no default backend is configured
      then an exception will be raised.

    * `:no_match_log_level` determines the logging level for
      the case when no matching locale is configured to meet the users
      request. The default is `:warn`. If set to `nil` then no logging
      is performed.

    ## Example

        # Using a specific backend to validate
        # and match locales
        plug Cldr.Plug.AcceptLanguage,
          cldr_backend: MyApp.Cldr

        # Using the default backend to validate
        # and match locales
        plug Cldr.Plug.AcceptLanguage

    """

    import Plug.Conn
    require Logger

    @language_header "accept-language"
    @default_log_level :warn

    @doc false
    def init(options \\ []) do
      backend = Keyword.get_lazy(options, :cldr_backend, &Cldr.default_backend!/0)
      log_level = Keyword.get(options, :no_match_log_level, @default_log_level)
      %{backend: backend, log_level: log_level}
    end

    @doc false
    def call(conn, options) do
      case get_req_header(conn, @language_header) do
        [accept_language] ->
          put_private(conn, :cldr_locale, best_match(accept_language, options))

        [accept_language | _] ->
          put_private(conn, :cldr_locale, best_match(accept_language, options))

        [] ->
          put_private(conn, :cldr_locale, nil)
      end
    end

    @doc """
    Returns the locale which is the best match for the provided
    accept-language header

    """
    def best_match(nil, _) do
      nil
    end

    def best_match(accept_language, options) do
      case Cldr.AcceptLanguage.best_match(accept_language, options.backend) do
        {:ok, locale} ->
          locale

        {:error, {Cldr.NoMatchingLocale = exception, reason}} ->
          if options.log_level,
            do: Logger.log(options.log_level, "#{inspect(exception)}: #{reason}")

          nil

        {:error, {exception, reason}} ->
          Logger.warn("#{inspect(exception)}: #{reason}")
          nil
      end
    end

    @doc """
    Return the locale set by `Cldr.Plug.AcceptLanguage`

    """
    def get_cldr_locale(conn) do
      conn.private[:cldr_locale]
    end
  end
end