0
# Text and Localization
1
2
Comprehensive localization system with customizable text for all user-facing strings including help text, error messages, and documentation.
3
4
## Quick Reference
5
6
```typescript
7
import { text_en } from "@stricli/core";
8
9
const app = buildApplication(command, {
10
name: "myapp",
11
localization: {
12
defaultLocale: "en",
13
loadText: (locale) => {
14
if (locale.startsWith("es")) return text_es;
15
if (locale.startsWith("en")) return text_en;
16
return undefined;
17
}
18
}
19
});
20
21
await run(app, inputs, { process, locale: process.env.LANG });
22
```
23
24
## ApplicationText Interface
25
26
Full set of static text and text-returning callbacks necessary for Stricli output. Extends error formatting and documentation text.
27
28
```typescript { .api }
29
/**
30
* Complete text interface for Stricli output
31
* Includes error messages, documentation text, and version warnings
32
*/
33
interface ApplicationText extends ApplicationErrorFormatting, DocumentationText {
34
/**
35
* Generate warning when latest version is not installed
36
* @param args - Current version, latest version, upgrade command, color flag
37
* @returns Formatted warning message
38
*/
39
readonly currentVersionIsNotLatest: (args: {
40
readonly currentVersion: string;
41
readonly latestVersion: string;
42
readonly upgradeCommand?: string;
43
readonly ansiColor: boolean;
44
}) => string;
45
}
46
47
/**
48
* Default English text implementation
49
*/
50
const text_en: ApplicationText;
51
52
interface DocumentationKeywords {
53
default: string;
54
separator: string;
55
}
56
57
interface DocumentationHeaders {
58
usage: string;
59
aliases: string;
60
commands: string;
61
flags: string;
62
arguments: string;
63
}
64
65
interface DocumentationBriefs {
66
help: string;
67
helpAll: string;
68
version: string;
69
argumentEscapeSequence: string;
70
}
71
72
interface ApplicationErrorFormatting extends CommandErrorFormatting {
73
noCommandRegisteredForInput: (args: {
74
input: string;
75
corrections: readonly string[];
76
ansiColor: boolean;
77
}) => string;
78
noTextAvailableForLocale: (args: {
79
requestedLocale: string;
80
defaultLocale: string;
81
ansiColor: boolean;
82
}) => string;
83
}
84
85
interface CommandErrorFormatting {
86
exceptionWhileParsingArguments: (exc: unknown, ansiColor: boolean) => string;
87
exceptionWhileLoadingCommandFunction: (exc: unknown, ansiColor: boolean) => string;
88
exceptionWhileLoadingCommandContext: (exc: unknown, ansiColor: boolean) => string;
89
exceptionWhileRunningCommand: (exc: unknown, ansiColor: boolean) => string;
90
commandErrorResult: (err: Error, ansiColor: boolean) => string;
91
}
92
```
93
94
## Default English Text
95
96
```typescript
97
import { text_en } from "@stricli/core";
98
99
const app = buildApplication(command, {
100
name: "myapp",
101
localization: { defaultLocale: "en", loadText: (locale) => text_en }
102
});
103
```
104
105
## Creating Custom Localization
106
107
### Example 1: Spanish Localization
108
109
```typescript
110
import { ApplicationText, text_en } from "@stricli/core";
111
112
const text_es: ApplicationText = {
113
// Inherit English text as base
114
...text_en,
115
116
// Override with Spanish text
117
headers: {
118
usage: "USO",
119
aliases: "ALIAS",
120
commands: "COMANDOS",
121
flags: "OPCIONES",
122
arguments: "ARGUMENTOS"
123
},
124
125
keywords: {
126
default: "predeterminado =",
127
separator: "separador ="
128
},
129
130
briefs: {
131
help: "Mostrar información de ayuda y salir",
132
helpAll: "Mostrar toda la información de ayuda y salir",
133
version: "Mostrar información de versión y salir",
134
argumentEscapeSequence: "Todas las entradas posteriores deben interpretarse como argumentos"
135
},
136
137
noCommandRegisteredForInput: ({ input, corrections }) => {
138
const mensaje = `No hay comando registrado para \`${input}\``;
139
if (corrections.length > 0) {
140
return `${mensaje}, ¿quiso decir ${corrections.join(" o ")}?`;
141
}
142
return mensaje;
143
},
144
145
noTextAvailableForLocale: ({ requestedLocale, defaultLocale }) => {
146
return `La aplicación no admite el idioma "${requestedLocale}", usando "${defaultLocale}" por defecto`;
147
},
148
149
exceptionWhileParsingArguments: (exc) => {
150
return `No se pudieron analizar los argumentos: ${exc}`;
151
},
152
153
exceptionWhileLoadingCommandFunction: (exc) => {
154
return `No se pudo cargar la función de comando: ${exc}`;
155
},
156
157
exceptionWhileLoadingCommandContext: (exc) => {
158
return `No se pudo cargar el contexto del comando: ${exc}`;
159
},
160
161
exceptionWhileRunningCommand: (exc) => {
162
return `El comando falló: ${exc}`;
163
},
164
165
commandErrorResult: (err) => {
166
return err.message;
167
},
168
169
currentVersionIsNotLatest: ({ currentVersion, latestVersion, upgradeCommand }) => {
170
if (upgradeCommand) {
171
return `La última versión disponible es ${latestVersion} (actualmente ejecutando ${currentVersion}), actualice con "${upgradeCommand}"`;
172
}
173
return `La última versión disponible es ${latestVersion} (actualmente ejecutando ${currentVersion})`;
174
}
175
};
176
177
// Use in application
178
const app = buildApplication(command, {
179
name: "myapp",
180
localization: {
181
defaultLocale: "es",
182
loadText: (locale) => {
183
if (locale.startsWith("es")) {
184
return text_es;
185
}
186
if (locale.startsWith("en")) {
187
return text_en;
188
}
189
return undefined;
190
}
191
}
192
});
193
```
194
195
### Example 2: French Localization
196
197
```typescript
198
import { ApplicationText, text_en } from "@stricli/core";
199
200
const text_fr: ApplicationText = {
201
...text_en,
202
203
headers: {
204
usage: "UTILISATION",
205
aliases: "ALIAS",
206
commands: "COMMANDES",
207
flags: "OPTIONS",
208
arguments: "ARGUMENTS"
209
},
210
211
keywords: {
212
default: "par défaut =",
213
separator: "séparateur ="
214
},
215
216
briefs: {
217
help: "Afficher les informations d'aide et quitter",
218
helpAll: "Afficher toutes les informations d'aide et quitter",
219
version: "Afficher les informations de version et quitter",
220
argumentEscapeSequence: "Toutes les entrées suivantes doivent être interprétées comme des arguments"
221
},
222
223
noCommandRegisteredForInput: ({ input, corrections }) => {
224
const message = `Aucune commande enregistrée pour \`${input}\``;
225
if (corrections.length > 0) {
226
return `${message}, vouliez-vous dire ${corrections.join(" ou ")} ?`;
227
}
228
return message;
229
},
230
231
noTextAvailableForLocale: ({ requestedLocale, defaultLocale }) => {
232
return `L'application ne prend pas en charge la locale "${requestedLocale}", utilisation de "${defaultLocale}" par défaut`;
233
},
234
235
exceptionWhileParsingArguments: (exc) => {
236
return `Impossible d'analyser les arguments : ${exc}`;
237
},
238
239
exceptionWhileLoadingCommandFunction: (exc) => {
240
return `Impossible de charger la fonction de commande : ${exc}`;
241
},
242
243
exceptionWhileLoadingCommandContext: (exc) => {
244
return `Impossible de charger le contexte de la commande : ${exc}`;
245
},
246
247
exceptionWhileRunningCommand: (exc) => {
248
return `La commande a échoué : ${exc}`;
249
},
250
251
commandErrorResult: (err) => {
252
return err.message;
253
},
254
255
currentVersionIsNotLatest: ({ currentVersion, latestVersion, upgradeCommand }) => {
256
if (upgradeCommand) {
257
return `La dernière version disponible est ${latestVersion} (actuellement ${currentVersion}), mise à jour avec "${upgradeCommand}"`;
258
}
259
return `La dernière version disponible est ${latestVersion} (actuellement ${currentVersion})`;
260
}
261
};
262
```
263
264
### Example 3: Dynamic Locale Loading
265
266
```typescript
267
import { buildApplication, text_en, ApplicationText } from "@stricli/core";
268
269
// Lazy-load translations
270
async function loadLocale(locale: string): Promise<ApplicationText | undefined> {
271
if (locale.startsWith("en")) {
272
return text_en;
273
}
274
275
try {
276
// Dynamic import based on locale
277
const module = await import(`./locales/${locale}.js`);
278
return module.default;
279
} catch {
280
// Locale not available
281
return undefined;
282
}
283
}
284
285
const app = buildApplication(command, {
286
name: "myapp",
287
localization: {
288
defaultLocale: "en",
289
loadText: (locale) => {
290
// Synchronous loading from cache or return undefined
291
const cached = localeCache.get(locale);
292
if (cached) {
293
return cached;
294
}
295
296
// For runtime, we need synchronous loading
297
// Pre-load locales during build
298
return undefined;
299
}
300
}
301
});
302
303
// Pre-load common locales
304
const localeCache = new Map<string, ApplicationText>();
305
localeCache.set("en", text_en);
306
307
(async () => {
308
const es = await loadLocale("es");
309
if (es) localeCache.set("es", es);
310
311
const fr = await loadLocale("fr");
312
if (fr) localeCache.set("fr", fr);
313
})();
314
```
315
316
### Example 4: Custom Error Messages
317
318
```typescript
319
import { ApplicationText, text_en, formatMessageForArgumentScannerError, ArgumentScannerError } from "@stricli/core";
320
321
const customText: ApplicationText = {
322
...text_en,
323
324
exceptionWhileParsingArguments: (exc, ansiColor) => {
325
if (exc instanceof ArgumentScannerError) {
326
// Custom formatting for specific errors
327
return formatMessageForArgumentScannerError(exc, {
328
FlagNotFoundError: (err) => {
329
const flag = ansiColor ? `\x1B[1m${err.input}\x1B[22m` : err.input;
330
if (err.corrections.length > 0) {
331
return `Unknown option: ${flag}\nDid you mean: ${err.corrections.join(", ")}?`;
332
}
333
return `Unknown option: ${flag}`;
334
},
335
ArgumentParseError: (err) => {
336
const value = ansiColor ? `\x1B[1m${err.input}\x1B[22m` : err.input;
337
const param = ansiColor
338
? `\x1B[1m${err.externalFlagNameOrPlaceholder}\x1B[22m`
339
: err.externalFlagNameOrPlaceholder;
340
return `Invalid value ${value} for ${param}`;
341
}
342
});
343
}
344
return text_en.exceptionWhileParsingArguments(exc, ansiColor);
345
},
346
347
exceptionWhileRunningCommand: (exc, ansiColor) => {
348
// Add more context to errors
349
const prefix = ansiColor ? "\x1B[31mError:\x1B[39m" : "Error:";
350
if (exc instanceof Error) {
351
return `${prefix} ${exc.message}\n\nRun with --help for usage information.`;
352
}
353
return `${prefix} ${String(exc)}`;
354
}
355
};
356
```
357
358
### Example 5: Branded Text
359
360
```typescript
361
import { ApplicationText, text_en } from "@stricli/core";
362
363
const brandedText: ApplicationText = {
364
...text_en,
365
366
currentVersionIsNotLatest: ({ currentVersion, latestVersion, upgradeCommand }) => {
367
// Custom branding
368
const message = `🎉 New version available!\n` +
369
` Current: v${currentVersion}\n` +
370
` Latest: v${latestVersion}\n`;
371
372
if (upgradeCommand) {
373
return message + ` Run: ${upgradeCommand}`;
374
}
375
return message;
376
},
377
378
exceptionWhileRunningCommand: (exc, ansiColor) => {
379
// Friendly error messages
380
const prefix = ansiColor ? "😞 \x1B[31mOops!\x1B[39m" : "Oops!";
381
if (exc instanceof Error) {
382
return `${prefix} Something went wrong:\n\n ${exc.message}\n\n` +
383
`💡 Tip: Run with --help for usage information`;
384
}
385
return `${prefix} ${String(exc)}`;
386
}
387
};
388
```
389
390
## Multi-Language Support
391
392
```typescript
393
const locales = { en: text_en, es: text_es, fr: text_fr };
394
395
const app = buildApplication(command, {
396
name: "myapp",
397
localization: {
398
defaultLocale: "en",
399
loadText: (locale) => {
400
// Try exact match
401
if (locale in locales) return locales[locale];
402
// Try language prefix (e.g., "en-US" -> "en")
403
const lang = locale.split("-")[0];
404
if (lang in locales) return locales[lang];
405
// Fall back to English
406
return text_en;
407
}
408
}
409
});
410
411
await run(app, inputs, {
412
process,
413
locale: process.env.LANG || "en"
414
});
415
```
416
417
## Complete Example
418
419
Here's a comprehensive example with multiple locales:
420
421
```typescript
422
import {
423
buildApplication,
424
buildCommand,
425
run,
426
text_en,
427
type ApplicationText
428
} from "@stricli/core";
429
430
// Define Spanish text
431
const text_es: ApplicationText = {
432
...text_en,
433
headers: {
434
usage: "USO",
435
aliases: "ALIAS",
436
commands: "COMANDOS",
437
flags: "OPCIONES",
438
arguments: "ARGUMENTOS"
439
},
440
briefs: {
441
help: "Mostrar ayuda",
442
helpAll: "Mostrar toda la ayuda",
443
version: "Mostrar versión",
444
argumentEscapeSequence: "Interpretar como argumentos"
445
},
446
// ... other translations
447
};
448
449
// Build application with localization
450
const app = buildApplication(
451
buildCommand({
452
func: async function(flags) {
453
if (flags.verbose) {
454
this.process.stdout.write("Modo detallado activado\n");
455
}
456
this.process.stdout.write("¡Hola mundo!\n");
457
},
458
parameters: {
459
flags: {
460
verbose: {
461
kind: "boolean",
462
brief: "Salida detallada"
463
}
464
}
465
},
466
docs: {
467
brief: "Comando de saludo"
468
}
469
}),
470
{
471
name: "myapp",
472
versionInfo: {
473
currentVersion: "1.0.0"
474
},
475
localization: {
476
defaultLocale: "en",
477
loadText: (locale) => {
478
if (locale.startsWith("es")) {
479
return text_es;
480
}
481
if (locale.startsWith("en")) {
482
return text_en;
483
}
484
return undefined;
485
}
486
}
487
}
488
);
489
490
// Run with locale from environment
491
await run(app, process.argv.slice(2), {
492
process,
493
locale: process.env.LANG?.split(".")[0] || "en"
494
});
495
```
496
497
## Related Documentation
498
499
- [Configuration and Context](./configuration-and-context.md) - Localization configuration
500
- [Documentation and Help](./documentation-and-help.md) - Help text generation
501
- [Error Handling](./error-handling.md) - Error message formatting
502