Skip to content

Real World Examples

Cyril Kato edited this page Jan 20, 2026 · 1 revision

Table of Contents

Real-World Examples

This page presents practical patterns and complete examples for common use cases. Each example is production-ready and addresses real-world requirements.

E-Commerce: Multi-Region Store

An online store serving customers across Europe with region-specific pricing and content.

Requirements

  • Support English, French, German, Spanish, Italian
  • Regional variants for pricing (EUR vs GBP)
  • Fallback chain for similar languages

Implementation

# config/locales.rb
module StoreLocales
  SUPPORTED = {
    en: { currency: "GBP", region: "UK" },
    "en-US": { currency: "USD", region: "US" },
    fr: { currency: "EUR", region: "FR" },
    "fr-BE": { currency: "EUR", region: "BE" },
    de: { currency: "EUR", region: "DE" },
    "de-AT": { currency: "EUR", region: "AT" },
    "de-CH": { currency: "CHF", region: "CH" },
    es: { currency: "EUR", region: "ES" },
    it: { currency: "EUR", region: "IT" }
  }.freeze

  AVAILABLE = SUPPORTED.keys.freeze
  DEFAULT = :en

  def self.config_for(locale)
    SUPPORTED[locale] || SUPPORTED[DEFAULT]
  end
end
# app/controllers/concerns/store_locale.rb
module StoreLocale
  extend ActiveSupport::Concern

  included do
    before_action :set_store_locale
    helper_method :current_locale, :current_currency, :current_region
  end

  private

  def set_store_locale
    @current_locale = detect_locale
    @locale_config = StoreLocales.config_for(@current_locale)
    I18n.locale = @current_locale
  end

  def detect_locale
    locale_from_params ||
      locale_from_session ||
      locale_from_header ||
      StoreLocales::DEFAULT
  end

  def locale_from_params
    locale = params[:locale]&.to_sym
    return locale if StoreLocales::AVAILABLE.include?(locale)

    nil
  end

  def locale_from_session
    locale = session[:locale]&.to_sym
    return locale if StoreLocales::AVAILABLE.include?(locale)

    nil
  end

  def locale_from_header
    header = request.headers["HTTP_ACCEPT_LANGUAGE"]
    return if header.nil?

    AcceptLanguage.parse(header).match(*StoreLocales::AVAILABLE)
  end

  def current_locale
    @current_locale
  end

  def current_currency
    @locale_config[:currency]
  end

  def current_region
    @locale_config[:region]
  end
end

Usage

<!-- app/views/products/show.html.erb -->
<h1><%= @product.name %></h1>
<p class="price">
  <%= number_to_currency(@product.price, unit: current_currency) %>
</p>
<p><%= t("shipping_info", region: current_region) %></p>

SaaS Platform: User Preferences

A SaaS application where users can set their preferred language, with browser detection as fallback for new users.

Requirements

  • Logged-in users: use saved preference
  • New visitors: detect from browser
  • Support for team-wide default language

Implementation

# app/models/user.rb
class User < ApplicationRecord
  belongs_to :team

  def effective_locale
    preferred_locale || team&.default_locale || I18n.default_locale
  end
end
# app/models/team.rb
class Team < ApplicationRecord
  has_many :users

  validates :default_locale, inclusion: { in: I18n.available_locales.map(&:to_s) },
                             allow_nil: true
end
# app/controllers/concerns/saas_locale.rb
module SaasLocale
  extend ActiveSupport::Concern

  included do
    before_action :set_locale
  end

  private

  def set_locale
    I18n.locale = determine_locale
  end

  def determine_locale
    if user_signed_in?
      current_user.effective_locale
    else
      locale_from_header || I18n.default_locale
    end
  end

  def locale_from_header
    header = request.headers["HTTP_ACCEPT_LANGUAGE"]
    return if header.nil?

    AcceptLanguage.parse(header).match(*I18n.available_locales)
  end
end
# app/controllers/user_preferences_controller.rb
class UserPreferencesController < ApplicationController
  def update
    if current_user.update(user_params)
      redirect_to settings_path, notice: t("preferences.updated")
    else
      render :edit
    end
  end

  private

  def user_params
    params.require(:user).permit(:preferred_locale)
  end
end

REST API: Content Negotiation

A REST API that returns localized content based on the Accept-Language header.

Requirements

  • Return localized error messages
  • Include content language in response
  • Support quality values for nuanced preferences

Implementation

