Référence / CLI
Page traduite automatiquement
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 --jsonOptions
| Option | Argument | Défaut | Effet |
|---|---|---|---|
--out | <path> | verbatra-types.d.ts | où é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 |
--check | aucun | dé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 argumentCe 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 message | Ce qui est déclaré |
|---|---|
| aucun placeholder | VerbatraNoArguments, 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 arb | number |
un argument ICU date ou time ({d, date, short}) dans next-intl-json ou arb | Date | number |
un argument ICU select ({gender, select, ...}) dans next-intl-json ou arb | string |
un formateur i18next number ({{count, number}}) dans i18next-json, ngx-translate-json ou yaml | number |
un formateur i18next datetime ({{d, datetime}}) dans ces trois mêmes formats | Date | 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 properties | number |
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érifient | VerbatraUnknownArguments, et la clé est nommée dans la sortie |
| un message qui mélange arguments nommés, numérotés et anonymes | VerbatraUnknownArguments, 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_ et cart.item_ 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_ 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_ quand il :
- ne nomme aucun fichier, y compris quand il se termine par un séparateur de chemin comme
generated/(un--outvide échoue plus tôt, avecINVALID_),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,.mtsou.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.cjsouverbatra.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
glossarydans ta configuration, - est un fichier existant qui ne commence pas par la ligne d'en-tête
// Generated by verbatra fromque 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_ 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=lfSortie 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é :
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 typesConsulte Sortie dans le terminal pour la couleur, le spinner et les variables qui les contrôlent.
Codes de sortie
| Code | Signification |
|---|---|
0 | la 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) |
2 | n'a pas pu s'exécuter : une erreur d'utilisation, un problème de configuration ou de source, un chemin de sortie refusé (TYPES_), ou une déclaration qui n'a pas pu être écrite (TYPES_) |
Consulte Codes de sortie pour le contrat que suit chaque commande.
Voir aussi
verbatra extractajoute au catalogue les clés que ton code utilise ;typesles déclare ensuite.verbatra checkest 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.