@urbicon-ui/i18n 6.3.10 → 6.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +35 -0
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -302,6 +302,41 @@ import type {
302
302
 
303
303
  `en`, `de` ship data. `fr`, `es`, `it`, `nl` are declared target locales (in the `Locale` union / `SUPPORTED_LOCALES`) — register your own bundles for them via `createPackageI18n`.
304
304
 
305
+ ## Translation Auditing
306
+
307
+ Three layers catch i18n problems — untranslated strings, unused keys, and copy that bypassed i18n entirely. The data-level audit and the runtime sink ship from the main entry (dependency-free, usable in a Vitest test); the source scanner lives on the dev-only `@urbicon-ui/i18n/audit` subpath; the `urbicon i18n` CLI (`@urbicon-ui/design`) is the filesystem front end over all three.
308
+
309
+ **1. Data-level parity & quality** — `auditTranslations(packageName, bundles)` diffs locale bundles for missing/extra keys, empty values, interpolation-param drift (`{{name}}` in one locale but not another), value-equals-key placeholders, and malformed / CLDR-incomplete `_plural` objects. Pure and deterministic — run it as a test (the richer successor to `validatePackageTranslations`, kept for back-compat):
310
+
311
+ ```ts
312
+ import { auditTranslations } from '@urbicon-ui/i18n';
313
+ import { appTranslations } from '$lib/i18n';
314
+
315
+ it('translations are in parity', () => {
316
+ expect(auditTranslations('app', appTranslations).ok).toBe(true);
317
+ });
318
+ ```
319
+
320
+ **2. Runtime missing-key sink** — `onMissingKey` (via `configureI18n`) fires when a key resolves nowhere and would render as its raw string. `createMissingKeyCollector()` packages it for tests/E2E — assert that nothing rendered a raw key, including dynamically-built keys a static scan can't see:
321
+
322
+ ```ts
323
+ import { configureI18n, createMissingKeyCollector } from '@urbicon-ui/i18n';
324
+
325
+ const misses = createMissingKeyCollector();
326
+ configureI18n({ onMissingKey: misses.onMissingKey });
327
+ // … render / exercise the app …
328
+ expect(misses.isClean()).toBe(true);
329
+ ```
330
+
331
+ **3. Source scan & CLI** — `urbicon i18n` scans your sources for **unused** keys (defined but referenced nowhere), **used-but-undefined** keys (a typo that renders raw), and **hardcoded** UI strings. Run it under Bun (it loads `.ts` locale bundles):
332
+
333
+ ```bash
334
+ urbicon i18n audit src/ --translations src/lib/translations # parity + unused + hardcoded
335
+ urbicon i18n unused --dynamic-keys 'errors.*' --json # just the scan, allowlisting dynamic key families
336
+ ```
337
+
338
+ It gates (exit 1) on parity errors + used-but-undefined; unused keys and hardcoded strings are advisory (`--strict` gates them too). The pure scanner core — `scanSources`, `findUnusedKeys`, `findHardcodedStrings` — is on the `@urbicon-ui/i18n/audit` subpath for programmatic use, with `typescript` + `svelte` as optional peers it lazily imports. See the [CI gate template](../design/templates/ci-github.yml).
339
+
305
340
  ## Development
306
341
 
307
342
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbicon-ui/i18n",
3
- "version": "6.3.10",
3
+ "version": "6.3.12",
4
4
  "description": "Runes-based localization for Svelte 5 apps and the Urbicon UI design system",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,7 +24,7 @@
24
24
  "@sveltejs/package": "^2.5.8",
25
25
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
26
26
  "@types/node": "^25.9.4",
27
- "@urbicon-ui/shared-types": "6.3.10",
27
+ "@urbicon-ui/shared-types": "6.3.12",
28
28
  "prettier": "^3.8.4",
29
29
  "prettier-plugin-svelte": "^4.1.1",
30
30
  "prettier-plugin-tailwindcss": "^0.8.0",