Traduire l'interface utilisateur
Pivotick écrit son propre texte : le rail, les menus, la barre latérale, le tableau, le panneau croisé dynamique, l'historique, les toasts et chaque info-bulle title et aria-label derrière eux. L'option i18n met tout cela dans votre langue.
const graph = new Pivotick(container, data, {
i18n: { locale: 'fr' },
})Le texte que vous transmettez à la bibliothèque est dessiné exactement comme indiqué et n'a jamais été recherché : étiquettes de nœuds et de bords, étiquettes de facettes et de légendes, noms de pivots, modes de rail, onglets d'ancrage, actions de la barre supérieure et lignes que vous ajoutez à un menu contextuel. Traduisez-les avant de les transmettre.
Langues groupées
Chaque build comprend des traductions complètes de l'interface utilisateur intégrée :
| Langue | Paramètres régionaux | Exemples de balises régionales |
|---|---|---|
| Anglais (par défaut) | en | en-GB, en-US |
| Chinois simplifié | zh | zh-CN, zh-Hans |
| Français | fr | fr-FR, fr-CA |
| Allemand | de | de-DE, de-CH |
| Portugais | pt | pt-BR, pt-PT |
Définissez i18n.locale pour en sélectionner un. Les balises régionales utilisent le même catalogue de langues tout en conservant leurs propres règles de formatage, de tri et de pluriel des nombres. Le catalogue chinois utilise des caractères simplifiés ; il n’existe pas de traduction distincte en chinois traditionnel. Un paramètre régional sans catalogue groupé utilise l'anglais, sauf si vous fournissez messages ou translate. L'omission de locale conserve la valeur par défaut de l'anglais existant ; la langue du navigateur n'est pas détectée.
Les catalogues sont exportés sous messagesEn, messagesZh, messagesFr, messagesDe, messagesPt et ensemble sous bundledMessages, également disponibles sur Pivotick dans les versions de navigateur.
L'option
| Propriété | Type | Valeur par défaut | Description |
|---|---|---|---|
locale | string | 'en' | Une balise BCP 47 telle que 'fr', 'de-CH' ou 'pt-BR'. Il choisit les règles du pluriel, la manière dont les nombres sont écrits et la manière dont les listes sont triées. Lorsqu'il est donné, il est également défini comme le lang de la racine du graphique. |
messages | Partial<Messages> | {} | Remplacements par clé. Les clés manquantes utilisent la langue fournie, puis l'anglais. |
translate | (key, params, fallback) => string | undefined | Un pont vers votre propre bibliothèque de traduction, demandé pour chaque clé que messages ne possède pas. |
La langue est définie lors de la construction du graphique. Pour le changer, appelez graph.destroy() et reconstruisez le graphique avec les nouvelles options.
Comment trouver un message
Chaque morceau de texte est recherché dans cet ordre et la première réponse l'emporte :
- L'option propre de la fonctionnalité, lorsqu'elle en a une :
UI.emptyState.render,UI.busyIndicator.label,UI.simplify.typeLabel, ou lestitleetconfirmLabelque vous passez àctx.confirm(). Ceux-ci sont plus spécifiques que n’importe quel catalogue. messages, votre table de clés.translate, votre pont.- La langue groupée, sélectionnée par
locale. - English, lorsque la langue n'a pas de catalogue groupé.
Les remplacements partiels sont pris en charge : modifiez uniquement les termes dont votre application a besoin et le reste utilise la langue sélectionnée. Les langues non prises en charge passent à l'anglais sans avertissement.
Clés et messages
Chaque message possède une clé stable en pointillés nommée d'après l'endroit où il apparaît : 'contextMenu.node.pin', 'history.menu.empty', 'sidebar.neighbors.connections'. Les clés sont saisies à partir du catalogue anglais, donc dans TypeScript une clé mal orthographiée ne parvient pas à être compilée et votre éditeur complète le reste.
Un message prend l’une des trois formes suivantes.
Texte avec espaces réservés
Une chaîne, avec {name} où va une valeur :
messages: {
'search.result.inGroup': 'dans {group}',
'history.menu.count': '{count} sur {limit}',
}Un nombre est écrit de la même façon que la locale l'écrit avant d'entrer : 1,234 sous 'en', 1.234 sous 'de', 1 234 sous 'fr'. Une chaîne entre telle qu’elle est écrite.
Formes plurielles
Un message qui dépend d'un décompte est un objet avec une entrée par catégorie plurielle utilisée par le langage. La catégorie est choisie parmi le paramètre count par le Intl.PluralRules du navigateur, donc les règles sont celles que CLDR définit pour les paramètres régionaux. other est requis, puisque chaque langue l'a.
// English and French need two forms; French counts zero as one
'count.nodes': { one: '{count} nœud', other: '{count} nœuds' },
// Polish needs four: 1 węzeł, 3 węzły, 5 węzłów, 1,5 węzła
'count.nodes': { one: '{count} węzeł', few: '{count} węzły', many: '{count} węzłów', other: '{count} węzła' },Les catégories sont zero, one, two, few, many et other. Une forme que le langage n'utilise pas n'est jamais sélectionnée, et une forme manquante revient à other.
L'anglais de secours conserve les règles du pluriel anglais dans n'importe quelle langue. Par exemple, un langage non pris en charge qui traite le zéro comme singulier obtient toujours « 0 nœuds » de la solution de secours anglaise.
Une fonction
Pour tout ce qu'un modèle ne peut pas dire, un message peut être fonction des paramètres bruts et du I18n du graphique :
'simplify.typeLabel': ({ count, type }, i18n) =>
`${i18n.formatNumber(Number(count))} ${type === 'ip' ? 'adresses IP' : type}`,Quelle que soit sa forme, un message est un texte. Il est inséré sous forme de texte, jamais analysé comme HTML, donc <b> apparaît tel qu'écrit.
Modification des fichiers de traduction de PO
Le répertoire locales/ du référentiel contient un modèle gettext, pivotick.pot et des fichiers complets zh.po, fr.po, de.po et pt.po. Ces fichiers sont également livrés dans le package npm. Ouvrez un fichier .po dans Poedit ou dans n'importe quel éditeur de texte UTF-8. Chaque msgctxt est la clé de message stable ; msgid est la source anglaise et msgstr est la traduction. Gardez {placeholders} intact. Pour les entrées au pluriel, traduisez chaque emplacement msgstr[n] : le chinois a other uniquement, et le français, l'allemand et le portugais ont one puis other. Le runtime utilise Intl.PluralRules, avec many revenant à other, plutôt que d'évaluer l'en-tête gettext comme JavaScript.
À partir d'une extraction de source avec npm ci terminée :
# Refresh the template and merge English changes into the PO files
npm run i18n:extract
# Edit locales/*.po, finish empty entries and review any fuzzy translations
# Compile PO files into the TypeScript catalogues used by every build
npm run i18n:compile
# Check completeness, source text, placeholders, plurals and generated files
npm run i18n:check
npm run test:i18nValidez ensemble les fichiers PO, le modèle et les fichiers src/i18n/locales/*.ts générés. Ne modifiez pas directement les fichiers générés. L'extraction préserve les traductions existantes et les commentaires du traducteur, marque le texte anglais modifié comme fuzzy et laisse les nouvelles entrées vides. Résolvez ces entrées avant de compiler. Les clés supprimées sont supprimées du catalogue actif. npm run build exécute i18n:check, donc une traduction incomplète ou un catalogue généré obsolète fait échouer la construction. Ces commandes utilisent les dépendances Node existantes ; aucune installation du système gettext n'est nécessaire.
Pour regrouper une autre langue, ajoutez ses paramètres régionaux et son mappage d'emplacements pluriels à scripts/i18n.mjs, extrayez et traduisez le nouveau fichier PO, compilez-le, puis exportez le catalogue généré depuis src/i18n/locales/index.ts et ajoutez-le à bundledMessages. Pour un langage réservé aux applications, passer un catalogue en tant que i18n.messages fonctionne toujours sans changer Pivotick.
Démarrer un catalogue à partir de messagesEn
Le catalogue anglais est exporté sous la forme messagesEn, à partir de la version ES et sous la forme Pivotick.messagesEn à partir des versions du navigateur. Il contient chaque clé avec son texte anglais, et il s'agit de données simples, elles peuvent donc être écrites comme le fichier à partir duquel vos traducteurs commencent :
import { messagesEn } from 'pivotick'
console.log(JSON.stringify(messagesEn, null, 4))Chargez à nouveau le fichier traduit sous messages. Dans TypeScript, un catalogue déclaré avec satisfies Messages doit nommer chaque clé, donc une clé ajoutée dans une version ultérieure apparaît comme une erreur de compilation plutôt que comme une chaîne anglaise parasite :
import type { Messages } from 'pivotick'
export const messagesFr = {
'count.nodes': { one: '{count} nœud', other: '{count} nœuds' },
// …every other key
} satisfies MessagesUtilisez plutôt Partial<Messages> pour traduire au fur et à mesure.
Relier votre propre bibliothèque de traduction
Si votre application traduit déjà via une bibliothèque, translate conserve une source de vérité et un changement de langue. Il est appelé pour chaque clé que messages n'a pas, avec :
key: la clé du message, telle que'count.nodes';params: les valeurs brutes, avec des nombres non formatés, de sorte que les règles de pluriel et de nombre de votre bibliothèque s'appliquent ;fallback: le texte anglais, déjà renseigné.
Renvoyez une chaîne pour l'utiliser, ou undefined pour utiliser la langue fournie, puis l'anglais. Ici avec i18next, dont le paramètre count pilote déjà ses pluriels :
const graph = new Pivotick(container, data, {
i18n: {
locale: i18next.language,
translate: (key, params, fallback) => {
const found = i18next.exists(`pivotick.${key}`)
return found ? i18next.t(`pivotick.${key}`, params) : undefined
},
},
})Vos fichiers de ressources écrivent ensuite leurs messages dans la syntaxe propre à votre bibliothèque ("{{count}} nœuds" dans i18next), sous les mêmes clés.
Un translate lancé est intercepté : la console indique quelle clé et la langue fournie (ou l'anglais pour une langue non prise en charge) est affichée. Le crochet reçoit toujours l'anglais comme argument fallback, préservant le contrat de pont. Lorsque messages et translate ont tous deux une clé, messages gagne et translate n'est pas demandé.
Nombres, listes, tri et lang
locale couvre plus que des mots. En dessous :
- Counts s'écrivent avec
Intl.NumberFormat: "1,234 nodes" en anglais, "1,234 Knoten" en allemand. L'anglais de secours utilise également le formatage numérique des paramètres régionaux sélectionnés. - Les listes de noms dans une phrase sont jointes à
Intl.ListFormat: "a, b et c", "a, b et c". - Tris dans le tableau, la liste des voisins et le volet de triage pivot utilisent
Intl.Collator, donc "Émile" trie à côté de "Emma", le suédois met "ä" après "z", et "élément 10" vient après "élément 9".
Lorsque vous déclarez un locale, l'élément racine du graphique l'obtient comme lang, tout comme les parties que la bibliothèque monte en dehors de la racine (info-bulles, listes déroulantes, listes de suggestions). Les lecteurs d’écran lisent ensuite l’interface avec la bonne voix. Sans locale, aucun lang n'est défini : le texte de la bibliothèque est en anglais, mais vos données à l'intérieur du graphique sont dans la langue de votre page, et le lang de la page le décrit le mieux.
La disposition de droite à gauche n'est pas prise en charge : il n'y a pas de gestion dir et l'interface est disposée de gauche à droite dans toutes les langues.
Une langue par graphique
Chaque graphique a son propre langage. Deux graphiques sur une page peuvent utiliser deux langues, et aucune n’affecte l’autre.
Un graphique à l'intérieur d'un graphique parle celui de son parent : le sous-graphe dessiné à l'intérieur d'un cluster et le graphique des petits voisins dans la barre latérale.
Le texte que la bibliothèque écrit dans vos données est écrit une seule fois, dans le langage du graphique : l'étiquette d'une entrée d'historique ("3 nœuds masqués") et le corps par lequel commence une nouvelle note. La reconstruction du graphique dans une autre langue les laisse tels quels.
En code : graph.i18n et plugins
graph.i18n est le langage du graphe, le même objet avec lequel la bibliothèque écrit :
| Membre | Ce que ça fait |
|---|---|
locale | Le tag utilisé : le vôtre, ou 'en'. |
t(key, params?) | Le texte d'un message intégré, via le lookup ci-dessus. |
formatNumber(value, options?) | Un nombre parmi les chiffres des paramètres régionaux. Prend les options Intl.NumberFormat. |
formatList(items, options?) | Éléments joints sous forme de phrase. Prend les options Intl.ListFormat. |
compare(a, b) | Un comparateur dans l'ordre des paramètres régionaux, pouvant passer en toute sécurité à sort tel quel. |
collator(options?) | Le Intl.Collator derrière compare, numérique par défaut. |
Un plugin lit le même objet que ctx.i18n. Le texte que le plugin écrit lui-même est celui du plugin à traduire, saisi sur ctx.i18n.locale ; le formatage de ses décomptes et le tri de ses listes via ctx.i18n les maintiennent cohérents avec le reste du graphique.
const nodeCounter = {
name: 'node-counter',
install(ctx) {
const words = { fr: 'Total', de: 'Gesamt' }
const label = words[ctx.i18n.locale.split('-')[0]] ?? 'Total'
const count = ctx.i18n.formatNumber(ctx.graph.getNodes().length)
// …draw `${label}: ${count}`
},
}Ce qui reste tel quel
- Vos données. Les étiquettes de nœuds et de bords, les noms de propriétés et les valeurs sont dessinés tels quels. Un nom de propriété sans étiquette déclarée est mis en casse à partir de sa clé (
first_seenlit "Premier vu"); déclareznodePropertiesMapouedgePropertiesMappour le nommer dans votre langue. La ligneidque les listes de propriétés ajoutent au-dessus de vos propres clés est écrite comme la clé qu'elle est. - Texte du développeur. Les avertissements de la console et les erreurs générées restent en anglais.
- Les glyphes clés.
⌃ ⇧ ⌥ ⌘sont affichés tels quels. Les noms de clés sont des messages (keys.ctrlCombo,keys.escape), donc une interface allemande peut direStrg+Z. - Le badge compte sur le bord d'un nœud (
2k,99+), qui doit contenir trois caractères.