Aller au contenu
Aller au contenu

Référence / CLI

Page traduite automatiquement

Cette page a été traduite automatiquement, elle peut donc contenir des erreurs ou se lire un peu bizarrement. La version anglaise fait foi. Lire l'original en anglais.

verbatra types

Génère des déclarations TypeScript pour les clés de ton catalogue et les arguments de leurs messages, sans appeler de fournisseur ni lire de clé d'API.

Transforme une clé mal orthographiée ou un argument d'interpolation oublié en erreur de compilation. types lit ton catalogue source via l'adaptateur de format configuré et écrit un fichier de déclarations : une union de toutes les clés et, pour chaque clé, les arguments que prend son message. Il n'appelle aucun fournisseur, ne lit aucune clé d'API et ne touche jamais un fichier de locale cible.

Synopsis

verbatra types [flags]

Exemples

# write verbatra-types.d.ts from the source catalog
verbatra types

# write it where the app already imports from
verbatra types --out src/generated/messages.d.ts

# CI gate: exit 1 if the committed declaration is stale
verbatra types --check

# machine-readable result for a script
verbatra types --json

Options

OptionArgumentDéfautEffet
--out<path>verbatra-types.d.tsoù écrire la déclaration, relativement au répertoire de travail et à l'intérieur de la racine du projet ; les répertoires manquants sont créés
--checkaucundésactivécompare octet par octet la déclaration commitée à une génération fraîche, sans rien écrire ; sortie 1 si elle est périmée ou manquante

Accepte aussi les options globales --cwd, --config et --json.

Comportement

Ce qui est généré

À partir d'un locales/en.json comme celui-ci :

{
  "app": { "title": "Verbatra" },
  "greeting": "Hello {{name}}",
  "cart": { "item_one": "{{count}} item", "item_other": "{{count}} items" }
}

tu obtiens ceci :

// Generated by verbatra from locales/en.json (i18next-json). Do not edit by hand.
// Re-run `verbatra types` after the source catalog changes.

/** An argument whose type the source catalog does not record. */
export type VerbatraArgument = string | number;

/** The arguments of a message that takes none: passing any argument is a type error. */
export type VerbatraNoArguments = Record<string, never>;

/**
 * The arguments of a message verbatra could not analyse, so nothing is claimed about them.
 * Every key carrying this type is listed by `verbatra types` when it generates this file.
 */
export type VerbatraUnknownArguments = Record<string, VerbatraArgument>;

/** Every key in the source catalog, mapped to the arguments its message takes. */
export interface VerbatraMessages {
  "app.title": VerbatraNoArguments;
  "greeting": { readonly "name": VerbatraArgument };
  "cart.item": { readonly "count": VerbatraArgument };
  "cart.item_one": { readonly "count": VerbatraArgument };
  "cart.item_other": { readonly "count": VerbatraArgument };
}

/** Every key in the source catalog. */
export type VerbatraMessageKey = keyof VerbatraMessages;

/** The arguments the message at `Key` takes. */
export type VerbatraMessageArguments<Key extends VerbatraMessageKey> =
  VerbatraMessages[Key];

/** The keys the source catalog marks as carrying plural forms. */
export type VerbatraPluralMessageKey = "cart.item_one" | "cart.item_other";

Type ta propre fonction de traduction avec, et le compilateur fait le reste :

import type { VerbatraMessageArguments, VerbatraMessageKey } from "./verbatra-types.js";

declare function t<Key extends VerbatraMessageKey>(
  key: Key,
  args: VerbatraMessageArguments<Key>,
): string;

t("greeting", { name: "Ada" }); // fine
t("greting", { name: "Ada" });  // error: not a key in the catalog
t("greeting", {});              // error: the name argument is required
t("app.title", { name: "Ada" }); // error: this message takes no argument

Ce qui est affirmé et ce qui ne l'est pas

Les clés sont exactement celles que l'adaptateur de format a produites, dans l'ordre du document, donc le même catalogue produit toujours les mêmes octets. Les arguments viennent des jetons de placeholders que l'adaptateur a extraits ; les messages next-intl-json et arb sont lus avec l'analyseur ICU de l'adaptateur, donc un argument que seules certaines branches utilisent est quand même déclaré. Rien n'est deviné, et rien n'est déclaré en any.

