Multilingual website, redirects and setting locale

I’m trying to come up with a solution for redirects and setting the right locale for my multi-lingual CMS.
Some clients have a domain for every language, others don’t, and there are cases where there is a bit of everything.

My table “language_variants” stores the following data

lv_id;language_id;is_root;domain;domain_redirect;route_code;is_default
8;1;true;localhost;;cs-cz;true
9;2;true;localhost.en;;en-gb;true
12;4;false;localhost.fr;localhost.en;fr-fr;false
13;3;false;localhost.de;localhost.en;de-de;false
14;1;false;localhost.cz;localhost;cs-cs;false
10;4;true;localhost.en;;fr-fr;false
11;3;true;localhost.en;;de-de;false

“domain_redirect” - if defined, immediately redirect here
“is_default” - it’s like a “base” language, if we go to www.example.com, we know we should use “en-gb”, but if we go to www.example.com/fr-fr, we should display the website in French

So far, my redirect plug looks like this

def call(conn, _header) do
    host = conn.host

    case DbContext.const_get_language_variants() do
      {:ok, language_variants} ->
        for language_variant <- language_variants do
          item = Map.from_struct(language_variant)

          %{
            :domain => domain,
            :is_default => is_default,
            :domain_redirect => domain_redirect,
            :route_code => lang
          } = item

          if host === domain do
            cond do
              !is_nil(domain_redirect) ->
                Phoenix.Controller.redirect(conn, external: build_url(conn, domain_redirect, lang))
              is_default && !Map.has_key?(conn.params, "locale") ->
                Phoenix.Controller.redirect(conn, external: build_url(conn, domain, lang))
              true ->
                Plug.Conn.put_session(conn, :lang, lang)
            end
          end
        end

      {:error} = error ->
        error
    end

    conn
  end

  defp build_url(conn, to, lang) do
    host = conn.host
    port = to_string(conn.port)
    scheme = to_string(conn.scheme)

    scheme <> "://" <> to <> ":" <> port <> "/" <> lang
  end
end
  • I am not convinced whether or not it’s better to use locale for just everything (including “is_default” rows) i.e. www.exmaple.com would always be www.exmaple.com/en-gb, I am not sure how I could deal with this in routes
  • If a particular locale does not exist, should I redirect the user to the “default” one or throw just a 404?
1 Like

The approach I take in ex_cldr and friends is based upon:

  1. Set the locale from the request with Cldr.Plug.SetLocale

  2. Optionally get the locale from the hostname with Cldr.Locale.locale_from_host/3. Note in this case there are certain registered TLDs like .tv that are considered more general use than territory specific - this is taken into account when deriving the locale. Note this function only looks at the TLD of the host, not any subdomain prefix although that could be added quite trivially.

4 Likes

Thanks for the suggestion, I haven’t heard of this package before.
I’m going to check it out!