Globally, There is no doubt that China’s overall Internet technology is second only to that of the United States and far ahead of all other countries. But if we had to pick one area where Chinese Internet companies don’t do well, internationalizing their products is one of them. Although we also have successful cases such as AliExpress and Tmall International, we have to say that most Chinese companies have not been able to reap the expected returns after choosing to go overseas. There are many reasons for this, but the lack of a common internationalization plan that can be platformized and productized has always been one of the very important reasons.

Once I naively thought that internationalization was nothing more than key-value matching of several JSON files, but after in-depth understanding of the internationalization requirements of some products, I realized that it is not so easy to make a good internationalization scheme.

Server-side internationalization

The first challenge of internationalization for front end engineers is that not all data can be internationalized at the front end. Common examples are goods or merchant information of e-commerce products, which have strong update requirements and need to be stored in the back-end database and updated through the product background. If a product is to be sold to the United States, Germany, France, Spain, Thailand, Indonesia, and the operation personnel only want to maintain a set of commodity information based on Chinese, then the internationalization of such data needs to be done on the server side.

We can certainly ask our backend engineers to help us return translations in different tables based on the domain name or content-language in the HTTP header of each request, but if you are a front-end engineer working towards a full-stack approach, you can try to serv the need for internationalization. Node.js is used to encapsulate an internationalized middleware that translates the return value of each request before it is returned.

Since each company’s technical architecture is different, we’ll skip over the technical details for the moment. However, it is important to know that the internationalization of the back-end interface is more critical and important than the internationalization of the front-end. It’s about putting our core data in a language that users can understand, and internationalization is more than just translating a few strings into a corresponding language.

What data needs to be internationalized

Before discussing specific internationalization solutions, we first need to clarify which data in the product needs to be internationalized.

In short, all words, statements, and nested data rendered at the front end need to be internationalized, excluding the data returned at the back end. At the code level, you need to make sure that there are no hard-coded strings and symbols in your code. Whether it’s as big as a block title or as small as a copy of a confirm button, all display information needs to be internationalized.

Key – value pair matching with multi-language support

Going back to the front end, let’s start with the simplest internationalization scenario.

For example, the “select” placeholder in the drop-down list input box, suppose we need to translate it into English and French at the same time, first we need to import two language files:

// en-US.json
{
  "web_select": "Select"
}

// fr-FR.json
{
  "web_select": "Sélectionner"
}Copy the code

It also provides a global localeutil.js that supports passing in the language type and key, and returns the corresponding translation.

Two best practices are provided here.

One is to store translations for different languages in separate JSON files. Although we could use nested data structures to store all translations in a locale.json, given that language files are typically loaded on demand in production environments, having separate JSON files for different languages is a better choice.

Second, the naming of key values in the same language is not recommended to adopt nested structures. Flat language files are more readable and can be evaluated more efficiently, and it is also possible to use underscores to distinguish between different levels, such as web_homepage_banner_title (platform-page-mode_value), which can be adjusted as needed.

Template matching and conditional operators

Now that we know the simplest scenario, let’s consider a more complex use case.

For scalability and other reasons, the backend design of the table structure does not store the price of the item directly as a string. Instead, the table is split into currency symbols (string) and prices (float). In the front-end display, we often encounter scenes that render it as a promotional phrase, such as:

Buy before September 1, 2017, only 100 yuan.Copy the code

For the internationalization scheme of time data, we temporarily click the table here, interested students can study the implementation of moment.js, which is also the representative of the front-end date internationalization at present.

Since $100 is a dynamic variable, our localeutil.js also needs to support incoming variables. A common call here could be:

localeGet(
  'en-US', // locale
  'web_merchantPage_item_promotion', // key
  { currency: item.currency, promoPrice: item.promoPrice }, // variable
);Copy the code

The template in the language file could be:

"web_merchantPage_item_promotion": "Before YYYY/MM/DD, purchase at {currency} {price}."Copy the code

Another common scenario is the singular and plural problem of English nouns. Here we choose to solve it through the thinking of conditional operators:

The offer will end in three days.Copy the code
"web_merchantPage_item_promotion_condition": "Promotion will end in {count, =1{# day} other{# days}}".Copy the code

Data internationalization

In addition to dates and currencies, numbers are another difficulty in internationalizing strings. Let’s look at the following example.

