返回 CodeWhale
types.ts
根目录 / web / lib / i18n / dictionaries / types.ts
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 · Work · 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 captured media paths.
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 * The primary strip: Product / Models / Plugins / Docs. The
35 * older six (Start, Install, FAQ, Community, Contribute) stay in the
36 * dictionary for the compact sheet's second group and the footer.
37 */
38 navProduct: string;
39 navModels: string;
40 navPlugins: string;
41
42 /**
43 * Skip-to-content link rendered before the nav in app/[locale]/layout.tsx.
44 * It sits on EVERY page of EVERY locale, so it belongs to shared chrome —
45 * leaving it hardcoded is what kept an EN/ZH branch alive in the layout.
46 */
47 skipToContent: string;
48
49 /** aria-label for the primary <nav> landmark (components/nav-links.tsx). */
50 navPrimaryAria: string;
51 /** aria-label for the wordmark link back to the locale home. */
52 navHomeAria: string;
53
54 /** Mobile-menu and masthead call to action, e.g. "Install →". */
55 installCta: string;
56
57 /**
58 * The header's one identity door to the Codewhale app (app.codewhale.net).
59 * Account creation is offered on the sign-in page, not beside it.
60 */
61 authSignIn: string;
62
63 /**
64 * BCP 47 tag used for the masthead weekday via `toLocaleDateString` — not
65 * rendered copy, but per-locale, so it belongs beside it. Without this the
66 * masthead date renders in English for every non-Chinese locale.
67 */
68 dateLocale: string;
69
70 /** Mobile-menu toggle labels. */
71 menuOpen: string;
72 menuClose: string;
73
74 /** Docs theme toggle: the three cycle states. */
75 themeAuto: string;
76 themeLight: string;
77 themeDark: string;
78 /** Theme toggle aria-label, e.g. "Docs theme: {mode} (click to cycle)". */
79 themeAria: string;
80 /** Theme toggle title attribute. */
81 themeTitle: string;
82
83 // --- footer ---
84 footerTagline: string;
85 footerProduct: string;
86 footerProject: string;
87 footerDocs: string;
88 footerGuide: string;
89 footerInstall: string;
90 footerModels: string;
91 footerRuntime: string;
92 footerFaq: string;
93 footerIssues: string;
94 footerContribute: string;
95 footerLicense: string;
96 /** Footer link to the terms route, e.g. "Terms". */
97 footerTerms: string;
98 /** Footer link to the privacy route, e.g. "Privacy". */
99 footerPrivacy: string;
100 /** Footer Product-column link to the release record, e.g. "Changelog". */
101 footerChangelog: string;
102 /** Prefix before the canonical-source link, e.g. "Canonical source: ". */
103 footerCanonicalSource: string;
104 /** Separator + label before the releases link, e.g. " · Releases: ". */
105 footerReleases: string;
106 /** Link text for the GitHub releases page. */
107 footerReleasesLink: string;
108 /** Link text for the security-contact mailto. */
109 footerSecurity: string;
110
111 /** aria-label for the locale switcher control. */
112 switcherLabel: string;
113 /** Two-locale toggle aria-label, e.g. "Switch to {label}". */
114 switcherSwitchTo: string;
115 /**
116 * Visible badge marking a partial locale pack in the switcher, e.g.
117 * "(partial)" — honest scope signal, per the localization quality
118 * contract. Keep it short. The switcher shows it, brackets stripped, as
119 * the heading of the group that lists partial packs.
120 */
121 partialBadge: string;
122 }
123
124 export interface HomeDict {
125 /**
126 * `<title>` and meta description for the locale home route, consumed by
127 * `generateMetadata` in app/[locale]/layout.tsx.
128 */
129 metaTitle: string;
130 metaDescription: string;
131
132 /** A complete headline that wraps naturally in each locale. */
133 heroTitle: string;
134 /**
135 * Hero lede. Carries a `{brand}` token so the brand can be typeset in its
136 * own span wherever the sentence needs it — the page splits on the token
137 * instead of concatenating fragments around it.
138 */
139 heroIntro: string;
140 /** Primary action → /install, e.g. "Get Codewhale". */
141 getCodewhale: string;
142 /** Accessible name for the hero install command and its platform choice. */
143 heroInstallAria: string;
144 /** Secondary action → /product, e.g. "Explore the product". */
145 exploreProduct: string;
146
147 /** Screenshot caption, first item of the dot chain, e.g. "Terminal preview". */
148 shotPreview: string;
149 /** Screenshot caption, build item with a `{version}` token. */
150 shotBuild: string;
151 /** Screenshot alt text for the current media manifest capture. */
152 screenshotAlt: string;
153
154 /** "Latest release {tag}" */
155 latestRelease: string;
156 releaseUnavailable: string;
157 /** "Source" / "Unreleased" — prepended to `v{version}`. */
158 currentSource: string;
159 sourceCandidate: string;
160 /** "released" / "unreleased" — the machine-readable source-state label. */
161 publishedRelease: string;
162 figcaptionSourceCandidate: string;
163 /** Running head of the terminal chapter ("01 / Your terminal"). */
164 chapterTerminal: string;
165 /** Title of the terminal chapter, above the live capture. */
166 chapterTerminalTitle: string;
167
168 /** What a person gains: heading, lede, and three [title, body] columns. */
169 gainHeading: string;
170 gainLede: string;
171 gain: [string, string][];
172
173 /** Running head of the models chapter ("02 / Your models"). */
174 chapterModels: string;
175 modelsHeading: string;
176 modelsBody: string;
177 /** Three [route kind, description] rows. */
178 modelsFacts: [string, string][];
179 modelsLink: string;
180
181 startHeading: string;
182 startLede: string;
183 startGuideLink: string;
184 startVocabularyLink: string;
185
186 /** Running head of the availability chapter ("04 / Where it runs"). */
187 chapterAvailability: string;
188 availabilityHeading: string;
189 availabilityLede: string;
190 availability: [string, string, string, string?][];
191 availabilityNote: string;
192 accountLink: string;
193
194 surfacesHeading: string;
195 /** Five [name, description] surfaces. */
196 surfaces: [string, string][];
197 runtimeLink: string;
198
199 installBandHeading: string;
200 copy: string;
201 copied: string;
202 binaries: string;
203 chinaMirrors: string;
204 installGuideLink: string;
205
206 communityHeading: string;
207 communityBody: string;
208 communityLinksAria: string;
209 contribute: string;
210 }
211
212 /**
213 * Docs "Getting started" page (`app/[locale]/docs/guide/page.tsx`).
214 *
215 * First of the per-page dictionaries that retire the page-body `isZh`
216 * branches left after #4934. Page dictionaries are optional per locale:
217 * English is the required reference, any other locale that ships the file
218 * is held to exact key parity (`check-locales.mjs` OPTIONAL_FILES), and a
219 * locale without it falls back to English at lookup time — matching how
220 * page bodies already behave for partial locales.
221 */
222 export interface DocsGuideDict {
223 metaTitle: string;
224 metaDescription: string;
225 /** Body-copy typography for this locale (CJK needs looser leading). */
226 bodyClassName: string;
227 overviewTitle: string;
228 overviewLead: string;
229 sessionTitle: string;
230 sessionLead: string;
231 nextTitle: string;
232 sourceNote: string;
233 }
234
235 /**
236 * The docs shell: the portal hero in `app/[locale]/docs/layout.tsx` that
237 * wraps every docs page, plus the metadata of the hub page it frames
238 * (`app/[locale]/docs/page.tsx`, whose body is the `DocsSearch` component).
239 *
240 * No `bodyClassName` here: the hero typesets through `portal-*` classes,
241 * which never varied by locale.
242 */
243 export interface DocsShellDict {
244 metaTitle: string;
245 metaDescription: string;
246 portalMark: string;
247 heroTitle: string;
248 heroLead: string;
249 installCta: string;
250
251 // --- release truth band (docs layout; facts + CHANGELOG.md) ---
252 /** Eyebrow over the band, e.g. "Release". */
253 releaseLabel: string;
254 /** "Latest release {tag} · {date}" — date already formatted per locale. */
255 releasePublished: string;
256 /** Shown while the source candidate is ahead of the published release. */
257 releaseCandidate: string;
258 /** Shown when the pages describe exactly the published release. */
259 releaseMatches: string;
260 /** Link to the /changelog route. */
261 releaseChangelog: string;
262
263 // --- task/topic search (components/docs-search.tsx) ---
264 searchLabel: string;
265 searchPlaceholder: string;
266 searchClear: string;
267 /** "{matched} of {total} entries match “{query}”". */
268 searchMatches: string;
269 /** "Nothing matches “{query}”". */
270 searchNoMatches: string;
271 tasksHeading: string;
272 tasksLead: string;
273 topicsHeading: string;
274 /** Row tag for a first-party page. */
275 webGuideTag: string;
276 /** Row tag for a GitHub source document. */
277 sourceDocTag: string;
278 /** Per-row disclosure that reveals the repository files a topic comes from. */
279 sourceDetails: string;
280 emptyTitle: string;
281 emptyBody: string;
282 emptyCta: string;
283 indexNote: string;
284
285 // --- sidebar and breadcrumb (components/docs-sidebar.tsx, docs-breadcrumb.tsx) ---
286 sidebarHeading: string;
287 sidebarAria: string;
288 breadcrumbAria: string;
289 breadcrumbHome: string;
290 breadcrumbDocs: string;
291
292 // --- contextual help band under every docs page ---
293 helpTitle: string;
294 helpLead: string;
295 /** "Source: {name}" — the topic's repository document(s). */
296 helpSource: string;
297 helpTroubleshooting: string;
298 helpFaq: string;
299 helpDiscord: string;
300 helpIssue: string;
301
302 // --- docs page bodies (app/[locale]/docs/_components/doc-article.tsx) ---
303 /** Heading over each page's closing "where to go next" links. */
304 nextHeading: string;
305 /** Word (with its punctuation) that marks an aside, so it reads without color. */
306 noteLabel: string;
307 /** Accessible name of a page's table of contents. */
308 onThisPage: string;
309
310 // --- session recording panel (components/session-media.tsx) ---
311 /** Shown in place of a recording that has not been made yet. */
312 mediaPendingNote: string;
313 mediaPlanLink: string;
314 mediaGifFallback: string;
315 mediaTranscript: string;
316 }
317
318 /**
319 * Shared surface states (`components/surface-state.tsx`,
320 * `components/connection-banner.tsx`, the `loading.tsx` / `error.tsx` /
321 * `not-found.tsx` boundaries). One dictionary for every empty, loading,
322 * error, retry, recovery, and connection state so no page invents its own.
323 */
324 export interface StatesDict {
325 loadingLabel: string;
326 emptyTitle: string;
327 emptyBody: string;
328 errorTitle: string;
329 errorBody: string;
330 retry: string;
331 reload: string;
332 homeLink: string;
333 /** Documentation recovery link on the 404 page. */
334 docsIndexLink: string;
335 notFoundTitle: string;
336 notFoundBody: string;
337 notFoundHomeLink: string;
338 /** Alt text for the 404 poster (public/codwhale-404.webp). */
339 notFoundPosterAlt: string;
340 /**
341 * A data-bearing page whose source was not asked (build-time prerender)
342 * or refused (rate limit, outage). Distinct from `empty`, which asserts
343 * that nothing exists, and from `error`, which means the render threw.
344 */
345 unavailableTitle: string;
346 unavailableBody: string;
347 /** Connection banner (typed state in lib/connection-state.ts). */
348 offlineTitle: string;
349 offlineBody: string;
350 reconnectingTitle: string;
351 /** "Checking the connection (attempt {attempt})." */
352 reconnectingBody: string;
353 degradedTitle: string;
354 degradedBody: string;
355 onlineTitle: string;
356 onlineBody: string;
357 retryNow: string;
358 dismiss: string;
359 /** "Last checked {time}". */
360 lastChecked: string;
361 }
362
363 /* ------------------------------------------------------------------ */
364 /* Docs page bodies */
365 /* ------------------------------------------------------------------ */
366
367 /**
368 * One content block on a docs page. Strings are prose: `code` in backticks
369 * is typeset as inline code and `[label](/docs/x)` becomes a locale-aware
370 * link (see `app/[locale]/docs/_components/doc-article.tsx`). Commands in
371 * `code` blocks are shown verbatim and must match the engine source.
372 */
373 export type DocsBlock =
374 | { p: string }
375 /** A copyable command or config block; `lang` labels it (e.g. "Terminal"). */
376 | { code: string; lang?: string }
377 /** A `[term, detail]` table; `codeTerms` sets the term column as code. */
378 | { rows: readonly (readonly [string, string])[]; codeTerms?: boolean }
379 /** Numbered steps, in order. */
380 | { steps: readonly string[] }
381 /** An unordered list. */
382 | { list: readonly string[] }
383 /** A short aside: a limit, a caution, or something not built yet. */
384 | { note: string };
385
386 export interface DocsSection {
387 /** Stable anchor id. */
388 id: string;
389 title: string;
390 blocks: readonly DocsBlock[];
391 }
392
393 /**
394 * The one shape every task page in `/docs` uses: what this page helps you
395 * do (title + lede), how (sections), and where to go next.
396 */
397 export interface DocsPageDict {
398 metaTitle: string;
399 metaDescription: string;
400 /** Body-copy typography for this locale (CJK needs looser leading). */
401 bodyClassName: string;
402 title: string;
403 /** What the reader can do here and why — two sentences at most. */
404 lede: string;
405 sections: readonly DocsSection[];
406 /** Where to go next; `href` is locale-relative. */
407 next: readonly { href: string; label: string; note: string }[];
408 /** Maintainer pointer, kept out of the rendered copy. */
409 sourceNote: string;
410 }
411
412 export type DocsReviewDict = DocsPageDict;
413
414 export type DocsComputersDict = DocsPageDict;
415
416 export type DocsAuthDict = DocsPageDict;
417
418 export type DocsTrustDict = DocsPageDict;
419
420 /** `app/[locale]/changelog/page.tsx` — version-aware release record. */
421 export interface ChangelogDict {
422 metaTitle: string;
423 metaDescription: string;
424 kicker: string;
425 title: string;
426 lead: string;
427 publishedLabel: string;
428 /** "{tag} · published {date}" */
429 publishedValue: string;
430 candidateLabel: string;
431 /** "{version} · unreleased" */
432 candidateValue: string;
433 /** Shown when the candidate equals the published release. */
434 candidateMatches: string;
435 releasesLink: string;
436 unreleasedHeading: string;
437 unreleasedNote: string;
438 compareLink: string;
439 releasePageLink: string;
440 /** "{shown} of {total} entries" */
441 moreEntries: string;
442 fullNotes: string;
443 /** "Full notes for {version}" — per-release deep link into CHANGELOG.md. */
444 releaseNotesLink: string;
445 emptyTitle: string;
446 emptyBody: string;
447 }
448
449 export interface LegalTermsDict {
450 metaTitle: string;
451 metaDescription: string;
452 kicker: string;
453 title: string;
454 /** "Effective and last updated {date}." — zh also says the English text binds. */
455 updated: string;
456 privacyLink: string;
457 homeLink: string;
458 }
459
460 export interface LegalPrivacyDict {
461 metaTitle: string;
462 metaDescription: string;
463 kicker: string;
464 title: string;
465 /** Same template as `LegalTermsDict.updated`. */
466 updated: string;
467 termsLink: string;
468 homeLink: string;
469 }
470
471 export interface DigestDict {
472 metaTitle: string;
473 metaDescription: string;
474 /** Heading shown with the empty state. */
475 emptyTitle: string;
476 emptyBody: string;
477 title: string;
478 lead: string;
479 }
480
481 /** `app/[locale]/feed/page.tsx`. */
482 export interface FeedDict {
483 metaTitle: string;
484 metaDescription: string;
485 /** Page H1. */
486 title: string;
487 /** The H1 again in the other script, set beside it (the bilingual Han title). */
488 titleAside: string;
489 /** BCP 47 language of `titleAside`. */
490 titleAsideLang: string;
491 /** `{repo}` is where the page typesets the repository link. */
492 lede: string;
493 pulls: string;
494 issues: string;
495 /** Items in a column; `{count}` is filled at render time. */
496 shownCount: string;
497 openIssue: string;
498 openPull: string;
499 startDiscussion: string;
500 }
501
502 /** `app/[locale]/community/page.tsx`. */
503 export interface CommunityDict {
504 metaTitle: string;
505 metaDescription: string;
506 kicker: string;
507 /** Page H1. */
508 title: string;
509 lede: string;
510 fileIssue: string;
511 browsePulls: string;
512 readGuide: string;
513 pathsTitle: string;
514 pathsScope: string;
515 recordTitle: string;
516 recordScope: string;
517 creditTitle: string;
518 /** Credit scope once the source version is the published release. */
519 creditScope: string;
520 /** Credit scope while the source version is ahead of the published release. */
521 creditScopeUnreleased: string;
522 /** Credit heading for the published release; `{version}` is filled at render time. */
523 creditLabel: string;
524 /** Credit heading while the source version is unreleased; `{version}` is filled at render time. */
525 creditLabelUnreleased: string;
526 mergedTitle: string;
527 helpersTitle: string;
528 fullRecord: string;
529 }
530
531 /** `app/[locale]/faq/page.tsx` and its `components/faq-search.tsx`. */
532 export interface FaqDict {
533 metaTitle: string;
534 metaDescription: string;
535 eyebrow: string;
536 /** Page H1. */
537 title: string;
538 /** The H1 again in the other script, set beside it (the bilingual Han title). */
539 titleAside: string;
540 /** BCP 47 language of `titleAside`. */
541 titleAsideLang: string;
542 lead: string;
543 notCovered: string;
544 openIssue: string;
545 searchPlaceholder: string;
546 searchLabel: string;
547 searchClear: string;
548 /** `{matched}`, `{total}` and `{query}` are filled at render time. */
549 searchMatches: string;
550 /** `{query}` is filled at render time. */
551 searchNoMatches: string;
552 /** Extra classes on each answer. Empty in both locales today: CJK leading lives in `.prose` (primitives.css). */
553 answerClassName: string;
554 sourcesLabel: string;
555 noResultsTitle: string;
556 noResultsBody: string;
557 }
558
559 /** `app/[locale]/roadmap/page.tsx`. */
560 export interface RoadmapDict {
561 metaTitle: string;
562 metaDescription: string;
563 eyebrow: string;
564 /** Page H1. */
565 title: string;
566 introduction: string;
567 sectionTitle: string;
568 browseIssues: string;
569 /** Track size; `{count}` is filled at render time. */
570 trackCount: string;
571 /** Track size when it is exactly one. */
572 trackCountOne: string;
573 contributeTitle: string;
574 contributeBody: string;
575 issuesDetail: string;
576 discussionsDetail: string;
577 pullsDetail: string;
578 }
579
580 /** `app/[locale]/docs/vocabulary/page.tsx`. */
581 export interface DocsVocabularyDict {
582 metaTitle: string;
583 metaDescription: string;
584 /** Classes on every body paragraph; zh loosens leading and tracking. */
585 bodyClassName: string;
586 /** Page H1. */
587 title: string;
588 lead: string;
589 executionHeading: string;
590 controlHeading: string;
591 controlLead: string;
592 routeHeading: string;
593 advisoryHeading: string;
594 measurementHeading: string;
595 leaderboardNote: string;
596 /** Maintainer pointer, carried in a hidden `data-source-note`. */
597 sourceNote: string;
598 }
599
600 /** `app/[locale]/contribute/page.tsx`. */
601 export interface ContributeDict {
602 metaTitle: string;
603 metaDescription: string;
604 kicker: string;
605 /** Page H1. */
606 title: string;
607 lede: string;
608 fileIssue: string;
609 browsePulls: string;
610 fullGuide: string;
611 pathsTitle: string;
612 workflowTitle: string;
613 reviewTitle: string;
614 reviewScope: string;
615 devTitle: string;
616 devScope: string;
617 }
618
619 /** `app/[locale]/constitution/page.tsx` and its `components/thinking-trace.tsx`. */
620 export interface ConstitutionDict {
621 metaTitle: string;
622 metaDescription: string;
623 kicker: string;
624 /** Page H1. */
625 title: string;
626 /** The H1 again in the other script, set beside it (the bilingual Han title). */
627 titleAside: string;
628 /** BCP 47 language of `titleAside`. */
629 titleAsideLang: string;
630 lede: string;
631 /** Status label on the setup callout. */
632 since: string;
633 sinceBody: string;
634 rankTitle: string;
635 rankScope: string;
636 boundaryTitle: string;
637 boundaryBody: string;
638 traceTitle: string;
639 traceScope: string;
640 /** The label every traced scene carries: these are illustrations, not transcripts. */
641 illustration: string;
642 install: string;
643 configuration: string;
644 }
645
646 /** `app/[locale]/runtime/page.tsx`. */
647 export interface RuntimeDict {
648 metaTitle: string;
649 metaDescription: string;
650 kicker: string;
651 /** Page H1. */
652 title: string;
653 /** The H1 again in the other script, set beside it (the bilingual Han title). */
654 titleAside: string;
655 /** BCP 47 language of `titleAside`. */
656 titleAsideLang: string;
657 lede: string;
658 integrationsTitle: string;
659 /** Status label on an experimental integration. */
660 experimental: string;
661 trustTitle: string;
662 factsTitle: string;
663 version: string;
664 toolCount: string;
665 sandboxBackends: string;
666 /** Summary of the maintainer-facing disclosure (crates, source revision). */
667 details: string;
668 sourceRevision: string;
669 docsLead: string;
670 runtimeApiDoc: string;
671 mcpDoc: string;
672 }
673
674 export type DocsHooksDict = DocsPageDict;
675
676 export type DocsTroubleshootingDict = DocsPageDict;
677
678 export type DocsConfigurationDict = DocsPageDict;
679
680 export type DocsFleetDict = DocsPageDict;
681
682 export type DocsMcpDict = DocsPageDict;
683
684 export type DocsModesDict = DocsPageDict;
685
686 export type DocsRuntimeApiDict = DocsPageDict;
687
688 export type DocsSandboxDict = DocsPageDict;
689
690 export type DocsSubagentsDict = DocsPageDict;
691
692 export type DocsWebDict = DocsPageDict;
693
694 export type DocsWorkDict = DocsPageDict;
695
696 /** Copy for `app/[locale]/computer-use/page.tsx` and the install page's Computer Use section. */
697 export interface ComputerUseDict {
698 metaTitle: string;
699 metaDescription: string;
700 title: string;
701 lead: string;
702 publisher: string;
703 /** Primary button: the notarized disk image when the release carries one, else the archive. */
704 download: string;
705 /** Secondary link to the archive the in-app updater consumes. */
706 downloadZip: string;
707 requirements: string;
708 included: string;
709 pendingTitle: string;
710 pendingBody: string;
711 unavailableTitle: string;
712 unavailableBody: string;
713 releases: string;
714 receipt: string;
715 setup: string;
716 /** Four numbered setup steps, rendered in order. */
717 steps: { title: string; body: string }[];
718 controlsTitle: string;
719 controlsBody: string;
720 updateTitle: string;
721 updateBody: string;
722 help: string;
723 notes: string;
724 demo: string;
725 source: string;
726 platforms: string;
727 }
728
728 lines TYPESCRIPT