第4章 资源文件管理:JSON/YAML/Properties格式选择、文件目录结构设计、命名规范

资源文件管理这事儿,看着简单,其实坑不少。我见过太多项目,一开始随便选个格式,目录结构随性一搭,结果到了后期维护成本直线飙升。今天咱们就聊聊这块,把我这些年踩过的坑和总结的经验都倒出来。

4.1 三种格式的选型:JSON、YAML、Properties

先说结论:没有银弹。每种格式都有自己的脾气,选对了事半功倍,选错了天天加班。

特性 JSON YAML Properties
可读性 中等 优秀 良好
层级支持 原生支持 原生支持 需约定(如用点号)
注释支持 不支持 支持 支持(#号)
多语言场景 适合 适合 传统Java项目常用
工具链成熟度 极高 中等

我的建议:

  • 前端项目:优先用JSON。浏览器原生支持,解析零成本。
  • 后端Java项目:Properties依然能打,但YAML更适合复杂配置。
  • 配置文件需要写注释的:别选JSON,选YAML或Properties。

我在项目中遇到过一件事:有个团队用JSON做多语言文件,结果翻译人员看不懂嵌套结构,经常把括号搞丢。后来换成YAML,翻译同事直接上手,再也没出过格式错误。你想想看,工具选对了,沟通成本能降一大截。

4.2 文件目录结构设计

目录结构这事儿,说白了就是给资源文件找个家。家找对了,找东西方便;家找错了,翻箱倒柜都找不到。

我个人习惯用这种结构:

locales/
├── en/
│   ├── common.json
│   ├── login.json
│   ├── dashboard.json
│   └── errors.json
├── zh-CN/
│   ├── common.json
│   ├── login.json
│   ├── dashboard.json
│   └── errors.json
├── ja/
│   ├── common.json
│   ├── login.json
│   ├── dashboard.json
│   └── errors.json
└── index.js  // 统一导出入口

为什么要按语言分文件夹?嗯,这里要注意:

  • 按语言分文件夹:方便CI/CD按需打包。比如只部署中文版时,直接忽略其他文件夹。
  • 按模块分文件:避免单个文件过大。我见过一个项目把所有中文翻译塞进一个文件,足足5000行,每次改个文案都要搜索半天。
  • 统一入口文件:方便做懒加载。比如用户切换到日语时,只加载ja文件夹下的内容。

小技巧:文件名尽量用短横线命名法(kebab-case)。比如user-profile.json,而不是userProfile.jsonuser_profile.json。为什么?因为文件系统对大小写敏感的问题,在Linux上容易翻车。

4.3 命名规范:别让命名成为技术债

命名规范这事儿,我曾经吃过亏。早期一个项目,翻译key命名随心所欲,有的用驼峰,有的用下划线,有的直接用中文拼音。结果到了后期,想找个文案,得全局搜索半天。

我总结了一套规则,用了好几年,团队反馈不错:

// 推荐格式:[模块].[子模块].[具体描述]
{
  "login.title": "登录",
  "login.username.placeholder": "请输入用户名",
  "login.password.placeholder": "请输入密码",
  "login.button.submit": "提交",
  "login.error.invalid_credentials": "用户名或密码错误"
}

规则要点:

  • 用点号分隔层级:清晰表达从属关系。比如login.error.invalid_credentials,一看就知道是登录模块下的错误信息。
  • 统一用英文小写:避免大小写问题。翻译key不是给人看的,是给代码用的,小写最安全。
  • 描述性要强login.button.submitlogin.btn1好一万倍。你想想看,半年后你回来改代码,看到btn1是什么鬼?
  • 避免数字后缀title1title2这种命名,迟早会出问题。用语义化的描述代替。

避坑指南:我曾经在一个项目里看到有人用common.okcommon.cancel这种命名。乍一看没问题,但后来需求变了,有的页面"确定"要改成"我知道了",有的页面保持"确定"。结果因为key太通用,改一个全变了。正确的做法是按页面或组件细分:dialog.confirm.okdialog.info.got_it

4.4 实战:一个完整的资源文件示例

说了这么多理论,咱们看个实际例子。这是一个登录页面的多语言文件:

// zh-CN/login.json
{
  "login.page.title": "欢迎回来",
  "login.page.subtitle": "请登录您的账户",
  "login.form.username.label": "用户名",
  "login.form.username.placeholder": "请输入邮箱或手机号",
  "login.form.password.label": "密码",
  "login.form.password.placeholder": "请输入密码",
  "login.form.remember_me": "记住我",
  "login.form.forgot_password": "忘记密码?",
  "login.button.submit": "登录",
  "login.button.loading": "登录中...",
  "login.error.required": "请填写所有必填项",
  "login.error.invalid_email": "邮箱格式不正确",
  "login.error.too_many_attempts": "登录尝试次数过多,请{minutes}分钟后再试"
}
// en/login.json
{
  "login.page.title": "Welcome Back",
  "login.page.subtitle": "Sign in to your account",
  "login.form.username.label": "Username",
  "login.form.username.placeholder": "Enter email or phone",
  "login.form.password.label": "Password",
  "login.form.password.placeholder": "Enter your password",
  "login.form.remember_me": "Remember me",
  "login.form.forgot_password": "Forgot password?",
  "login.button.submit": "Sign In",
  "login.button.loading": "Signing in...",
  "login.error.required": "Please fill in all required fields",
  "login.error.invalid_email": "Invalid email format",
  "login.error.too_many_attempts": "Too many attempts. Please try again in {minutes} minutes"
}

注意看几个细节:

  • 占位符用{minutes}这种花括号语法,方便动态替换。
  • 错误信息单独归类到login.error下,方便统一管理。
  • 按钮状态分submitloading,避免在代码里拼接字符串。

4.5 工具链推荐

光有规范还不够,工具也得跟上。我常用的几个:

  • i18next-scanner:自动扫描代码中的翻译key,生成资源文件。省去手动维护的麻烦。
  • FormatJS CLI:检查资源文件是否有遗漏的key,避免上线后出现英文文案。
  • Lokalise / Crowdin:如果团队有专业翻译人员,用这些平台管理翻译流程,比直接改JSON文件靠谱得多。

最后说一句:资源文件管理这事儿,前期多花点心思,后期能省下大把时间。别等到项目上线了,才发现翻译key乱成一锅粥,那时候再改就晚了。