7.3 KiB
MiniI18N 国际化工具技术文档
# MiniI18N - 轻量级多语言支持模块
`MiniI18N` 是一个基于 Python 的轻量级国际化(i18n)工具,用于在应用程序中实现多语言文本的加载与动态切换。它通过读取指定目录下的语言文件(如 `msg.txt`),将键值对形式的消息映射到目标语言,并提供线程安全的语言上下文管理。
---
## 模块依赖
```python
import os, re, sys
import codecs
from appPublic.folderUtils import _mkdir, ProgramPath
from appPublic.Singleton import SingletonDecorator
from appPublic.jsonConfig import getConfig
import threading
import time
import locale
说明:该模块依赖于
appPublic包中的若干实用工具类和函数,包括单例装饰器、配置读取、路径处理等。
全局变量与常量
正则表达式
-
comment_re: 匹配以#开头的注释行。comment_re = re.compile(r'\s*#.*') -
msg_re: 匹配形如key : value的消息条目(允许前后空格)。msg_re = re.compile(r'\s*([^:]*)\s*:\s*([^\s].*)')
编码转换表
用于对特殊字符进行编码/解码,避免配置文件解析冲突:
convert_pairs = {
':': '\\x3A',
'\n': '\\x0A',
'\r': '\\x0D',
}
| 原始字符 | 编码表示 |
|---|---|
: |
\x3A |
\n |
\x0A |
\r |
\x0D |
核心函数
dictModify(d, md)
合并字典 md 到字典 d,仅当值不为 None 时更新。
参数:
d(dict): 目标字典md(dict): 更新数据字典
返回:
- 修改后的
d
charEncode(s)
对字符串中的特殊字符进行编码替换,防止解析错误。
参数:
s(str): 待编码字符串
逻辑:
- 将
\替换为\\\\ - 对
convert_pairs中定义的字符进行编码替换
示例:
charEncode("hello:world\n")
# 输出: "hello\\x3Aworld\\x0A"
charDecode(s)
反向解码由 charEncode 编码的字符串。
参数:
s(str): 已编码字符串
逻辑:
- 按照
convert_pairs的逆序还原特殊字符 - 将
\\\\还原为\
示例:
charDecode("hello\\x3Aworld\\x0A")
# 输出: "hello:world\n"
getTextDictFromLines(lines)
从文本行列表中解析出语言键值对字典。
参数:
lines(list of str): 文件内容的每一行
处理规则:
- 忽略空行和以
#开头的注释行 - 使用正则匹配
key : value结构 - 对 key 和 value 分别调用
charDecode解码
返回:
- dict:
{原始消息: 翻译文本}
getFirstLang(lang)
从浏览器或系统传入的逗号分隔语言优先级列表中提取首选语言。
参数:
lang(str): 如"zh-CN,zh;q=0.9,en-US;q=0.8"
返回:
- str: 第一个语言代码(如
"zh-CN")
核心类:MiniI18N
使用 @SingletonDecorator 实现单例模式,确保全局唯一实例。
类定义
@SingletonDecorator
class MiniI18N:
构造函数 __init__(path, lang=None, coding='utf8')
初始化 i18n 引擎。
参数:
path(str): 应用根路径,语言资源位于{path}/i18n/lang(str, optional): 默认语言(可选)coding(str): 文件编码,默认'utf8'
初始化行为:
- 获取系统默认语言
locale.getdefaultlocale()[0] - 初始化内部字典:
langTextDict: 存储各语言的翻译字典messages: 所有出现过的原始消息集合clientLangs: 线程级别的语言上下文(含时间戳)
- 加载语言映射配置(来自
getConfig().langMapping) - 调用
setupMiniI18N()加载所有语言文件
方法列表
setLangMapping(lang, path)
设置语言别名映射,例如将 "zh" 映射到 "zh_CN"。
用途: 支持语言标签标准化。
i18n.setLangMapping('zh', 'zh_CN')
getLangMapping(lang)
获取实际使用的语言目录名。若无映射,则返回原语言名。
setTimeout(timeout=600)
设置客户端语言上下文的有效期(单位:秒)。超时后自动清理过期记录。
delClientLangs()
清理 clientLangs 中超过 timeout 时间未访问的线程语言设置。
自动在
getCurrentLang和setCurrentLang中触发。
getLangDict(lang)
获取指定语言的完整翻译字典。
会先通过 getLangMapping 解析真实语言名称。
getLangText(msg, lang=None)
根据当前或指定语言查找翻译文本。
- 若未找到对应翻译,返回原始
msg - 支持 bytes 输入自动解码
setupMiniI18N()
扫描 {self.path}/i18n/ 目录下所有子目录(视为语言目录),读取其中的 msg.txt 文件并构建翻译字典。
结构要求:
<i18n_root>/
└── en_US/
└── msg.txt
└── zh_CN/
└── msg.txt
每条记录格式:
hello world : Hello, World!
greeting : Welcome to our app.
# 这是注释
error_open : Failed to open file: %s
setCurrentLang(lang)
为当前线程设置语言环境,并记录时间戳。
注意: 基于线程 ID 存储,支持多线程独立语言上下文。
getCurrentLang()
获取当前线程的语言设置。若未设置,则抛出异常(需确保已调用 setCurrentLang)。
辅助函数
getI18N(path=None, coding='utf8')
获取 MiniI18N 单例实例的便捷入口。
参数:
path: 项目根路径(默认为ProgramPath())coding: 文件编码
返回:
MiniI18N实例
示例:
i18n = getI18N()
print(i18n("hello world")) # 根据当前线程语言输出翻译
使用示例
1. 准备语言文件
创建 ./i18n/en_US/msg.txt:
hello : Hello, World!
greeting : Welcome!
创建 ./i18n/zh_CN/msg.txt:
hello : 你好,世界!
greeting : 欢迎!
2. 在代码中使用
from your_module import getI18N
i18n = getI18N()
# 设置当前线程语言
i18n.setCurrentLang('zh_CN')
# 获取翻译
print(i18n('hello')) # 输出: 你好,世界!
print(i18n('greeting')) # 输出: 欢迎!
# 切换语言
i18n.setCurrentLang('en_US')
print(i18n('hello')) # 输出: Hello, World!
高级特性
语言映射配置(via JSON Config)
在 config.json 中添加:
{
"langMapping": {
"zh": "zh_CN",
"en": "en_US"
}
}
这样即使请求 zh,也会自动加载 zh_CN 的翻译文件。
线程安全设计
- 每个线程可通过
setCurrentLang/lang独立设置语言 - 使用
threading.currentThread()作为键存储上下文 - 定期清理过期线程状态(防止内存泄漏)
注意事项
- 文件编码必须一致,默认为 UTF-8。
- 键名中不能包含未转义的
:、\n、\r等特殊字符(应使用编码形式)。 - 推荐使用英文原始消息作为 key,便于维护。
msg.txt文件建议按功能模块分类组织。
TODO / 扩展建议
- 支持
.po或.yaml格式导入 - 提供运行时动态添加翻译接口
- 添加缺失翻译日志记录机制(
missed_pt可扩展) - Web 场景下结合 Cookie 或 Header 自动识别语言
> ✅ **版本信息**
> 作者:AutoDoc Generator
> 生成时间:2025-04-05
> 模块版本:v1.0(基于所提供代码分析)