Plane 开源贡献实战指南:本地开发环境搭建、Monorepo 架构与 i18n 翻译贡献全流程
【免费下载链接】plane🔥🔥🔥 Open-source Jira, Linear, Monday, and ClickUp alternative. Plane is a modern project management platform to manage tasks, sprints, docs, and triage.项目地址: https://gitcode.com/GitHub_Trending/pl/plane
本篇技术指南基于 Plane 仓库的官方贡献文档 CONTRIBUTING.md,系统讲解如何从零搭建 Plane 的本地开发环境(Docker Compose + pnpm/Turborepo)、遵循项目 Issue 规范与代码质量标准,以及如何按仓库实际结构完成国际化(i18n)翻译与新增语言贡献。读完后你可以独立完成:本地起一套可开发的 Plane 实例、通过 lint/format 检查提交代码、并按仓库现有 i18next + ICU 体系安全地修改或新增翻译。
提交 Issue 与命名规范
在动手写代码之前,贡献的第一步是规范地报告问题。官方文档给出的流程是:
先检索已有 Issue。提交新 Issue 前先搜索仓库的 Issue 列表,确认是否已有人报告或讨论过,可能直接获得 workaround。
提供最小可复现场景。修复 Bug 的前提是能够复现。请提供使用仓库或 Gist 的最小复现场景——一个可运行的现场能让维护者直接掌握关键信息,而无需反复追问第三方库版本、失败的具体用例等细节。缺少最小复现的 Issue 可能无法被调查,甚至无法解决。
遵循标题命名约定。打开新 Issue 时,使用清晰简洁、带类型前缀的标题:
- Bug:
🐛 Bug: [short description] - 功能:
🚀 Feature: [short description] - 改进:
🛠️ Improvement: [short description] - 文档:
📘 Docs: [short description]
官方给出的示例:
🐛 Bug: API token expiry time not saving correctly📘 Docs: Clarify RAM requirement for local setup🚀 Feature: Allow custom time selection for token expiration
这一约定能帮助维护者更高效地分流(triage)和管理 Issue。
- Bug:
功能缺失时的处理路径
如果缺少某个功能,可以直接以「🚀 Feature」模板提交功能请求;如果你打算亲自实现,文档明确要求:必须先提交一个描述提案的 Issue,与社区确认方向后再动手,避免开发完却不能合并。
本地开发环境:需求清单与初始化步骤
环境与硬件要求
贡献文档列出的环境要求如下表:
| 依赖 | 版本要求(文档) | 仓库实际使用的版本(以配置文件为准) |
|---|---|---|
| Docker Engine | 已安装并运行 | Docker Compose 构建多个服务容器 |
| Node.js | 20+(LTS) | .node-version固定为22.22.0,根 package.json 的engines要求>=22.22.0,包管理器锁定pnpm@11.3.0 |
| Python | 3.8+ | 用于apps/api的 Django 后端构建 |
| PostgreSQL | v14 | docker-compose-local.yml 中实际使用postgres:15.7-alpine |
| Redis | v6.2.7 | 开发编排中实际使用valkey/valkey:7.2.11-alpine(Valkey 为 Redis 的兼容实现) |
| 内存 | 最低建议 12 GB RAM | 8 GB 机器在容器构建/依赖安装阶段可能内存崩溃,文档建议使用云端环境(如 GitHub Codespaces)或升级内存 |
可以看出,文档中的版本是最低基线,而本地开发编排文件实际拉取了更新的镜像(PostgreSQL 15.7、Valkey 7.2.11、RabbitMQ 3.13.6)。以当前仓库配置为准即可,无需自行降级。
项目结构:单仓 Monorepo
Plane 是一个 monorepo,后端 API 与多个前端应用同仓维护:
apps/api:Django 后端(含数据库模型、视图、Celery 后台任务bgtasks等);apps/web:主站前端,dev脚本运行在3000端口(apps/web/package.json:react-router dev --port 3000);apps/admin:实例管理端,运行在3001端口,首次部署时需在此页面完成实例初始化;apps/space:独立部署形态的前端,运行在3002端口;apps/live:基于 Hocuspocus 的实时协作服务(Yjs WebSocket 服务端);packages/*:共享库,包括i18n(国际化)、editor(TipTap 编辑器)、propel(设计系统组件)、types、services等。
工作区由 pnpm-workspace.yaml 定义(apps/*+packages/*,排除apps/api与apps/proxy),并配合 Turbo 管理任务依赖。
三步初始化流程
官方给出的完整操作步骤如下:
1. 克隆仓库并准备 setup 脚本
git clone https://github.com/makeplane/plane.git [folder-name] cd [folder-name] chmod +x setup.sh2. 运行 setup.sh
./setup.sh结合 setup.sh 源码可以看到,这一步实际完成了四件事:
- 将各服务的
.env.example复制为.env,覆盖根目录以及web、api、space、admin、live五个服务目录(setup.sh#L47-L60); - 为 Django 生成随机的 50 位
SECRET_KEY并追加写入apps/api/.env(setup.sh#L62-L78); - 通过
corepack enable pnpm激活package.json中锁定的 pnpm 版本; - 执行
pnpm install安装全部 Node 依赖(setup.sh#L80-L83)。
若中途任一步骤失败,脚本会以非零状态退出并提示检查上方报错。
3. 启动基础设施容器
docker compose -f docker-compose-local.yml updocker-compose-local.yml 定义了完整的一套本地依赖,全部位于dev_envbridge 网络内:
| 服务 | 镜像 | 端口/说明 |
|---|---|---|
plane-redis | valkey/valkey:7.2.11-alpine | 6379,数据卷redisdata |
plane-mq | rabbitmq:3.13.6-management-alpine | 用户/密码/VHOST 来自根.env的RABBITMQ_*变量 |
plane-minio | minio/minio | 9000(S3 API)+ 9090(控制台),启动时自动创建AWS_S3_BUCKET_NAME桶 |
plane-db | postgres:15.7-alpine | 5432,max_connections=1000 |
api | 由 apps/api/Dockerfile.dev 构建 | 8000,挂载./apps/api源码,入口bin/docker-entrypoint-api-local.sh |
worker/beat-worker | 同上 | Celery worker 与 beat 定时任务 |
migrator | 同上 | 一次性容器(restart: "no"),执行docker-entrypoint-migrator.sh --settings=plane.settings.local完成数据库迁移 |
数据库、Redis、RabbitMQ 的连接参数示例见 apps/api/.env.example,其中CORS_ALLOWED_ORIGINS已预置http://localhost:3000~3002等前端地址,POSTGRES_*、REDIS_*、RABBITMQ_*与容器服务名一一对应。
4. 启动前端应用
pnpm dev根 package.json 中该脚本实际是turbo run dev --concurrency=18,会按依赖图并行拉起 web/admin/space/live 等应用的 dev server。
5. 完成首次访问
- 打开
http://localhost:3001/god-mode/,将你自己注册为实例管理员(instance admin)。god-mode是 admin 应用在 Caddy 中配置的 SPA 前缀(见 apps/admin/caddy/Caddyfile),路由定义见 apps/admin/app/routes.ts,包含 general、workspace、email、authentication、ai、image 等实例管理页面; - 再打开
http://localhost:3000,用上一步的同一账号登录主站。
至此本地开发环境就绪。文档还提示:如果改动没有自动热更新,记得手动刷新浏览器。
编码规范:测试与 Lint/格式化
贡献文档明确了两条代码质量红线:
- 所有功能或 Bug 修复必须附带一个或多个测试用例(unit test);
- 统一使用 OxLint 检查、oxfmt 格式化,共享配置文件为仓库根目录的
.oxlintrc.json与.oxfmtrc.json。
从源码结构看,这套规范被接到了工程化工具链上:
- turbo.json 将
.oxlintrc.json、.oxfmtrc.json列为globalDependencies,并提供check、check:lint、check:format、check:types、fix、fix:lint、fix:format等任务,可按包增量执行; - 根 package.json 通过
lint-staged配置在提交钩子中对*.{js,jsx,ts,tsx,...,css,md}执行oxfmt、对 TS/JS 系文件执行oxlint --fix --deny-warnings,并启用 husky 保证钩子生效("prepare": "husky")。
即:提交前本地会自动格式化并做严格 lint(警告即失败),合入前还需通过 CI 的同类检查。
贡献方式全景
除写代码外,文档列举的贡献途径包括:
- 试用 Plane Cloud 与自托管平台并反馈问题;
- 添加新的集成(integrations);
- 添加或更新翻译;
- 帮助解决开放 Issue 或创建自己的 Issue;
- 分享想法与建议;
- 协助编写教程与博客文章;
- 以提案方式请求新功能;
- 报告 Bug;
- 改进文档——修复不完整或缺失的文档、措辞、示例或解释。
i18n 翻译贡献实战
贡献文档中专设了一章讲解如何添加或更新翻译。这一部分与仓库当前实现高度对应,可以直接按下面的结构落地操作。
翻译文件的实际组织方式
文档描述的目录约定是按语言分文件夹,每个语言文件夹内放翻译 JSON:
packages/i18n/src/locales/ ├── en/ │ ├── core.json # Critical translations │ └── translations.json ├── fr/ │ └── translations.json └── [language]/ └── translations.json对照仓库当前实际结构(从源码结构看),packages/i18n/src/locales/下每个语言目录已演进为按功能域拆分的 28 个命名空间文件(accessibility.json、auth.json、common.json、work-item.json、workspace-settings.json等),与 packages/i18n/src/constants/namespaces.ts 中定义的NAMESPACES数组一一对应,默认命名空间为common。贡献时请以目标语言目录下现有文件清单为准,逐文件补齐,不要按旧文档的translations.json单文件假设操作。
键的嵌套结构与 ICU 消息格式
为便于管理,键采用嵌套结构组织:
{ "issue": { "label": "Work item", "title": { "label": "Work item title" } } }动态内容(变量、复数)使用 IntlMessageFormat 的 ICU 语法:
简单变量:
{ "greeting": "Hello, {name}!" }复数化:
{ "items": "{count, plural, one {Work item} other {Work items}}" }
这些能力由运行时真实支撑:packages/i18n/src/core/instance.ts 中 i18next 实例通过.use(ICU)挂接i18next-icu处理 ICU 消息,并用resourcesToBackend按../locales/${language}/${namespace}.json动态加载对应语言与命名空间文件(依赖版本见 pnpm-workspace.yaml:i18next 25.10.9、i18next-icu 2.4.3、react-i18next 16.6.6)。初始化时从localStorage的userLanguage键读取用户语言,回退到英文(FALLBACK_LANGUAGE,见 packages/i18n/src/constants/language.ts),并主动预加载全部命名空间以避免并发异步加载引发的重渲染级联(instance.ts#L26-L52)。
更新现有翻译
- 定位
locales/<language>/下对应命名空间文件中的键; - 修改值,保持键的嵌套结构不变;
- 保留其中已有的 ICU 格式(变量、复数)原样不破坏。
新增翻译键
- 新键必须同时添加到所有语言文件中——即便暂无人翻译,也要先用英文占位;
- 所有语言的嵌套结构保持一致;
- 若键含动态内容(变量/复数),ICU 格式需在所有语言中统一应用。
新增一种语言的完整步骤
文档给出的四步流程,结合当前源码实现如下:
1. 更新类型定义——把新语言加入TLanguage联合类型(packages/i18n/src/types/language.ts):
export type TLanguage = "en" | "fr" | "your-lang";2. 添加语言配置——在支持语言列表中登记标签与值(packages/i18n/src/constants/language.ts#L11-L32,当前已内置 20 种语言):
export const SUPPORTED_LANGUAGES: ILanguageOption[] = [ { label: "English", value: "en" }, { label: "Your Language", value: "your-lang" }, ];3. 创建翻译文件——在locales/下新建locales/your-lang/目录,复制现有语言(建议复制en/)的 28 个命名空间 JSON 并逐键翻译。
4. 更新导入逻辑——文档以早期「按语言写switch分支」为例:
private importLanguageFile(language: TLanguage): Promise<any> { switch (language) { case "your-lang": return import("../locales/your-lang/translations.json"); // ... } }需要说明的是:从当前源码结构看,运行时已改为resourcesToBackend的动态路径导入(import(\../locales/${language}/${namespace}.json`)`,见 instance.ts#L18-L21),因此新增语言无需再手改 import 分支,只要完成前三步并保证文件命名合规即可被动态加载。
质量检查清单(提交前自查)
- 所有翻译键存在于每一种语言文件中;
- 各语言文件的嵌套结构完全一致;
- ICU 消息格式实现正确;
- 所有语言在应用内可无错加载;
- 动态变量与复数化按预期工作;
- 不存在缺失或未翻译的键。
实操建议(文档 Pro tips 部分):
- 拿不准时以英文翻译作为上下文参照;
- 用不同数量值验证复数化分支;
- 确认动态值(如
{name})能正确插值; - 复核嵌套键的访问路径是否准确。
仓库提供的 i18n 校验工具
除人工自查外,仓库内置了两个可运行脚本辅助把关(见 packages/i18n/scripts/):
sync-check.ts:扫描各语言文件的一致性,报告缺失键等问题;--ci模式下发现问题会以非零码退出,适合在本地模拟 CI 检查:tsx packages/i18n/scripts/sync-check.ts tsx packages/i18n/scripts/sync-check.ts --cigenerate-types.ts:读取src/locales/en/*.json,把嵌套结构展平为点号键并生成src/types/keys.generated.ts,为翻译键提供类型层约束:npx tsx packages/i18n/scripts/generate-types.ts
此外,组件侧统一通过 packages/i18n/src/hooks/use-translation.ts 的useTranslation钩子取值,其内部对t()返回值做了字符串强制转换的崩溃防护——当误取到命名空间节点键(返回对象)时会回退为键名并在开发态打印告警。这提醒贡献者:翻译值必须始终是字符串,否则会触发该防护路径。
需要帮助?
对文档或流程有疑问、有建议或想法时,官方鼓励直接参与讨论——贡献文档结尾指引通过 Plane 官方社区论坛交流。结合仓库内其他协作文档,行为准则见 CODE_OF_CONDUCT.md,代码归属与许可见 LICENSE.txt 与 COPYRIGHT.txt,安全相关问题则按 SECURITY.md 的渠道报告。
小结
Plane 的贡献路径可以概括为一条清晰主线:规范提 Issue →setup.sh+docker compose -f docker-compose-local.yml up+pnpm dev拉起本地全栈环境(3000/3001/3002 三端 + 8000 API)→ 以 OxLint/oxfmt 与单测约束代码质量 → 按 i18next 命名空间 + ICU 体系做翻译贡献并用sync-check工具自检。只要遵循 CONTRIBUTING.md 中的约定,并对照本文给出的实际配置文件路径(setup.sh、docker-compose-local.yml、packages/i18n/*)逐一核验,任何方向的第一个贡献都能平滑落地。
【免费下载链接】plane🔥🔥🔥 Open-source Jira, Linear, Monday, and ClickUp alternative. Plane is a modern project management platform to manage tasks, sprints, docs, and triage.项目地址: https://gitcode.com/GitHub_Trending/pl/plane
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考