# app/controllers/api/v1/base_controller.rb
module Api
  module V1
    class BaseController < ActionController::API
      before_action :set_locale
      after_action :set_content_language_header

      rescue_from ActiveRecord::RecordNotFound, with: :not_found
      rescue_from ActionController::ParameterMissing, with: :bad_request

      private

      def set_locale
        I18n.locale = detect_locale || I18n.default_locale
      end

      def detect_locale
        header = request.headers["HTTP_ACCEPT_LANGUAGE"]
        return if header.nil?

        AcceptLanguage.parse(header).match(*I18n.available_locales)
      end

      def set_content_language_header
        response.headers["Content-Language"] = I18n.locale.to_s
      end

      def not_found
        render json: { error: t("api.errors.not_found") }, status: :not_found
      end

      def bad_request(exception)
        render json: { error: t("api.errors.bad_request", param: exception.param) },
               status: :bad_request
      end
    end
  end
end
# app/controllers/api/v1/articles_controller.rb
module Api
  module V1
    class ArticlesController < BaseController
      def index
        @articles = Article.published.includes(:translations)
        render json: serialize_articles(@articles)
      end

      def show
        @article = Article.find(params[:id])
        render json: serialize_article(@article)
      end

      private

      def serialize_articles(articles)
        articles.map { |a| serialize_article(a) }
      end

      def serialize_article(article)
        {
          id: article.id,
          title: article.title_for(I18n.locale),
          body: article.body_for(I18n.locale),
          locale: I18n.locale.to_s,
          available_locales: article.available_locales
        }
      end
    end
  end
end

API Response

Request:

GET /api/v1/articles/42
Accept-Language: fr-CH, fr;q=0.9, en;q=0.8

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: fr

{
  "id": 42,
  "title": "Introduction à Ruby",
  "body": "Ruby est un langage de programmation...",
  "locale": "fr",
  "available_locales": ["en", "fr", "de"]
}

Static Site Generator: Build-Time Detection

Pre-generate locale-specific redirects for a static site.

Implementation

# scripts/generate_redirects.rb
require "accept_language"

SUPPORTED_LOCALES = [:en, :fr, :de, :ja]
DEFAULT_LOCALE = :en

# Common Accept-Language patterns to pre-compute
COMMON_PATTERNS = [
  "en-US,en;q=0.9",
  "en-GB,en;q=0.9",
  "fr-FR,fr;q=0.9,en;q=0.8",
  "fr-CA,fr;q=0.9,en;q=0.8",
  "de-DE,de;q=0.9,en;q=0.8",
  "de-AT,de;q=0.9,en;q=0.8",
  "de-CH,de;q=0.9,fr;q=0.8,en;q=0.7",
  "ja-JP,ja;q=0.9,en;q=0.8",
  "zh-CN,zh;q=0.9,en;q=0.8",
  "es-ES,es;q=0.9,en;q=0.8"
]

results = COMMON_PATTERNS.map do |pattern|
  locale = AcceptLanguage.parse(pattern).match(*SUPPORTED_LOCALES) || DEFAULT_LOCALE
  { pattern: pattern, locale: locale }
end

puts "Redirect mappings:"
results.each do |r|
  puts "  #{r[:pattern]} => /#{r[:locale]}/"
end

Output:

Redirect mappings:
  en-US,en;q=0.9 => /en/
  en-GB,en;q=0.9 => /en/
  fr-FR,fr;q=0.9,en;q=0.8 => /fr/
  fr-CA,fr;q=0.9,en;q=0.8 => /fr/
  de-DE,de;q=0.9,en;q=0.8 => /de/
  de-AT,de;q=0.9,en;q=0.8 => /de/
  de-CH,de;q=0.9,fr;q=0.8,en;q=0.7 => /de/
  ja-JP,ja;q=0.9,en;q=0.8 => /ja/
  zh-CN,zh;q=0.9,en;q=0.8 => /en/
  es-ES,es;q=0.9,en;q=0.8 => /en/

Gaming Platform: Regional Content

A gaming platform with region-locked content and language preferences.

Requirements

  • Match language with available game localizations
  • Fall back gracefully for partially localized games
  • Track language for analytics

Implementation

# app/models/game.rb
class Game < ApplicationRecord
  serialize :supported_locales, Array

  def best_locale_for(header)
    return default_locale if header.nil?

    AcceptLanguage.parse(header).match(*supported_locales.map(&:to_sym)) ||
      default_locale
  end

  def default_locale
    supported_locales.first&.to_sym || :en
  end
end
# app/controllers/games_controller.rb
class GamesController < ApplicationController
  def show
    @game = Game.find(params[:id])
    @game_locale = @game.best_locale_for(accept_language_header)

    track_locale_selection(@game, @game_locale)
  end

  private

  def accept_language_header
    request.headers["HTTP_ACCEPT_LANGUAGE"]
  end

  def track_locale_selection(game, locale)
    Analytics.track(
      event: "game_locale_selected",
      properties: {
        game_id: game.id,
        selected_locale: locale,
        available_locales: game.supported_locales,
        accept_language: accept_language_header
      }
    )
  end
