4.0 KiB
I18N_CODES and I18nErrorCode — i18n error code registry
Purpose
I18N_CODES is the single, frozen registry of every fully-qualified
error code that any package in the @suaveplan/i18n* family is
permitted to throw. Keeping all 18 codes in one runtime constant has
two non-negotiable benefits: (1) instanceof I18nError plus a
switch (err.code) discrimination is exhaustive at the type level
without importing every leaf, and (2) OTel span events, log sinks,
and dashboards see one stable "I18N." prefix rather than 13
separate per-leaf prefixes. I18nErrorCode is the discriminant
union derived from the registry — use it as the TCode parameter
when extending SuaveplanError.
Features
- 18 codes covering parser, interpolation, pluralization, backends, formatters, detector, and plugin failures
- Every code is namespaced under
"I18N."(e.g."I18N.MISSING_KEY") - Frozen registry —
I18N_CODES.MISSING_KEY = "x"throws at runtime - Type-level discriminant union (
I18nErrorCode) is exhaustive - Back-compat alias
I18nCodefor pre-split call-sites
Usage: Basic Example
import { I18N_CODES, I18nError } from "@suaveplan/i18n-shared";
throw new I18nError(I18N_CODES.MISSING_KEY, "i18n.missing", {
context: { key: "checkout.button" },
});
Usage: Advanced Example
import {
I18N_CODES,
I18nError,
type I18nErrorCode,
} from "@suaveplan/i18n-shared/errors";
function categorise(err: I18nError): "user" | "config" | "system" {
const code: I18nErrorCode = err.code;
switch (code) {
case I18N_CODES.MISSING_KEY:
case I18N_CODES.INVALID_LOCALE:
return "user";
case I18N_CODES.INVALID_OPTIONS:
case I18N_CODES.INVALID_FORMATTER_OPTIONS:
case I18N_CODES.UNSUPPORTED_LOCALE:
case I18N_CODES.PLUGIN_ALREADY_INSTALLED:
return "config";
default:
return "system";
}
}
API Reference
I18N_CODES
Frozen Record of code-name → "I18N.<code>" literal. The 18 codes:
| Cluster | Code | Failure |
|---|---|---|
| Parser | MISSING_KEY |
Lookup found no value |
| Parser | INVALID_LOCALE |
Locale tag is not BCP-47 parseable |
| Parser | INVALID_OPTIONS |
Catalog/parser options failed validation |
| Parser | LOADER_FAILED |
Async catalog loader rejected |
| Parser | INTERPOLATION_ERROR |
Placeholder render threw |
| Parser | PLURAL_RULE_NOT_FOUND |
No plural rule for the locale |
| Parser | NESTING_DEPTH_EXCEEDED |
Recursion exceeded maxDepth |
| Backend | BACKEND_READ_FAILED |
Read raised |
| Backend | BACKEND_WRITE_FAILED |
Write raised |
| Backend | BACKEND_NOT_REGISTERED |
Lookup against unknown backend |
| Backend | BACKEND_LOCALE_NOT_AVAILABLE |
Locale missing from backend |
| Backend | BACKEND_TIMEOUT |
Backend exceeded its deadline |
| Backend | CHAINED_BACKEND_ALL_FAILED |
Every chain candidate raised |
| Formatter | INVALID_FORMATTER_OPTIONS |
Formatter opts rejected |
| Formatter | UNSUPPORTED_LOCALE |
Locale not in supported set |
| Formatter | FORMATTER_CACHE_OVERFLOW |
LRU cap exceeded |
| Detector | DETECTOR_CHAIN_EXHAUSTED |
No detector returned a locale |
| Plugin | PLUGIN_ALREADY_INSTALLED |
Duplicate plugin registration |
I18nErrorCode
type I18nErrorCode = (typeof I18N_CODES)[keyof typeof I18N_CODES] —
discriminant union over all 18 fully-qualified strings.
I18nCode
Back-compat alias for I18nErrorCode. New code SHOULD prefer
I18nErrorCode.
Implementation Notes
- The registry is constructed via
defineCodes("I18N", [...] as const)from@suaveplan/error, which freezes the result withObject.freezeand infers the literal-typedRecord. - The four "new" codes (
BACKEND_TIMEOUT,CHAINED_BACKEND_ALL_FAILED,DETECTOR_CHAIN_EXHAUSTED,FORMATTER_CACHE_OVERFLOW) model failure modes the per-package split surfaces — they were latent in the monolith but never formally typed. - Cardinality 18 is asserted at type level via
expectTypeOfin the unit test so future drift is caught at typecheck time.