Internationalization (i18n)
Relevant source files
The following files were used as context for generating this wiki page:
The Internationalization (i18n) system in the putio-cli provides a robust, type-safe framework for managing and rendering user-facing strings across different locales. It is primarily built upon the i18next library but includes a custom TypeScript-heavy wrapper to ensure that translation keys and their associated interpolation parameters are validated at compile-time.
The system is utilized throughout the CLI to handle terminal output, error messages, and command metadata. By separating string definitions from implementation logic, the project maintains a clean architecture where technical errors (such as network failures or rate limits) are transformed into human-readable instructions via specialized localizers.
System Architecture
The i18n architecture is centered around a central translator instance and a hierarchical string catalog. The system uses a "Translation Leaf" pattern to define nested keys within a TypeScript object, which are then flattened into dot-notation strings for use with the underlying i18next engine.
Core Components
| Component | Description |
|---|---|
StringsCatalog | A TypeScript type derived from the English catalog (en.ts), serving as the single source of truth for available keys. |
StringKey | A recursive type that generates all valid dot-notation paths from the catalog. |
createTranslator | A factory function that initializes an i18next instance with specific locale settings. |
translate | The default exported translation function, pre-configured for the default locale. |
Sources: src/i18n/translate.ts:40-42, src/i18n/translate.ts:64-72
Translation Flow
The following diagram illustrates how a string request moves from a UI component through the type-safety layer into the translation engine:
The diagram shows the synchronous flow of resolving a localized string with type validation. Sources: src/i18n/translate.ts:46-60
Implementation Details
Type-Safe Keys and Parameters
The system uses advanced TypeScript features to prevent runtime translation errors. It extracts placeholders (e.g., {{seconds}}) from strings in the catalog and enforces that the caller provides a matching parameters object.
type ExtractPlaceholders<T extends string> = T extends `${string}{{${infer Param}}}${infer Rest}`
? Param | ExtractPlaceholders<Rest>
: never;
export type StringParams<K extends StringKey> =
PathValue<StringsCatalog, K> extends string
? TranslationParams<PathValue<StringsCatalog, K>>
: never;
Sources: src/i18n/translate.ts:30-44
String Catalog Structure
Strings are organized into a nested object in src/i18n/catalog/en.ts. Key categories include:
- cli.auth: Strings for login flows, profile management, and status reports.
- cli.error: Specialized error titles, recovery suggestions, and hints.
- cli.files: Summaries for file listings and mutation outcomes (created, renamed, deleted).
Sources: src/i18n/catalog/en.ts:44-245
Error Localization Pattern
One of the primary consumers of the i18n system is the Error Localization module. Instead of raw SDK errors being printed to the terminal, they are passed through matchers that resolve to specific localized keys.
Localizer Logic
Localizers like the rateLimitLocalizer or transportLocalizer identify specific error tags (e.g., PutioRateLimitError) and use the translate function to build a structured LocalizedError object.
The flow shows how technical errors are intercepted and mapped to localized UI content. Sources: src/internal/localizers/shared.ts:36-155
Common Localized Errors
| Error Category | Message Key (Base) | Recovery Example |
|---|---|---|
| Rate Limit | cli.error.rateLimit.title | "Wait about {{seconds}}s and try again." |
| Auth Failure | app.auth.failed.title | "Please sign in again to continue." |
| Network Error | cli.error.network.title | "Check your internet connection." |
Sources: src/internal/localizers/shared.ts:74-118, src/i18n/catalog/en.ts:110-138
Usage in Terminal Rendering
Terminal rendering functions utilize the i18n system to generate consistent, formatted output.
Example: Files Listing
In src/internal/terminal/files-terminal.ts, the system handles pluralization and contextual summaries (e.g., showing files inside a specific parent folder vs. a root list).
export const renderFilesTerminal = <T extends FilesTextInput>(value: T) => {
if (value.files.length === 0) {
return value.parent?.name
? translate("cli.files.terminal.emptyInParent", { name: value.parent.name })
: translate("cli.files.terminal.empty");
}
// ...
}
Sources: src/internal/terminal/files-terminal.ts:20-25
Example: Auth Success
In src/internal/terminal/auth-terminal.ts, the i18n system provides the labels for the branded success panel, including translations for "yes/no" boolean states.
translate("cli.auth.success.browserOpened", {
value: value.browserOpened ? translate("cli.common.yes") : translate("cli.common.no"),
})
Sources: src/internal/terminal/auth-terminal.ts:91-93
Summary
The Internationalization system in putio-cli ensures that the application remains accessible and professional by centralizing all user-facing text. By leveraging i18next alongside strict TypeScript typing, the system guarantees that developers cannot reference non-existent strings or omit required interpolation parameters. The integration with error localizers further ensures that even complex API failures result in actionable, human-readable instructions.
Sources: src/i18n/translate.ts, src/i18n/catalog/en.ts