开源项目国际化复盘:从单语言到i18n支持的工程改造与社区协作 开源项目国际化复盘从单语言到i18n支持的工程改造与社区协作一、国际化不是把文本翻译了就行AgenFlow项目最初所有文档、代码注释、错误信息都是英文。当中国用户提出能不能支持中文文档时第一反应是直接翻译README——但很快就意识到i18n远不止翻译日期格式美国MM/DD/YYYY vs 中国YYYY-MM-DD时区处理time.Now()在不同时区返回不同值代码中的硬编码英文文案约200处文档的版本同步——英文文档更新了中文还在旧版本CLI输出的对齐——中文是全角字符英文的终端对齐在中文字符下会错位二、代码i18n的工程实现选择go-i18n轻量、文件格式简单TOML/YAML/JSON、支持复数形式。# locales/en-US.toml [greeting] one Hello other Hello [error.not_found] one {{.Resource}} not found other {{.Resource}} not found# locales/zh-CN.toml [greeting] one 你好 other 你好 [error.not_found] one 未找到{{.Resource}} other 未找到{{.Resource}}代码中消除硬编码import github.com/nicksnyder/go-i18n/v2/i18n type I18nBundle struct { bundle *i18n.Bundle } func (b *I18nBundle) T(lang string, messageID string, templateData map[string]interface{}) string { localizer : i18n.NewLocalizer(b.bundle, lang) msg, err : localizer.Localize(i18n.LocalizeConfig{ MessageID: messageID, TemplateData: templateData, }) if err ! nil { return messageID // fallback返回messageID作为默认英文 } return msg } // 使用 func (s *Service) GetUser(ctx context.Context, id string) (*User, error) { user, err : s.repo.Find(ctx, id) if err ! nil { lang : extractLang(ctx) msg : s.i18n.T(lang, error.not_found, map[string]interface{}{ Resource: User, }) return nil, errors.New(msg) // User not found 或 未找到User } return user, nil }CLI输出的特殊处理中文字符宽度是英文的2倍在终端表格中需要手动计算对齐import github.com/mattn/go-runewidth func padRight(s string, width int) string { return s strings.Repeat( , width-runewidth.StringWidth(s)) }三、文档i18n的社区协作使用Crowdin做翻译管理平台开源项目免费源语言文件英文Markdown自动推送到Crowdin社区志愿者在Crowdin上翻译翻译审核后自动PR回GitHubCI构建多语言文档站# crowdin.yml files: - source: /docs/**/*.md translation: /i18n/%locale%/docs/**/%original_file_name% languages_mapping: locale: zh-CN: zh ja: ja贡献者激励在CONTRIBUTORS.md中单独列出翻译贡献者每月在社区公告中致谢。翻译贡献也计入贡献者阶梯提升为Reviewer的考虑因素之一。四、i18n的持续维护成本维护项频率时间新增文案的翻译每次Release约30条翻译质量Review每月1小时Crowdin同步自动0文档翻译同步每次文档更新2小时关键挑战英文文档更新后中文翻译可能滞后。解决——Crowdin自动检测源文件变更标记需要更新的翻译。在文档站顶部显示此页面翻译更新时间YYYY-MM-DD。五、总结开源项目i18n的核心经验go-i18n处理代码文案Crowdin管理翻译协作——两者分离错误信息用messageID代替硬编码文案——messageID也是英文fallbackCLI输出注意中文字符宽度——go-runewidth解决对齐问题文档翻译通过Crowdin 社区志愿者完成——降低维护者翻译负担翻译贡献者也需要激励和认可——翻译贡献者列表当前支持3种语言英文、简体中文、日文。日文是社区贡献者自发完成的1位日本开发者翻译了全部文档成为项目在日语社区增长的关键推力。最大的教训i18n不是一次性翻译而是持续维护。每次Release新增的文案需要翻译每次文档更新需要同步翻译。如果没有自动化工具Crowdin和社区志愿者i18n的维护成本会很快超过维护者的承受能力。i18n的核心不是翻译能力是翻译流程的自动化。