DEV Community

Cover image for Negotiating Languages with Ruby: A Journey through Linguistic Diversity
Cyril Kato
Cyril Kato

Posted on • Updated on

Negotiating Languages with Ruby: A Journey through Linguistic Diversity

Our world, with its vast array of languages, is a melting pot of diverse cultures and histories. Linguistic diversity is a remarkable aspect of human society that underscores our uniqueness. Just as the web is an intricate network of hyperlinks that binds web pages together, languages interconnect humanity. This myriad of human languages is innumerable, yet the BCP 47 specification helps us identify and code them by their name, regional variation, and script.

In the realm of web services, the Accept-Language HTTP header field articulates client preferences concerning languages.

Consider a Japanese user, a native speaker who has adjusted their browser settings to receive content primarily in Japanese. This preference is reflected in the following request:

Accept-Language: ja
Enter fullscreen mode Exit fullscreen mode

Suppose this user is from Osaka and has a preference for the Kansai dialect. The parameter can be fine-tuned as follows:

Accept-Language: ja-KS, ja;q=0.9
Enter fullscreen mode Exit fullscreen mode

Further, let's assume the user has a basic understanding of Thai, but only in the Latin script. The request now becomes:

Accept-Language: ja-KS, ja;q=0.9, th-Latn;q=0.8
Enter fullscreen mode Exit fullscreen mode

Lastly, let's presume the user is open to other languages, except French and English:

Accept-Language: ja-KS, ja;q=0.9, th-Latn;q=0.8, *;q=0.1 fr;q=0, en;q=0
Enter fullscreen mode Exit fullscreen mode

Ruby offers a compact and handy library, accept_language, that simplifies language negotiations between the client and server.

If a server, for instance, has translations in Japanese and Korean for a certain resource, the accept_language library can find a common ground between the user's preferred languages and those supported by the application:

AcceptLanguage.parse("ja-KS, ja;q=0.9, th-Latn;q=0.8, *;q=0.1 fr;q=0, en;q=0").match(:ko, :ja) # => :ja
Enter fullscreen mode Exit fullscreen mode

In the context of a Rails application, the usage might look like this:

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

  def best_locale_from_request!
    I18n.locale = best_locale_from_request
  end

  def best_locale_from_request
    return I18n.default_locale unless request.headers.key?("HTTP_ACCEPT_LANGUAGE")

    string = request.headers.fetch("HTTP_ACCEPT_LANGUAGE")
    locale = AcceptLanguage.parse(string).match(*I18n.available_locales)

    # If the server cannot serve any matching language,
    # it can theoretically send back a 406 (Not Acceptable) error code.
    # But, for a better user experience, this is rarely done and more
    # common way is to ignore the Accept-Language header in this case.
    return I18n.default_locale if locale.nil?

    locale
  end
end
Enter fullscreen mode Exit fullscreen mode

Here's to the joy of linguistic diversity! Let's embrace the challenge of matching as many languages as possible. Happy coding!

Oldest comments (0)