数据库参数国际化:从 13 个迁移学到的设计原则

3 min read
Zekari
数据库国际化迁移系统设计

数据不该懂语言

数据库参数有一个字段叫 config_schema。里面存储的是 JSON,定义了 AI 模型的可配置参数。

某一天,你发现这个 JSON 中的 labelui:description 字段都是中文:

{
  "prompt": {
    "label": "提示词",
    "ui:description": "描述你想生成的视频内容,最多 2000 个字符"
  }
}

这看起来没问题。直到你需要支持英文界面。或者日文。或者任何非中文的语言。

数据库不该懂语言。数据是结构,不是展示。当你把展示层的语言硬编码到数据层,系统的边界就被锁死了。

💡 Click the maximize icon to view in fullscreen

国际化不是"添加多语言支持",是系统设计的底层约束。

对齐的本质

我们有 13 个 AI 模型需要参数对齐。从 ByteDance V1 Lite 到 Google Veo-3 HD,每个模型的 config_schema 都嵌入了中文。

对齐不是翻译。翻译是把"提示词"变成"Prompt"。对齐是重新定义系统边界——数据层不应该知道展示层用什么语言。

这次迁移的目标:

  1. 将所有中文标签和描述替换为英文
  2. 确保参数符合官方 API 文档
  3. 保持迁移的幂等性和可验证性

为什么选择英文作为数据层的默认语言?因为英文是 API 文档的标准语言。数据层的职责是准确反映 API 契约,不是服务特定语言的用户。

关于如何安全地执行数据库迁移,参考 database-migration-methods

关于边界设计的思考,参考 from-intent-to-architecture

迁移的设计原则

13 个迁移文件,每个文件对应一个模型。迁移不是一次性脚本,是系统演进的历史记录。

原则 1:幂等性

每个迁移都可以安全地重复执行。

-- 错误的写法
UPDATE ai_models SET config_schema = ...;

-- 正确的写法
UPDATE ai_models
SET config_schema = ...
WHERE api_identifier = 'specific-model-id';

幂等性来自精确的 WHERE 子句。不是"更新所有模型",是"更新这一个特定模型"。

原则 2:可验证性

每个迁移执行后,立即验证结果。

-- 执行迁移
UPDATE ai_models SET ...;

-- 验证结果
SELECT
  api_identifier,
  config_schema->'properties'->'prompt'->>'label' as prompt_label
FROM ai_models
WHERE api_identifier = 'specific-model-id';

验证查询不是可选项,是迁移的一部分。如果验证失败,迁移就是失败的。

原则 3:小步前进

一个迁移只改一个模型。不是"一次更新 13 个模型",是"13 次迁移,每次更新 1 个模型"。

为什么?因为出错时容易定位。如果第 7 个迁移失败了,你知道问题在哪。如果一次更新 13 个,你不知道是哪个模型的参数有问题。

💡 Click the maximize icon to view in fullscreen

从 13 个迁移学到的

这次迁移涉及 13 个模型:

  • ByteDance V1 Lite (文本转视频 + 图像转视频)
  • Kling V2.1 (Master, Standard, Pro)
  • WAN 2.2 A14B (文本转视频 + 图像转视频)
  • OpenAI Sora 2 (文本转视频 + 图像转视频)
  • Runway Gen-3 Alpha Turbo
  • Google Veo-3 (Fast + HD)

每个模型的参数都有细微差异。Kling 支持 negative_prompt,Sora 不支持。Veo-3 有独特的 quality 参数,WAN 有 seeds 数组。

关键教训:不要假设参数是一致的

即使都是"文本转视频"模型,参数也不完全相同。迁移不是批量操作,是逐个确认。

在对齐参数的过程中,我们发现了另一个问题:长描述文本导致参数表单容器溢出。

问题的根源:ui:description 字段可能包含 100+ 字符的说明文本,直接渲染时撑破了容器宽度。

