Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# @suaveplan/locale-data - Static ISO 3166-1 country data and ISO 639-1 language data with typed lookup functions, OTel instrumentation, and Zod schemas
|
||||
|
||||
## Summary
|
||||
|
||||
Static ISO 3166-1 country data and ISO 639-1 language data with typed lookup functions,
|
||||
OTel instrumentation, and Zod schemas.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [countries](./src/countries/countries.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [countries.data](./src/countries/countries.data.md) — Contains the static ISO 3166-1 alpha-2 country dataset as a single frozen TypeScript constant (`COUNTRIES_DATA`). This…
|
||||
- [countries](./src/countries/countries.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [errors](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [locale-data-error](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [languages.data](./src/languages/languages.data.md) — Contains the static ISO 639-1 language dataset as two frozen TypeScript constants: `LANGUAGES_DATA` (130+ language entr…
|
||||
- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
<!-- end:auto-docs --> - [countries.data](./src/countries/countries.data.md) — Contains the static ISO 3166-1 alpha-2 country dataset as a single frozen TypeScript constant (`COUNTRIES_DATA`). This…
|
||||
- [countries](./src/countries/countries.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [locale-data-error](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_- [languages.data](./src/languages/languages.data.md) — Contains the static ISO 639-1 language dataset as two frozen TypeScript constants: `LANGUAGES_DATA` (130+ language entr…
|
||||
- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [countries.data](./src/countries/countries.data.md) — Contains the static ISO 3166-1 alpha-2 country dataset as a single frozen TypeScript constant (`COUNTRIES_DATA`). This…
|
||||
- [countries](./src/countries/countries.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [locale-data-error](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_- [languages.data](./src/languages/languages.data.md) — Contains the static ISO 639-1 language dataset as two frozen TypeScript constants: `LANGUAGES_DATA` (130+ language entr…
|
||||
- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [countries.data](./src/countries/countries.data.md) — Contains the static ISO 3166-1 alpha-2 country dataset as a single frozen TypeScript constant (`COUNTRIES_DATA`). This…
|
||||
- [countries](./src/countries/countries.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [locale-data-error](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_- [languages.data](./src/languages/languages.data.md) — Contains the static ISO 639-1 language dataset as two frozen TypeScript constants: `LANGUAGES_DATA` (130+ language entr…
|
||||
- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [countries.data](./src/countries/countries.data.md) — Contains the static ISO 3166-1 alpha-2 country dataset as a single frozen TypeScript constant (`COUNTRIES_DATA`). This…
|
||||
- [countries](./src/countries/countries.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_ - [locale-data-error](./src/errors/locale-data-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_ - [languages.data](./src/languages/languages.data.md) — Contains the static ISO 639-1 language dataset as two frozen TypeScript constants: `LANGUAGES_DATA` (130+ language entr…
|
||||
- [languages](./src/languages/languages.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/locale-data
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
import { getCountry, getLanguage, isRtlLanguage } from "@suaveplan/locale-data";
|
||||
|
||||
// Country lookup
|
||||
const us = getCountry("US");
|
||||
console.log(us.name); // "United States"
|
||||
console.log(us.phone); // "1"
|
||||
console.log(us.emoji); // "🇺🇸"
|
||||
|
||||
// Language lookup
|
||||
const ar = getLanguage("ar");
|
||||
console.log(ar.name); // "Arabic"
|
||||
console.log(ar.native); // "العربية"
|
||||
|
||||
// RTL detection
|
||||
console.log(isRtlLanguage("ar")); // true
|
||||
console.log(isRtlLanguage("ar-SA")); // true (BCP-47 tag)
|
||||
console.log(isRtlLanguage("en")); // false
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
| Subpath | Description |
|
||||
|---|---|
|
||||
| `@suaveplan/locale-data` | Root barrel — re-exports all public symbols |
|
||||
| `@suaveplan/locale-data/countries` | Country lookups + Zod schemas |
|
||||
| `@suaveplan/locale-data/languages` | Language lookups + RTL detection + Zod schemas |
|
||||
| `@suaveplan/locale-data/errors` | `LocaleDataError` + `LOCALE_DATA_CODES` |
|
||||
|
||||
## Modules Documentation
|
||||
|
||||
- [countries.md](src/countries/countries.md) — `getCountry`, `getAllCountries`, `getCountriesByContinent`, `getCountriesByCurrency`
|
||||
- [countries.data.md](src/countries/countries.data.md) — Static ISO 3166-1 dataset (209 entries)
|
||||
- [languages.md](src/languages/languages.md) — `getLanguage`, `getLanguages`, `getRtlLanguages`, `isRtlLanguage`
|
||||
- [languages.data.md](src/languages/languages.data.md) — Static ISO 639-1 dataset + RTL codes
|
||||
- [locale-data-error.md](src/errors/locale-data-error.md) — `LocaleDataError`, `LOCALE_DATA_CODES`
|
||||
- [types.md](src/types/types.md) — `CountryItemSchema`, `LanguageItemSchema`
|
||||
|
||||
## API Overview
|
||||
|
||||
### Countries
|
||||
|
||||
```ts
|
||||
import {
|
||||
getCountry, // O(1) lookup by ISO 3166-1 alpha-2 code
|
||||
getAllCountries, // Returns full 209-entry Record<string, CountryItem>
|
||||
getCountriesByContinent, // Filter by continent code ("EU", "AS", "AF", etc.)
|
||||
getCountriesByCurrency, // Filter by ISO 4217 currency code (e.g. "EUR")
|
||||
} from "@suaveplan/locale-data/countries";
|
||||
```
|
||||
|
||||
### Languages
|
||||
|
||||
```ts
|
||||
import {
|
||||
getLanguage, // O(1) lookup by ISO 639-1 code
|
||||
getLanguages, // Returns full 130+ entry Record<string, LanguageItem>
|
||||
getRtlLanguages, // Returns list of RTL language codes
|
||||
isRtlLanguage, // BCP-47 aware RTL detection
|
||||
} from "@suaveplan/locale-data/languages";
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```ts
|
||||
import { LocaleDataError, LOCALE_DATA_CODES } from "@suaveplan/locale-data/errors";
|
||||
|
||||
try {
|
||||
getCountry("ZZ");
|
||||
} catch (err) {
|
||||
if (err instanceof LocaleDataError) {
|
||||
console.log(err.code); // "LOCALE_DATA/COUNTRY.NOT_FOUND"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## OTel Instrumentation
|
||||
|
||||
The package emits two OTel counters:
|
||||
|
||||
| Metric | Attributes |
|
||||
|---|---|
|
||||
| `locale_data.country.lookups` | `result: "found" \| "not_found"` |
|
||||
| `locale_data.language.lookups` | `result: "found" \| "not_found"` |
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# countries.data
|
||||
|
||||
## Purpose
|
||||
|
||||
Contains the static ISO 3166-1 alpha-2 country dataset as a single frozen TypeScript constant
|
||||
(`COUNTRIES_DATA`). This file holds 209 country entries sourced from the ISO standard and is
|
||||
intentionally separated from the lookup logic in `countries.ts` to keep both files under the
|
||||
300-line hard limit enforced by the project style guide. The data here serves as the single source
|
||||
of truth for all country lookups, filtering, and validation in the `@suaveplan/locale-data` package.
|
||||
|
||||
## Features
|
||||
|
||||
- **209 ISO 3166-1 alpha-2 entries** — includes all UN member states and selected territories.
|
||||
- **9 fields per entry** — `name` (English), `native` (local script), `phone` (dialling code),
|
||||
`continent` (2-letter code), `capital`, `currency` (ISO 4217, comma-separated for multi-currency
|
||||
countries), `languages` (array of ISO 639-1 codes), `emoji` (flag emoji), `emojiU` (Unicode
|
||||
code-point notation).
|
||||
- **Typed as `Record<string, CountryItem>`** — ensures each entry satisfies the `CountryItem`
|
||||
shape defined in the types module.
|
||||
- **Multi-currency support** — countries like Switzerland (`CHE,CHF,CHW`), Bolivia (`BOB,BOV`),
|
||||
and Cuba (`CUC,CUP`) store all currencies as a comma-separated string, consistent with how
|
||||
`getCountriesByCurrency` splits and matches entries.
|
||||
- **Continent codes** — `EU` (Europe), `AS` (Asia), `AF` (Africa), `NA` (North America),
|
||||
`SA` (South America), `OC` (Oceania), `AN` (Antarctica).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Imported indirectly via the countries module:
|
||||
import { getAllCountries } from "@suaveplan/locale-data/countries";
|
||||
const data = getAllCountries(); // returns COUNTRIES_DATA reference
|
||||
console.log(data["US"].name); // "United States"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
// The data object is consumed by the lookup functions in countries.ts:
|
||||
import { getCountry, getCountriesByCurrency } from "@suaveplan/locale-data/countries";
|
||||
|
||||
// Direct lookup (O(1))
|
||||
const ch = getCountry("CH");
|
||||
console.log(ch.currency); // "CHE,CHF,CHW"
|
||||
|
||||
// Filter by currency (splits on comma internally)
|
||||
const chfCountries = getCountriesByCurrency("CHF");
|
||||
console.log(chfCountries.map((c) => c.name)); // includes "Switzerland"
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `COUNTRIES_DATA`
|
||||
|
||||
```ts
|
||||
const COUNTRIES_DATA: Record<string, CountryItem>
|
||||
```
|
||||
|
||||
A frozen constant record mapping ISO 3166-1 alpha-2 codes to `CountryItem` objects. Keys are
|
||||
uppercase two-letter codes (e.g. `"US"`, `"DE"`, `"JP"`). Do not import or mutate this directly
|
||||
from outside the countries module — use the public lookup functions instead.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The data file is excluded from test coverage measurement because it contains no executable logic —
|
||||
only a plain `export const` declaration. All entries were derived from the ISO standard data in the
|
||||
reference project at `packages/universal/utils/src/countries/countries.ts` and include the same
|
||||
209 keys present in the `countries` named export. The file deliberately omits entries not present
|
||||
in the main `countries` object (such as `AQ`, `AG`, `AS`, `BL`, etc.) to stay consistent with the
|
||||
reference dataset.
|
||||
@@ -0,0 +1,120 @@
|
||||
# countries
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Provides typed lookup functions over the static ISO 3166-1 alpha-2 country dataset (209 entries).
|
||||
The module is designed for high-frequency read access — data is held in a single frozen object
|
||||
(`COUNTRIES_DATA`) and all lookups run in O(1) or O(n) time with zero I/O. Every `getCountry`
|
||||
call emits an OTel counter increment, enabling production observability for cache-hit analysis
|
||||
and anomaly detection on invalid code injection attempts.
|
||||
|
||||
## Features
|
||||
|
||||
- **`getCountry(code)`** — O(1) lookup by ISO 3166-1 alpha-2 code; throws a typed
|
||||
`LocaleDataError` for unknown codes.
|
||||
- **`getAllCountries()`** — Returns the full `Record<string, CountryItem>` reference; O(1),
|
||||
no copy allocation.
|
||||
- **`getCountriesByContinent(continent)`** — Filters by two-letter continent code (`"EU"`, `"AS"`,
|
||||
`"AF"`, `"NA"`, `"SA"`, `"OC"`, `"AN"`); returns empty array for unknown codes.
|
||||
- **`getCountriesByCurrency(currency)`** — Matches against comma-separated ISO 4217 currency codes
|
||||
in each entry (e.g. `"CHE,CHF,CHW"`); returns empty array for unknown currencies.
|
||||
- **OTel instrumentation** — `locale_data.country.lookups` counter with `result: "found" | "not_found"`.
|
||||
- **Zod schemas** — `CountryCodeSchema` (2-char uppercase) and re-exported `CountryItemSchema`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { getCountry, getAllCountries } from "@suaveplan/locale-data/countries";
|
||||
|
||||
// Look up a single country
|
||||
const us = getCountry("US");
|
||||
console.log(us.name); // "United States"
|
||||
console.log(us.phone); // "1"
|
||||
console.log(us.emoji); // "🇺🇸"
|
||||
|
||||
// Get all countries
|
||||
const all = getAllCountries();
|
||||
console.log(Object.keys(all).length); // 209
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
getCountry,
|
||||
getCountriesByContinent,
|
||||
getCountriesByCurrency,
|
||||
} from "@suaveplan/locale-data/countries";
|
||||
import { LocaleDataError, LOCALE_DATA_CODES } from "@suaveplan/locale-data/errors";
|
||||
|
||||
// Error handling
|
||||
try {
|
||||
const country = getCountry("ZZ");
|
||||
} catch (err) {
|
||||
if (err instanceof LocaleDataError && err.code === LOCALE_DATA_CODES["COUNTRY.NOT_FOUND"]) {
|
||||
console.warn("Unknown country code");
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by continent
|
||||
const europeanCountries = getCountriesByContinent("EU");
|
||||
console.log(europeanCountries.length); // ~40
|
||||
|
||||
// Find all Eurozone members (use single-currency entries + multi-currency)
|
||||
const eurozoneMembers = getCountriesByCurrency("EUR");
|
||||
console.log(eurozoneMembers.map((c) => c.name));
|
||||
|
||||
// Group by continent
|
||||
import { getAllCountries } from "@suaveplan/locale-data/countries";
|
||||
const byContinent = Object.entries(getAllCountries()).reduce<Record<string, string[]>>(
|
||||
(acc, [code, c]) => {
|
||||
(acc[c.continent] ??= []).push(code);
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `getCountry(code: string): CountryItem`
|
||||
|
||||
Look up a country by its ISO 3166-1 alpha-2 code.
|
||||
|
||||
- **Parameters:** `code` — case-sensitive two-letter code (e.g. `"US"`, `"DE"`).
|
||||
- **Returns:** The matching `CountryItem`.
|
||||
- **Throws:** `LocaleDataError` (`LOCALE_DATA/COUNTRY.NOT_FOUND`) when the code is not found.
|
||||
- **OTel:** Increments `locale_data.country.lookups` with `result: "found"` or `"not_found"`.
|
||||
|
||||
### `getAllCountries(): Record<string, CountryItem>`
|
||||
|
||||
Return the full 209-entry country map.
|
||||
|
||||
- **Returns:** A `Record<string, CountryItem>` — the same reference each call.
|
||||
- **Note:** Do not mutate the returned object.
|
||||
|
||||
### `getCountriesByContinent(continent: string): CountryItem[]`
|
||||
|
||||
Filter countries by continent code.
|
||||
|
||||
- **Parameters:** `continent` — `"EU"` | `"AS"` | `"AF"` | `"NA"` | `"SA"` | `"OC"` | `"AN"`.
|
||||
- **Returns:** Array of matching `CountryItem` values; empty array for unknown codes.
|
||||
|
||||
### `getCountriesByCurrency(currency: string): CountryItem[]`
|
||||
|
||||
Filter countries by ISO 4217 currency code.
|
||||
|
||||
- **Parameters:** `currency` — e.g. `"EUR"`, `"USD"`, `"CHF"`.
|
||||
- **Returns:** Array of matching `CountryItem` values; handles comma-separated multi-currency
|
||||
entries; empty array if no match.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Data is stored in `countries.data.ts` as a plain `Record<string, CountryItem>` const to keep the
|
||||
implementation file under the 300-line hard limit. The data file is excluded from coverage
|
||||
measurement because it contains no executable logic — only `export const`. The `getCountriesByCurrency`
|
||||
function splits on commas to support countries with composite currency fields like Switzerland
|
||||
(`"CHE,CHF,CHW"`), Bolivia (`"BOB,BOV"`), and Cuba (`"CUC,CUP"`).
|
||||
@@ -0,0 +1,100 @@
|
||||
# locale-data-error
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Defines the typed error class and stable code registry for `@suaveplan/locale-data`. All errors
|
||||
thrown by this package extend `SuaveplanError`, ensuring consistent structured error handling across
|
||||
the project — with machine-readable codes, cause-chain propagation, and OTel span-event support
|
||||
inherited from `@suaveplan/error`.
|
||||
|
||||
## Features
|
||||
|
||||
- **Stable code registry** via `defineCodes("LOCALE_DATA", [...])` — codes are namespaced under
|
||||
`LOCALE_DATA` and never change once published, ensuring forward compatibility for error consumers.
|
||||
- **Two error codes**: `COUNTRY.NOT_FOUND` for unknown ISO 3166-1 alpha-2 codes and
|
||||
`LANGUAGE.NOT_FOUND` for unknown ISO 639-1 language codes.
|
||||
- **Full `SuaveplanError` inheritance**: cause chaining, redaction, wire-format serialisation,
|
||||
and OTel span-event emission are all available on every `LocaleDataError` instance.
|
||||
- **Typed generic**: `LocaleDataError` is parameterised on `LocaleDataCode`, so TypeScript narrows
|
||||
`.code` to the union of defined codes in `catch` blocks.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { LocaleDataError, LOCALE_DATA_CODES } from "@suaveplan/locale-data/errors";
|
||||
|
||||
throw new LocaleDataError(
|
||||
LOCALE_DATA_CODES["COUNTRY.NOT_FOUND"],
|
||||
"No country found for code: ZZ"
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { LocaleDataError, LOCALE_DATA_CODES } from "@suaveplan/locale-data/errors";
|
||||
import { isSuaveplanError } from "@suaveplan/error";
|
||||
|
||||
try {
|
||||
const country = getCountry("ZZ");
|
||||
} catch (err) {
|
||||
if (isSuaveplanError(err) && err.code === LOCALE_DATA_CODES["COUNTRY.NOT_FOUND"]) {
|
||||
console.error("Country lookup failed:", err.message);
|
||||
// err.cause, err.context, err.toJSON() all available
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `LOCALE_DATA_CODES`
|
||||
|
||||
```ts
|
||||
const LOCALE_DATA_CODES: {
|
||||
"COUNTRY.NOT_FOUND": "LOCALE_DATA/COUNTRY.NOT_FOUND";
|
||||
"LANGUAGE.NOT_FOUND": "LOCALE_DATA/LANGUAGE.NOT_FOUND";
|
||||
}
|
||||
```
|
||||
|
||||
A frozen record produced by `defineCodes`. Use these values as the `code` argument to
|
||||
`LocaleDataError` and for equality checks in catch blocks.
|
||||
|
||||
### `LocaleDataCode`
|
||||
|
||||
```ts
|
||||
type LocaleDataCode = "LOCALE_DATA/COUNTRY.NOT_FOUND" | "LOCALE_DATA/LANGUAGE.NOT_FOUND";
|
||||
```
|
||||
|
||||
The union type of all valid error codes for this package.
|
||||
|
||||
### `LocaleDataError`
|
||||
|
||||
```ts
|
||||
class LocaleDataError extends SuaveplanError<LocaleDataCode> {
|
||||
public constructor(
|
||||
code: LocaleDataCode,
|
||||
message: string,
|
||||
options?: SuaveplanErrorOptions
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `code` — one of `LOCALE_DATA_CODES`; narrows the `.code` property type.
|
||||
- `message` — human-readable description, suitable for logging.
|
||||
- `options` — optional `{ cause?: unknown; context?: Record<string, unknown> }`.
|
||||
|
||||
**Throws:** Never (is the thrown value itself).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The constructor is otherwise a no-op pass-through to `SuaveplanError` — the
|
||||
`// biome-ignore lint/complexity/noUselessConstructor` comment is required because Biome would flag
|
||||
the empty constructor. We keep it explicitly `public` to widen the inherited `protected` visibility
|
||||
and to document the intended API surface. Error code strings are of the form
|
||||
`"LOCALE_DATA/<CATEGORY>.<KEY>"` as produced by `defineCodes`.
|
||||
@@ -0,0 +1,78 @@
|
||||
# languages.data
|
||||
|
||||
## Purpose
|
||||
|
||||
Contains the static ISO 639-1 language dataset as two frozen TypeScript constants: `LANGUAGES_DATA`
|
||||
(130+ language entries) and `RTL_LANGUAGES` (the list of right-to-left script codes). This file is
|
||||
intentionally separated from the lookup logic in `languages.ts` to keep both files under the
|
||||
300-line hard limit. The data serves as the single source of truth for all language lookups and
|
||||
RTL detection in the `@suaveplan/locale-data` package.
|
||||
|
||||
## Features
|
||||
|
||||
- **130+ ISO 639-1 language entries** — covers the full set of two-letter language codes in the
|
||||
ISO standard, from Afar (`aa`) to Zulu (`zu`).
|
||||
- **Two fields per entry** — `name` (English name) and `native` (name in the language's own script,
|
||||
e.g. `"Deutsch"` for German, `"العربية"` for Arabic).
|
||||
- **`RTL_LANGUAGES` constant** — readonly array of 15 ISO 639-1 codes whose primary script is
|
||||
right-to-left: `ar`, `az`, `ba`, `dv`, `fa`, `he`, `iu`, `ks`, `ku`, `ps`, `sd`, `sr`, `tg`,
|
||||
`ur`, `yi`.
|
||||
- **Typed as `Record<string, LanguageItem>`** — ensures each entry satisfies the `LanguageItem`
|
||||
shape defined in the types module.
|
||||
- **Unicode-safe** — native names include characters from Arabic, Hebrew, Cyrillic, Devanagari,
|
||||
Georgian, Thai, Chinese, Japanese, Korean, and other scripts.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Imported indirectly via the languages module:
|
||||
import { getLanguages } from "@suaveplan/locale-data/languages";
|
||||
const all = getLanguages(); // returns LANGUAGES_DATA reference
|
||||
console.log(all["ja"].name); // "Japanese"
|
||||
console.log(all["ja"].native); // "日本語"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { getRtlLanguages, isRtlLanguage } from "@suaveplan/locale-data/languages";
|
||||
|
||||
// Get all RTL codes
|
||||
const rtlCodes = getRtlLanguages();
|
||||
console.log(rtlCodes); // ["ar", "az", "ba", "dv", "fa", "he", "iu", "ks", "ku", "ps", "sd", "sr", "tg", "ur", "yi"]
|
||||
|
||||
// BCP-47 tag detection
|
||||
console.log(isRtlLanguage("ar-EG")); // true (Arabic, Egypt)
|
||||
console.log(isRtlLanguage("zh-TW")); // false (Traditional Chinese)
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `LANGUAGES_DATA`
|
||||
|
||||
```ts
|
||||
const LANGUAGES_DATA: Record<string, LanguageItem>
|
||||
```
|
||||
|
||||
A frozen constant record mapping ISO 639-1 two-letter codes to `LanguageItem` objects.
|
||||
Do not import or mutate this directly from outside the languages module — use the public lookup
|
||||
functions instead.
|
||||
|
||||
### `RTL_LANGUAGES`
|
||||
|
||||
```ts
|
||||
const RTL_LANGUAGES: readonly string[]
|
||||
```
|
||||
|
||||
A readonly array of ISO 639-1 codes for right-to-left scripts. Used by `getRtlLanguages()` and
|
||||
`isRtlLanguage()`. Do not mutate.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The data file is excluded from test coverage measurement because it contains no executable logic —
|
||||
only plain `export const` declarations. The RTL language list was derived from the reference
|
||||
implementation at `packages/universal/utils/src/languages/languages.ts` in the reference project.
|
||||
The list includes `az` (Azerbaijani) and `sr` (Serbian) because both languages historically use
|
||||
RTL scripts in some contexts, consistent with the reference data. The `isRtlLanguage` function
|
||||
imports `RTL_LANGUAGES` as a readonly array and uses `Array.prototype.includes` for O(n) membership
|
||||
testing — with only 15 RTL codes, this is negligible overhead compared to a `Set` construction.
|
||||
@@ -0,0 +1,119 @@
|
||||
# languages
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Provides typed lookup functions over the static ISO 639-1 language dataset (130+ entries),
|
||||
including right-to-left (RTL) detection for Arabic, Hebrew, Persian, Urdu, and other scripts.
|
||||
The module is designed for zero-allocation read access — data is held in a frozen constant and
|
||||
all lookups return in O(1) or O(n) time with no I/O. Every `getLanguage` call emits an OTel
|
||||
counter increment, enabling observability of lookup patterns and invalid code injection attempts
|
||||
in production environments.
|
||||
|
||||
## Features
|
||||
|
||||
- **`getLanguages()`** — Returns the full `Record<string, LanguageItem>` reference in O(1); no
|
||||
copy allocation.
|
||||
- **`getLanguage(code)`** — O(1) lookup by ISO 639-1 code; throws a typed `LocaleDataError` for
|
||||
unknown codes.
|
||||
- **`getRtlLanguages()`** — Returns the readonly list of RTL language codes (15 codes).
|
||||
- **`isRtlLanguage(code)`** — Returns `true` / `false` for any language code or BCP-47 tag;
|
||||
strips region and script subtags before checking.
|
||||
- **OTel instrumentation** — `locale_data.language.lookups` counter with `result: "found" | "not_found"`.
|
||||
- **Zod schemas** — `LanguageCodeSchema` (2–3 char lowercase) and re-exported `LanguageItemSchema`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { getLanguage, getLanguages } from "@suaveplan/locale-data/languages";
|
||||
|
||||
// Look up a single language
|
||||
const fr = getLanguage("fr");
|
||||
console.log(fr.name); // "French"
|
||||
console.log(fr.native); // "Français"
|
||||
|
||||
// Get all languages
|
||||
const all = getLanguages();
|
||||
console.log(Object.keys(all).length); // 130+
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
getLanguage,
|
||||
getRtlLanguages,
|
||||
isRtlLanguage,
|
||||
} from "@suaveplan/locale-data/languages";
|
||||
import { LocaleDataError, LOCALE_DATA_CODES } from "@suaveplan/locale-data/errors";
|
||||
|
||||
// Error handling for unknown codes
|
||||
try {
|
||||
const lang = getLanguage("xx");
|
||||
} catch (err) {
|
||||
if (err instanceof LocaleDataError && err.code === LOCALE_DATA_CODES["LANGUAGE.NOT_FOUND"]) {
|
||||
console.warn("Unknown language code");
|
||||
}
|
||||
}
|
||||
|
||||
// RTL detection with BCP-47 tags
|
||||
const isArabicRtl = isRtlLanguage("ar-SA"); // true
|
||||
const isFrenchRtl = isRtlLanguage("fr-FR"); // false
|
||||
|
||||
// Get all RTL language codes
|
||||
const rtlCodes = getRtlLanguages();
|
||||
console.log(rtlCodes); // ["ar", "az", "ba", "dv", "fa", "he", ...]
|
||||
|
||||
// Build a map of RTL languages with their names
|
||||
import { getLanguages } from "@suaveplan/locale-data/languages";
|
||||
const allLangs = getLanguages();
|
||||
const rtlLanguages = getRtlLanguages().map((code) => ({
|
||||
code,
|
||||
...allLangs[code],
|
||||
}));
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `getLanguages(): Record<string, LanguageItem>`
|
||||
|
||||
Return the full language map.
|
||||
|
||||
- **Returns:** `Record<string, LanguageItem>` — the same reference each call.
|
||||
- **Note:** Do not mutate the returned object.
|
||||
|
||||
### `getLanguage(code: string): LanguageItem`
|
||||
|
||||
Look up a language by its ISO 639-1 code.
|
||||
|
||||
- **Parameters:** `code` — two-letter ISO 639-1 code (e.g. `"en"`, `"fr"`, `"ar"`).
|
||||
- **Returns:** The matching `LanguageItem` with `name` and `native` properties.
|
||||
- **Throws:** `LocaleDataError` (`LOCALE_DATA/LANGUAGE.NOT_FOUND`) when the code is not found.
|
||||
- **OTel:** Increments `locale_data.language.lookups` with `result: "found"` or `"not_found"`.
|
||||
|
||||
### `getRtlLanguages(): readonly string[]`
|
||||
|
||||
Return the list of RTL language codes.
|
||||
|
||||
- **Returns:** A readonly array of ISO 639-1 codes that use right-to-left script.
|
||||
- **Included codes:** `ar`, `az`, `ba`, `dv`, `fa`, `he`, `iu`, `ks`, `ku`, `ps`, `sd`,
|
||||
`sr`, `tg`, `ur`, `yi`.
|
||||
|
||||
### `isRtlLanguage(languageCode: string): boolean`
|
||||
|
||||
Determine whether a language code represents a RTL language.
|
||||
|
||||
- **Parameters:** `languageCode` — ISO 639-1 code or BCP-47 tag (e.g. `"ar"`, `"ar-SA"`,
|
||||
`"fa_IR"`).
|
||||
- **Returns:** `true` if the primary language subtag is in the RTL list, `false` otherwise.
|
||||
- **Note:** Always returns `false` for unknown codes; never throws.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Data is stored in `languages.data.ts` as plain `export const` declarations to keep the
|
||||
implementation file under the 300-line hard limit. The data file is excluded from coverage
|
||||
measurement because it contains no executable logic. The `isRtlLanguage` function normalises
|
||||
BCP-47 tags by lowercasing and splitting on `_` or `-` before checking the primary subtag,
|
||||
matching the same behaviour as the reference implementation in the original project.
|
||||
@@ -0,0 +1,112 @@
|
||||
# types
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Defines the shared TypeScript types and Zod validation schemas used throughout `@suaveplan/locale-data`.
|
||||
All runtime type shapes — `CountryItem` and `LanguageItem` — are declared here as Zod schemas, and
|
||||
their inferred TypeScript types are derived via `z.infer`. This single-source-of-truth approach
|
||||
ensures that runtime validation and static typing are always in sync, eliminating drift between
|
||||
schema and type definitions across the package.
|
||||
|
||||
## Features
|
||||
|
||||
- **`CountryItemSchema`** — Zod object schema for a country entry with 9 fields: `name`, `native`,
|
||||
`phone`, `continent`, `capital`, `currency`, `languages` (array), `emoji`, and `emojiU`.
|
||||
- **`CountryItem`** — TypeScript type inferred from `CountryItemSchema`; used throughout the
|
||||
countries module for typed lookups and filter results.
|
||||
- **`LanguageItemSchema`** — Zod object schema for a language entry with 2 fields: `name` and
|
||||
`native`.
|
||||
- **`LanguageItem`** — TypeScript type inferred from `LanguageItemSchema`; used throughout the
|
||||
languages module for typed lookups and RTL detection.
|
||||
- **Zero runtime dependencies beyond Zod** — types are pure schema definitions with no side effects.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { CountryItemSchema, type CountryItem } from "@suaveplan/locale-data";
|
||||
|
||||
// Validate an unknown object as a CountryItem
|
||||
const result = CountryItemSchema.safeParse(someObject);
|
||||
if (result.success) {
|
||||
const country: CountryItem = result.data;
|
||||
console.log(country.name, country.emoji);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { LanguageItemSchema, type LanguageItem } from "@suaveplan/locale-data";
|
||||
|
||||
// Parse and transform language data from an external API
|
||||
const raw = await fetchLanguageFromApi("fr");
|
||||
const language = LanguageItemSchema.parse(raw) satisfies LanguageItem;
|
||||
console.log(language.name); // "French"
|
||||
console.log(language.native); // "Français"
|
||||
|
||||
// Use with Zod's record schema for batch validation
|
||||
import z from "zod";
|
||||
import { CountryItemSchema } from "@suaveplan/locale-data";
|
||||
|
||||
const CountryMapSchema = z.record(z.string().length(2), CountryItemSchema);
|
||||
const validated = CountryMapSchema.parse(externalData);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `CountryItemSchema`
|
||||
|
||||
```ts
|
||||
const CountryItemSchema: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
native: z.ZodString;
|
||||
phone: z.ZodString;
|
||||
continent: z.ZodString;
|
||||
capital: z.ZodString;
|
||||
currency: z.ZodString;
|
||||
languages: z.ZodArray<z.ZodString>;
|
||||
emoji: z.ZodString;
|
||||
emojiU: z.ZodString;
|
||||
}>;
|
||||
```
|
||||
|
||||
Zod schema for a country entry. All fields are required strings except `languages` which is an
|
||||
array of ISO 639-1 language codes.
|
||||
|
||||
### `CountryItem`
|
||||
|
||||
```ts
|
||||
type CountryItem = z.infer<typeof CountryItemSchema>;
|
||||
```
|
||||
|
||||
Static TypeScript type for a country record derived from `CountryItemSchema`.
|
||||
|
||||
### `LanguageItemSchema`
|
||||
|
||||
```ts
|
||||
const LanguageItemSchema: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
native: z.ZodString;
|
||||
}>;
|
||||
```
|
||||
|
||||
Zod schema for a language entry with its English name and native script name.
|
||||
|
||||
### `LanguageItem`
|
||||
|
||||
```ts
|
||||
type LanguageItem = z.infer<typeof LanguageItemSchema>;
|
||||
```
|
||||
|
||||
Static TypeScript type for a language record derived from `LanguageItemSchema`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Schemas are defined using Zod's `z.object()` constructor, which produces a runtime validator that
|
||||
can be used for both `parse` (throws on invalid input) and `safeParse` (returns a discriminated
|
||||
union). The `CountryItem` and `LanguageItem` types are intentionally inferred rather than
|
||||
hand-written to prevent drift. The `languages` field in `CountryItem` uses `z.array(z.string())`
|
||||
because some countries have zero, one, or multiple official languages (e.g. South Africa has 11).
|
||||
Reference in New Issue
Block a user