/** * 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 // 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}', };