smeevil / set_locale

elixir phoenix plug that can extract locale from browser headers or paths for redirection and setting of get text locales
Do What The F*ck You Want To Public License
78 stars 28 forks source link

API usecase #21

Open char0n opened 4 years ago

char0n commented 4 years ago

This library is great, but doesn't support API usecase. I have an API and want to use set_locale as a simple mechanism to read the Accept-Language header and setting the locale without any redirects and reading cookies etc. Have you thought about supporting API usecases ?

char0n commented 4 years ago

I've create a Plug specifically for API usecase for anybody looking for API only solution:

defmodule SetLocale.Api do
  @moduledoc """
  Module for setting locale from Accept-Language header.

  This implementation is appropriate for API use-cases only.
  The only thing this plug does is determining the locale from
  the `Accept-Language` header and setting the locale to gettext.

  The options for the plugs are
   - gettext: mandatory
   - default_locale: mandatory, used as last step in fallback chain
   - additional_locales: optional, if given it allows to whitelist locales that are not defined via Gettext. Possible scenario: You want to use Gettext and some SaaS localization service (e.g. http://bablic.com/) in parallel. Whitelisting these additional languages allows you to have proper routing for the locales and trigger the wanted JS behaviour depending on the assigned locale in your templates.
  """

  import Plug.Conn

  defmodule Config do
    @moduledoc "Struct for SetLocale config."
    @enforce_keys [:gettext, :default_locale]
    defstruct [:gettext, :default_locale, additional_locales: []]
  end

  def init(opts) when is_tuple(hd(opts)), do: struct!(Config, opts)

  def call(%Plug.Conn{} = conn, config) do
    requested_locale = determine_locale(conn, config)

    if supported_locale?(requested_locale, config) do
      if Enum.member?(config.additional_locales, requested_locale) do
        Gettext.put_locale(config.gettext, config.default_locale)
      else
        Gettext.put_locale(config.gettext, requested_locale)
      end
    else
      Gettext.put_locale(config.gettext, config.default_locale)
    end

    assign(conn, :locale, requested_locale)
  end

  defp determine_locale(conn, config) do
    determined_locale = get_locale_from_header(conn, config)

    if supported_locale?(determined_locale, config),
      do: determined_locale,
      else: config.default_locale
  end

  defp get_locale_from_header(conn, gettext) do
    conn
    |> SetLocale.Headers.extract_accept_language()
    |> Enum.find(nil, fn accepted_locale -> supported_locale?(accepted_locale, gettext) end)
  end

  defp supported_locale?(locale, config), do: Enum.member?(supported_locales(config), locale)

  defp supported_locales(config),
    do: Gettext.known_locales(config.gettext) ++ config.additional_locales
end

Would you be interested in PR ? To merge this we'd need to consolidate some code to be able to reuse as much as possible from the original implementation.