Skip to content

Blog


Overcoming Localization Challenges for International Expansions

June 9, 2021

|

Noah Zempsky

Ethan Myers

Siddhartha Kakarla

Winston Zhao

Bryan Huang

Xilin Liu

Le lancement d'un service dans un nouveau pays ou une nouvelle région nécessite l'adaptation de la plateforme aux coutumes et conventions locales. Bien qu'un grand nombre de ces ajustements puissent être effectués manuellement, la mise en place d'un processus rationalisé de localisation internationale permet de gagner du temps lors de l'expansion d'une application ou d'une plateforme dans plusieurs nouveaux pays. 

DoorDash, une entreprise américaine, a eu plus de facilité à étendre sa plateforme pour soutenir les services au Canada et en Australie, des pays anglophones dont le format d'adresse est similaire et dont la monnaie est le dollar, qu'à se lancer au Japon, qui s'est avéré beaucoup plus difficile.

Notre arrivée au Japon a nécessité de nombreux ajustements en matière d'internationalisation et de localisation, communément appelés i18n et l10n, principalement pour s'assurer que les devises, les adresses, les noms de personnes et les formats d'heure fonctionnent localement. Nous avons saisi cette opportunité non seulement pour adapter notre plateforme au Japon, mais aussi pour mettre en place des processus et des outils qui faciliteront l'expansion de DoorDash sur n'importe quel nouveau marché international à l'avenir.

Définir les quatre principaux défis 

En préparant la plateforme DoorDash pour le Japon, nous avons trouvé quatre thèmes principaux que nous devions localiser pour faciliter un lancement en douceur :

  • Monnaie: Affichage du yen japonais et calcul de sa valeur dans nos systèmes dorsaux. 
  • Adresses: Configuration des champs d'adresse japonais, qui sont fondamentalement différents de ceux des États-Unis.  
  • Honorifiques: Ajout de la possibilité d'afficher les titres honorifiques, un aspect culturel important de la société japonaise.  
  • Dates: Afficher l'année en premier dans la date, comme dans la plupart des pays du monde, mais différemment du format de date standard aux États-Unis.

Tous ces domaines étaient nécessaires pour assurer le succès du lancement au Japon, mais comme tout bon ingénieur, nous nous sommes attachés à généraliser ces solutions pour qu'elles puissent fonctionner dans n'importe quelle langue ou région d'un nouveau marché.  

Avant d'entrer dans les détails de ces quatre domaines, nous devons d'abord définir la façon dont nous abordons les paramètres locaux dans nos applications et sur le web. Les éléments clés d'une locale sont la langue et la région, qui sont essentielles pour la localisation. Par exemple, la locale en-US signifie que la langue est l'anglais (en) et que la région est les États-Unis (US). Le choix de la langue, de la région ou des deux dépend du marché ou du pays. 

Calculer une devise étrangère

La première étape de tout lancement international consiste à s'assurer que les clients peuvent voir les prix et payer dans la monnaie locale. Les défis auxquels nous avons dû faire face ici se résumaient principalement à s'assurer que la devise était affichée correctement et que nous pouvions réellement représenter la devise dans nos systèmes, en la distinguant du dollar. 

Modélisation de la monnaie

La modélisation de la monnaie est un système en deux parties, la première moitié étant le type de monnaie et la seconde le montant de cette monnaie. Par exemple, lorsqu'on affiche 3,03 $, le type de monnaie est en fait ambigu car de nombreux pays utilisent le $ pour représenter la valeur monétaire. Il est important de connaître le type exact de monnaie affichée et sa valeur relative.

Il est relativement facile de modéliser le type de monnaie. L'Organisation internationale de normalisation (ISO) a créé des codes de devises, comme indiqué dans la norme ISO 4217. Cette norme permet aux systèmes de DoorDash de communiquer facilement le type de monnaie entre eux.

Pour représenter le montant d'une devise spécifique, on peut par exemple considérer 3,03 $ et le stocker simplement comme un nombre décimal ou à virgule flottante. Cette stratégie semble raisonnable, mais lorsque cette information passe d'un type de langage à l'autre, comme Swift, Java et Python, elle peut se heurter à des bogues de conversion.

