Rails I18n set_locale
Asked Answered
S

1

8

I want to set the locale by the clients browserlocale request.env['HTTP_ACCEPT_LANGUAGE'] and by the URL.

  1. If a user visits a URL (e.g.: myapp.com) it should check the HTTP_ACCEPT_LANGUAGE and redirect to the right URL (e.g.: myapp.com/en - if the browserlocale is en)

  2. If a user then chooses a different language via a language-menu it should change the URL to e.g.: myapp.com/de.

Here is my what I got so far:

class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :set_locale

private

  # set the language
  def set_locale
    if params[:locale].blank?
      I18n.locale = extract_locale_from_accept_language_header
    else
      I18n.locale = params[:locale]
    end
  end

  # pass in language as a default url parameter
  def default_url_options(options = {})
    {locale: I18n.locale}
  end

  # extract the language from the clients browser
  def extract_locale_from_accept_language_header
    browser_locale = request.env['HTTP_ACCEPT_LANGUAGE'].try(:scan, /^[a-z]{2}/).try(:first).try(:to_sym) 
    if I18n.available_locales.include? browser_locale
      browser_locale
    else
      I18n.default_locale
    end
  end
end

In my routes file I got:

Myapp::Application.routes.draw do
  # set language path
  scope ":locale", locale: /#{I18n.available_locales.join("|")}/ do

    root :to => "mycontrollers#new"
    ...

  end

  match '*path', to: redirect("/#{I18n.locale}/%{path}"), constraints: lambda { |req| !req.path.starts_with? "/#{I18n.default_locale}/" }

  match '', to: redirect("/#{I18n.locale}")
end

The Problem is that the routesfile gets executed first and the HTTP_ACCEPT_LANGUAGE has no effect, because the url-param already got set when it comes to the controller.

Does anyone has a solution for that? Maybe solve it by a middleware?

Septuagenarian answered 2/5, 2012 at 15:19 Comment(0)
C
8

I'd change a few things in your routes.

First:

scope :path => ":locale" do
  ... 
end

Second:

I see what you are trying to do here:

match '', to: redirect("/#{I18n.locale}")

It seems redundant though.

I'd get rid of that line and just modify the set_locale method, like this:

# set the language
def set_locale
  if params[:locale].blank?
    redirect_to "/#{extract_locale_from_accept_language_header}"
  else
    I18n.locale = params[:locale]
  end
end
Catholicon answered 24/5, 2012 at 19:37 Comment(3)
The application_controller is not called, in case of match '' -> so you end up not setting the locale and without redirect. At least that's what happened when I implemented it.Newsstand
I just checked in a brand new Rails (4.1.0) app and it's working as expected. Instead of match '', to: redirect("/#{I18n.locale}") you will need to write get '', to: redirect("/#{I18n.locale}"). That first request won't use application_controller.rbCatholicon
For some reason it doesn't work for me (tested this on 4.0 tough) - anyway I came up with the following solution: #23189486Newsstand

© 2022 - 2024 — McMap. All rights reserved.