DEV Community

Dwarves Foundation
Dwarves Foundation

Posted on

I18n Frontend guideline

frontend

In a front-end web application, locales are used to determine the language and geographic location of the user and to display the appropriate content to them.

When a user visits a web page, the application will first detect their locale, then use this information to determine which version of the content to display.

To support multiple languages and locales, the application needs to have the necessary content available for each locale. This can be achieved by creating separate files or resources for each locale or by using a localization library like i18next that allows for dynamic localization based on the user's detected locale.

In this frontend guideline, we will discuss general principles and provide examples using i18next with React.js

Locale detection

There are several ways to detect the user's locale (i.e., their language and geographic location) in a front-end application:

  • Browser settings: The user's browser settings may indicate their preferred language. This information can be accessed through the navigator.language or navigator.userLanguage property in JavaScript.
  • IP geolocation: The user's IP address can be used to determine their geographic location. This can be done by making a request to a geolocation service, such as MaxMind or IP-API, which returns the user's location based on their IP address.
  • Server-side detection: An HTTP header that relays these language preferences to the server with each request. This is the Accept-Language header, and it often looks something like this: Accept-Language: en-CA,ar-EG;q=0.5.
  • Use of cookies or web storage: The user's locale preference can be stored in a cookie or HTML5 web storage when the user first interacts with the application. The locale would then be read from this location for all subsequent requests.

There are a few libraries where that offer locale detection based on the settings listed above. For instance, In React.js, there is the i18next-browser-languageDetector library that detects language based on:

  • Cookie
  • LocalStorage
  • Navigator
  • Query(?lng=LANGUAGE)
  • HtmlTag
  • Path
  • Subdomain.

Another example would be Next.js, where the locale will be automatically detected based on the Accept-Language header and the current domain. Locale detection is enabled by default.

Internationalized routing

Internationalized routing is a way to handle different URLs for the same page based on the user's detected locale. There are two types of URL routing:

  • Sub-path routing (e.g. example.com/en/home, example.com/fr/home)
  • Domain routing (e.g. example.en, example.fr)

For example in React.js, the routing process can be implemented like this

proccess

With Next.js, there is built-in support for internationalized routing since v10.0.0

// next.config.js
module.exports = {
  i18n: {
    locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
    defaultLocale: 'en-US',
    domains: [
      {
        // Note: subdomains must be included in the domain value to be matched
        // e.g. www.example.com should be used if that is the expected hostname
        domain: 'example.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'example.fr',
        defaultLocale: 'fr',
      },
      {
        domain: 'example.nl',
        defaultLocale: 'nl-NL',
        // specify other locales that should be redirected to this domain
        locales: ['nl-BE'],
      },
    ],
  },
}
Enter fullscreen mode Exit fullscreen mode

Supports LTR and RTL text

For some languages, such as Arabic, the letters are arranged from right to left. To ensure that your application supports Right-To-Left (RTL) layout rendering for such languages, you need to add LTR or RTL support.

To add LTR or RTL support to the application, we will set the dir attribute on the body element dynamically in the index.html file.

You can also set the dir attribute on global components such as Header and Footer.

Here's an example of setting the dir attribute dynamically in the App component:

import React from 'react';
import { useTranslation } from 'react-i18next';
import './App.css';

function App()  {
    const { t, i18n } = useTranslation();
    document.body.dir = i18n.dir(); // return ltr or rtl of current language
    return (
      <div className="App">
        {t('welcome')}
      </div>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Here's an example of setting the dir attribute on a global Header component:

import { useEffect } from 'react';
import { useTranslation } from 'next-i18next';

const Header = () => {
  const { t, i18n } = useTranslation('common');
  const localeDir = i18n.dir(); // return ltr or rtl of current language

  useEffect(() => {
    document.querySelector('html')?.setAttribute('dir', localeDir);
  }, [localeDir]);

  return (
    <header>
                {...}
    </header>
  );
};

export default Header;
Enter fullscreen mode Exit fullscreen mode

Formatting

Starting from i18next version 21.3.0, you can take advantage of the built-in formatting functions based on the Intl API for the following formats:

  • Number
  • Currency
  • DateTime
  • RelativeTime
  • List

With these built-in formatting functions, you can easily format and localize various types of data in your application to match the user's preferred language and regional settings, improving the overall user experience of your application.

Here's an example of how you can use them:

// Translation JSON
{
  "number": "Number: {{val, number}}",
  "currency": "Currency: {{val, currency(USD)}}",
  "dateTime": "Date/Time: {{val, datetime}}",
  "relativeTime": "Relative Time: {{val, relativetime}}",
  "list": "List: {{val, list}}",
  "weekdays": [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday"
  ]
}

// Number
i18next.t('number', { val: 1000 }); // --> Number: 1,000
i18next.t('number', { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } }); // --> Number: 1,000.100

// Currency
i18next.t('currency', { val: 2000 }); // --> Currency: $2,000.00
i18next.t('currency', {
  val: 2000.12,
  currency: 'CAD',
  locale: 'fr-CA'
}); // --> Currency: 2 000,12 $ CA

// DateTime
i18next.t('dateTime', { val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) }); // --> Date/Time: 12/20/2012
i18next.t('dateTime', {
  val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)),
  formatParams: {
    val: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' },
  },
}); // --> Date/Time: Thursday, December 20, 2012

// RelativeTime
i18next.t('relativeTime', { val: 3 }); // --> Relative Time: in 3 days

// List
i18next.t('list', {
  val: i18next.t('weekdays', { returnObjects: true }),
}); // --> List: Monday, Tuesday, Wednesday, Thursday, and Friday
Enter fullscreen mode Exit fullscreen mode

Conclusion

Multilingual is a very important function for web or mobile applications nowadays, Users will come from all over the world and always ask for support for their language. Above is a guide to help you install multi-language for your application or website, You can implement it according to the instructions to automatically find the user's language, setup multi-language by domain or subpath, support LTR and RTL text, and finally format of number, currency, time, list…

If you have a difficult problem that you would like us to help you on, please feel free to submit a challenge request here.

Come be with us

We’d love to have you in our next chapter, by all means.

Follow our journey

Top comments (0)