How generaltranslation.com is built and changed in gt-cloud's apps/landing.
node scripts/install-skills.mjs gt-website --project <dir>Run it from a Prototemplate checkout. It links skills/gt-website into the project’s .claude/skills and .agents/skills; --copy vendors the folder instead, and --dry-run prints each step first.
General Translation (GT) is an internationalization platform for developers: open-source SDKs (gt-next, gt-react and others), the gt CLI, AI translation, the Locadex coding agent and CDN delivery of translations (as /llms.txt on the site describes it). Its website, generaltranslation.com, is the Next.js app in apps/landing of the private gt-cloud monorepo (generaltranslation/gt-cloud). It serves the marketing pages, the documentation on Fumadocs and the blog, and Vercel deploys it from gt-cloud's main branch. This skill records how Kevin builds and changes it: where each part lives, the rules each part follows, the traps met in 2026, and the checks a change passes before its pull request.
Paths that start with src/, public/, scripts/ or __tests__/ are inside $GT_CLOUD/apps/landing. Other paths are relative to a gt-cloud checkout ($GT_CLOUD, which is ~/gt/gt-cloud on Kevin's machine), and files at the gt-cloud root carry the prefix, as in $GT_CLOUD/scripts/deploy-landing.sh. content: marks the public generaltranslation/content repository and $PROTOTEMPLATE a Prototemplate checkout. Detail sits in three references: references/docs.md for the documentation site, references/blog.md for posts and their components, and references/pages.md for pricing, the hero's agent button, the AI features and the /world map.
apps/landing is an App Router app with every page under src/app/[locale]:(home) holds the marketing routes: the home page, pricing, enterprise, careers, contact, legal, report-card and supported-locales. Its layout.tsx wraps them in the shared Header with footer={false} and appends SiteFooterMount.blog holds the index and blog/[slug].docs/[variant]/[...slug] holds the documentation.src/app/): robots.ts, sitemap.ts, sitemap.md, AGENTS.md, agent-prompt.md, rss.xml, openapi.json and openapi.yaml, locales.json, docs/platform/openapi/llms-full.txt, and the social image handlers api/og and api/og-home. The llms*.txt family and the markdown twins are per locale under src/app/[locale]/.apps/landing/content submodule, the public generaltranslation/content repository. Legal pages come from the pinned, shallow apps/landing/legal submodule..agents/skills/gt-landing/SKILL.md and its references/design.md own the file map: the home page's bands, the editing map, the section recipe and the landing rules. Read both before editing apps/landing, with CLAUDE.md at the gt-cloud root for the repository's code style. For UI work also load gt-cloud's gt-ui, react-best-practices and react-useeffect; for new shader art glyphfield; for comments code-comments; for tests gt-testing.next 16.2.12, pinned in the landing's own package.json while the workspace catalog carries 16.3.8; React 19.2; Tailwind 4; fumadocs-ui 16.2.0 in the landing and 16.0.10 in packages/ui, whose DocsLayout the docs render; next-mdx-remote 6 for the blog; pagefind 1.5.2.Cta for every button, Title Case labels, section heads as a heading and one lead paragraph, Heroicons solid for meaning and Lucide for controls, Inter as the only face) are in the gt-landing-pages skill and gt-cloud's gt-landing skill.~/gt/gt-cloud-wt-<topic> on branch k/<topic>) and its own dev server. The gt-ship skill holds the branch and worktree rules.apps/landing/.env.local copied from the main checkout, pnpm install --frozen-lockfile, and pnpm turbo run build --filter="landing^..." to build the workspace packages the landing imports. Without that build the app cannot resolve @generaltranslation/settings/cookies.js, whose export points at dist/. Run pnpm --dir apps/landing exec next typegen before the first tsc..claude/launch.json (untracked, local to Kevin's machine) with a port of its own: APP_NODE_ENV=development pnpm --dir <worktree>/apps/landing exec next dev --turbopack --port <port>. .claude/skills beside it is tracked (links to .agents/skills), so never delete a worktree's whole .claude folder; git checkout -- .claude restores it. pnpm dev uses port 3001 and regenerates the framework variant manifest first. The root answers 307 to the locale path, so poll with curl -L.pnpm install --frozen-lockfile and restart the dev server before judging routes. On 2026-09-24 a dependency missing from a worktree made every docs page 404 with nothing in the log.kill $(lsof -ti tcp:<port> -sTCP:LISTEN). Without -sTCP:LISTEN the command also kills the browser that holds a connection to the port.next dev can keep serving stale _next/image variants after a source image changes. rm -rf .next and a restart clear them.pnpm-workspace.yaml sets minimumReleaseAge: 2880, so pnpm add can fail on an unrelated recent bump. Add the dependency to package.json, run pnpm install --config.minimumReleaseAge=0, keep only the lockfile lines the change needs, and confirm with pnpm install --frozen-lockfile. Restart any dev server that ran during the relink; it fails with "Can't resolve 'gt-next/link'" until then.Header from packages/ui/src/components/frame/NewHeader.tsx, used by the (home) and blog layouts and the locale not-found.tsx. A header destination goes in both items (the mobile menu) and columns (the desktop dropdown), because each platform reads one of them. Search, the language selector and the theme toggle sit in the same frame folder.@generaltranslation/ui/components/ui/. Flags render through LocaleFlag. The pricing components and the dollar formatter live in packages/ui/src/components/pricing/.src/components/landing/shell/engine.css scopes the site grammar and its --tc-* tokens under .toolchain-root, with dark values under [data-theme='dark'] .toolchain-root. src/app/globals.css exposes the tokens to Tailwind as the tc-* utilities (text-tc-ink-2, border-tc-hair, bg-tc-card, px-tc-gut, font-tc-mono).references/design.md: the token values, the unlayered heading and paragraph resets that beat Tailwind utilities, the one-rail law and the Tailwind 4 text-[length:var(--x)] form.--tc-* tokens exist only inside .toolchain-root. A development page outside the site layouts imports engine.css and src/components/landing/home/v0-pages.css itself, and src/components/blog/blog.css for blog parts.k/docs-perf branch, a font face that blog CSS joined to inter was fetched by the home page. Scope a route's rules under its root class (.blog-root), and give a route-only face a family name that only that route's CSS uses.rm -rf .next, because the candidate cache persists.@theme block that no utility uses is dropped from the build. Tokens read by inline styles or third-party CSS go in @theme static, as in packages/ui/src/css/fd-theme.css.document.hidden and pauses requestAnimationFrame, so canvas fields render blank there. Use its JavaScript for measurements and playwright-core from the gt-cloud pnpm store for pixels.next dev never prefetches <Link>, so a pending navigation lasts longer in development than in production.gt-next provides runtime translation. Finished UI strings live in ui.<locale>.json at the app root (ui.en-US.json). Inline copy is acceptable while the copy is still being shaped.<T> per static block with <Var>, <Num>, <Currency>, <DateTime> and the branch components for dynamic parts, gt() for props, msg() for data arrays, const gt = useGT(); and never const { gt } = useGT()) are in the gt-components skill and gt-cloud's CLAUDE.md. On the landing, .oxlintrc.json turns on gt-react/static-jsx ("The <T> component must only have static children") and gt-react/static-string from @generaltranslation/react-core-linter.meta.json, generated trees, separators and synthetic groups. Translate the rendered label on the server with a gt() string the extractor can find (the metadataLabels map in src/app/[locale]/docs/layout.tsx, read by buildDocsPageTree in src/lib/docs-nav.ts). Routes and file references stay untranslated. Each new label type gets a regression test in __tests__/docs-tree.test.ts.gt-next's Link adds the locale to every href that starts with /. A link to a file at the app root (/openapi.json, /sitemap.xml, a .md twin) is a plain <a>. gt-next exports no router, and useRouter().push from Next drops the locale prefix.gt.config.json for production and staging.gt.config.json for the staging translate step. next.config.ts never gates locales on VERCEL_ENV: the staging pipeline runs with VERCEL_ENV=preview, and gt-next refuses an option that conflicts with the config file.pnpm --dir apps/landing validate runs gt validate. The deploy runs gt translate before it builds.references/docs.md holds the full detail for this section.
content:docs/<locale>/ with meta.json per folder, loaded through source.config.ts and src/lib/source.ts, and rendered by src/app/[locale]/docs/[variant]/[...slug]/page.tsx inside DocsLayout from packages/ui._v-react by default, _v-next (Next.js), _v-ts (TanStack Start) and _v-rn (React Native). src/proxy.ts picks it from the gt-fw-pref cookie, only for the pages that have framework tabs, so the active tab is right on first paint and the page stays a static cache hit./llms.txt, /llms-index.txt, /llms-full.txt, scoped /docs/<scope>/llms.txt, a .md and .mdx twin of every page, /AGENTS.md, /agent-prompt.md and /sitemap.md. The twin route prerenders every locale with dynamicParams = false, because the deployed function cannot read the content submodule from disk. The proxy serves the twin to known AI user agents and to requests that accept text/markdown.src/proxy.ts passes /aeo through, then runs the unsupported-locale guard, the legacy docs redirects (src/lib/legacy-docs-redirects.json), the markdown rewrite, the variant rewrite, the signed-in dashboard redirect and gtMiddleware. A new docs rewrite sits below the guard, and every branch that returns early calls captureAdAttribution so an ad click id survives. The proxy matcher skips every path with a dot, so rules for .md URLs live in next.config.ts or the route.redirect() thrown during a page render commits the layout above it first and paints an intermediate shell. Every link the site renders points at the resolved leaf page (footer-links.test.ts holds the footer to that)./en-US/docs/cli/reference/commands/configure.content:DOCS-SKILL.md) and a public authentication boundary in content:AGENTS.md: the docs cover API-key authentication and the CLI sign-in commands and leave out user-token internals.references/blog.md holds the full detail for this section.
content:blog/en-US/<slug>.mdx with authors in content:authors/. The frontmatter names the dark cover under images, its light twin under imagesLight and the social card under ogImages.next-mdx-remote with JavaScript expressions blocked, which deletes every prop={expression} silently. Blog components (Carousel and CarouselItem, HitList and HitItem, AuthorSpotlight) take string attributes and child elements.src/mdx-components.tsx and gets a simple stand-in in content:apps/content/src/mdx-components.tsx, or the content preview build fails.public/static/blogs/: 3840 px webp illustrations and covers, a 2400 by 1260 PNG social card, GIF clips, and a ?v=YYYYMMDD-HHMM stamp on every reference. BlogPostCover requests quality 95 for webp covers.git submodule update --init --remote in $GT_CLOUD/scripts/deploy-landing.sh), and the content repository's deploy hook rebuilds the site on each merge, so merging the content pull request publishes the post. Merge the gt-cloud side first whenever the post needs a new component, new images or new redirects, and the content side second.lottie-web canvas traps are in the reference.references/pages.md holds the full detail for this section.
packages/ui/src/components/pricing/dollar-format.ts.content:docs/en-US/overview/for-coding-agents.mdx and is served raw at /agent-prompt.md. The prompt must work when handed to a coding agent unchanged, so every change to it, the CLI or the quickstarts is followed by an end-to-end run on a fresh app (Kevin, 2026-09-30).provider/model ids, rate-limit through the shared src/lib/rate-limit.ts, set Gemini's thinking budget to 0, and always answer, with a local fallback when the gateway refuses. The FAQ assistant stays off until FAQ_ASSISTANT_ENABLED is set, which waits on a rate limit shared by every function instance./world (branch k/language-map, unmerged on 2026-10-05) is organized by language and names no country or territory. Its facts come from CLDR, Natural Earth and GT's locale data, and its artifact pictures carry no readable English (Kevin, 2026-10-03 and 2026-10-05).src/app/robots.ts on main allows every user agent on / with /api/ and /private/ disallowed, lets Twitterbot fetch /api/og and /api/og-home, and names the sitemap. The staging build disallows everything, and the locale layout's metadata emits index, follow everywhere except staging. Never add a rule that blocks an AI crawler or a public page; a Content-Signal line is Kevin's decision.AI_USER_AGENTS in src/proxy.ts) get the markdown twin of a docs page.x-robots-tag: noindex, so Lighthouse's crawlable audit fails on a preview and passes on generaltranslation.com. Leave it.src/components/landing/shell/SiteFooterMount.tsx renders V0Footer inside .toolchain-root sgdh-root with ThemeAttributeBridge and a tc-rail column. The sgdh-root class is required, because the light theme's token remaps in v0-pages.css are scoped to the page root classes and the footer stays dark on light pages without it. The (home) and blog layouts pass footer={false} to Header and append the mount right after the content; the home page has no footer of its own. The docs have no site footer. Footer links live in src/components/landing/shell/footer-links.tsx, tested in shell/__tests__/footer-links.test.ts.src/app/[locale]/layout.tsx emits ThemePreferenceInitScript and an inline script (DATA_THEME_PREPAINT) that sets data-theme from the stored theme or the system preference. The site's stylesheets key on [data-theme] while next-themes sets the dark class, and ThemeAttributeBridge (src/components/pages/home/ThemeAttributeBridge.tsx) mirrors the class onto the attribute after hydration. SiteFooterMount, the blog layout and HomePage mount the bridge; a page that renders none of them keeps its first-paint theme when the reader switches themes.landing in the team scope general-translation, with generaltranslation.com as the production alias. Its build command runs $GT_CLOUD/scripts/deploy-landing.sh: check out content main, prepare legal, a filtered install (pnpm i --filter=landing... --filter=. --frozen-lockfile), build @generaltranslation/settings, gt translate, build:landing (variant pages, next build, Pagefind), then Sentry source maps.main and staging only (apps/landing/vercel.json). The content repository's deploy hook adds a deploy for each merge to content main. Pull request branches get no preview; measure on production after the merge or deploy a preview by hand.vercel ls landing --scope general-translation, then vercel inspect <url> --logs --scope general-translation. Compare the first error line of the newest production build with the last Ready build's commit. While builds fail, production keeps serving the last good deployment, and docs merges do not go live.TS6306: Referenced project packages/email must have setting composite came from a tsconfig.json project reference to a package the landing never imports. The filtered install leaves such a package without node_modules, so its config cannot resolve. The landing's tsconfig.json references only packages it imports (gt-cloud #5076).next 16.2.12 in its own package.json while the dashboard and admin stay on the catalog's 16.3.8: "16.2 it is" (Kevin, 2026-10-01, gt-cloud #5079). When the landing returns to 16.3, restore agentRules: false in next.config.ts, and weigh that 16.3.8 is a security release for image optimization with allow-listed remote hosts, which the landing uses.screenshots/pr-* fail on Vercel at once with "Root Directory apps/landing does not exist". Those failures need no action.pnpm --dir apps/landing run build without APP_NODE_ENV=production (the dashboard URL guard refuses it). It prerenders about 7,000 pages and prints fumadocs-openapi "Failed to generate typescript schema" warnings that predate any current change.pnpm --dir apps/landing lint runs oxlint with the root .oxlintrc.json, gt-ui rules included, on the landing. The root pnpm lint adds the email identity check, the agent skills check ($GT_CLOUD/scripts/check-agent-skills.mjs, added in gt-cloud #5145 on 2026-10-05, which checks each .agents/skills skill's frontmatter, the files its links and path spans name, and its .claude/skills link) and oxfmt --check . over the whole repository; format changed files with pnpm exec oxfmt <files>.pnpm --dir apps/landing test, or pnpm --dir apps/landing exec vitest run <path> for one area. Band decisions (hrefs, tracking slugs, data tables) are exported constants tested in src/components/landing/sections/__tests__/; routes, the sitemap, the docs and the proxy are tested in __tests__/.pnpm --dir apps/landing exec tsc --noEmit (after next typegen in a fresh worktree). The landing's typescript is the catalog:ts6 alias of the JS compiler, which next build needs; the packages use the native v7 compiler (gt-cloud CLAUDE.md).pnpm --dir apps/landing validate when <T>, gt() or msg() strings change.next.config.ts, fonts or prerendering change.git diff --numstat origin/main...HEAD).Cta, icon tiers, Inter) and gt-components' translation rules, and docs labels translate on the server.gt-next's Link.src/proxy.ts sits below the unsupported-locale guard, and rules for dotted paths live in next.config.ts or the route.llms*.txt routes and the machine-only anchors still render for the changed pages.?v= stamp and returns 200.robots.ts still lets every crawler reach every public page.(home) or blog groups get the site footer through SiteFooterMount and the theme through the root layout.tsconfig.json references only packages the landing imports, and next stays on 16.2.x until 16.3's prerender memory is fixed.tsc, gt validate and, where needed, a production build pass, and the pull request body shows crops in both themes.General skills from Kevin's wiki: agent-browser (pages, screenshots and measurements), design-engineering-polish (the final visual pass), create-graphics (diagrams for posts outside the graphics toolchain). In this set: gt-landing-pages (page and section conventions), gt-components (shared UI), gt-voice (copy, alt text and post prose), gt-graphics (blog illustrations and covers), gt-dither (dithered fields and artifact pictures), gt-lints and gt-ship. In gt-cloud: gt-landing, gt-ui, glyphfield, code-comments, gt-testing, react-useeffect.
.agents/skills/gt-landing/SKILL.md, .agents/skills/gt-landing/references/design.md, CLAUDE.md, apps/landing/next.config.ts, apps/landing/package.json, apps/landing/vercel.json, apps/landing/src/app/robots.ts, apps/landing/src/app/[locale]/layout.tsx, apps/landing/src/app/[locale]/(home)/layout.tsx, apps/landing/src/app/[locale]/blog/layout.tsx, apps/landing/src/app/[locale]/docs/layout.tsx, apps/landing/src/components/landing/shell/SiteFooterMount.tsx, apps/landing/src/components/landing/shell/engine.css, apps/landing/src/app/globals.css, apps/landing/src/proxy.ts, apps/landing/src/lib/fonts.ts, apps/landing/src/lib/fonts-prose.ts, scripts/deploy-landing.sh, scripts/check-agent-skills.mjs, .gitmodules, pnpm-workspace.yaml, package.json, .oxlintrc.json, tooling/oxlint-plugins/gt-ui.ts, tooling/oxlint-plugins/gt-react.ts, .github/workflows/pr-policy.yml.AGENTS.md, DOCS-SKILL.md, apps/content/src/mdx-components.tsx..agents/skills/gt-blog-mdx-components/SKILL.md (2026-09-18, absorbed into this skill), content/blog/designing-docs-for-humans.mdx, docs/GRAPHICS.md.agent-readable-docs.md, docs-perf-investigation.md, docs-shell-transition-traps.md, landing-deploy-failures.md, fuma-blog-pipeline.md, docs-redesign-post-part2.md, blog-lottie-figure.md, landing-ai-gateway-faq.md, world-language-map.md, lighthouse-round-conventions.md, pricing-money-format.md, landing-hero-agent-button.md, agent-prompt-test.md, tailwind-port-conventions.md, landing-inter-only.md, pr-screenshots-and-gallery.md, pr-size-discipline.md./world organized by language (2026-10-03); no English on artifact pictures (2026-10-05).The folder skills/gt-website as agents fetch it. Each file opens raw.
SKILL.mdreferences/blog.mdreferences/docs.mdreferences/pages.md