Alibaba invested $1.1 billion in Tokopedia, an Indonesian e-commerce site. Alibaba leadsThe $1.1b investment inIndonesia or 's Tokopedia.Copy the code

Here we need to translate “$1.1 billion” into “$1.1b”. In order to achieve this goal, we first need to establish the basic unit mapping corresponding to each language file, such as:

// zh-CN
"hundred": "Best"."thousand": "Thousand"."ten_thousand": "万"."million": "Millions"."hundred_million": "亿"."billion": "Billion",

// en-US
"hundred": "hundred"."thousand": "thousand"."thousand_abbr": "k"."million": "million"."million_abbr": "m"."billion": "billion"."billion_abbr": "b".Copy the code

Then we need to implement a function that converts a floating-point number into a pure number and a unit key of the language used:

function formatNum(num, isAbbr = false) {...return{number: number, // 1.1 unit: unit, //"billion_abbr"}}Copy the code

You can then call localeGet to get the translation:

localeGet(
  'en-US'.'news_tilte', {number: 1.1, unit: localeGet('billion_abbr'),
   currency: localeGet('currency_symbol'),},)Copy the code

The template in the language file is as follows:

// zh-CN
"news_tilte": "Alibaba invests in Indonesian e-commerce site Tokopedia {number}{unit}{currency}."

// en-US
"news_tilte: "Alibaba leads {currency}{number}{unit} investment in Indonesia's Tokopedia."Copy the code

In the whole process, we can abstract out two ways to solve the problem.

One is to break down and abstract out the underlying data, such as units.

Second, flexibly use templates and variables to adjust them to the translation that most conforms to local users’ reading habits.

Similar ideas can be extended to deal with dates, decimals, fractions, percentages, etc.

Internationalization schemes under React

As mentioned earlier, loading language files on demand is a necessary part of the internationalization scheme. In short, we can load the required language files in the project entry file, but for the sake of overall project unity, it is best to mount the language files in a branch of the global Redux Store so that each page can be easily evaluated by props. Furthermore, loading the language file at the Redux store level ensures that all pages use the same language file, and there is no need to pass in a specific locale value in the localeGet function later.

Example code is as follows:

import enUS from 'i18n/en-US.json';

function updateIntl(locale = 'en-US', file = enUS) {
  store.dispatch({
    type: 'UPDATE_INTL',
    payload: {
      locale,
      file,
    },
  });
}Copy the code

This makes it easy to mount the language files under a branch of the Redux Store:

const mapStateToProps = (state) => ({
  intl: state.intl,
});

// usage
localeGet(this.props.intl, 'web_select');

// with defaultValue to prevent undefined return
localeGet(this.props.intl, 'web_select'.'Select');Copy the code

other

In addition to the issues mentioned above, there are two things to be aware of in a production environment:

  • HTML escape characters
  • Unicode transcoding for special languages, such as Simplified Chinese, Traditional Chinese, Thai, etc

As mentioned in the beginning, internationalization is a general systematic engineering, and the above points are inevitably missing, and more best practices still need to be refined, summarized and precipitated in the actual development work.

summary

For any company that wants to expand the international market, product internationalization is a necessity. From a technical point of view, we can certainly settle for a Node.js middleware or a front-end NPM package to solve this problem generically. In fact, we can go one step further and turn internationalization into a full SASS product, such as OneSky.

The additional features OneSky offers, such as cloud storage, support for multiple file types, and real-time translation collaboration with multiple people, are each new territory, and this is where service productization becomes difficult.

For example, the internationalization scheme mentioned in the previous article assumes that all translation work is done and jSONized, and can be directly imported into the project for use, which is a mental blind spot that technical people often fall into. Translating large Numbers of language itself is a very difficult thing, how to make the non-technical backgrounds of the translators in all over the world to collaborate and convenient products in a production environment, real-time update language files, only in the internationalization service to make all these problems after a mature commercial products will be considered.

In fact, the current in each big Internet companies, technical service transition has become an unstoppable trend, many technical engineers who are beginning to realize that only a set of technical personnel to understand and use the solution is not enough, only the “profound” technical service transition, making a fool of, will be able to open a larger battlefield, Enabling technology to truly serve commercial products and generate greater value in the real world.