解决方案:

  • 使用 line-clamp 截断长文本
  • 添加 Tooltip 显示完整内容
  • 在描述旁边添加 HelpCircle 图标提示

这个问题暴露了数据层和展示层的边界模糊。数据层不应该关心"文本会不会撑破容器",展示层不应该假设"描述一定是短文本"。

边界清晰,系统才稳健。

验证比执行更重要

迁移的核心不是 UPDATE 语句,是验证查询。

每个迁移文件的结构:

-- 1. 执行更新
UPDATE ai_models
SET
  config_schema = jsonb_set(...),
  description = 'English description'
WHERE api_identifier = 'model-id';

-- 2. 验证结果
SELECT
  api_identifier,
  name,
  config_schema->'properties'->'prompt'->>'label' as prompt_label,
  config_schema->'properties'->'prompt'->>'ui:description' as prompt_desc
FROM ai_models
WHERE api_identifier = 'model-id';

验证查询回答三个问题:

  1. 迁移是否执行成功?
  2. 数据是否符合预期?
  3. 如果失败,问题出在哪?

没有验证的迁移,就是盲目的变更。你不知道它做了什么,也不知道它是否成功。

有人会问:如果迁移失败,为什么不用事务自动回滚?

因为失败的原因需要被看见。

自动回滚隐藏了问题。迁移失败可能是因为:

  • JSON 格式错误
  • 参数定义不符合 API 文档
  • WHERE 子句没有匹配到目标模型

这些问题需要手动检查和修复。自动回滚让系统看起来"没问题",但实际上问题还在。

验证查询的目的不是"确保成功",是"让失败可见"。

国际化是底层约束

13 个迁移执行完成后,数据库中的所有参数标签和描述都变成了英文。

但这不是终点。

下一步需要在展示层实现真正的国际化:

  1. 前端从 i18n 文件加载翻译
  2. 根据用户语言偏好动态渲染
  3. 数据层保持语言无关

💡 Click the maximize icon to view in fullscreen

数据层定义"有哪些参数",i18n 文件定义"参数叫什么"。职责分离,边界清晰。

国际化不是后期添加的功能,是系统设计时就要考虑的约束。如果数据层从一开始就嵌入了语言,重构的成本会很高。

这次 13 个迁移就是代价。

最后

数据不该懂语言。

当你在数据库中存储"提示词"而不是"prompt",当你在 JSON 中硬编码中文描述,你就在数据层和展示层之间埋下了耦合。

系统的边界应该清晰:

  • 数据层:语言无关,契约驱动
  • 业务层:逻辑处理,规则验证
  • 展示层:用户体验,多语言支持

国际化不是翻译工作,是系统设计的底层约束。

如果你的数据库参数还在说中文,现在是重新定义边界的时候了。

Related Posts

Articles you might also find interesting

CRUD 操作

2 min read

四个字母背后,是数据的生命周期,是权限的边界,也是系统设计的基础逻辑

系统设计软件工程
Read More

执行数据库迁移的三种路径

2 min read

CLI、MCP 与线上 SQL——每种方法背后的权衡与适用场景。迁移不只是执行命令,更是选择控制权与便利性之间的平衡点。

数据库迁移
Read More

单例模式管理 Redis 连接

5 min read

连接不是技术细节,而是系统与外部世界的第一次握手,是可靠性的起点

系统设计后端架构
Read More

查询先于假设

3 min read

数据库迁移后,所有功能失效。问题不在迁移本身,而在假设。真相只存在于查询结果中。

数据库迁移
Read More

PostgreSQL 原生不支持直接添加枚举值

1 min read

当一个类型系统拒绝改变,它在保护什么?

数据库系统设计
Read More

管理后台需要两次设计

3 min read

第一次设计回答"发生了什么",第二次设计回答"我能做什么"。在第一次就试图解决所有问题,结果是功能很多但都不够深入。

系统设计API 设计
Read More

告警分级与响应时间

2 min read