Ton messageCe qui est déclaré
aucun placeholderVerbatraNoArguments, donc passer quoi que ce soit est une erreur de type
des placeholders nommés ({{name}}, {name}, %(name)s)une forme d'objet portant ces noms
des placeholders numérotés ou anonymes ({0}, %@, %1$d)un tuple readonly, une position par argument
un argument que seules certaines branches select ou plural utilisent (next-intl-json, arb)un membre optionnel, name?:, ou une position de tuple optionnelle en fin de tuple ([number, VerbatraArgument?]) ; un argument utilisé par toutes les branches reste obligatoire
un argument ICU number, plural ou selectordinal dans next-intl-json ou arbnumber
un argument ICU date ou time ({d, date, short}) dans next-intl-json ou arbDate | number
un argument ICU select ({gender, select, ...}) dans next-intl-json ou arbstring
un formateur i18next number ({{count, number}}) dans i18next-json, ngx-translate-json ou yamlnumber
un formateur i18next datetime ({{d, datetime}}) dans ces trois mêmes formatsDate | number
une interpolation i18next non échappée ({{- name}})le nom derrière le préfixe, name
un argument MessageFormat number, plural ou choice ({0,number}) dans propertiesnumber
une conversion printf numérique (%d, %1$d)number
une conversion printf de chaîne (%s, %(name)s)string
tout autre placeholder, y compris un argument ICU simple ({name})VerbatraArgument, un alias de string | number
une syntaxe de message invalide, que seuls next-intl-json et arb vérifientVerbatraUnknownArguments, et la clé est nommée dans la sortie
un message qui mélange arguments nommés, numérotés et anonymesVerbatraUnknownArguments, et la clé est nommée dans la sortie
un argument numéroté sous la position 0 ou à partir de la position 64 (%0$s, {64})VerbatraUnknownArguments, et la clé est nommée dans la sortie

Un format dont les placeholders ne portent pas de nom (Apple .strings, Android strings.xml, conversions gettext positionnelles) reçoit un tuple, jamais des noms inventés. Un nom utilisé avec des types différents reçoit leur union : {d, date, short} à côté d'un {d} simple devient Date | number | string. Les clés sont toujours des littéraux de chaîne entre guillemets, donc une clé avec un point, un mot réservé, un chiffre initial ou un guillemet est déclarée telle quelle. Une clé que l'adaptateur a exclue de la traduction (une feuille qui n'est pas une chaîne, une ressource Android translatable="false") n'est jamais déclarée et est nommée dans la sortie.

Une clé dont le nom contient lui-même un point

La clé déclarée s'écrit comme verbatra l'écrit dans verbatra.lock.json, pas dans une syntaxe de recherche que ta bibliothèque i18n comprend. Un catalogue JSON contenant {"a.b": "..."} porte la clé a\.b, volontairement distincte du chemin imbriqué a.b :

export interface VerbatraMessages {
  "a\\.b": VerbatraNoArguments;
}

i18next n'a pas d'échappement pour son séparateur de clés, donc t("a\\.b") ne cherche pas cette clé. Si ton catalogue porte de telles clés, règle keySeparator sur un autre caractère (ou sur false pour un catalogue plat) et fais correspondre la clé déclarée à la clé du runtime dans ton propre wrapper typé.

Le seul cas où la déclaration se trompe au lieu de simplement se taire

Seuls next-intl-json et arb valident la syntaxe des messages. Dans tout autre format, une valeur cassée comme "Hello {name" ne produit aucun jeton de placeholder : le message est donc déclaré sans argument, et lui passer l'argument dont il a vraiment besoin devient une erreur de compilation. Rien ne le signale, puisque rien ne l'a détecté. Corrige le catalogue, pas la déclaration.

Clés au pluriel

Rien n'est regroupé. i18next porte le pluriel comme suffixe de clé, donc cart.item_one et cart.item_other sont déclarées telles qu'elles sont écrites et, à côté, la clé de base cart.item que cherche t("cart.item", { count }), qui exige count et chaque argument que prend l'une des formes. Les types propres à i18next acceptent eux aussi les deux écritures. Un groupe ordinal comme place_ordinal_one déclare place, et une clé de base que le catalogue contient déjà garde sa propre déclaration. VerbatraPluralMessageKey liste les clés que l'adaptateur a marquées comme portant des formes plurielles, celles avec suffixe, pour que tu puisses t'y restreindre.

Où atterrit la déclaration

Par défaut elle atterrit dans verbatra-types.d.ts à la racine de ton projet. Commite-la : un collègue ou un job de CI vérifie les types dessus sans lancer verbatra, et --check a besoin d'un fichier commité auquel se comparer. Rien ne l'ajoute au .gitignore.

Un chemin de sortie est refusé avec TYPES_OUTPUT_CONFLICT quand il :

  • ne nomme aucun fichier, y compris quand il se termine par un séparateur de chemin comme generated/ (un --out vide échoue plus tôt, avec INVALID_OUT),
  • est absolu,
  • sort de la racine du projet avec ..,
  • n'est pas un fichier TypeScript, c'est-à-dire que son nom ne se termine pas par .ts, .mts ou .cts,
  • nomme un fichier de locale configuré, source ou cible, pour que la génération n'écrase jamais un catalogue,
  • est le fichier de verrouillage, verbatra.lock.json,
  • est le fichier de provenance, verbatra.provenance.json,
  • est le cache de mémoire de traduction, verbatra.cache.json,
  • est un fichier où verbatra cherche sa configuration : package.json, .verbatrarc, .verbatrarc.json, .verbatrarc.yaml, .verbatrarc.yml, .verbatrarc.js, .verbatrarc.cjs, .verbatrarc.ts, verbatra.config.js, verbatra.config.cjs ou verbatra.config.ts,
  • est le fichier de configuration que l'exécution a réellement chargé, y compris celui que tu as indiqué avec --config,
  • est le fichier de glossaire vers lequel pointe glossary dans ta configuration,
  • est un fichier existant qui ne commence pas par la ligne d'en-tête // Generated by verbatra from que verbatra écrit en tête de chaque déclaration (une marque d'ordre des octets et des lignes vides en tête sont ignorées), ou est trop volumineux pour être vérifié.

