Comment formater des listes de mesures avec unités ?

Affichez plusieurs mesures comme 5 km, 10 km, 15 km avec un formatage de liste adapté à la locale en utilisant les API Intl de JavaScript

Introduction

Les applications qui affichent des mesures doivent souvent montrer plusieurs valeurs ensemble. Une application de fitness pourrait afficher des temps intermédiaires comme "5 km, 10 km, 15 km". Une application météo pourrait présenter les températures de la semaine comme "20°C, 22°C, 25°C, 23°C". Une recette pourrait énumérer les quantités d'ingrédients comme "2 tasses, 1 cuillère à soupe, 3 cuillères à café".

Ces listes combinent deux défis d'internationalisation. Premièrement, chaque mesure nécessite un formatage d'unité adapté à la locale. Deuxièmement, la liste elle-même nécessite une ponctuation et des séparateurs appropriés pour la langue cible. L'anglais utilise des virgules et parfois des conjonctions comme "and". D'autres langues utilisent différents séparateurs et suivent des règles grammaticales différentes.

JavaScript fournit deux API pour résoudre ce problème. Intl.NumberFormat formate les mesures individuelles avec des unités. Intl.ListFormat combine plusieurs valeurs en une liste grammaticalement correcte. Cette leçon explique comment utiliser ces deux API ensemble pour formater des listes de mesures qui correspondent aux attentes des utilisateurs dans n'importe quelle locale.

Les listes de mesures nécessitent deux étapes de formatage

Lorsque vous formatez une liste de mesures, vous ne pouvez ignorer aucune étape de formatage. Si vous formatez la liste sans formater les mesures, vous obtenez des séparateurs adaptés à la locale mais un affichage d'unité incorrect. Si vous formatez les mesures sans formater la liste, vous obtenez des unités correctes mais des séparateurs incorrects.

const distances = [5, 10, 15];

// Incorrect : liste formatée mais pas les mesures
console.log(distances.join(', '));
// Résultat : "5, 10, 15" (unités manquantes)

// Incorrect : mesures formatées mais pas la liste
const formatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});
console.log(distances.map(d => formatter.format(d)).join(', '));
// Résultat : "5 km, 10 km, 15 km" (virgule codée en dur peut être incorrecte pour certaines locales)

L'approche correcte consiste à formater d'abord les mesures, puis à formater le tableau de chaînes résultant en une liste.

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const formattedMeasurements = distances.map(d => numberFormatter.format(d));
// Résultat : ["5 km", "10 km", "15 km"]

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedMeasurements));
// Résultat : "5 km, 10 km, 15 km"

Ce modèle fonctionne pour tout type de mesure et toute locale. Vous formatez chaque mesure avec son unité, puis vous formatez le tableau de chaînes formatées en une liste.

Utiliser le type unit pour les listes de mesures

Le constructeur Intl.ListFormat accepte une option type qui contrôle la façon dont les éléments de la liste sont combinés. L'option type: 'unit' formate les listes selon les conventions pour les données techniques et scientifiques.

const measurements = ['5 km', '10 km', '15 km'];

const unitList = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(unitList.format(measurements));
// Output: "5 km, 10 km, 15 km"

Les listes avec type: 'unit' omettent les conjonctions comme "et" ou "ou". Elles utilisent des séparateurs simples entre les éléments. Cela correspond à la façon dont les mesures sont généralement écrites dans les contextes techniques.

Comparez cela à type: 'conjunction', qui ajoute "et" avant le dernier élément.

const measurements = ['5 km', '10 km', '15 km'];

const conjunctionList = new Intl.ListFormat('en-US', {
  type: 'conjunction'
});

console.log(conjunctionList.format(measurements));
// Output: "5 km, 10 km, and 15 km"

La forme de conjonction se lit naturellement dans la prose mais semble incorrecte dans les contextes techniques. Lors de l'affichage de plusieurs mesures, utilisez type: 'unit' pour suivre les conventions standard de l'écriture scientifique et technique.

Formater les mesures de distance dans les listes

Les mesures de distance utilisent des identificateurs d'unité comme kilometer, meter, mile et foot. Après avoir formaté chaque distance avec son unité, combinez-les dans une liste.