不是所有问题都需要立即响应。RPC失败会在凌晨3点叫醒人。安全事件每15分钟检查一次。支付成功只记录,不告警。系统的响应时间应该匹配问题的紧急程度。

系统设计监控
Read More

文档标准是成本计算的前提

3 min read

API文档不只是写给开发者看的。它定义了系统的边界、成本结构和可维护性。统一的文档标准让隐性成本变得可见。

API文档
Read More

BullMQ 队列

3 min read

队列不是技术选型,而是对时间的承认,对顺序的尊重,对不确定性的应对

系统设计异步处理
Read More

BullMQ Worker

2 min read

Worker 的本质是对时间的重新分配,是对主线的解放,也是对专注的追求

系统设计异步处理
Read More

配置不会自动同步

2 min read

视频生成任务永远pending,代码完美部署,队列正确配置。问题不在代码,在于配置的独立性被低估。静默失败比错误更危险。

部署配置管理
Read More

诊断 Supabase 连接失败:借助 MCP 工具链

2 min read

连接失败不仅是配置问题,更是关于理解系统状态边界的过程。通过 Supabase MCP 与 Claude Code,让不可见的问题变得可观测。

SupabaseMCP
Read More

Stripe Webhook中的防御性编程

2 min read

三个Bug揭示的真相:假设是代码中最危险的东西。API返回类型、环境配置、变量作用域——每个看似合理的假设都可能导致客户损失。

Web开发系统设计
Read More

双重验证:Stripe生产模式的防御性切换

7 min read

从测试到生产不是更换API keys,而是建立一套双重验证系统。每一步都在两个环境中验证,确保真实支付不会因假设而失败。

系统设计Stripe
Read More

错误隔离

3 min read

失败是必然的。真正的问题不是失败本身,而是失败如何蔓延。错误隔离不是为了消除失败,而是为了控制失败的范围。

系统设计可靠性工程
Read More

在运行的系统上生长新功能

3 min read

扩展不是推倒重来,而是理解边界,找到生长点。管理层作为观察者和调节器,附着在核心系统上,监测它,影响它,但不改变它的运行逻辑。

系统设计架构
Read More

实现幂等性处理,忽略已处理的任务

3 min read

在代码层面识别和忽略已处理的任务,不是简单的布尔检查,而是对时序、并发和状态的深刻理解

系统设计并发控制
Read More

缺失值的级联效应

3 min read

一个NULL值如何在调用链中传播,最终导致错误的错误消息。理解防御层的设计,在失败传播前拦截。

系统设计防御性编程
Read More

监控观察期法

3 min read

部署不是结束,而是验证的开始。修复代码只是假设,监控数据才是证明。48小时观察期:让错误主动暴露,让数据证明修复。

系统设计监控
Read More

用 MCP 让 Claude Code 执行 Prisma 迁移

2 min read

借助 Model Context Protocol,Claude Code 可以直接操作 Supabase 云数据库,完成 Prisma schema 的迁移和部署

Claude CodeMCP
Read More

Props Drilling

3 min read

数据在组件树中层层传递,每一层都不需要它,却必须经手。这不是技术债,是架构对真实需求的误解。

React组件设计
Read More

队列生产者实例的工厂函数

4 min read

工厂函数不是设计模式的炫技,而是对重复的拒绝,对集中管理的追求,对变化的准备

系统设计设计模式
Read More

监听 Redis 连接事件 - 让不可见的脆弱变得可见

4 min read

连接看起来应该是透明的。但当它断开时,你才意识到透明不等于可靠。监听不是多余,而是对脆弱性的承认。

系统设计Redis
Read More

资源不会消失,只会泄露

2 min read

在积分系统中,用户的钱不会凭空消失,但会因为两个时间窗口而泄露:并发请求之间的竞争,和回调永不到达的沉默。

系统设计并发控制
Read More

RPC函数的原子化处理

1 min read

当一个远程函数做太多事情,失败就变得难以理解

分布式系统RPC
Read More