国际化 (i18n)
ACE Framework 内置轻量国际化模块,提供消息文件加载、语言协商中间件、占位符替换等核心能力,无需引入外部依赖。
消息文件格式
消息文件采用 key=value 纯文本格式,一行一条,# 开头为注释:
# messages/zh.txt
greeting=你好,{0}!
order.created=订单 {0} 已创建,共 {1} 件商品
error.notFound=找不到资源:{0}# messages/en.txt
greeting=Hello, {0}!
order.created=Order {0} created with {1} item(s)
error.notFound=Resource not found: {0}{0}、{1} 等为位置占位符,按 args 数组下标替换。
推荐目录结构
config/
messages/
zh.txt # 简体中文(默认)
zh-TW.txt # 繁体中文
en.txt # 英文
ja.txt # 日文也可按模块拆分,在应用启动时合并加载:
config/messages/
order/
zh.txt
en.txt
user/
zh.txt
en.txt初始化配置
在应用启动入口加载消息文件并设置默认语言:
cangjie
import ace_framework.i18n.*
import std.fs.*
func main(): Unit {
// 加载消息文件(locale 标识 + 文件内容字符串)
let zh = File.readText("config/messages/zh.txt")
let en = File.readText("config/messages/en.txt")
loadMessages("zh", zh)
loadMessages("en", en)
setDefaultLocale("zh") // 找不到匹配语言时回退到此
let app = App()
// 挂载语言协商中间件(建议放在最前)
app.use(localeMiddleware())
app.use(router.routes())
listen(app, "0.0.0.0", 8080)
}API 参考
| 函数 | 签名 | 说明 |
|---|---|---|
loadMessages | (locale: String, text: String): Unit | 解析并注册一个语言包 |
setDefaultLocale | (locale: String): Unit | 设置默认/回退语言 |
t | (locale: String, key: String, args: Array<String>): String | 翻译并替换占位符 |
localeMiddleware | (): Middleware | 从 Accept-Language 头自动解析语言 |
翻译函数 t()
cangjie
import ace_framework.i18n.*
// 无占位符
let msg = t("zh", "greeting", ["张三"])
// => "你好,张三!"
// 多个占位符
let msg2 = t("en", "order.created", ["ORD-001", "3"])
// => "Order ORD-001 created with 3 item(s)"
// key 不存在时返回 key 本身(不抛异常)
let msg3 = t("zh", "unknown.key", [])
// => "unknown.key"回退策略
当请求语言包中找不到对应 key 时,ACE 会先尝试默认语言包,再返回 key 字符串本身,保证页面不会出现空白。
语言协商中间件
localeMiddleware() 解析请求头 Accept-Language,将语言标识写入 ctx.state["ace.locale"],后续处理器直接读取即可:
cangjie
@Controller["/api"]
class ProductController {
@Get["/products/{id}"]
func getProduct(@PathParam id: String, ctx: Context): JsonResponse {
let locale = ctx.state["ace.locale"] as? String ?? "zh"
let name = productService.getName(id, locale)
let msg = t(locale, "product.detail", [name])
return json({"message": msg, "id": id})
}
}中间件语言匹配规则:
- 精确匹配
Accept-Language首选项(如zh-CN→zh-CN) - 语言前缀匹配(如
zh-CN→zh) - 回退到
setDefaultLocale设置的默认语言
语言标识大小写
loadMessages 和 setDefaultLocale 的 locale 字符串区分大小写,建议统一使用小写(zh、en、zh-tw),并确保与消息文件名保持一致。
完整示例
下面展示一个支持中英文切换的完整 API 服务:
cangjie
package example
import ace_framework.*
import ace_framework.i18n.*
import ace_web.*
import std.fs.*
// 启动入口
func main(): Unit {
loadMessages("zh", File.readText("config/messages/zh.txt"))
loadMessages("en", File.readText("config/messages/en.txt"))
setDefaultLocale("zh")
let app = App()
let router = Router()
app.use(localeMiddleware())
router.get("/hello") { ctx =>
let locale = ctx.state["ace.locale"] as? String ?? "zh"
let name = ctx.query("name") ?? "访客"
ctx.body = t(locale, "greeting", [name])
}
app.use(router.routes())
listen(app, "0.0.0.0", 8080)
}bash
# 请求中文(默认)
curl http://localhost:8080/hello?name=小明
# => 你好,小明!
# 请求英文
curl -H "Accept-Language: en" http://localhost:8080/hello?name=Tom
# => Hello, Tom!
# 请求不支持的语言,自动回退到默认语言
curl -H "Accept-Language: fr" http://localhost:8080/hello?name=Pierre
# => 你好,Pierre!与 @Service 集成
在 Service 层同样可以调用 t() 函数,但需由 Controller 层将 locale 字符串传入,避免 Service 直接读取 HTTP 上下文,保持层次清晰。