多语言翻译
This commit is contained in:
+99
-29
@@ -1,54 +1,124 @@
|
||||
package langx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// options 是库的全局配置。
|
||||
//
|
||||
// 它是值类型, 一旦随快照发布就不再被修改: 任何配置变更都必须构造一份新的 options
|
||||
// 并通过 update/updateErr 原子替换, 这样读路径可以完全不加锁, 也不会与写入产生竞态。
|
||||
type options struct {
|
||||
// defaultCode 是未知 key 返回的响应码。
|
||||
// 默认 -1, 表示"未定义", 避免 key 拼写错误时静默返回成功码。
|
||||
defaultCode int
|
||||
|
||||
// defaultLang 是默认语言(已归一化), 兜底时使用。
|
||||
defaultLang string
|
||||
replaceKey string
|
||||
// ctxLangKey string
|
||||
|
||||
// replaceKey 是占位符模式, 形如 "#%s#"。
|
||||
replaceKey string
|
||||
|
||||
// replacePrefix/replaceSuffix 由 replaceKey 推导, 避免每次替换都做一次 Sprintf。
|
||||
replacePrefix string
|
||||
replaceSuffix string
|
||||
|
||||
// missing 是译文缺失时的回调, 可为 nil。
|
||||
missing func(lang, key string)
|
||||
}
|
||||
|
||||
func defaultOptions() *options {
|
||||
return &options{
|
||||
defaultCode: 200,
|
||||
func defaultOptions() options {
|
||||
o := options{
|
||||
defaultCode: -1,
|
||||
defaultLang: "zh",
|
||||
replaceKey: "#%s#",
|
||||
// ctxLangKey: "language",
|
||||
}
|
||||
o.derive()
|
||||
return o
|
||||
}
|
||||
|
||||
// 设置
|
||||
func InitLangx(ops ...Option) {
|
||||
for _, opt := range ops {
|
||||
opt(l.ops)
|
||||
// derive 由 replaceKey 推导出占位符的前缀与后缀
|
||||
func (o *options) derive() {
|
||||
const probe = "\x00langx\x00"
|
||||
out := fmt.Sprintf(o.replaceKey, probe)
|
||||
i := strings.Index(out, probe)
|
||||
if i < 0 {
|
||||
o.replacePrefix, o.replaceSuffix = "", ""
|
||||
return
|
||||
}
|
||||
o.replacePrefix = out[:i]
|
||||
o.replaceSuffix = out[i+len(probe):]
|
||||
}
|
||||
|
||||
func (o options) validate() error {
|
||||
if strings.TrimSpace(o.defaultLang) == "" {
|
||||
return fmt.Errorf("langx: 默认语言不能为空")
|
||||
}
|
||||
return validateReplaceKey(o.replaceKey)
|
||||
}
|
||||
|
||||
// validateReplaceKey 要求模式中有且只有一个 %s
|
||||
func validateReplaceKey(pattern string) error {
|
||||
if pattern == "" {
|
||||
return fmt.Errorf("langx: replaceKey 不能为空, 形如 %q", "#%s#")
|
||||
}
|
||||
out := fmt.Sprintf(pattern, "x")
|
||||
if strings.Contains(out, "%!") || !strings.Contains(out, "x") {
|
||||
return fmt.Errorf("langx: replaceKey=%q 非法, 需要且只需要一个 %%s 占位符, 例如 %q", pattern, "#%s#")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Option 是配置项, 通过 InitLangx 应用。
|
||||
type Option func(*options)
|
||||
|
||||
// 设置默认的code
|
||||
// InitLangx 应用配置项, 可重复调用, 后一次在前一次的配置上叠加。
|
||||
//
|
||||
// 与旧版本的区别: 配置非法时返回错误, 且本次传入的所有 Option 都不会生效(不会写入一半)。
|
||||
// 不需要错误处理时可以直接当语句使用: langx.InitLangx(...)
|
||||
func InitLangx(ops ...Option) error {
|
||||
return updateErr(func(old *state) (*state, error) {
|
||||
next := old.ops // 值拷贝, 不影响已经发布的配置
|
||||
for _, opt := range ops {
|
||||
if opt == nil {
|
||||
continue
|
||||
}
|
||||
opt(&next)
|
||||
}
|
||||
if err := next.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
next.derive()
|
||||
return setOptions(old, next), nil
|
||||
})
|
||||
}
|
||||
|
||||
// Reset 清空所有已注册的响应码与译文, 并恢复默认配置。便于测试与重新初始化。
|
||||
func Reset() {
|
||||
update(func(*state) *state { return newState() })
|
||||
}
|
||||
|
||||
// SetDefaultCode 设置未知 key 返回的响应码, 默认 -1。
|
||||
//
|
||||
// 如果 code 会被直接当作 HTTP 状态码使用, 建议设置为 500, 让缺失的 key 以失败收场。
|
||||
func SetDefaultCode(code int) Option {
|
||||
return func(o *options) {
|
||||
o.defaultCode = code
|
||||
}
|
||||
return func(o *options) { o.defaultCode = code }
|
||||
}
|
||||
|
||||
// 从ctx里面获取语言的key
|
||||
// func SetCtxLangKey(key string) Option {
|
||||
// return func(o *options) {
|
||||
// o.ctxLangKey = key
|
||||
// }
|
||||
// }
|
||||
|
||||
// 默认语言
|
||||
// SetDefaultLanguage 设置默认(兜底)语言, 默认 "zh"。
|
||||
func SetDefaultLanguage(lang string) Option {
|
||||
return func(o *options) {
|
||||
o.defaultLang = lang
|
||||
}
|
||||
return func(o *options) { o.defaultLang = normalizeLang(lang) }
|
||||
}
|
||||
|
||||
// 替换规则 %s 为占位符 需要填进去
|
||||
// SetReplaceKey 设置占位符模式, 必须包含且只包含一个 %s, 默认 "#%s#"(即 #name#)。
|
||||
func SetReplaceKey(key string) Option {
|
||||
return func(o *options) {
|
||||
o.replaceKey = key
|
||||
}
|
||||
return func(o *options) { o.replaceKey = key }
|
||||
}
|
||||
|
||||
// SetMissingHandler 设置译文缺失时的回调, 便于记录漏翻的 key。传 nil 表示取消回调。
|
||||
//
|
||||
// 回调在读取路径上同步执行, 且不在任何锁内, 因此可以安全地再次调用本库的函数。
|
||||
func SetMissingHandler(fn func(lang, key string)) Option {
|
||||
return func(o *options) { o.missing = fn }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user