or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdcommands-and-routing.mdconfiguration-and-context.mddocumentation-and-help.mderror-handling.mdexit-codes.mdflag-parameters.mdindex.mdparameter-parsers.mdpositional-parameters.mdtext-and-localization.md

text-and-localization.mddocs/

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