const distances = [5, 10, 15, 20];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "5 km, 10 km, 15 km, 20 km"

Le même modèle fonctionne pour les miles.

const distances = [3, 6, 9];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'mile'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "3 mi, 6 mi, 9 mi"

Vous pouvez formater les distances avec des décimales en définissant des options de formatage de nombres.

const distances = [5.2, 10.7, 15.3];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  maximumFractionDigits: 1
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "5.2 km, 10.7 km, 15.3 km"

Le formateur de nombres gère l'arrondi et les décimales avant que le formateur de liste ne combine les valeurs.

Formater les mesures de poids dans les listes

Les mesures de poids suivent le même modèle en utilisant des identificateurs d'unité comme kilogram, pound, ounce, et gram.

const weights = [50, 75, 100];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilogram'
});

const formattedWeights = weights.map(w => numberFormatter.format(w));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedWeights));
// Output: "50 kg, 75 kg, 100 kg"

Vous pouvez afficher le poids en livres à la place.

const weights = [110, 165, 220];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'pound'
});

const formattedWeights = weights.map(w => numberFormatter.format(w));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedWeights));
// Output: "110 lb, 165 lb, 220 lb"

Le formateur de nombres utilise automatiquement l'abréviation correcte pour chaque unité.

Formater les mesures de température dans les listes

Les mesures de température utilisent des identificateurs d'unité comme celsius et fahrenheit.

const temperatures = [20, 22, 25, 23, 21];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'celsius'
});

const formattedTemperatures = temperatures.map(t => numberFormatter.format(t));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedTemperatures));
// Output: "20°C, 22°C, 25°C, 23°C, 21°C"

Les formateurs de température incluent automatiquement les symboles de degré dans la sortie.

Fahrenheit fonctionne de la même manière.

const temperatures = [68, 72, 77, 73, 70];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'fahrenheit'
});

const formattedTemperatures = temperatures.map(t => numberFormatter.format(t));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedTemperatures));
// Output: "68°F, 72°F, 77°F, 73°F, 70°F"

Le modèle reste identique pour différents types de mesures. Seul l'identificateur d'unité change.

Formater les mesures de volume dans les listes

Les mesures de volume utilisent des identificateurs d'unité comme liter, gallon, milliliter, et fluid-ounce.

const volumes = [1, 2, 3];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'liter'
});

const formattedVolumes = volumes.map(v => numberFormatter.format(v));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedVolumes));
// Output: "1 L, 2 L, 3 L"

Les mesures de volume fonctionnent avec des valeurs décimales.

const volumes = [0.5, 1.5, 2.5];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'liter',
  maximumFractionDigits: 1
});

const formattedVolumes = volumes.map(v => numberFormatter.format(v));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedVolumes));
// Output: "0.5 L, 1.5 L, 2.5 L"

Le formateur de nombres gère la précision décimale avant que le formateur de liste ne traite les valeurs.

Formater les mesures de vitesse dans les listes

Les mesures de vitesse utilisent des unités composées comme kilometer-per-hour et mile-per-hour.

const speeds = [50, 75, 100];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer-per-hour'
});

const formattedSpeeds = speeds.map(s => numberFormatter.format(s));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedSpeeds));
// Output: "50 km/h, 75 km/h, 100 km/h"

Les miles par heure fonctionnent de la même manière.

const speeds = [30, 45, 60];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'mile-per-hour'
});

const formattedSpeeds = speeds.map(s => numberFormatter.format(s));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedSpeeds));
// Output: "30 mph, 45 mph, 60 mph"

Les unités composées se formatent automatiquement avec les abréviations et séparateurs corrects.

La locale détermine le format du séparateur de liste

Le paramètre de locale contrôle comment les éléments de liste sont séparés et ponctués. Différentes langues utilisent différentes conventions pour le formatage des listes.

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const enList = new Intl.ListFormat('en-US', {
  type: 'unit'
});

const frList = new Intl.ListFormat('fr-FR', {
  type: 'unit'
});

const deList = new Intl.ListFormat('de-DE', {
  type: 'unit'
});