Les noms de fichiers sont comparés sans tenir compte de la casse, et les vérifications sont refaites une fois les liens symboliques résolus, si bien qu'un répertoire lié ne peut porter la déclaration ni hors de la racine du projet ni sur un fichier protégé. La dernière vérification n'a lieu que si une exécution allait écrire, et laisse ton fichier intact ; --check ne refuse jamais un fichier existant. Un chemin qui passe toutes les vérifications peut encore échouer à l'écriture (un répertoire en lecture seule, un disque plein) : l'exécution s'arrête avec TYPES_UNWRITABLE et nomme le fichier et le code du système de fichiers, comme EACCES.

Rester honnête en CI

Lance types --check à côté de verbatra check dans le même job : check repère les locales qui prennent du retard sur la source, types --check tes types. La comparaison se fait octet par octet et la déclaration est toujours écrite avec des fins de ligne line feed, alors fixe-les dans .gitattributes, sinon un checkout qui les réécrit laisse le fichier périmé pour de bon :

verbatra-types.d.ts text eol=lf

Sortie JSON

Schéma : types-envelope.json valide l'enveloppe entière ; Schémas liste les autres.

Avec --json, l'enveloppe porte un résultat :

interface GenerateTypesResult {
  path: string;               // absolute path of the declaration file
  sourcePath: string;         // the source catalog, relative to the project root
  keys: number;               // how many keys are declared
  withArguments: number;      // keys taking at least one determined argument
  unresolved: { key: string; reason: "invalid-message-syntax" | "mixed-argument-styles" | "argument-index-out-of-range" }[];
  excluded: string[];         // keys the adapter excluded, never declared
  plural: string[];           // keys carrying plural forms
  written: boolean;           // false under --check and when the file already matched
  stale: boolean;             // the file on disk differed when the run started
  missing: boolean;           // no file existed when the run started
  check: boolean;             // whether this was a --check run
}

Retour dans le terminal

Le résumé va sur stdout ; une ligne de spinner et, après l'écriture, une suggestion de committer le fichier vont sur stderr (--check ne suggère rien, et --quiet supprime les deux). Une exécution qui génère, une relance sans changement et un --check périmé :

Sortie
verbatra: generating the declarations... done (0.2s)
verbatra types
  214 keys declared, 63 of them taking arguments, from locales/en.json
  arguments not determined (1):
    legal.notice  invalid-message-syntax
  excluded by the adapter (1): meta.version
  wrote verbatra-types.d.ts
next: commit verbatra-types.d.ts (verbatra types --check compares against it in CI)

verbatra types
  214 keys declared, 63 of them taking arguments, from locales/en.json
  unchanged verbatra-types.d.ts

verbatra types
  215 keys declared, 63 of them taking arguments, from locales/en.json
  verbatra-types.d.ts is out of date, re-run verbatra types

Consulte Sortie dans le terminal pour la couleur, le spinner et les variables qui les contrôlent.

Codes de sortie

CodeSignification
0la déclaration a été générée, que le fichier ait changé ou non ; ou --check l'a trouvée à jour
1--check a trouvé la déclaration commitée périmée ou manquante (is missing, run verbatra types to create it)
2n'a pas pu s'exécuter : une erreur d'utilisation, un problème de configuration ou de source, un chemin de sortie refusé (TYPES_OUTPUT_CONFLICT), ou une déclaration qui n'a pas pu être écrite (TYPES_UNWRITABLE)

Consulte Codes de sortie pour le contrat que suit chaque commande.

Voir aussi

  • verbatra extract ajoute au catalogue les clés que ton code utilise ; types les déclare ensuite.
  • verbatra check est le contrôle de CI pour la dérive des locales, à côté de celui-ci pour la dérive des types.
  • Formats liste la syntaxe de placeholders de chaque format.
  • Codes de sortie et sortie JSON couvre l'enveloppe et le contrat des codes de sortie.
Modifier sur GitHub

Sur cette page