Avec ces différents langages de programmation utilisant la monnaie, DoorDash a opté pour la solution de représenter la monnaie en termes de montant unitaire et de code de devise. Le montant unitaire représente le montant dans la plus petite unité de cette devise. Dans notre exemple de 3,03 $, le montant unitaire = 303 et la devise = USD. Ce format nous permet de traiter les devises comme des nombres entiers, qui ne posent aucun problème de précision. Le montant monétaire peut être converti en un nombre décimal grâce à l'équation suivante :

Nombre décimal = montant de l'unité / (10 ^ précision décimale)

Le dollar américain (USD) est représenté avec une précision de deux décimales. Cela signifie que la valeur la plus basse que peut avoir le dollar est 0,01 $. Toutefois, le niveau de précision dépend de la monnaie. Le yen japonais (¥) a une précision de zéro décimale, tandis que le dinar jordanien a une précision de trois décimales. Cela signifie que la plus petite quantité de yens que l'on puisse avoir est de 1¥, et la plus petite quantité de dinar jordanien est de 0,001.  

Affichage précis des devises

La manière d'afficher les devises dépend de la région dans laquelle elles doivent être affichées. La région est le terme que nous utilisons pour décrire un lieu en combinant sa langue (par exemple l'anglais) et sa région (par exemple le Japon). Les formats d'écriture des devises varient d'une localité à l'autre. Certains font suivre le chiffre du symbole de la monnaie, tandis que d'autres utilisent une virgule au lieu d'un point pour séparer le chiffre. Par exemple, en Espagne, ce que les Américains écrivent 2,22 € (deux euros et vingt-deux cents), les Espagnols l'écrivent 2,22 €. Ce que les Américains et les Japonais écrivent comme ¥120 (cent vingt yens japonais), les Britanniques l'écrivent comme JP¥120.  

Il existe différentes bibliothèques pour chaque langage de programmation qui peuvent être utilisées pour aider à afficher les devises avec précision, comme NumberFormatter pour Swift ou ICU pour Java. En passant la locale à ces bibliothèques, on obtient le format correct. Cependant, la création et l'utilisation de ces formateurs peuvent être coûteuses en termes de programmation. Chez DoorDash, nous atténuons cette utilisation de ressources en calculant la plupart des chaînes d'affichage monétaires sur notre backend et en les transmettant au client. Cette méthode permet à nos clients de fonctionner plus rapidement et dissuade les équipes de construire des calculs côté client, qui peuvent être plus difficiles à dépanner que les calculs monétaires du backend.

L'exemple de code suivant montre comment créer une représentation sous forme de chaîne de caractères d'une valeur monétaire dans iOS :

/// Helper method to correctly calculate the value using the number of decimal places 
private func stringUsing(formatter: NumberFormatter, money: Money) -> String {        let factor = pow(10.0, Double(money.decimalPlaces))        let value = Double(money.unitAmount) / factor        return formatter.string(from: NSNumber(value: value))!    }

DoorDash utilise principalement Kotlin pour son backend. Cet exemple de code montre comment créer une représentation sous forme de chaîne de caractères d'une valeur monétaire en Kotlin :

fun formatMoney(
    unitAmount: Int,
    currencyIso: String,
    locale: Locale,
): String {
    val currency = Currency.getInstance(currencyIso)
    val formatter = getCurrencyInstance(ULocale.forLocale(locale))
        .apply {
            this.currency = currency
        }
    val moneyAmountWithDecimalPlaces = unitAmount.toDouble() / 10.0.pow(currency.defaultFractionDigits.toDouble())
    return formatter.format(moneyAmountWithDecimalPlaces)
}

Traduire les adresses pour tous les utilisateurs 

