| 1 | /** |
| 2 | * Dictionary shapes for the website localization layer (#3091, #4934). |
| 3 | * |
| 4 | * `ChromeDict` covers shared chrome: the newspaper masthead, nav, mobile |
| 5 | * menu, theme toggle, live ticker, footer, and the locale switcher with its |
| 6 | * visible partial-pack badge. `HomeDict` covers the landing page |
| 7 | * (`app/[locale]/page.tsx`). Templates use `{name}` tokens interpolated |
| 8 | * with `fill()` from dictionaries/index.ts — never concatenate translated |
| 9 | * sentences around variables in JSX. |
| 10 | * |
| 11 | * English (`dictionaries/en/`) is the reference shape; every routed locale — |
| 12 | * including Chinese — must define exactly the same keys. Parity is enforced |
| 13 | * by `web/scripts/check-locales.mjs` and `web/lib/i18n/dictionaries.test.ts`. |
| 14 | * A locale without a dictionary falls back to the English one at lookup |
| 15 | * time, so an untranslated string renders English copy — never a key. |
| 16 | * |
| 17 | * Code-owned strings stay out of these dictionaries per docs/VOICE.md: |
| 18 | * "Plan · Act · Operate", "Ask · Auto-Review · Full Access", |
| 19 | * "TUI · exec · web · API", "Codewhale", "GitHub", "Issues", |
| 20 | * `npm install -g codewhale`, `cargo test --locked`, `codewhale exec`, |
| 21 | * package-manager proper nouns, mirror names, and `/codewhale-tui.png`. |
| 22 | */ |
| 23 | |
| 24 | export interface ChromeDict { |
| 25 | // --- primary nav labels (components/nav.tsx via lib/i18n/links.ts) --- |
| 26 | navDocs: string; |
| 27 | navStart: string; |
| 28 | navInstall: string; |
| 29 | navFaq: string; |
| 30 | navCommunity: string; |
| 31 | navContribute: string; |
| 32 | |
| 33 | /** |
| 34 | * Bilingual secondary nav labels — the small companion label the |
| 35 | * newspaper masthead sets beside each primary link. |
| 36 | * |
| 37 | * The English edition uses the Han seal pair (文档 / 指引 / …) as an |
| 38 | * editorial device; every other locale supplies its OWN pairing (native |
| 39 | * primary, short English secondary). Never hardcode Han characters at a |
| 40 | * call site — a locale that wants no second label still needs a value |
| 41 | * here, because empty strings are rejected by dictionaries.test.ts. |
| 42 | */ |
| 43 | navDocsSecondary: string; |
| 44 | navStartSecondary: string; |
| 45 | navInstallSecondary: string; |
| 46 | navFaqSecondary: string; |
| 47 | navCommunitySecondary: string; |
| 48 | navContributeSecondary: string; |
| 49 | |
| 50 | /** |
| 51 | * Skip-to-content link rendered before the nav in app/[locale]/layout.tsx. |
| 52 | * It sits on EVERY page of EVERY locale, so it belongs to shared chrome — |
| 53 | * leaving it hardcoded is what kept an EN/ZH branch alive in the layout. |
| 54 | */ |
| 55 | skipToContent: string; |
| 56 | |
| 57 | /** aria-label for the primary <nav> landmark (components/nav-links.tsx). */ |
| 58 | navPrimaryAria: string; |
| 59 | /** aria-label for the wordmark link back to the locale home. */ |
| 60 | navHomeAria: string; |
| 61 | |
| 62 | /** Mobile-menu and masthead call to action, e.g. "Install →". */ |
| 63 | installCta: string; |
| 64 | |
| 65 | /** Wordmark seal glyph beside the masthead brand (components/seal.tsx). */ |
| 66 | wordmarkSeal: string; |
| 67 | /** Wordmark strapline under the brand, e.g. "any model, local-first". */ |
| 68 | wordmarkTag: string; |
| 69 | |
| 70 | /** Masthead issue line, e.g. "Issue {date}". */ |
| 71 | issueLabel: string; |
| 72 | /** |
| 73 | * BCP 47 tag used for the masthead weekday via `toLocaleDateString` — not |
| 74 | * rendered copy, but per-locale, so it belongs beside it. Without this the |
| 75 | * masthead date renders in English for every non-Chinese locale. |
| 76 | */ |
| 77 | dateLocale: string; |
| 78 | |
| 79 | /** aria-label on the star-count link, e.g. "GitHub stars". */ |
| 80 | starsAria: string; |
| 81 | /** Star-badge label when the live count is unavailable. */ |
| 82 | githubFallback: string; |
| 83 | |
| 84 | /** Live-ticker seal label (components/ticker.tsx). */ |
| 85 | tickerLiveLabel: string; |
| 86 | /** Live-ticker mono tag beside the seal label, e.g. "LIVE". */ |
| 87 | tickerLiveTag: string; |
| 88 | |
| 89 | /** |
| 90 | * Ticker event verbs — the chrome around the repository's own record. |
| 91 | * Pull-request titles, issue titles, release tags, and contributor handles |
| 92 | * are CONTENT and stay verbatim in every locale; these verbs are copy and |
| 93 | * must be translated. |
| 94 | * |
| 95 | * `tickerReleased` covers `state: "published"`, and `tickerOpened` covers |
| 96 | * both a newly filed issue and an open pull request. There is deliberately |
| 97 | * no draft verb: the strip reports events, and a draft pull request is one |
| 98 | * its author has marked not-ready (components/ticker.tsx `EVENT_STATES`). |
| 99 | */ |
| 100 | tickerMerged: string; |
| 101 | tickerOpened: string; |
| 102 | tickerClosed: string; |
| 103 | tickerReleased: string; |
| 104 | /** |
| 105 | * Mark shown when GitHub itself reports the author as a |
| 106 | * FIRST_TIME_CONTRIBUTOR — the warmest item on the strip, and never our |
| 107 | * inference. Keep it short; it sits inline in a scrolling mono line. |
| 108 | */ |
| 109 | tickerFirstContribution: string; |
| 110 | /** |
| 111 | * By-line template carrying a `{handle}` token, e.g. "by {handle}". The |
| 112 | * handle is typeset in its own element, so a locale may place it anywhere |
| 113 | * (or make it the whole value, as ja/ko do with an honorific suffix). |
| 114 | */ |
| 115 | tickerBy: string; |
| 116 | /** aria-label for the ticker's group landmark. */ |
| 117 | tickerAria: string; |
| 118 | |
| 119 | /** TerminalPlayer title-bar label, e.g. "reasoning trace". */ |
| 120 | traceLabel: string; |
| 121 | /** aria-label for the TerminalPlayer scene tablist. */ |
| 122 | traceTabsAria: string; |
| 123 | |
| 124 | /** Mobile-menu toggle labels. */ |
| 125 | menuOpen: string; |
| 126 | menuClose: string; |
| 127 | |
| 128 | /** Docs theme toggle: the three cycle states. */ |
| 129 | themeAuto: string; |
| 130 | themeLight: string; |
| 131 | themeDark: string; |
| 132 | /** Theme toggle aria-label, e.g. "Docs theme: {mode} (click to cycle)". */ |
| 133 | themeAria: string; |
| 134 | /** Theme toggle title attribute. */ |
| 135 | themeTitle: string; |
| 136 | |
| 137 | // --- footer --- |
| 138 | footerTagline: string; |
| 139 | footerProduct: string; |
| 140 | footerProject: string; |
| 141 | footerDocs: string; |
| 142 | footerGuide: string; |
| 143 | footerInstall: string; |
| 144 | footerModels: string; |
| 145 | footerRuntime: string; |
| 146 | footerFaq: string; |
| 147 | footerIssues: string; |
| 148 | footerContribute: string; |
| 149 | footerLicense: string; |
| 150 | /** Prefix before the canonical-source link, e.g. "Canonical source: ". */ |
| 151 | footerCanonicalSource: string; |
| 152 | /** Separator + label before the releases link, e.g. " · Releases: ". */ |
| 153 | footerReleases: string; |
| 154 | /** Link text for the GitHub releases page. */ |
| 155 | footerReleasesLink: string; |
| 156 | /** Link text for the security-contact mailto. */ |
| 157 | footerSecurity: string; |
| 158 | |
| 159 | /** aria-label for the locale switcher control. */ |
| 160 | switcherLabel: string; |
| 161 | /** Two-locale toggle aria-label, e.g. "Switch to {label}". */ |
| 162 | switcherSwitchTo: string; |
| 163 | /** |
| 164 | * Visible badge marking a partial locale pack in the switcher, e.g. |
| 165 | * "(partial)" — honest scope signal, per the localization quality |
| 166 | * contract. Keep it short. |
| 167 | */ |
| 168 | partialBadge: string; |
| 169 | } |
| 170 | |
| 171 | export interface HomeDict { |
| 172 | /** |
| 173 | * `<title>` and meta description for the locale home route, consumed by |
| 174 | * `generateMetadata` in app/[locale]/layout.tsx. These were the last |
| 175 | * inline EN/ZH pair on the required slice; per-locale metadata is the |
| 176 | * whole point of routing a locale, so it lives in the dictionary. |
| 177 | */ |
| 178 | metaTitle: string; |
| 179 | metaDescription: string; |
| 180 | |
| 181 | /** Hero pill, e.g. "Open source · Any model · Runs in your terminal". */ |
| 182 | kicker: string; |
| 183 | heroTitleA: string; |
| 184 | heroTitleB: string; |
| 185 | /** |
| 186 | * Hero lede. Carries a `{brand}` token so the brand can be typeset in its |
| 187 | * own span wherever the sentence needs it — the page splits on the token |
| 188 | * instead of concatenating fragments around it. |
| 189 | */ |
| 190 | heroIntro: string; |
| 191 | install: string; |
| 192 | docs: string; |
| 193 | copy: string; |
| 194 | copied: string; |
| 195 | |
| 196 | /** Eyebrow above the one-line install block, e.g. "one-line install". */ |
| 197 | installEyebrow: string; |
| 198 | /** Install prerequisite line, e.g. "needs Node 18+ — no Rust toolchain". */ |
| 199 | installRequirement: string; |
| 200 | /** Link to the other install methods, e.g. "other ways →". */ |
| 201 | installOtherWays: string; |
| 202 | |
| 203 | /** "Latest release {tag}" */ |
| 204 | latestRelease: string; |
| 205 | releaseUnavailable: string; |
| 206 | /** "Current source" / "Source candidate" — prepended to `v{version}:`. */ |
| 207 | currentSource: string; |
| 208 | sourceCandidate: string; |
| 209 | /** "{count} provider routes" */ |
| 210 | providerRoutes: string; |
| 211 | /** "published release" / "source candidate" — the source-state label. */ |
| 212 | publishedRelease: string; |
| 213 | figcaptionSourceCandidate: string; |
| 214 | |
| 215 | /** Screenshot toolbar label, e.g. "Current session". */ |
| 216 | shotSession: string; |
| 217 | /** Screenshot alt text for /codewhale-tui.png. */ |
| 218 | screenshotAlt: string; |
| 219 | /** Screenshot figcaption. */ |
| 220 | figcaption: string; |
| 221 | |
| 222 | proofHeading: string; |
| 223 | proofBody: string; |
| 224 | |
| 225 | /** Section seal glyph for the "see how it decides" band. */ |
| 226 | sealDecides: string; |
| 227 | decidesEyebrow: string; |
| 228 | decidesHeading: string; |
| 229 | decidesLede: string; |
| 230 | |
| 231 | /** Section seal glyph for the workflow band. */ |
| 232 | sealWorkflow: string; |
| 233 | workflowHeading: string; |
| 234 | /** Four [title, description] steps. */ |
| 235 | workflow: [string, string][]; |
| 236 | receiptAria: string; |
| 237 | /** |
| 238 | * Right-hand column of the example receipt. The verbs (inspect / act / |
| 239 | * verify / report), `$ codewhale exec …`, and `cargo test --locked` stay |
| 240 | * code-owned literals in the JSX per docs/VOICE.md. |
| 241 | */ |
| 242 | receiptInspect: string; |
| 243 | receiptAct: string; |
| 244 | receiptReport: string; |
| 245 | |
| 246 | /** Section seal glyph for the getting-started band. */ |
| 247 | sealStart: string; |
| 248 | startHeading: string; |
| 249 | startLede: string; |
| 250 | startGuideLink: string; |
| 251 | startVocabularyLink: string; |
| 252 | |
| 253 | /** Section seal glyph for the boundaries band. */ |
| 254 | sealBoundaries: string; |
| 255 | boundariesHeadingA: string; |
| 256 | boundariesHeadingB: string; |
| 257 | boundariesBody: string; |
| 258 | hostedGatewayLocal: string; |
| 259 | planActOperateDesc: string; |
| 260 | askAutoReviewDesc: string; |
| 261 | tuiExecWebDesc: string; |
| 262 | |
| 263 | /** Section seal glyph for the surfaces band. */ |
| 264 | sealSurfaces: string; |
| 265 | surfacesHeading: string; |
| 266 | /** Five [name, description] surfaces. */ |
| 267 | surfaces: [string, string][]; |
| 268 | runtimeLink: string; |
| 269 | |
| 270 | installBandHeading: string; |
| 271 | binaries: string; |
| 272 | chinaMirrors: string; |
| 273 | installGuideLink: string; |
| 274 | |
| 275 | /** Section seal glyph for the community band. */ |
| 276 | sealCommunity: string; |
| 277 | communityHeading: string; |
| 278 | communityBody: string; |
| 279 | communityLinksAria: string; |
| 280 | contribute: string; |
| 281 | } |
| 282 |