console.log(enList.format(formattedDistances));
// Output: "5 km, 10 km, 15 km"

console.log(frList.format(formattedDistances));
// Output: "5 km, 10 km, 15 km"

console.log(deList.format(formattedDistances));
// Output: "5 km, 10 km, 15 km"

Bien que les abréviations de kilomètre restent similaires dans ces locales, les conventions d'espacement et de séparateur peuvent varier. L'API Intl.ListFormat gère automatiquement ces règles de formatage spécifiques à la locale.

Certaines langues utilisent différents séparateurs ou modèles de ponctuation pour les listes. L'API garantit que vos listes suivent les conventions correctes pour chaque locale sans que vous ayez besoin de connaître les règles spécifiques.

Faire correspondre la locale des nombres à la locale des listes

Lors du formatage des listes de mesures, utilisez la même locale pour le formateur de nombres et le formateur de listes. Cela garantit un formatage cohérent dans tout le résultat.

const distances = [1000, 2000, 3000];

const locale = 'de-DE';

const numberFormatter = new Intl.NumberFormat(locale, {
  style: 'unit',
  unit: 'meter'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat(locale, {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "1.000 m, 2.000 m, 3.000 m"

Le formatage allemand utilise des points comme séparateurs de milliers. Le formateur de nombres et le formateur de listes utilisent tous deux les conventions allemandes car ils partagent la même locale.

L'utilisation de différentes locales pour le formatage des nombres et des listes crée un résultat incohérent.

const distances = [1000, 2000, 3000];

const numberFormatter = new Intl.NumberFormat('de-DE', {
  style: 'unit',
  unit: 'meter'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "1.000 m, 2.000 m, 3.000 m"

Cela crée un formatage mixte où les nombres utilisent les conventions allemandes mais la liste utilise les conventions anglaises. Utilisez toujours la même locale pour les deux formateurs.

Contrôler le style d'affichage des listes

L'option style contrôle le niveau de verbosité du formatage de liste. Cette option accepte trois valeurs : "long", "short" et "narrow".

const measurements = ['5 km', '10 km', '15 km'];

const longList = new Intl.ListFormat('en-US', {
  type: 'unit',
  style: 'long'
});

const shortList = new Intl.ListFormat('en-US', {
  type: 'unit',
  style: 'short'
});

const narrowList = new Intl.ListFormat('en-US', {
  type: 'unit',
  style: 'narrow'
});

console.log(longList.format(measurements));
// Output: "5 km, 10 km, 15 km"

console.log(shortList.format(measurements));
// Output: "5 km, 10 km, 15 km"

console.log(narrowList.format(measurements));
// Output: "5 km 10 km 15 km"

Les styles long et short produisent des résultats similaires pour les listes d'unités en anglais. Le style narrow utilise un espacement minimal et omet les séparateurs entre les éléments.

Différentes locales montrent plus de variation entre les styles. La locale détermine le formatage exact pour chaque niveau de style.

Combiner avec des noms d'unités longs

Vous pouvez formater les mesures avec des noms d'unités complets au lieu d'abréviations en définissant unitDisplay: 'long' dans le formateur de nombres.

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "5 kilometers, 10 kilometers, 15 kilometers"

Le formateur de nombres gère automatiquement les formes singulières et plurielles. Le formateur de liste combine les chaînes formatées, qu'elles utilisent des abréviations ou des noms complets.

const distances = [1, 5];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "1 kilometer, 5 kilometers"

Le formateur de nombres utilise "kilometer" pour 1 et "kilometers" pour 5. Le formateur de liste les combine avec les séparateurs appropriés.

Réutiliser les formateurs pour de meilleures performances

La création d'instances Intl.NumberFormat et Intl.ListFormat implique le chargement de données locales et le traitement d'options. Lorsque vous formatez plusieurs listes de mesures, créez les formateurs une seule fois et réutilisez-les.

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

const distanceLists = [
  [5, 10, 15],
  [20, 25, 30],
  [35, 40, 45]
];

distanceLists.forEach(distances => {
  const formattedDistances = distances.map(d => numberFormatter.format(d));
  console.log(listFormatter.format(formattedDistances));
});
// Output:
// "5 km, 10 km, 15 km"
// "20 km, 25 km, 30 km"
// "35 km, 40 km, 45 km"

Ce modèle crée chaque formateur une seule fois et l'utilise plusieurs fois. La différence de performance devient significative lors du formatage de nombreuses listes.

Créer une fonction de formatage réutilisable

Vous pouvez encapsuler le modèle de formatage en deux étapes dans une fonction réutilisable.

function formatMeasurementList(values, locale, unit) {
  const numberFormatter = new Intl.NumberFormat(locale, {
    style: 'unit',
    unit: unit
  });

  const formattedValues = values.map(v => numberFormatter.format(v));

  const listFormatter = new Intl.ListFormat(locale, {
    type: 'unit'
  });

  return listFormatter.format(formattedValues);
}

console.log(formatMeasurementList([5, 10, 15], 'en-US', 'kilometer'));
// Résultat : "5 km, 10 km, 15 km"

console.log(formatMeasurementList([50, 75, 100], 'en-US', 'kilogram'));
// Résultat : "50 kg, 75 kg, 100 kg"

console.log(formatMeasurementList([20, 22, 25], 'en-US', 'celsius'));
// Résultat : "20°C, 22°C, 25°C"

Cette fonction gère tout type de mesure et de locale. Vous pouvez l'étendre pour accepter des options de formatage supplémentaires.

function formatMeasurementList(values, locale, unit, options = {}) {
  const numberFormatter = new Intl.NumberFormat(locale, {
    style: 'unit',
    unit: unit,
    ...options
  });

  const formattedValues = values.map(v => numberFormatter.format(v));

  const listFormatter = new Intl.ListFormat(locale, {
    type: 'unit'
  });

  return listFormatter.format(formattedValues);
}

console.log(formatMeasurementList(
  [5.123, 10.789, 15.456],
  'en-US',
  'kilometer',
  { maximumFractionDigits: 1 }
));
// Résultat : "5.1 km, 10.8 km, 15.5 km"

console.log(formatMeasurementList(
  [1, 5, 10],
  'en-US',
  'kilometer',
  { unitDisplay: 'long' }
));
// Résultat : "1 kilometer, 5 kilometers, 10 kilometers"

La fonction transmet des options supplémentaires au formateur de nombres, permettant de contrôler les décimales, l'affichage des unités et d'autres paramètres de formatage.

Formater les listes pour la locale de l'utilisateur

Au lieu de coder en dur une locale spécifique, vous pouvez utiliser les préférences linguistiques du navigateur de l'utilisateur. La propriété navigator.language renvoie la locale préférée de l'utilisateur.

const userLocale = navigator.language;

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat(userLocale, {
  style: 'unit',
  unit: 'kilometer'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat(userLocale, {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Le résultat varie selon la locale de l'utilisateur

Cette approche affiche les listes de mesures selon les attentes de formatage de chaque utilisateur. Différents utilisateurs voient les mêmes données formatées selon les conventions de leur locale.

Afficher des listes de mesures dans les applications

Vous pouvez utiliser ce modèle partout où vous affichez plusieurs mesures aux utilisateurs. Cela inclut les applications de fitness montrant les temps intermédiaires, les applications météo affichant les prévisions de température, les applications de recettes montrant les quantités d'ingrédients, et les applications scientifiques présentant des données expérimentales.

const splitTimes = [5, 10, 15, 20];

const numberFormatter = new Intl.NumberFormat(navigator.language, {
  style: 'unit',
  unit: 'kilometer',
  maximumFractionDigits: 1
});

const formattedTimes = splitTimes.map(t => numberFormatter.format(t));

const listFormatter = new Intl.ListFormat(navigator.language, {
  type: 'unit'
});

const result = listFormatter.format(formattedTimes);

document.getElementById('split-times').textContent = result;
// Affiche : "5 km, 10 km, 15 km, 20 km" (ou équivalent selon la locale)

Les chaînes formatées fonctionnent comme n'importe quelle autre valeur de chaîne. Vous pouvez les insérer dans du contenu textuel, des attributs, ou tout contexte où vous affichez des informations aux utilisateurs.