Avant le lancement au Japon, tous les pays pris en charge par DoorDash utilisaient l'alphabet romain et avaient des formats d'adresse similaires. Cela signifiait que nous étions en mesure d'afficher les mêmes chaînes de caractères pour tous les utilisateurs : le consommateur qui commande la nourriture, le Dasher (notre terme pour désigner un livreur) qui livre la nourriture, et nos agents d'assistance qui nous aident en cas de problème. L'ajout du Japon à notre plateforme a posé un certain nombre de problèmes, le plus évident étant que les Japonais utilisent le système d'écriture Kanji au lieu de l'alphabet romain.(Hiragana et Katakana sont également utilisés pour les adresses, mais nous nous sommes concentrés sur le Kanji pour les services DoorDash). Bien que de nombreux utilisateurs japonais soient capables de lire les adresses en alphabet romain, le Kanji est la forme locale préférée.

Pour résoudre ce problème, nous avons dû revoir la façon dont nous présentions les adresses. Auparavant, si un consommateur saisissait "〒027-0052 岩手県宮古市宮町1丁目1-38" comme adresse, c'est ce qui s'affichait pour toutes les parties. Maintenant, nous traduisons toutes les adresses Kanji en Romaji (la romanisation du japonais). Si la langue préférée d'un client est le japonais, nous retraduisons l'adresse en kanji avant de la renvoyer. Cela nous permet d'afficher des adresses à différents clients dans la langue qu'ils ont associée à leur compte.

Affichage des noms dans différentes langues

Nous utilisons trois formats différents pour afficher les noms d'utilisateurs dans les applications DoorDash à travers nos plateformes mobiles et web : formalName, informalName, formalNameAbbreviated. Nous choisissons le format à afficher en fonction du contexte ou de l'écran.

Écrans de l'application DoorDash côte à côte
Figure 1 : Notre localisation nous permet d'afficher différentes formes d'un nom en fonction du contexte ou de l'écran.

Pour givenName = "Sid" et familyName = "Kakarla", nous pourrions enregistrer :

Nom informel"Sid
nom formel"Sid Kakarla
Nom officielAbrégé"Sid K

Notez que les pays occidentaux ont tendance à utiliser la terminologie du prénom et du nom de famille, alors que la terminologie du prénom et du nom de famille s'applique partout dans le monde. 

Ces formats fonctionnent bien pour les marchés dans lesquels DoorDash opère actuellement et la logique pour générer ces trois formats en utilisant les prénoms et les noms de famille est répartie dans nos services backend, nos clients et nos BFF, parmi d'autres systèmes. 

Au Japon, les noms commencent par le nom de famille suivi du prénom, dans l'ordre inverse de la plupart des pays occidentaux. Il est culturellement inapproprié de s'adresser à quelqu'un en utilisant son prénom, même dans une application. En outre, des termes honorifiques tels que sama (様) et san (さん) sont couramment ajoutés en tant que suffixes aux noms. Une autre différence majeure est l'absence d'espace entre le nom de famille et le prénom. L'adaptation de notre plateforme à ces différences a nécessité un changement de logique.

Application des coutumes japonaises en matière de dénomination 

An honorific is a title that conveys esteem, courtesy, or respect for position or rank when used in addressing or referring to a person. In Japan, san (さん) is the most commonplace honorific and is a title of respect typically used between equals of any age. Although the closest analog in English are the honorifics Mr., Miss, Ms., and Mrs., san is almost universally added to a person's name.

Sama (様) est une version plus respectueuse pour les personnes d'un rang plus élevé que le sien. Les entités divines, les invités, les clients et parfois les personnes que l'on admire sont des exemples d'usages appropriés.

Nous avons décidé d'utiliser la langue comme facteur déterminant pour le choix de l'adjectif honorifique à utiliser. Imaginons qu'un utilisateur japonais se rende aux États-Unis. Si la langue de son téléphone est réglée sur le japonais, il doit s'attendre à voir les conventions de dénomination japonaises. De même, si un utilisateur américain se rend au Japon et que la langue de son appareil est réglée sur l'anglais, il doit s'attendre à voir la version anglaise des noms. Pour prendre en charge de tels cas d'utilisation, c'est la langue des paramètres régionaux de l'utilisateur qui est utilisée, et non la région.

                                 Pour givenName = "Sid" et familyName = "Kakarla", nous enregistrons maintenant :

FormatLangue anglaise (en-JP, en-US)Langue japonaise (ja-US, ja-JP)
Nom informel"Sid"Kakarlaさん"
nom formel"Sid Kakarla"KakarlaSid様"
Nom officielAbrégé"Sid K"Kakarla様"

Nous affichons également des champs de noms individuels dans la page du compte pour ajouter/mettre à jour les noms de famille et les prénoms respectifs.

Page du compte DoorDash en anglais

Pour le cas d'utilisation japonais, ces champs sont modifiés pour afficher le nom de famille avant le prénom.

Page du compte DoorDash en japonais

Mise en œuvre des conventions de nommage 

Comme nous l'avons mentionné plus haut, notre implémentation du nommage est dispersée dans différents systèmes, ce qui rend difficile sa mise à jour pour les futurs marchés potentiels. Pour systématiser notre approche, nous avons créé des bibliothèques pour toutes les plateformes que nous utilisons actuellement, y compris Kotlin, Python et JavaScript. L'objectif est de mettre en œuvre la logique dans une seule source de vérité, la bibliothèque, au lieu d'avoir le même code mis en œuvre à plusieurs reprises en tant qu'utilitaires spécifiques à un service.

La mise en œuvre a été assez simple, car nous n'avons eu à prendre en charge que deux conventions de dénomination pour notre lancement au Japon, tout en la rendant extensible à l'avenir. L'extrait de code ci-dessous montre la logique selon laquelle, si la langue locale de l'utilisateur est le japonais, nous générons les noms en conséquence.  

object NameLocalizer {
    fun getLocalizedNames(userInfo: UserInfo, locale: Locale): LocalizedNames {
        return when (locale.language) {
            Locale.JAPAN.language -> JapaneseNameLocalizer.getLocalizedNames(userInfo)
            else -> DefaultNameLocalizer.getLocalizedNames(userInfo)
        }
    }
}

Normalisation des formats de date et d'heure  

Outre la localisation des devises, des noms et des adresses, comme indiqué ci-dessus, le dernier domaine sur lequel nous nous sommes concentrés est celui de la date et de l'heure. Nous utiliserons dorénavant le terme "datetimes" pour désigner ce domaine. Les dates sont des informations importantes qui indiquent un moment précis pour un événement ou une action. Par exemple, l'application consommateur peut afficher la phrase "Votre livraison DoorDash a été livrée le 14/04/2021 à 16h32". Comme les dates sont représentées différemment selon les marchés, nous devons localiser cet affichage en fonction du lieu de résidence de l'utilisateur. Voici quelques exemples :

Locale : en-USLocale : ja-JP
2/22/212021/02/23
15:30 HEURES午後3:30
23 février 2021 à 15:30:00 PST2021年2月23日 15:30:00 GMT-8

Avant notre travail sur l'internationalisation, nous utilisions simplement des bibliothèques de datetime spécifiques à la plateforme pour le formatage, comme la bibliothèque de datetime de Python, ce qui fonctionnait bien pour les marchés auxquels nous nous adressions. Cependant, lorsque nous avons commencé à préparer notre lancement au Japon, nous avons réalisé que la solution actuelle n'était pas évolutive. Nous avons donc créé des bibliothèques Python, Kotlin et JavaScript et les avons intégrées à notre plateforme afin que les services correspondants puissent les adopter et localiser les dates. Ces bibliothèques réduisent la charge des services, qui n'ont pas besoin d'utiliser une logique personnalisée, et offrent une expérience utilisateur cohérente aux utilisateurs de DoorDash. 

