feat(client): per-locale PWA manifest (name, short_name, description) #226

Open
opened 2026-06-21 18:15:41 +00:00 by james · 0 comments
Owner

Context

#208 (PR #223) shipped apps/client/public/manifest.webmanifest with name, short_name, and description hardcoded in English. Carol's user-facing copy convention (CLAUDE.md → "No hardcoded user-facing strings") makes an exception here because the manifest is static and the install prompt fires before the bundle resolves a locale.

That's fine while Carol ships in en + a partial es catalog. Once a third locale lands — or the es catalog reaches full coverage and a user installing from Spanish-default Chrome gets the English install prompt — it'll feel off.

Source

Follow-up flagged in #223.

Scope

  • Move the manifest from a static public/manifest.webmanifest to a per-locale build artifact. Options:
    • Build-time templating. A script consumes packages/i18n/messages/<locale>.json and emits manifest.<locale>.webmanifest; the +html.tsx injection picks the right <link rel="manifest" href="…"> based on the resolved locale.
    • Runtime selection. Single manifest references all translations via the manifest's lang + content-negotiation pattern — simpler but supported unevenly across browsers.
  • Pick build-time templating unless the runtime path turns out to be reliable enough to ship without conditionals.
  • Add name, short_name, description entries to each locale catalog under a manifest.* key.
  • Update pnpm -F @carol/client export:web (or a pre:export:web script) to emit one manifest per locale in SUPPORTED_LOCALES.
  • Update the catch-all in apps/api/app/[...spa]/route.ts if necessary so each locale's manifest URL resolves; alternatively the locale prefix is already part of the SPA path and the existing serving handles it.

Acceptance criteria

  • pnpm -F @carol/client export:web produces one manifest per SUPPORTED_LOCALES entry.
  • An install initiated from Spanish-default Chrome surfaces the Spanish manifest copy.
  • The English manifest content is unchanged from #208's shipped version (no regression).
  • packages/i18n/messages/{en,es}.json carry manifest.{name,shortName,description} keys.

Out of scope

  • Translating theme_color or icon URLs per locale.
  • A full lang + content-negotiation backend pipeline.
  • Adding locales beyond what's already in SUPPORTED_LOCALES.

Composes with

  • #208 — PWA install + offline shell.
  • ADR-0025 — i18n runtime.

Part of

#176

## Context [#208](https://forge.wynning.tech/james/carol/issues/208) (PR [#223](https://forge.wynning.tech/james/carol/pulls/223)) shipped `apps/client/public/manifest.webmanifest` with `name`, `short_name`, and `description` hardcoded in English. Carol's user-facing copy convention (`CLAUDE.md` → "No hardcoded user-facing strings") makes an exception here because the manifest is static and the install prompt fires before the bundle resolves a locale. That's fine while Carol ships in `en` + a partial `es` catalog. Once a third locale lands — or the `es` catalog reaches full coverage and a user installing from Spanish-default Chrome gets the English install prompt — it'll feel off. ## Source Follow-up flagged in [#223](https://forge.wynning.tech/james/carol/pulls/223). ## Scope - Move the manifest from a static `public/manifest.webmanifest` to a per-locale build artifact. Options: - **Build-time templating.** A script consumes `packages/i18n/messages/<locale>.json` and emits `manifest.<locale>.webmanifest`; the `+html.tsx` injection picks the right `<link rel="manifest" href="…">` based on the resolved locale. - **Runtime selection.** Single manifest references all translations via the [manifest's `lang` + content-negotiation pattern](https://developer.mozilla.org/en-US/docs/Web/Manifest/lang) — simpler but supported unevenly across browsers. - Pick build-time templating unless the runtime path turns out to be reliable enough to ship without conditionals. - Add `name`, `short_name`, `description` entries to each locale catalog under a `manifest.*` key. - Update `pnpm -F @carol/client export:web` (or a `pre:export:web` script) to emit one manifest per locale in `SUPPORTED_LOCALES`. - Update the catch-all in `apps/api/app/[...spa]/route.ts` if necessary so each locale's manifest URL resolves; alternatively the locale prefix is already part of the SPA path and the existing serving handles it. ## Acceptance criteria - [ ] `pnpm -F @carol/client export:web` produces one manifest per `SUPPORTED_LOCALES` entry. - [ ] An install initiated from Spanish-default Chrome surfaces the Spanish manifest copy. - [ ] The English manifest content is unchanged from #208's shipped version (no regression). - [ ] `packages/i18n/messages/{en,es}.json` carry `manifest.{name,shortName,description}` keys. ## Out of scope - Translating `theme_color` or icon URLs per locale. - A full `lang` + content-negotiation backend pipeline. - Adding locales beyond what's already in `SUPPORTED_LOCALES`. ## Composes with - [#208](https://forge.wynning.tech/james/carol/issues/208) — PWA install + offline shell. - [ADR-0025](docs/adr/0025-i18n-next-intl.md) — i18n runtime. ## Part of [#176](https://forge.wynning.tech/james/carol/issues/176)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
james/carol#226
No description provided.