Merge pull request #6794 from Lstarsky0/feat/community-dictionary-spine

Verified original PR #6794 at a31d9e7238: exact-head Linux, macOS and Windows tests and doctests, required checks and resolved review threads. Contributor authorship and existing history are preserved.

Signed-off-by: CodeWhale Bot <[email protected]>
This commit is contained in:
Hunter Bown authored and GitHub committed 2026-09-30 06:16:05 -07:00
commit 568abae0e3
9 files changed
+215 -120

No files matched your search

+92 -111
View File
@@ -2,6 +2,7 @@ import Link from "next/link";
import { Icon, type IconName } from "@/components/icon";
import { PageHeader, Section } from "@/components/page-header";
import { getFacts } from "@/lib/facts";
import { fill, getCommunity, pickTextLocale } from "@/lib/i18n/dictionaries";
import { buildPageMetadata } from "@/lib/page-meta";
import { RELEASE_CONTRIBUTORS, RELEASE_HELPERS } from "@/lib/release-credits";
@@ -9,113 +10,111 @@ export const revalidate = 300;
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const isZh = locale === "zh";
const t = getCommunity(locale);
return buildPageMetadata({
path: "/community",
locale,
title: isZh ? "社区 · Codewhale" : "Community · Codewhale",
description: isZh
? "了解 Codewhale 的国际开源社区,提交 issue、发送 pull request、改进翻译并查看版本贡献者。"
: "File issues, send pull requests, improve translations, and see who contributed to each Codewhale release.",
title: t.metaTitle,
description: t.metaDescription,
});
}
const pathsEn = [
{
title: "Report a problem",
description: "File a bug, compatibility problem, or unclear behavior with system details, reproduction steps, and any logs you can share safely.",
cta: "File an issue",
href: "https://github.com/Hmbown/CodeWhale/issues/new/choose",
},
{
title: "Improve code or tests",
description: "Pick one problem with clear edges, write the smallest patch that fixes it, and add a regression test that covers it.",
cta: "Browse open issues",
href: "https://github.com/Hmbown/CodeWhale/issues",
},
{
title: "Improve documentation or translations",
description: "Fix a wrong sentence, add an example, or help finish a language pack.",
cta: "Open the localization guide",
href: "https://github.com/Hmbown/CodeWhale/blob/main/docs/LOCALIZATION.md",
},
{
title: "Reproduce and review existing work",
description: "Try an issue or pull request on your platform and provider. Post the commands you ran, what happened, and what is still wrong.",
cta: "Browse pull requests",
href: "https://github.com/Hmbown/CodeWhale/pulls",
},
];
const pathsZh = [
{
title: "报告问题",
description: "报告 bug、兼容性问题或不清楚的行为,并附上系统信息、复现步骤和可以安全分享的日志。",
cta: "提交 issue",
href: "https://github.com/Hmbown/CodeWhale/issues/new/choose",
},
{
title: "改进代码或测试",
description: "挑一个范围清楚的问题,写最小的补丁,加一个覆盖改动的回归测试。",
cta: "查看开放 issues",
href: "https://github.com/Hmbown/CodeWhale/issues",
},
{
title: "改进文档或翻译",
description: "改正说错的地方,补一个示例,或者帮忙完成一个语言包。",
cta: "查看本地化指南",
href: "https://github.com/Hmbown/CodeWhale/blob/main/docs/LOCALIZATION.md",
},
{
title: "复现并审查现有工作",
description: "在你的平台和提供商上验证 issue 或 pull request,然后分享你运行的命令、结果和剩余问题。",
cta: "查看 pull requests",
href: "https://github.com/Hmbown/CodeWhale/pulls",
},
];
const activityEn = [
{ title: "Repository activity", description: "Recent issues and pull requests.", path: "/feed" },
{ title: "Community digest", description: "The weekly project record, reviewed by a maintainer.", path: "/digest" },
{ title: "Public roadmap", description: "Shipped, underway, considered, and ruled-out work.", path: "/roadmap" },
];
const activityZh = [
{ title: "仓库动态", description: "最近的 issues 与 pull requests。", path: "/feed" },
{ title: "社区摘要", description: "经过维护者审核的每周项目记录。", path: "/digest" },
{ title: "公开路线图", description: "已发布、正在进行、考虑中和明确不做的工作。", path: "/roadmap" },
];
export default async function CommunityPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const isZh = locale === "zh";
const t = getCommunity(locale);
const p = (path: string) => `/${locale}${path}`;
const facts = await getFacts();
const sourceIsPublished = facts.latestPublishedRelease?.version === facts.version;
const textLocale = pickTextLocale(locale);
const contributionPaths = { en: pathsEn, zh: pathsZh }[textLocale];
const activityLinks = { en: activityEn, zh: activityZh }[textLocale];
const contributionPaths = isZh
? [
{
title: "报告问题",
description: "报告 bug、兼容性问题或不清楚的行为,并附上系统信息、复现步骤和可以安全分享的日志。",
cta: "提交 issue",
href: "https://github.com/Hmbown/CodeWhale/issues/new/choose",
},
{
title: "改进代码或测试",
description: "挑一个范围清楚的问题,写最小的补丁,加一个覆盖改动的回归测试。",
cta: "查看开放 issues",
href: "https://github.com/Hmbown/CodeWhale/issues",
},
{
title: "改进文档或翻译",
description: "改正说错的地方,补一个示例,或者帮忙完成一个语言包。",
cta: "查看本地化指南",
href: "https://github.com/Hmbown/CodeWhale/blob/main/docs/LOCALIZATION.md",
},
{
title: "复现并审查现有工作",
description: "在你的平台和提供商上验证 issue 或 pull request,然后分享你运行的命令、结果和剩余问题。",
cta: "查看 pull requests",
href: "https://github.com/Hmbown/CodeWhale/pulls",
},
]
: [
{
title: "Report a problem",
description: "File a bug, compatibility problem, or unclear behavior with system details, reproduction steps, and any logs you can share safely.",
cta: "File an issue",
href: "https://github.com/Hmbown/CodeWhale/issues/new/choose",
},
{
title: "Improve code or tests",
description: "Pick one problem with clear edges, write the smallest patch that fixes it, and add a regression test that covers it.",
cta: "Browse open issues",
href: "https://github.com/Hmbown/CodeWhale/issues",
},
{
title: "Improve documentation or translations",
description: "Fix a wrong sentence, add an example, or help finish a language pack.",
cta: "Open the localization guide",
href: "https://github.com/Hmbown/CodeWhale/blob/main/docs/LOCALIZATION.md",
},
{
title: "Reproduce and review existing work",
description: "Try an issue or pull request on your platform and provider. Post the commands you ran, what happened, and what is still wrong.",
cta: "Browse pull requests",
href: "https://github.com/Hmbown/CodeWhale/pulls",
},
];
const activityLinks = isZh
? [
{ title: "仓库动态", description: "最近的 issues 与 pull requests。", href: p("/feed") },
{ title: "社区摘要", description: "经过维护者审核的每周项目记录。", href: p("/digest") },
{ title: "公开路线图", description: "已发布、正在进行、考虑中和明确不做的工作。", href: p("/roadmap") },
]
: [
{ title: "Repository activity", description: "Recent issues and pull requests.", href: p("/feed") },
{ title: "Community digest", description: "The weekly project record, reviewed by a maintainer.", href: p("/digest") },
{ title: "Public roadmap", description: "Shipped, underway, considered, and ruled-out work.", href: p("/roadmap") },
];
const activityIcons: IconName[] = ["repeat", "book", "tide"];
return (
<>
<PageHeader
kicker={isZh ? "国际开源社区" : "International open-source community"}
title={isZh ? "与世界各地的贡献者一起构建 Codewhale。" : "Build Codewhale with contributors around the world."}
lede={
isZh
? "运行时、文档、测试和翻译,来自不同国家、语言和平台的贡献者。第一次参与不必是大功能。一份清楚的 bug 报告、一处文档修正、或一个带测试的小补丁,都算。"
: "The runtime, docs, tests, and translations come from contributors across countries, languages, and platforms. A first contribution does not have to be a feature. A clear bug report, a documentation fix, or a small tested patch counts."
}
kicker={t.kicker}
title={t.title}
lede={t.lede}
pose="talk"
actions={
<>
<Link href="https://github.com/Hmbown/CodeWhale/issues/new/choose" className="btn btn-primary btn-lg">
{isZh ? "提交 issue" : "File an issue"}
{t.fileIssue}
</Link>
<Link href="https://github.com/Hmbown/CodeWhale/pulls" className="btn btn-secondary btn-lg">
{isZh ? "查看 pull requests" : "Browse pull requests"}
{t.browsePulls}
</Link>
<Link href={p("/contribute")} className="btn btn-ghost btn-lg">
{isZh ? "阅读贡献指南" : "Read the contribution guide"}
{t.readGuide}
<Icon name="arrow-right" className="icon icon-flip" />
</Link>
</>
@@ -125,12 +124,8 @@ export default async function CommunityPage({ params }: { params: Promise<{ loca
<div className="page-body">
<Section
id="community-paths"
title={isZh ? "从一件小事开始。" : "Start with one small thing."}
scope={
isZh
? "报 bug、写代码、写测试、改文档、翻译、审查,都有用。选一样适合你时间的。"
: "Bug reports, code, tests, docs, translations, and review all help. Pick the one that fits the time you have."
}
title={t.pathsTitle}
scope={t.pathsScope}
>
<div className="grid-2">
{contributionPaths.map((path) => (
@@ -148,17 +143,13 @@ export default async function CommunityPage({ params }: { params: Promise<{ loca
<Section
id="community-record"
title={isZh ? "从提案到发布,都是公开的。" : "From proposal to release, in the open."}
scope={
isZh
? "动态页显示最近的仓库活动。社区摘要保留每周存档。路线图区分已发布的和还在讨论的。"
: "The activity feed shows recent repository work. The community digest keeps the weekly archive of repository activity. The roadmap separates what shipped from what is still being discussed."
}
title={t.recordTitle}
scope={t.recordScope}
>
<ul className="dir-list dir-list-card" role="list">
{activityLinks.map((item, index) => (
<li key={item.href}>
<Link href={item.href} className="dir-row">
<li key={item.path}>
<Link href={p(item.path)} className="dir-row">
<span className="dir-mark" aria-hidden="true"><Icon name={activityIcons[index] ?? "book"} /></span>
<span className="dir-text">
<span className="dir-title">{item.title}</span>
@@ -173,25 +164,15 @@ export default async function CommunityPage({ params }: { params: Promise<{ loca
<Section
id="community-credit"
title={isZh ? "贡献者署名是版本记录的一部分。" : "Contributor credit is part of the release record."}
scope={
isZh
? `${sourceIsPublished ? "这一版本" : "这一版"}包含社区提交的代码、测试、复现和验证。即使维护者改过补丁再合入,原作者的署名也保留在提交、更新日志和贡献者名单中。`
: `This ${sourceIsPublished ? "release" : "version"} includes code, tests, reproductions, and verification from the community. If a maintainer reworks a patch before it lands, the original author stays credited in the commit, the changelog, and the contributor record.`
}
title={t.creditTitle}
scope={sourceIsPublished ? t.creditScope : t.creditScopeUnreleased}
>
<p className="page-kicker mb-4">
{sourceIsPublished
? isZh
? `v${facts.version} 版本致谢`
: `v${facts.version} release credit`
: isZh
? `v${facts.version} 致谢(未发布)`
: `v${facts.version} credit (unreleased)`}
{fill(sourceIsPublished ? t.creditLabel : t.creditLabelUnreleased, { version: facts.version ?? "—" })}
</p>
<div className="grid-2">
<div className="tile">
<h3>{isZh ? "已合并或吸收的贡献" : "Merged or adapted contributions"}</h3>
<h3>{t.mergedTitle}</h3>
<ul className="credit-list" role="list">
{RELEASE_CONTRIBUTORS.map((handle) => (
<li key={handle}>
@@ -202,7 +183,7 @@ export default async function CommunityPage({ params }: { params: Promise<{ loca
</div>
{RELEASE_HELPERS.length > 0 ? (
<div className="tile">
<h3>{isZh ? "报告、复现与验证" : "Reports, reproductions, and verification"}</h3>
<h3>{t.helpersTitle}</h3>
<ul className="credit-list" role="list">
{RELEASE_HELPERS.map((handle) => (
<li key={handle}>
@@ -215,7 +196,7 @@ export default async function CommunityPage({ params }: { params: Promise<{ loca
</div>
<p className="status-line mt-4">
<Link href="https://github.com/Hmbown/CodeWhale/blob/main/docs/CONTRIBUTORS.md" className="link">
{isZh ? "完整贡献者名单" : "Full contributor record"}
{t.fullRecord}
</Link>
<Link href="https://github.com/Hmbown/CodeWhale/blob/main/CHANGELOG.md" className="link">CHANGELOG</Link>
</p>
+3
View File
@@ -22,6 +22,7 @@ import {
EN_DOCS_TROUBLESHOOTING,
EN_DIGEST,
EN_FEED,
EN_COMMUNITY,
EN_FAQ,
EN_HOME,
EN_LEGAL_PRIVACY,
@@ -53,6 +54,7 @@ import {
getDocsVocabulary,
getDigest,
getFeed,
getCommunity,
getFaq,
getHome,
getLegalPrivacy,
@@ -278,6 +280,7 @@ describe("website dictionaries", () => {
["legal-privacy", getLegalPrivacy, EN_LEGAL_PRIVACY],
["digest", getDigest, EN_DIGEST],
["feed", getFeed, EN_FEED],
["community", getCommunity, EN_COMMUNITY],
["faq", getFaq, EN_FAQ],
["roadmap", getRoadmap, EN_ROADMAP],
["contribute", getContribute, EN_CONTRIBUTE],
+34
View File
@@ -0,0 +1,34 @@
import type { CommunityDict } from "../types";
/**
* English reference dictionary for `app/[locale]/community/page.tsx`. Copy
* moved verbatim from its `isZh` ternaries. The contribution paths and
* activity links are content and stay in the page.
*/
export const community: CommunityDict = {
metaTitle: "Community · Codewhale",
metaDescription:
"File issues, send pull requests, improve translations, and see who contributed to each Codewhale release.",
kicker: "International open-source community",
title: "Build Codewhale with contributors around the world.",
lede: "The runtime, docs, tests, and translations come from contributors across countries, languages, and platforms. A first contribution does not have to be a feature. A clear bug report, a documentation fix, or a small tested patch counts.",
fileIssue: "File an issue",
browsePulls: "Browse pull requests",
readGuide: "Read the contribution guide",
pathsTitle: "Start with one small thing.",
pathsScope:
"Bug reports, code, tests, docs, translations, and review all help. Pick the one that fits the time you have.",
recordTitle: "From proposal to release, in the open.",
recordScope:
"The activity feed shows recent repository work. The community digest keeps the weekly archive of repository activity. The roadmap separates what shipped from what is still being discussed.",
creditTitle: "Contributor credit is part of the release record.",
creditScope:
"This release includes code, tests, reproductions, and verification from the community. If a maintainer reworks a patch before it lands, the original author stays credited in the commit, the changelog, and the contributor record.",
creditScopeUnreleased:
"This version includes code, tests, reproductions, and verification from the community. If a maintainer reworks a patch before it lands, the original author stays credited in the commit, the changelog, and the contributor record.",
creditLabel: "v{version} release credit",
creditLabelUnreleased: "v{version} credit (unreleased)",
mergedTitle: "Merged or adapted contributions",
helpersTitle: "Reports, reproductions, and verification",
fullRecord: "Full contributor record",
};
+14 -2
View File
@@ -39,6 +39,7 @@ import type {
HomeDict,
DigestDict,
FeedDict,
CommunityDict,
FaqDict,
LegalPrivacyDict,
LegalTermsDict,
@@ -113,6 +114,8 @@ import { digest as enDigest } from "./en/digest";
import { digest as zhDigest } from "./zh/digest";
import { feed as enFeed } from "./en/feed";
import { feed as zhFeed } from "./zh/feed";
import { community as enCommunity } from "./en/community";
import { community as zhCommunity } from "./zh/community";
import { faq as enFaq } from "./en/faq";
import { faq as zhFaq } from "./zh/faq";
import { roadmap as enRoadmap } from "./en/roadmap";
@@ -315,8 +318,8 @@ const DOCS_VOCABULARY: Record<string, DocsVocabularyDict> = {
};
/**
* Shared surface states, the changelog page, the two legal pages, the digest
* and feed pages, the FAQ, the roadmap, the contribute page and the
* Shared surface states, the changelog page, the two legal pages, the digest,
* feed and community pages, the FAQ, the roadmap, the contribute page and the
* constitution page follow the same optional per-locale rule as the docs page
* dictionaries: English is the reference, every other locale falls back to it
* at lookup time.
@@ -345,6 +348,10 @@ const FEED: Record<string, FeedDict> = {
zh: zhFeed,
};
const COMMUNITY: Record<string, CommunityDict> = {
zh: zhCommunity,
};
const FAQ: Record<string, FaqDict> = {
zh: zhFaq,
};
@@ -469,6 +476,10 @@ export function getFeed(locale: string): FeedDict {
return FEED[locale] ?? enFeed;
}
export function getCommunity(locale: string): CommunityDict {
return COMMUNITY[locale] ?? enCommunity;
}
export function getFaq(locale: string): FaqDict {
return FAQ[locale] ?? enFaq;
}
@@ -529,6 +540,7 @@ export const EN_LEGAL_TERMS = enLegalTerms;
export const EN_LEGAL_PRIVACY = enLegalPrivacy;
export const EN_DIGEST = enDigest;
export const EN_FEED = enFeed;
export const EN_COMMUNITY = enCommunity;
export const EN_FAQ = enFaq;
export const EN_ROADMAP = enRoadmap;
export const EN_CONTRIBUTE = enContribute;
+29
View File
@@ -499,6 +499,35 @@ export interface FeedDict {
startDiscussion: string;
}
/** `app/[locale]/community/page.tsx`. */
export interface CommunityDict {
metaTitle: string;
metaDescription: string;
kicker: string;
/** Page H1. */
title: string;
lede: string;
fileIssue: string;
browsePulls: string;
readGuide: string;
pathsTitle: string;
pathsScope: string;
recordTitle: string;
recordScope: string;
creditTitle: string;
/** Credit scope once the source version is the published release. */
creditScope: string;
/** Credit scope while the source version is ahead of the published release. */
creditScopeUnreleased: string;
/** Credit heading for the published release; `{version}` is filled at render time. */
creditLabel: string;
/** Credit heading while the source version is unreleased; `{version}` is filled at render time. */
creditLabelUnreleased: string;
mergedTitle: string;
helpersTitle: string;
fullRecord: string;
}
/** `app/[locale]/faq/page.tsx` and its `components/faq-search.tsx`. */
export interface FaqDict {
metaTitle: string;
+27
View File
@@ -0,0 +1,27 @@
import type { CommunityDict } from "../types";
/** Chinese dictionary for `app/[locale]/community/page.tsx`. */
export const community: CommunityDict = {
metaTitle: "社区 · Codewhale",
metaDescription: "了解 Codewhale 的国际开源社区,提交 issue、发送 pull request、改进翻译并查看版本贡献者。",
kicker: "国际开源社区",
title: "与世界各地的贡献者一起构建 Codewhale。",
lede: "运行时、文档、测试和翻译,来自不同国家、语言和平台的贡献者。第一次参与不必是大功能。一份清楚的 bug 报告、一处文档修正、或一个带测试的小补丁,都算。",
fileIssue: "提交 issue",
browsePulls: "查看 pull requests",
readGuide: "阅读贡献指南",
pathsTitle: "从一件小事开始。",
pathsScope: "报 bug、写代码、写测试、改文档、翻译、审查,都有用。选一样适合你时间的。",
recordTitle: "从提案到发布,都是公开的。",
recordScope: "动态页显示最近的仓库活动。社区摘要保留每周存档。路线图区分已发布的和还在讨论的。",
creditTitle: "贡献者署名是版本记录的一部分。",
creditScope:
"这一版本包含社区提交的代码、测试、复现和验证。即使维护者改过补丁再合入,原作者的署名也保留在提交、更新日志和贡献者名单中。",
creditScopeUnreleased:
"这一版包含社区提交的代码、测试、复现和验证。即使维护者改过补丁再合入,原作者的署名也保留在提交、更新日志和贡献者名单中。",
creditLabel: "v{version} 版本致谢",
creditLabelUnreleased: "v{version} 致谢(未发布)",
mergedTitle: "已合并或吸收的贡献",
helpersTitle: "报告、复现与验证",
fullRecord: "完整贡献者名单",
};
+1 -1
View File
@@ -7,7 +7,7 @@ const MIGRATION_HOME = join("lib", "i18n");
// One-way ceiling (#5519): the isZh migration converges or holds, never
// regresses. To lower the number: migrate branches into lib/i18n, then set
// CEILING to the new count reported by this test.
const CEILING = 4;
const CEILING = 3;
function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir)) {
+14 -6
View File
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import { FACTS } from "./facts.generated";
import { RELEASE_CONTRIBUTORS, RELEASE_HELPERS } from "./release-credits";
import { EN_CHROME, EN_DOCS_SHELL, EN_HOME, getChrome } from "./i18n/dictionaries";
import { EN_CHROME, EN_COMMUNITY, EN_DOCS_SHELL, EN_HOME, getChrome } from "./i18n/dictionaries";
function pageSource(path: string): string {
return readFileSync(new URL(`../app/[locale]/${path}`, import.meta.url), "utf8");
@@ -103,23 +103,31 @@ describe("public website copy contracts", () => {
expect(install).not.toContain("facts.version");
expect(install).not.toContain('"v0.8.x"');
expect(install).not.toContain("cnbInstall(facts.version");
expect(community).toContain("credit (unreleased)");
// The community credit wording moved into its dictionary (#5337) the same
// way: the page picks the unreleased label, and the EN reference says so.
expect(community).toContain("t.creditLabelUnreleased");
expect(EN_COMMUNITY.creditLabelUnreleased).toBe("v{version} credit (unreleased)");
});
it("presents providers as peers and puts contributor actions near the top", () => {
const providerCopy = `${pageSource("models/page.tsx")}\n${pageSource("faq/page.tsx")}`;
const community = pageSource("community/page.tsx");
// The page's own wording lives in its dictionary (#5337); the paths and
// links stay in the page.
const communityCopy = `${community}\n${Object.values(EN_COMMUNITY).join("\n")}`;
expect(providerCopy).not.toMatch(/first-class|一级支持|一级模型/);
expect(community).toContain("International open-source community");
expect(community).toContain("t.kicker");
expect(EN_COMMUNITY.kicker).toBe("International open-source community");
expect(community).toContain("issues/new/choose");
expect(community).toContain("docs/LOCALIZATION.md");
expect(community).toContain("Hmbown/CodeWhale/pulls");
expect(community).toContain("keeps the weekly archive of repository activity");
expect(community).not.toContain("latest one sits near the top");
expect(community).toContain("t.recordScope");
expect(EN_COMMUNITY.recordScope).toContain("keeps the weekly archive of repository activity");
expect(communityCopy).not.toContain("latest one sits near the top");
expect(community).not.toContain("<Ticker");
expect(community).not.toContain("<StatGrid");
expect(community).not.toContain("Today's dispatch");
expect(communityCopy).not.toContain("Today's dispatch");
});
it("keeps current-release website credits in exact changelog parity", () => {
+1
View File
@@ -49,6 +49,7 @@ const OPTIONAL_FILES = [
"legal-privacy.ts",
"digest.ts",
"feed.ts",
"community.ts",
"faq.ts",
"roadmap.ts",
"contribute.ts",