Repository navigation
Lang
Orange46707 Eery edited this page Sep 25, 2026
·
2 revisions
Lang(Language)模块提供统一的多语言错误消息翻译功能,支持无限语言、外部文件加载、异步加载,以及变量替换({{<var>nl.lang.NAME</var>}} 格式)和条件表达式求值。
typedef enum {
NL_VAR_TYPE_STRING, // 字符串
NL_VAR_TYPE_INTEGER, // 整数
NL_VAR_TYPE_FLOAT, // 浮点数
NL_VAR_TYPE_BOOL, // 布尔值
} nl_var_type_t;// 设置字符串变量
int nl_lang_var_set(const char* name, const char* value);
// 设置整数/浮点/布尔变量
int nl_lang_var_set_int(const char* name, long long value);
int nl_lang_var_set_float(const char* name, double value);
int nl_lang_var_set_bool(const char* name, int value);
// 获取变量
const char* nl_lang_var_get(const char* name);
long long nl_lang_var_get_int(const char* name, long long default_val);
double nl_lang_var_get_float(const char* name, double default_val);
int nl_lang_var_get_bool(const char* name, int default_val);
// 查询与清理
int nl_lang_var_exists(const char* name);
int nl_lang_var_remove(const char* name);
void nl_lang_var_clear_all(void);除静态设置外,变量还支持动态求值与外部来源:
// 动态变量:每次访问都调用 provider 计算值
int nl_lang_var_set_provider(const char* name, nl_var_provider_t provider, void* userdata);
int nl_lang_var_is_dynamic(const char* name);
// 外部变量:绑定到进程环境变量(每次访问实时读取)
int nl_lang_var_bind_env(const char* name, const char* env_name);
// 外部变量:批量导入
int nl_lang_var_load_env(const char* prefix); // 导入环境变量(可前缀过滤)
int nl_lang_var_load_file(const char* filepath, const char* prefix); // 导入 .env 风格文件- provider 在锁外调用,避免回调中再次访问变量 API 造成死锁;返回 0 时回退到静态值。
-
nl_lang_var_set*设置的静态值可作为动态/外部变量的兜底值。 - 动态/外部变量同样适用于
{{name}}、{{<var>name</var>}}替换与条件表达式。
// 将 input 中的 {{VAR_NAME}} 替换为实际值,结果写入 output
const char* nl_lang_var_replace(const char* input, char* output, size_t output_size);支持多类型变量:{{name}}、{{count}}、{{price}}、{{active}},支持变量名前后空格(自动 trim)。
// 将 input 中的 {{<var>nl.lang.VAR_NAME</var>}} 替换为 lang 变量值
// 也支持裸格式 {{<var>VAR_NAME</var>}}(无 nl.lang. 前缀)
const char* nl_lang_var_replace_html(const char* input, char* output, size_t output_size);
// 获取错误消息并替换其中的变量(便捷函数)
const char* nl_lang_get_error_with_vars(int lib_id, int error_code, char* buffer, size_t buffer_size);格式说明:
-
Lang 扩展格式:
{{<var>nl.lang.变量名</var>}}— 从 Lang 变量存储中取值 -
通用硬编码格式:
{{<var>变量名</var>}}— 同样从 Lang 变量存储中取值(无前缀) - 变量类型自动识别:字符串直接输出,整数/浮点数/布尔值格式化为字符串
示例:
nl_lang_var_set("user", "Alice");
nl_lang_var_set_int("code", 404);
char buf[256];
// Lang 扩展格式
nl_lang_var_replace_html("用户 {{<var>nl.lang.user</var>}} 报错 ({{<var>nl.lang.code</var>}})", buf, sizeof(buf));
// 结果: "用户 Alice 报错 (404)"
// 裸格式(效果相同)
nl_lang_var_replace_html("用户 {{<var>user</var>}} 报错", buf, sizeof(buf));
// 结果: "用户 Alice 报错"
// 通过 get_error_with_vars 便捷使用
nl_lang_set_error(lib_id, 404, "en_us", "{{<var>nl.lang.user</var>}} not found");
nl_lang_get_error_with_vars(lib_id, 404, buf, sizeof(buf));
// 结果: "Alice not found"// 评估条件表达式,返回 1(真)或 0(假)
int nl_lang_var_condition_eval(const char* expr);支持的表达式语法:
- 数值比较:
age > 18、price >= 3.14、count == 0 - 字符串比较:
user == 'Alice'、status != 'disabled' - 运算符:
==!=>=<=>< - 支持字面量(数字、单引号字符串、
true/false/null)和变量引用
通过回调注册脚本引擎,未来可接入 Lua / Python / JavaScript:
// 脚本执行结果结构
typedef struct {
int status; // 0=成功, !=0=失败
int exit_code;
int result_type; // 0=无结果, 1=字符串, 2=整数, 3=浮点数, 4=布尔
const char* result_str;
long long result_int;
double result_float;
int result_bool;
} nl_script_result_t;
// 脚本执行回调类型
typedef nl_script_result_t* (*nl_script_exec_func_t)(
const char* script, const char* lang_code, int error_code, void* userdata);
// 注册/注销脚本引擎
int nl_lang_register_script_engine(const char* engine_id, nl_script_exec_func_t exec_func, void* userdata);
void nl_lang_unregister_script_engine(const char* engine_id);
// 执行脚本
int nl_lang_execute_script(const char* engine_id, const char* script,
const char* lang_code, int error_code, char* result, size_t result_size);
// 获取已注册引擎列表
const char** nl_lang_get_script_engines(int* count);#include "netleaf_lang.h"
#include <stdio.h>
int main() {
nl_lang_init();
// 设置变量
nl_lang_var_set("user", "Alice");
nl_lang_var_set_int("age", 25);
nl_lang_var_set_float("price", 3.14);
nl_lang_var_set_bool("active", 1);
// 模板替换
char buf[256];
nl_lang_var_replace("Hello {{ user }}, age={{ age }}", buf, sizeof(buf));
printf("%s\n", buf); // Hello Alice, age=25
// 条件求值
printf("age>18: %d\n", nl_lang_var_condition_eval("age > 18")); // 1
printf("user=='Alice': %d\n", nl_lang_var_condition_eval("user == 'Alice'")); // 1
printf("price>=3.0: %d\n", nl_lang_var_condition_eval("price >= 3.0")); // 1
// 脚本引擎(示例 mock)
// nl_script_result_t* my_exec(...) { ... return &result; }
// nl_lang_register_script_engine("myengine", my_exec, NULL);
nl_lang_shutdown();
return 0;
}- ✅ 无限语言支持: 支持任意语言(en_us, zh_cn, ja_jp, ko_kr 等)
- ✅ 语言代码格式: 强制
xx_xx格式(忽略大小写,自动规范化) - ✅ 分开注册: 支持按语言分开注册错误消息
- ✅ 自定义语言代码: 支持注册自定义语言代码
- ✅ 多文件支持: 一个库可加载多个语言文件
- ✅ 多库共享: 多个库可共用一个翻译文件(需显示声明)
- ✅ 错误码检测: 防止重复注册错误码
- ✅ 异步加载: 支持异步加载语言文件
- ✅ 跨平台: Windows / Linux / macOS
| 平台 | 状态 |
|---|---|
| Windows | ✅ 完全支持 |
| Linux | ✅ 完全支持 |
| macOS | ✅ 完全支持 |
格式强制为 xx_xx(语言_国家),但忽略大小写:
| 输入 | 规范化后 | 状态 |
|---|---|---|
en_us |
en_us |
✅ 有效 |
EN_US |
en_us |
✅ 有效 |
Zh_Cn |
zh_cn |
✅ 有效 |
ja_JP |
ja_jp |
✅ 有效 |
en |
- | ❌ 无效 |
en-US |
- | ❌ 无效 |
// 设置当前语言(必须 xx_xx 格式)
int nl_lang_set(const char* code); // "en_us", "zh_cn", "ja_jp"
// 获取当前语言
const char* nl_lang_get(void);
// 设置默认语言
int nl_lang_set_default(const char* code);
// 获取默认语言
const char* nl_lang_get_default(void);
// 验证语言代码格式
int nl_lang_validate_code(const char* code);
// 获取所有注册的语言
const char** nl_lang_get_all(void);// 注册库及其支持的语言
int nl_lang_register_lib(int lib_id, const char** languages, int lang_count);
// 注册单个语言到库(可多次调用)
int nl_lang_register_language(int lib_id, const char* lang_code);
// 注销库
void nl_lang_unregister_lib(int lib_id);
// 注销单个语言
int nl_lang_unregister_language(int lib_id, const char* lang_code);
// 检查库是否已注册
int nl_lang_is_registered(int lib_id);
// 检查语言是否已注册
int nl_lang_has_language(int lib_id, const char* lang_code);// 添加错误消息(所有语言)
int nl_lang_add_error(int lib_id, int code, const char** messages);
// 设置单个语言的错误消息(自动创建条目)
int nl_lang_set_error(int lib_id, int code, const char* lang_code, const char* message);
// 移除错误消息
int nl_lang_remove_error(int lib_id, int code);
// 移除单个语言的错误消息
int nl_lang_remove_error_single(int lib_id, int code, const char* lang_code);
// 注册自定义错误码
int nl_lang_register_custom_code(int lib_id, int code);
// 检查错误码是否存在
int nl_lang_has_error(int lib_id, int code);
// 获取所有错误码
int* nl_lang_get_error_codes(int lib_id, int* count);为便于维护,可用 NL_ERROR_BEGIN / NL_ERROR / NL_ERROR_END 声明错误表并一次注册(中英双语):
NL_ERROR_BEGIN(my_errors)
NL_ERROR(0, "Success", "成功")
NL_ERROR(-1, "Failed", "失败")
NL_ERROR_END
nl_lang_register_errors(NL_LIB_MY, my_errors); // 注册 en_us + zh_cn各内置模块(autoroute / autocomplete / errorpage / ipc / linkagg / tls / mqtt / mqtt_server) 已通过该机制注册各自的返回码/错误码。
// 获取当前语言的错误消息
const char* nl_lang_get_error(int lib_id, int error_code);
// 获取指定语言的错误消息
const char* nl_lang_get_error_for(int lib_id, int error_code, const char* lang_code);
// 获取错误消息并替换其中的变量(支持 {{<var>nl.lang.NAME</var>}} 格式)
const char* nl_lang_get_error_with_vars(int lib_id, int error_code, char* buffer, size_t buffer_size);
// 格式化错误消息
const char* nl_lang_format_error(char* buffer, size_t buffer_size,
int lib_id, int error_code, const char* context);// 加载多个语言文件
int nl_lang_load_json_multi(int lib_id, const char** files, int file_count);// 共享文件配置结构
typedef struct {
const char* filepath; // JSON 文件路径
int* lib_ids; // 共享此文件的库 ID 数组
int lib_count; // 库数量
int allow_duplicate_codes; // 是否允许跨库重复错误码
} nl_lang_shared_file_t;
// 注册共享文件
int nl_lang_register_shared_file(const nl_lang_shared_file_t* config);
// 加载共享文件
int nl_lang_load_shared_file(const char* filepath);
// 检查库是否在共享组中
int nl_lang_is_shared_lib(int lib_id);
// 获取共享库列表
int* nl_lang_get_shared_libraries(int lib_id, int* count);// 检查错误码冲突
int nl_lang_check_code_conflict(int lib_id, int code);
// 返回: 0 = 无冲突, lib_id = 冲突的库 ID
// 启用/禁用严格重复检查
void nl_lang_set_strict_duplicates(int enabled);
// 获取严格检查设置
int nl_lang_get_strict_duplicates(void);// 异步回调类型
typedef void (*nl_lang_async_callback_t)(int lib_id, int result, void* user_data);
// 异步加载 JSON 文件
int nl_lang_load_json_async(int lib_id, const char* filepath,
nl_lang_async_callback_t callback, void* user_data);
// 异步加载多个文件
int nl_lang_load_json_multi_async(int lib_id, const char** files, int file_count,
nl_lang_async_callback_t callback, void* user_data);
// 异步加载 URL
int nl_lang_load_url_async(int lib_id, const char* url,
nl_lang_async_callback_t callback, void* user_data);
// 检查异步加载状态
int nl_lang_is_async_loading(int lib_id);
// 等待异步完成
int nl_lang_wait_async(int lib_id, int timeout_ms);
// 取消异步加载
int nl_lang_cancel_async(int lib_id);
// 获取异步进度
int nl_lang_get_async_progress(int lib_id);| 库 | 标识符 | 值 |
|---|---|---|
| Core | NL_LIB_CORE |
0x0000 |
| IPC | NL_LIB_IPC |
0x0001 |
| LinkAgg | NL_LIB_LINKAGG |
0x0002 |
| Auto-route | NL_LIB_AUTOROUTE |
0x0003 |
| Auto-complete | NL_LIB_AUTOCOMPLETE |
0x0004 |
| ErrorPage | NL_LIB_ERRORPAGE |
0x0005 |
| Lang | NL_LIB_LANG |
0x0006 |
#include "netleaf_lang.h"
#include <stdio.h>
int main() {
// 设置语言
nl_lang_set("zh_cn"); // 或 "en_us", "ja_jp" 等
// 获取错误消息
const char* msg = nl_lang_get_error(NL_LIB_LINKAGG, -9);
printf("错误: %s\n", msg);
// 输出: "不接入,ID被占用"
// 切换语言
nl_lang_set("en_us");
msg = nl_lang_get_error(NL_LIB_LINKAGG, -9);
printf("Error: %s\n", msg);
// 输出: "ID already in use, not connected"
return 0;
}#include "netleaf_lang.h"
int main() {
// 注册库
nl_lang_register_lib_name(NL_LIB_LINKAGG, "linkagg");
// 分开注册语言
nl_lang_register_language(NL_LIB_LINKAGG, "zh_cn");
nl_lang_register_language(NL_LIB_LINKAGG, "en_us");
nl_lang_register_language(NL_LIB_LINKAGG, "ja_jp"); // 后续添加
// 分开注册错误消息(按语言)
nl_lang_set_error(NL_LIB_LINKAGG, 0, "zh_cn", "成功");
nl_lang_set_error(NL_LIB_LINKAGG, 0, "en_us", "Success");
nl_lang_set_error(NL_LIB_LINKAGG, 0, "ja_jp", "成功");
nl_lang_set_error(NL_LIB_LINKAGG, -9, "zh_cn", "不接入,ID被占用");
nl_lang_set_error(NL_LIB_LINKAGG, -9, "en_us", "ID already in use");
nl_lang_set_error(NL_LIB_LINKAGG, -9, "ja_jp", "IDが使用中");
return 0;
}#include "netleaf_lang.h"
int main() {
// 注册自定义错误码(个性化体验)
nl_lang_register_custom_code(NL_LIB_LINKAGG, -100);
nl_lang_set_error(NL_LIB_LINKAGG, -100, "zh_cn", "业务逻辑错误");
nl_lang_set_error(NL_LIB_LINKAGG, -100, "en_us", "Business logic error");
// 使用自定义错误码
const char* msg = nl_lang_get_error(NL_LIB_LINKAGG, -100);
printf("%s\n", msg);
return 0;
}#include "netleaf_lang.h"
int main() {
// 配置共享文件
int lib_ids[] = {NL_LIB_IPC, NL_LIB_LINKAGG, NL_LIB_AUTOROUTE};
nl_lang_shared_file_t config = {
.filepath = "errors_shared.json",
.lib_ids = lib_ids,
.lib_count = 3,
.allow_duplicate_codes = 0 // 不允许重复错误码
};
// 注册共享文件
nl_lang_register_shared_file(&config);
// 加载共享文件
nl_lang_load_shared_file("errors_shared.json");
return 0;
}#include "netleaf_lang.h"
#include <stdio.h>
void on_load_complete(int lib_id, int result, void* user_data) {
if (result == 0) {
printf("Language file loaded successfully for lib %d\n", lib_id);
} else {
printf("Failed to load language file: %d\n", result);
}
}
int main() {
// 异步加载语言文件
nl_lang_load_json_async(NL_LIB_LINKAGG, "errors_linkagg.json",
on_load_complete, NULL);
// 检查加载状态
while (nl_lang_is_async_loading(NL_LIB_LINKAGG)) {
printf("Progress: %d%%\n", nl_lang_get_async_progress(NL_LIB_LINKAGG));
// 做其他工作...
}
// 或等待完成
nl_lang_wait_async(NL_LIB_LINKAGG, 5000); // 最多等待 5 秒
return 0;
}#include "netleaf_lang.h"
#include <stdio.h>
int main() {
// 启用严格重复检查
nl_lang_set_strict_duplicates(1);
// 检查错误码是否会冲突
int conflict = nl_lang_check_code_conflict(NL_LIB_LINKAGG, -9);
if (conflict != 0) {
printf("Error code -9 conflicts with lib %d\n", conflict);
}
return 0;
}{
"lib_id": 2,
"languages": ["en_us", "zh_cn", "ja_jp"],
"errors": {
"0": {
"en_us": "Success",
"zh_cn": "成功",
"ja_jp": "成功"
},
"-9": {
"en_us": "ID already in use, not connected",
"zh_cn": "不接入,ID被占用",
"ja_jp": "IDが使用中"
}
}
}| 错误码 | en_us | zh_cn |
|---|---|---|
| 0 | Success | 成功 |
| -1 | Unknown error | 未知错误 |
| -2 | Platform not supported | 平台不支持 |
| -8 | Exceeded maximum backends (512) | 超过最大后端数量(512个) |
| -9 | ID already in use, not connected | 不接入,ID被占用 |
| -10 | Invalid ID format (expected xxx.xxx) | ID格式错误(应为xxx.xxx) |
| 错误码 | en_us | zh_cn |
|---|---|---|
| 0 | Success | 成功 |
| -2 | Platform not supported (macOS) | 平台不支持(macOS) |
| -5 | Connection failed | 连接失败 |
| -10 | Path too long | 路径过长 |
- 程序初始化时设置语言
- 分开注册语言以支持渐进式添加
- 使用异步加载避免阻塞主线程
- 启用严格重复检查防止冲突
- 使用共享文件减少重复配置
许可声明 / License Notice 本仓库基于 MIT 协议开源。如果您使用了本项目,请保留原作者署名并附带 LICENSE 文件
This repository is open-sourced under the MIT License. If you use this project, please retain the original author's attribution and include the LICENSE file.
- Auto-complete - 自动补全charset和Vue引入
- Auto-route - 自动路由建议
- ErrorPage - 自定义错误页面模板
- IPC - 进程间通讯服务
- LinkAgg - 同端口链路聚合
- Lang - 多语言错误消息
- Vue - Vue.js后端支持
- Extension - 扩展库开发指南
- TLS - TLS/SSL 加密扩展
- MQTT - MQTT v5.0 协议客户端
- MQTT-Server - MQTT v5.0 协议服务端(Broker)
作者: 508364
版本: 2.4.2
许可: MIT License