• Non-obtrusive multi-language redirection (with RoR example)

At Spaces we’ve implemented a non-obtrusive redirection for our multi-language, multi-domain websites. Too many websites have the awful habit of forcing content to visitors that the visitor didn’t request in the first place.

Our rules are simple:

  • Redirect automatically visitor if all of these applies:
    • Visitor accesses root of website (e.g. https://gospaces.com/)
    • Visitor has preferred language (in http-accept-language) that we support
    • If visitor hasn’t been redirected in the same session
  • In all other cases show a small notice box for localized content if the content exists.

English speaking visitors going to https://gospaces.com.mx/ will be redirected. Going to that URL a second time will not cause a redirection. This works well in case it’s a shared link, and the user actually meant to get to the Spanish frontpage page.

English speaking visitors visiting https://gospaces.com.mx/contacto won’t be forced back to https://gospaces.com and vice-versa. Instead they’ll have a small popup notice guiding them.

Ruby on rails implementation

We’re using route_translator and http_accept_language gems.

First we need two helper methods to detect and generate the language urls:

# e.g. app/controllers/application_controller.rb
  helper_method :preferred_language
  def preferred_language
    http_accept_language.compatible_language_from(I18n.available_locales)
  end

  helper_method :generate_preferred_language_url
  def generate_preferred_language_url(opts = {})
    return if cookies[:hide_localized_content_notification] or !preferred_language or preferred_language == I18n.locale.to_sym

    url = localized_url(locale: preferred_language) do |localized_opts|
      url_for(params.to_h.merge(localized_opts).merge(locale: preferred_language.to_s).merge(opts))
    end

    # Removes /es/ and ?locale=es from any strings generated. Unfortunately, route_translator doesn't work well here. The url_for doesn't see any domain specific routes where /es/ and locale=es is not needed.
    if get_host_from_locale(preferred_language)
      url = url.sub("/#{preferred_language}/", "/").sub("locale=#{preferred_language}", "")

      # Remove ? if empty
      url.sub!(/\?$/, "")
    end

    url
  rescue ActionController::UrlGenerationError => e
    Rails.logger.warn("WARN: Could not generate preferred language url. #{e.message}")
  end

You’ll notice that if hide_localized_content_notification is set, the URL isn’t generated. This cookie is set by Javascript when clicking the close button. Something simple like this will do:

document.cookie = "hide_localized_content_notification=true; max-age=" + 3*24*60*60*1000 + "; path=/"

To force redirection on index page we’ll do this:

# app/controllers/index_controller.rb
class IndexController < ApplicationController
  before_filter :redirect_i18n, only: :index

  protected
    def redirect_i18n
      if url = generate_preferred_language_url(utm_source: "website", utm_medium: "redirect").presence and cookies[:frontpage_i18n_redirected].blank?
        cookies[:frontpage_i18n_redirected] = true
        redirect_to url
      end
    end
end

This shows a small notice:

# app/views/layouts/application.rb
<% if localized_content_url = generate_preferred_language_url(utm_source: "website", utm_medium: "localized-content-notification-box").presence %>
    <%= render partial: 'localized_content_notification', locals: { language: preferred_language, url: localized_content_url } %>
<% end %>

The Author

Dan Schultzer is an active experienced entrepreneur, starting the Being Proactive groups, Dream Conception organization, among other things. You can find him at twitter

Like this post? More from Dan Schultzer

Comments? We would love to hear from you, write us at @dreamconception.