Hello!
I’m trying to improve my localized application. I’ve got few questions about routing in this context.
Two locales are in place (en and fr). You can go to:
localhost/path => en locale
localhost/fr/path => fr locale
localhost/en/path would be valid but not “normally” used.
I plugged my locale switcher before the router:
defmodule MyApp.Plugs.SetLocale do
@locales ["fr", "en"]
def init(default) do
default
end
def call(conn, _opts) do
case conn.path_info do
[locale | rest] when locale in @locales ->
Gettext.put_locale(MyApp.Gettext, locale)
%{conn | path_info: rest}
|> Plug.Conn.assign(:locale, locale)
_ ->
Gettext.put_locale(MyApp.Gettext, "en")
Plug.Conn.assign(conn, :locale, "en")
end
end
end
If I want to maintain the locale in the url I have to prepend local everywhere so:
<%= link gettext("My Page"), to: Routes.page_path(@conn, :my_page) %>
Became:
<%= link gettext("My Page"), to: @locale <> Routes.page_path(@conn, :my_page) %>
The same changes are made in the forms.
I end up making a helper so I can do:
<%= link gettext("My Page"), to: localized_route(@locale, Routes.page_path(@conn, :my_page)) %>
and
<%= form_for @changeset, localized_route(@locale,@action), fn f -> %>
In the controllers, I have to add the locale to the redirect too, using the same helper:
defmodule MyApp.ResourceController do
use MyApp, :controller
alias MyApp.Resources
alias MyApp.Resources.Resource
import MyApp.Views.Helpers.LocaleHelpers
....
def update(conn, %{"id" => id, "resource" => resource_params}) do
resource = Resources.get_resource!(id)
case Resources.update_resource(resource, resource_params) do
{:ok, resource} ->
conn
|> put_flash(:info, "resource updated successfully.")
|> redirect(to: localized_route(conn.assigns.locale, Routes.resource_path(conn, :show, conn.params["account_uuid"], resource )))
# Or with the proper router.ex
# |> redirect(to: Routes.resource_path(conn, :show, conn.assigns.locale, conn.params["account_uuid"], resource.id ))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "edit.html", resource: resource, changeset: changeset)
end
end
end
I can write my redirection differently if I scope in the router. But to allow the “optional” locale I have to duplicate all routes which is not ideal in my opinion.
Here is an example of the router:
scope "/:account_uuid", MyAppWeb do
pipe_through [...]
...
resources "/resource", ResourceController
...
end
or
Enum.each ["/:locale", ""], fn locale ->
scope locale <> "/:account_uuid", MyAppWeb do
pipe_through [...]
...
resources "/resource", ResourceController
...
end
end
Questions:
1- Am I on the right track?
2- Is there a way to do an optional scope in the router like in rails (scope /(:locale)
) or adding a default option to the scope?
3- Is there a better way to add the locale to all the links and redirects? I want avoiding the error of missing it when adding new elements to the app and having to rewrite everything each time.