end
<!-- app/views/games/show.html.erb -->
<h1><%= @game.title(@game_locale) %></h1>
<p><%= @game.description(@game_locale) %></p>

<% if @game_locale != @game.supported_locales.first.to_sym %>
  <div class="notice">
    <%= t("games.partial_localization", locale: @game_locale) %>
  </div>
<% end %>

Documentation Site: Versioned Translations

A documentation site with multiple versions and languages.

Implementation

# lib/docs_locale.rb
require "accept_language"

class DocsLocale
  VERSIONS = {
    "v3" => [:en, :fr, :de, :ja, :zh],
    "v2" => [:en, :fr, :de],
    "v1" => [:en]
  }.freeze

  DEFAULT_VERSION = "v3"
  DEFAULT_LOCALE = :en

  def initialize(version: nil, accept_language: nil)
    @version = version || DEFAULT_VERSION
    @accept_language = accept_language
  end

  def available_locales
    VERSIONS[@version] || VERSIONS[DEFAULT_VERSION]
  end

  def best_locale
    return DEFAULT_LOCALE if @accept_language.nil?

    AcceptLanguage.parse(@accept_language).match(*available_locales) ||
      DEFAULT_LOCALE
  end

  def locale_available?(locale)
    available_locales.include?(locale.to_sym)
  end
end
# app/controllers/docs_controller.rb
class DocsController < ApplicationController
  before_action :set_docs_locale

  def show
    @doc = Doc.find_by!(
      version: params[:version],
      slug: params[:slug],
      locale: @docs_locale.best_locale
    )
  rescue ActiveRecord::RecordNotFound
    # Try English fallback
    @doc = Doc.find_by!(
      version: params[:version],
      slug: params[:slug],
      locale: :en
    )
  end

  private

  def set_docs_locale
    @docs_locale = DocsLocale.new(
      version: params[:version],
      accept_language: request.headers["HTTP_ACCEPT_LANGUAGE"]
    )
  end
end

Email Service: User Preference with Fallback

Send emails in the user's preferred language.

Implementation

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  def mail_with_locale(user:, **args)
    I18n.with_locale(locale_for(user)) do
      mail(**args)
    end
  end

  private

  def locale_for(user)
    # Priority: user preference > registration locale > default
    user.preferred_locale ||
      user.registration_locale ||
      I18n.default_locale
  end
end
# app/models/user.rb
class User < ApplicationRecord
  before_create :set_registration_locale

  private

  def set_registration_locale
    return if registration_locale.present?
    return if signup_accept_language.nil?

    self.registration_locale = AcceptLanguage
      .parse(signup_accept_language)
      .match(*I18n.available_locales)
  end
end
# app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
  def create
    @user = User.new(user_params)
    @user.signup_accept_language = request.headers["HTTP_ACCEPT_LANGUAGE"]

    if @user.save
      WelcomeMailer.welcome(@user).deliver_later
      redirect_to dashboard_path
    else
      render :new
    end
  end
end

Testing Patterns

RSpec Shared Examples

# spec/support/shared_examples/locale_detection.rb
RSpec.shared_examples "locale detection" do
  context "with Accept-Language header" do
    it "detects simple locale" do
      get endpoint, headers: { "HTTP_ACCEPT_LANGUAGE" => "fr" }
      expect(I18n.locale).to eq(:fr)
    end

    it "respects quality values" do
      get endpoint, headers: { "HTTP_ACCEPT_LANGUAGE" => "de;q=0.8, fr;q=0.9" }
      expect(I18n.locale).to eq(:fr)
    end

    it "handles regional variants" do
      get endpoint, headers: { "HTTP_ACCEPT_LANGUAGE" => "en-GB, en;q=0.9" }
      expect(I18n.locale).to eq(:en)
    end

    it "falls back for unsupported locale" do
      get endpoint, headers: { "HTTP_ACCEPT_LANGUAGE" => "xx" }
      expect(I18n.locale).to eq(I18n.default_locale)
    end
  end

  context "without Accept-Language header" do
    it "uses default locale" do
      get endpoint
      expect(I18n.locale).to eq(I18n.default_locale)
    end
  end
end
# spec/requests/home_spec.rb
RSpec.describe "Home" do
  let(:endpoint) { root_path }

  it_behaves_like "locale detection"
end

Summary

These patterns demonstrate AcceptLanguage in various contexts:

Use Case Key Pattern
E-Commerce Locale + currency + region mapping
SaaS User preference > team default > browser
REST API Content-Language response header
Static Site Pre-computed redirects
Gaming Per-resource locale availability
Documentation Version-specific locale support
Email Capture locale at registration

Further Reading