Skip to content

architecture(i18n): add versioned screen-scoped UI translation catalog #1731

Description

@seonghobae

Buyer-visible gap

Protected develop@042b0c70531b229af3acbd0421a2f23098d848b3 has no canonical UI localization/catalog boundary. A default-branch code search finds no product-owned i18n / translation resource implementation, while current UI surfaces continue to embed Korean display strings directly. Draft #1729 makes the gap concrete by adding OIDC pending labels (로그인 중, 로그아웃 중) but correctly refuses to invent a browser-wide catalog or copy mutable translation source from another service.

Naruon needs one product-owned localization bounded context for KO/EN/JA/ZH/VI/ES/DE/FR. This is UI copy authority only. Ontology/concept labels remain a separate semantic-data/ontology registry and must not be merged into the UI translation store.

Domain boundary

Create an explicit UI Localization Catalog bounded context with a small application-facing port. Keep product screen/message semantics in Naruon; do not leak a generic translation framework into every component.

Suggested Ubiquitous Language:

  • screen_key: stable product surface identifier such as settings.identity;
  • message_key: stable semantic UI message within one screen;
  • locale_code: one of the supported BCP 47 language tags for this release;
  • translation_revision: immutable revision of one translated message;
  • resource_version: deployable/catalog version binding a coherent set of revisions;
  • translation_receipt: server response identity containing screen, locale and resource version.

UI translation and ontology/concept label authority are separate bounded contexts. No ontology label is copied into this catalog merely because it is rendered in a screen; the UI may reference ontology labels through their released owner contract where that is actually the semantic source of truth.

Persistence contract

Use a normalized PostgreSQL model, descriptive two-or-more-word snake_case names, and no JSON mega-catalog row. A viable 3NF shape is:

  • ui_message_definition(message_definition_id, screen_key, message_key, source_message, semantic_context, placeholder_schema, created_at, retired_at);
  • ui_translation_revision(translation_revision_id, message_definition_id, locale_code, translated_message, revision_number, review_status, created_at, supersedes_revision_id);
  • ui_resource_version(resource_version_id, version_code, published_at, source_commit_sha, status);
  • ui_resource_binding(resource_version_id, translation_revision_id).

Exact schema may change through ADR, but preserve these invariants:

  1. (screen_key, message_key) is stable product identity; display text is not identity.
  2. Published resource versions are immutable. Corrections create a new revision/version rather than rewriting historical buyer evidence.
  3. Every published version is complete for KO/EN/JA/ZH/VI/ES/DE/FR for every active message included in that version. Missing per-key translations fail publication; they do not silently fall back at render time.
  4. Placeholder names/types are versioned and validated so a translation cannot drop, add or reinterpret interpolation parameters.
  5. User/customer values interpolated into a translation are never persisted back into the catalog, logs or metrics.
  6. No cross-service SQL, source copy, mutable Git-head dependency, or browser-loaded whole catalog.

API and cache boundary

Expose one bounded screen-scoped read contract, for example:

GET /api/ui-translation-resources/{screen_key}?locale={locale_code}

Response should carry at least screen_key, locale_code, resource_version, an immutable keyed message map for that screen only, and an ETag/version identity. The browser caches by (screen_key, locale_code, resource_version) and performs conditional refresh; it must not fetch the complete product catalog at login or hydrate a heavy client-side i18n runtime.

Locale selection order should be explicit and testable: persisted user preference when authorized → session/request preference → standards-compliant Accept-Language negotiation → product default locale. Once a locale is selected, missing active keys are errors, not silent per-message fallback. Unsupported locale input is normalized/rejected at the boundary; it never becomes a dynamic SQL identifier or filesystem path.

Write/publish authority is an operator/content-governance path, not a normal browser API. Runtime application code is read-only against published versions.

DDD / transaction / operability rules

  • Publishing one resource_version is the aggregate transaction boundary; do not hold a transaction open across translation generation, LLM calls, external review, or network work.
  • Translation drafting/review happens before the short publication transaction.
  • Use UPSERT/idempotency only for draft ingestion keyed by explicit revision identity; published rows are immutable.
  • Read path must be async and indexed by screen/locale/version. Measure realistic buyer-page p95 and keep the screen-resource fetch path at ≤20 ms where the deployment environment makes that target applicable; profile query/I/O/cache rather than hiding misses with unrealistic warm-up.
  • Define cache invalidation through version/ETag identity, not time-only browser expiry.
  • Backups/recovery must preserve immutable published versions and provenance.
  • CSAP/SOC 2 evidence should show who published which version, source/reviewer provenance, and rollback to a prior published version without editing it.

#1729 integration slice

Do not broaden #1729 into the catalog implementation. #1729 remains the bounded OIDC interaction-state owner and stays Draft until its own hosted/browser/a11y evidence is valid.

After this catalog reaches protected ancestry, ordinary/non-force adapt the Settings surface so at minimum these existing/new keys are served from the released resource rather than hard-coded Korean text:

  • OIDC login / login pending;
  • OIDC logout / logout pending;
  • provider/action errors that are product copy rather than raw provider text;
  • associated accessible labels/status text.

No #1729 workflow/review receipt transfers to the catalog owner, and catalog evidence does not substitute for rendered OIDC interaction evidence.

UI acceptance

For every supported locale KO/EN/JA/ZH/VI/ES/DE/FR, verify the exact current resource and UI composition in Storybook and real browser E2E:

  • normal, loading, empty, error and permission states where the screen supports them;
  • keyboard traversal, visible focus, screen-reader status/label semantics, touch target behavior;
  • CJK line breaking and font fallback for KO/JA/ZH;
  • Vietnamese diacritics and Latin-script fallback;
  • German/French/Spanish text expansion without clipping or control overlap;
  • responsive widths including mobile and intermediate breakpoints;
  • no hydration warning or flash that replaces one locale with another after first paint;
  • screenshots tied to exact resource version and source head.

Material UI acceptance must pass the product Delivery Gate; a DB/API implementation without rendered locale evidence is not complete.

Security / correctness RED cases

Add test-first coverage for at least:

  • missing translation in one of the eight required locales blocks publication;
  • placeholder schema mismatch blocks publication;
  • stale/superseded revision cannot mutate a published resource;
  • screen-scoped endpoint cannot enumerate or download unrelated screens/catalogs;
  • unsupported/hostile locale and screen identifiers fail closed;
  • ETag/version cache cannot serve a different locale or screen key;
  • permission separation prevents browser/runtime credentials from publishing catalog versions;
  • translated strings are rendered as text, not trusted HTML/script;
  • user-provided interpolation values do not appear in catalog persistence, logs or metrics;
  • rollback selects a prior immutable resource version without rewriting history.

Standards / traceability

Record exact code/API/test mappings in ADR + doctoring and cite primary standards in APA 7th style, including at least:

If machine translation or LLM-assisted drafting is introduced later, it is an offline content-production capability through the released contextual-orchestrator contract only; publication still requires deterministic placeholder/completeness validation and recorded review. Runtime page rendering must not depend on an LLM.

Completion

Close only after one canonical Naruon owner implements the catalog/API/cache/publish contract, docs/product-technical-gap-baseline.md and architecture/ADR/ERD/UML/operability/recovery docs are code-current, exact-head backend/frontend/security/coverage evidence is GREEN, all eight locale Storybook/browser/a11y cases pass, and #1729 or its verified successor consumes the released screen resource without source-copying the catalog.

Refs #1729 #1602. Keep this issue as the single product Gap until an implementation owner PR is created; do not create parallel UI-i18n writers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions