110 lines
5.3 KiB
JavaScript
110 lines
5.3 KiB
JavaScript
/**
|
|
* Host message catalog.
|
|
*
|
|
* Scope
|
|
* ─────
|
|
* HOST-OWNED strings only — the chrome around the editor (header,
|
|
* page title, PWA install banner, offline indicator, share-sheet
|
|
* title). Strings inside the goods-editor module (sidebar tabs,
|
|
* toolbar buttons, toasts, modal copy) have their OWN catalog
|
|
* inside the module and are not re-exported. That separation is
|
|
* deliberate: the host shouldn't be able to silently change the
|
|
* module's user-facing copy, and the module shouldn't be able to
|
|
* silently change the host's.
|
|
*
|
|
* Convention — flat keys
|
|
* ──────────────────────
|
|
* The dot in `header.save` is punctuation, not nesting. The lookup
|
|
* is a single object access. This makes:
|
|
* • grepping ("where do we use this string?") trivial,
|
|
* • diffs readable in code review,
|
|
* • the t() helper a one-liner with no recursion or path syntax.
|
|
*
|
|
* Adding a key
|
|
* ────────────
|
|
* 1. Append below with a key matching the namespace it belongs to
|
|
* (header.*, pwa.*, offline.*, share.*).
|
|
* 2. Use it via t('namespace.key') in the component.
|
|
* 3. When new locales land, copy this file (es.js, fr.js…) and
|
|
* translate the values. The t() helper picks the active catalog
|
|
* via the import in t.js.
|
|
*
|
|
* Placeholders
|
|
* ────────────
|
|
* `{name}` placeholders are substituted with the matching key in the
|
|
* params object passed to t():
|
|
*
|
|
* 'header.logo-home-aria': '{app} home'
|
|
* t('header.logo-home-aria', { app: 'Pawfectly Yours' }) → 'Pawfectly Yours home'
|
|
*
|
|
* Pluralization
|
|
* ─────────────
|
|
* No ICU plurals — we use the explicit `.singular` / `.plural` key
|
|
* convention, same pattern as the module's `bounds.singular` /
|
|
* `bounds.plural`. Caller picks the key based on count:
|
|
*
|
|
* t(count === 1 ? 'header.cart-aria.singular' : 'header.cart-aria.plural',
|
|
* { count })
|
|
*
|
|
* If we ever need real plural rules (Polish, Arabic, etc.) we'll move
|
|
* to formatjs/intl-messageformat then. For en-only at this scale,
|
|
* .singular/.plural is enough.
|
|
*/
|
|
|
|
export const en = {
|
|
// ── Header ──────────────────────────────────────────────────
|
|
// Save / Clear / Menu were removed from the header chrome during the
|
|
// module split — Save was redundant with Download (same export
|
|
// pipeline), Clear needs a module-side `clear()` method on the editor
|
|
// ref before it can be reintroduced, and Menu opened the host's own
|
|
// bottom sheet which is now inside the module. Their i18n keys are
|
|
// gone with them; readd if you put any of those buttons back.
|
|
'header.app-name': 'Pawfectly Yours',
|
|
'header.app-tagline': 'Wear Your Pet. Share Your Story.',
|
|
'header.page-title': 'Customize Your Shirt',
|
|
'header.logo-home-aria': '{app} home',
|
|
'header.preview': 'Print preview',
|
|
'header.preview-tooltip': 'See your design at print resolution',
|
|
'header.download': 'Download print file',
|
|
'header.download-tooltip': 'Download the print-resolution PNG',
|
|
'header.share': 'Share',
|
|
// Cart aria-label has two forms because pluralization affects the
|
|
// whole phrase. The caller picks the key based on count.
|
|
'header.cart-aria.singular': 'Cart, {count} item',
|
|
'header.cart-aria.plural': 'Cart, {count} items',
|
|
|
|
// ── Offline indicator ───────────────────────────────────────
|
|
'offline.message': 'You\u2019re offline \u2014 changes are saved locally',
|
|
|
|
// ── PWA install / update prompts ────────────────────────────
|
|
'pwa.install-message': 'Install Apparel Designer for offline access!',
|
|
'pwa.install': 'Install',
|
|
'pwa.dismiss': 'Later',
|
|
'pwa.update-message': 'New version available!',
|
|
'pwa.refresh': 'Refresh',
|
|
'pwa.close': 'Close',
|
|
|
|
// ── Share sheet ─────────────────────────────────────────────
|
|
// Title and body for the OS share sheet (iOS / Android / Web Share
|
|
// API). `share.title` is also passed to <ApparelDesigner shareTitle=…/>
|
|
// so the module uses the same string when invoking navigator.share()
|
|
// from inside its own save / share flows.
|
|
'share.title': 'My Pawfectly Yours design',
|
|
'share.text': 'Check out the shirt I made!',
|
|
|
|
// ── Toasts ──────────────────────────────────────────────────
|
|
// The host's toast surface (see src/components/Toast.jsx). The
|
|
// editor module has its own toast catalog (`toast.crop-…` etc.)
|
|
// inside the module — these are only the ones the HOST surfaces.
|
|
// Kept narrowly scoped so we don't drift from the module's copy
|
|
// for the same situation.
|
|
'toast.dismiss': 'Dismiss',
|
|
'toast.link-copied': 'Link copied to clipboard \u2728',
|
|
// 'toast.copy-failed' interpolates the URL so the user can read
|
|
// and copy it manually when both navigator.share and
|
|
// navigator.clipboard are unavailable. Longer auto-dismiss (8s)
|
|
// because the user needs time to read and copy.
|
|
'toast.copy-failed': 'Couldn\u2019t auto-copy. URL: {url}',
|
|
'toast.upload-failed': 'Upload failed: {error}',
|
|
};
|