Les bibliothèques que nous avons créées pour les dates sont basées sur les bibliothèques standard et open source ICU4J, PyICU et INTL (les mêmes bibliothèques que nous avons utilisées pour la localisation des devises) et permettent également de générer des formats de date courts, moyens, longs et complets. La bibliothèque prend en charge trois actions pour formater les dates, les heures et les durées. Voici un exemple de mise en œuvre du formatage d'une date dans la bibliothèque Kotlin :

 /**
      *
      * Formats a date time object as a date time string.
      *
      * Params:
      * dt – The date time object (with a time zone) to be formatted.
      * locale - The locale to display the time string in.
      * fmt - The format to display the time string in
      *
      * Returns: A string with the time formatted as specified.
      *
      * SHORT = '2/23/21, 3:30 PM'
      * MEDIUM = 'Feb 23, 2021, 3:30:00 PM'
      * LONG = 'February 23, 2021 at 3:30:00 PM PST'
      * FULL = 'Tuesday, February 23, 2021 at 3:30:00 PM Pacific Standard Time
      *
      */
     fun formatDateTime(
         dt: ZonedDateTime,
         locale: Locale,
         fmt: DateTimeFormat
     ): String {
         var df = when (fmt) {
             DateTimeFormat.SHORT -> DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale)
             DateTimeFormat.MEDIUM -> DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale)
             DateTimeFormat.LONG -> DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale)
             DateTimeFormat.FULL -> DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale)
         }
         df.timeZone = TimeZone.getFrozenTimeZone(dt.zone.id)
         return df.format(dt.toEpochSecond()*1000)
     } 

Codage de l'internationalisation et de la localisation dans notre plateforme

D'abord et avant tout, pour tout lancement international, l'élément le plus important est de s'assurer que toutes les interfaces utilisateur, mobiles et web, se sentent naturelles en localisant le produit avec des traductions précises. Pour le lancement au Japon, nous avons eu beaucoup de chance, car nous avons pu tirer parti des nombreux outils de traduction que nous avions déjà utilisés pour le lancement à Porto Rico, au Canada et en Australie. 

Service de traduction

Nous avons développé un service de traduction qui s'intègre à notre fournisseur de traduction tiers pour permettre à nos développeurs de créer et de localiser rapidement de nouvelles fonctionnalités. 

Toutes nos applications sont intégrées à ce service de traduction et, lors du développement d'une nouvelle fonctionnalité, un ingénieur n'a qu'à créer son propre service de traduction. Git avec un i18n_ et créer une demande d'extraction. Lorsque le service de traduction reçoit une demande d'extraction avec le préfixe i18n_ il effectuera un commit et appliquera les traductions. 

Les nouvelles chaînes étant ajoutées sans effort à la plateforme DoorDash, nous avons pu lancer le Japon et développer de nouvelles fonctionnalités dans notre place de marché, Dasher et nos systèmes logistiques.

Identifier la langue locale de l'utilisateur et obtenir les chaînes localisées

Chacun de nos clients, qu'il s'agisse d'iOS, d'Android ou d'Internet, a besoin d'une approche différente pour déterminer la langue locale de l'utilisateur afin de lui servir les chaînes de caractères correctes lors du chargement initial. 

Sur le web, nous disposons d'une bibliothèque de localisation qui utilise i18next, un cadre d'internationalisation open source, pour définir les paramètres locaux d'un utilisateur en fonction de plusieurs facteurs par ordre de priorité décroissant, en commençant par : 

  1. URL du navigateur (sur DoorDash.com, nous utilisons un préfixe dans l'URL, par exemple DoorDash.com/ja-JP)
  2. Query param (paramètre d'interrogation fixé à un paramètre local pris en charge, tel que ?intl=ja-JP)
  3. Cookies 
  4. Propriété du navigateur localStorage
  5. Le navigateur (nous nous basons sur la langue du navigateur de l'utilisateur pour déterminer sa localisation si tous les autres facteurs ne nous aident pas à identifier la localisation ; cela se produit généralement lors du chargement initial pour un invité qui navigue directement vers doordash.com).

Sur le web, nous stockons nos chaînes côté client dans un fichier JSON et nous initialisons notre bibliothèque de localisation lors du chargement initial de l'application, qui utilise les facteurs susmentionnés pour déterminer les paramètres régionaux de l'utilisateur. Cet exemple de code montre nos chaînes de caractères côté client :

 const i18n = createI18nInstance({
     // english us
     englishStrings: en_us,
     // french canada
     frenchStrings: fr_ca,
     // spanish mexico
     spanishMexicoStrings: es_mx,
     // English Canada
     englishCanadaStrings: en_ca,
     // English Australia
     englishAustraliaStrings: en_au,
     // Japanese Japan
     japaneseJapanStrings: ja_jp,
 }) 

Une fois que notre bibliothèque de localisation est initialisée, nous pouvons utiliser l'instance de la bibliothèque de localisation dans notre application et obtenir la chaîne de caractères correcte pour la langue locale de chaque utilisateur, comme le montre cet exemple de code :

i18n.t(‘exampleString_test’)

L'appel de la fonction ci-dessus renvoie la chaîne localisée en fonction de la langue de l'utilisateur.

Dans cet exemple, exampleString_test fait référence à la chaîne qui est une clé dans notre JSON ; nous avons une version localisée de exampleString_test pour chaque localité que nous prenons en charge. 

Localisation pour iOS

Pour nos applications iOS, nous définissons toutes nos chaînes à l'aide de fichiers Localizable.string et utilisons SwiftGen, un générateur de code Swift open source, pour convertir ces fichiers de chaînes en enums que nous référençons dans nos bases de code iOS, comme le montre la figure 2 ci-dessous :

Fichiers de chaînes localisables dans un IDE
Figure 2 : Les fichiers Localizable.string sont un moyen standard de localiser les applications iOS.

Localisation pour Android

Pour nos applications Android, nous utilisons la classe intégrée Locale.getDefault() pour obtenir la langue de l'utilisateur, qui est basée sur les paramètres de langue et de région de l'appareil. 

Nous stockons toutes nos chaînes de caractères sous forme de fichiers XML en suivant les meilleures pratiques définies dans la documentation sur la localisation d'Android. Cela nous permet d'utiliser getString(R.id.EXAMPLE_STRING) pour obtenir automatiquement la chaîne correcte en fonction de la locale de l'utilisateur.

Conclusion

Nous avons parcouru un long chemin en tant qu'entreprise depuis notre lancement au Canada et en Australie, et comme nous l'avons indiqué, le Japon a posé des défis importants que l'on ne retrouve pas dans les pays susmentionnés. Notre lancement au Japon nous a incités à mettre au point des outils et à automatiser le processus afin de le rendre plus facile à l'avenir.

Grâce à cet effort, de larges sections de notre base de code ont été localisées pour nous permettre de nous adapter à n'importe quelle langue et à n'importe quel endroit sans avoir à modifier le code. Nous pouvons prendre en charge n'importe quelle devise, et si nous voulons prendre en charge une langue supplémentaire, il suffit d'ajouter les traductions à notre système. 

Les méthodes décrites ci-dessus peuvent être utilisées par toute entreprise prête à proposer ses applications ou ses services dans de nouveaux pays. Une leçon importante que nous avons apprise, et dont d'autres ingénieurs devraient prendre note, est la nécessité d'un cadrage approfondi dès le début. Les ressources de notre équipe étaient limitées lorsque nous avons entrepris cette tâche, ce qui a restreint notre capacité à comprendre le problème en profondeur. En conséquence, nous avons rencontré des problèmes importants qui nous ont obligés à nous dépêcher à la dernière minute. Cette leçon s'applique à tous les projets : il faut comprendre le problème et toutes les tâches nécessaires pour le résoudre avant de commencer à coder.

Photo par Erik Eastman sur Unsplash.

À propos des auteurs

  • Noah Zempsky

  • Ethan Myers

  • Siddhartha Kakarla

  • Winston Zhao

  • Bryan Huang

  • Xilin Liu

Emplois connexes

Localisation
San Francisco, CA ; Mountain View, CA ; New York, NY ; Seattle, WA
Département
Ingénierie
Localisation
San Francisco, CA ; Sunnyvale, CA
Département
Ingénierie
Localisation
San Francisco, CA ; Sunnyvale, CA ; Seattle, WA
Département
Ingénierie
Localisation
Pune, Inde
Département
Ingénierie
Localisation
San Francisco, CA ; Seattle, WA ; Sunnyvale, CA
Département
Ingénierie