0

I am implementing a simple language switcher in the common HTML menu-bar on a website in Rails, where the locale is path-based with the Rails-standard I18n implementation, e.g., /en/articles for Index for the Article model in English (in the following example, I consistently use pages for the model Article).

The language switcher is a simple text link (<a> in HTML) to the same page in another language. For example, the French page for /en/article/7 should be /fr/article/7. Here is the most simplified code for the language-switcher link, which preserves all the GET query parameters for the current page:

str_link = link_to("fr", url_for(locale: "fr", params: request.query_parameters.except("locale")))

This code is included inside the common layout /app/views/layouts/application.html.erb so that it is applied to all pages on the website. This works most of time but fails on the page after a user-input to a new page turns out to be invalid (status: :unprocessable_entity), according to the Rails standard CRUD action.

In the new page, the required link for the language switcher for French should be /fr/article/new; the above code-snippet works fine when a user freshly opens the new page in English (/en/article/new). However, once a user's input turns out to be invalid, the given URL is /en/articles, whose contents are equivalent to the new page.

How can I obtain the path /en/article/new in such cases so as to make the language switcher provide the correct link?

Obviously, when a user just requests the Index page /en/articles, the counterpart French page is /fr/articles, the URL of which is identical to the failed new page. So, they must be distinguished, that is, the path cannot be guessed thoroughly from the current URL and it depends on whether it is a fresh request or unprocessable_entity.

An answer to the question "Ruby on Rails Link to Previous Page on Form Failing After Invalid Input" suggests implementing hidden_field that contains the new page URL and the algorithm uses it. However, the suggestion does not work well in this case because the hidden_field for action create is POST and not a GET parameter. In the present case of the common language switcher, I need to deal with an arbitrary number of models inside the common layout (application.html.erb), meaning which parameter in params to permit cannot be pre-determined and hence to access hidden_field in params is tricky.

Here are the routes and Article Controller generated according to the Rails standard:

% bin/rails g scaffold Article title:string content:text

Routes:

# config/routes.rb
Rails.application.routes.draw do
  filter :locale
  resources :articles
end

Article Controller:

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)

    respond_to do |format|
      if @article.save
        format.html { redirect_to article_url(@article), notice: "Article was successfully created." }
      else
        format.html { render :new, status: :unprocessable_entity }
      end
    end
  end

  private
    def article_params
      params.require(:article).permit(:title, :content)
    end
end

I am using Rails 7.0. But I think the Rails version is almost irrelevant.

Masa Sakano
  • 1,921
  • 20
  • 32

1 Answers1

0

The language needs to be defined as a variable in your routes:

# config/routes.rb
scope "/:locale" do
  resources :articles
end

This puts a language parameter in all your routes. Now you have to make sure that it's well-defined everywhere, as described here

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base

  around_action :switch_locale

  def switch_locale(&action)
    locale = params[:locale] || I18n.default_locale
    I18n.with_locale(locale, &action)
  end
end

And you need to define the default locale somewhere, e.g. in an intializer:

# config/initializers/locale.rb
  Rails.application.config.i18n.default_locale = I18n.default_locale = :en
Les Nightingill
  • 5,662
  • 1
  • 29
  • 32
  • Oops, sorry I forgot to include the statement `filter :locale` in `routes.rb` in my question – I have now edited the question. I understand it means the same as your suggested `routes.rb`. The i18n environment is appropriately set in the same way as your answer (I expressed it *path-based* in my question) and is working. My question is how to write the `` for the language switcher after a failed-*create* page. – Masa Sakano Oct 16 '22 at 18:19