Routing with locale parameter

Hello! I’m trying to implement a Plug module which will automatically set the locale.
Inside this module two things happens:

  1. gettext locale is put into the conn
  2. user is redirected to “/:locale/#{current_path}”

Then I call this module in the my browser pipeline:

pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    #...
    plug MyApp.Plugs.SetLocale
  end

And I have routes, for example:

scope "/:locale", MyApp do
  pipe_through :browser # Use the default browser stack
  #... 
  get "/test", PageController, :test
  get "/another/test", PageController, :test
end

Then, when I open http://localhost:4000/test it work fine and I’m redirected to the /locale/test path, but when I try to open http://localhost:4000/another/test I got an error: no route found for GET /another/test (MyApp.Router)

Do you have the source for this?

EDIT: Also, do you have an "/another/test" that is now in the `scope “/:locale”? Or are you trying to redirect automatically inline? Need code of above. :slight_smile:

Thank you for reply!

In error case SetLocale Plug even not started, route error stopped it.
Actually I fix this error by moving plug MyApp.Plugs.SetLocale line from the routes to endpoint.ex above the plug Tiberis.Router line, now all works fine, but it still interesting for me.

For example you can check this https://github.com/smeevil/set_locale I have about the same code and I think in this example my error will be present

I created simple app https://github.com/TakteS/rotes_test you can run it and try to open http://localhost:4000/test and http://localhost:4000/test/another

In the first case you will be redirected and in second case you’ll got an error

Not able to test running it yet (maybe tomorrow), but one immediate possible issue I see is at:
https://github.com/TakteS/rotes_test/blob/master/lib/routes_test/plugs/set_locale.ex#L7
Specifically it is calling for a redirect, but then it is still being passed up the line and handled as a normal call. And since here:
https://github.com/TakteS/rotes_test/blob/master/web/router.ex#L10
It is being set in the global browser pipeline then it is going to try to redirect every single time. In your plug you should probably test if it is already on a valid locale path and if so then pass on, else call redirect to a valid path then maybe halt() the plug pipeline so your redirect is done (that might prevent the redirect from running if it is just halted outright, give it a try?).

An initial curiosity, browsers already give a locale in their headers, like mine sends Accept-Language:en-US,en;q=0.8 on every request since it only accepts “en-US” and “en”, in that order of priority. Why not do away with the entire locale routing and instead use the locale the browser supplies (they all should do) defaulting to ‘en’ or so?

At the very least you can do default locale routing based on the headers too if you want to keep the path style. :slight_smile:

If you are using Gettext then you can get a valid locale list from it, including translations through it.

But yeah, at the very least the current plug looks incorrect as it will cause a redirect on every request (which may get overridden by pages doing their own thing). You can always detect the :locale in the controller on the path and if not a valid one then prepend /en to the path then re-direct too, no plug needed then either.

Otherwise I can try to test this tomorrow, please message again here to remind me. ^.^

It is just for example. In the real SetLocale Plug, of course,
redirecting work only if you do request without locale in other cases
plug just return a conn. But my example is enough for check how it work,
when you call /test/another, the plug is not run because phoenix throw an
error. Therefore I think that it isn’t my plug logic trouble, instead it is trouble with routing or more probably with my approach

What is the precise error, stacktrace and url you accessed? As well as what is the result of mix phoenix.routes?