feat(i18n): 翻译加载与 .qm 构建

- 启动时按 locale 加载 gxde-ai-see_<locale>.qm(用户数据目录 / /usr/share)
- translations/gxde-ai-see_en.ts 翻译模板,lupdate/lrelease 接入 CMake 并安装
- README 补充翻译流程
This commit is contained in:
2026-10-30 20:23:15 +08:00
parent 77a4369cd6
commit ba7f57c7d5
5 changed files with 571 additions and 0 deletions
+3
View File
@@ -33,3 +33,6 @@ debian/.debhelper/
debian/files debian/files
debian/debhelper-build-stamp debian/debhelper-build-stamp
debian/gxde-ai-see/ debian/gxde-ai-see/
# translations build output
*.qm
+20
View File
@@ -82,6 +82,26 @@ add_executable(gxde-ai-see-mcptest
target_include_directories(gxde-ai-see-mcptest PRIVATE src) target_include_directories(gxde-ai-see-mcptest PRIVATE src)
target_link_libraries(gxde-ai-see-mcptest PRIVATE Qt6::Core) target_link_libraries(gxde-ai-see-mcptest PRIVATE Qt6::Core)
# translations
find_program(LRELEASE_EXECUTABLE NAMES lrelease lrelease6)
file(GLOB TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts)
if (LRELEASE_EXECUTABLE AND TS_FILES)
set(QM_FILES)
foreach(ts ${TS_FILES})
get_filename_component(ts_name ${ts} NAME_WE)
set(qm ${CMAKE_CURRENT_BINARY_DIR}/translations/${ts_name}.qm)
add_custom_command(OUTPUT ${qm}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/translations
COMMAND ${LRELEASE_EXECUTABLE} ${ts} -qm ${qm}
DEPENDS ${ts}
VERBATIM)
list(APPEND QM_FILES ${qm})
endforeach()
add_custom_target(gxde-ai-see-translations ALL DEPENDS ${QM_FILES})
add_dependencies(gxde-ai-see gxde-ai-see-translations)
install(FILES ${QM_FILES} DESTINATION share/gxde-ai-see/translations)
endif()
install(TARGETS gxde-ai-see RUNTIME DESTINATION bin) install(TARGETS gxde-ai-see RUNTIME DESTINATION bin)
install(FILES data/gxde-ai-see.desktop data/gxde-ai-see-todo.desktop DESTINATION share/applications) install(FILES data/gxde-ai-see.desktop data/gxde-ai-see-todo.desktop DESTINATION share/applications)
install(DIRECTORY data/icons/ DESTINATION share/icons/hicolor) install(DIRECTORY data/icons/ DESTINATION share/icons/hicolor)
+1
View File
@@ -139,6 +139,7 @@ gxde-ai-see --daemon # 后台常驻(由 gxde-k9 拉起)
## 📄 文档 ## 📄 文档
- [设计方案(v0.2)](docs/PLAN.md) - [设计方案(v0.2)](docs/PLAN.md)
- 翻译:`translations/gxde-ai-see_en.ts`(`lupdate src -ts translations/gxde-ai-see_en.ts` 更新,`lrelease` 生成 .qm)
--- ---
+23
View File
@@ -4,6 +4,9 @@
#include <DApplication> #include <DApplication>
#include <QCommandLineOption> #include <QCommandLineOption>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QLocale>
#include <QStandardPaths>
#include <QTranslator>
#include "app.h" #include "app.h"
@@ -18,6 +21,26 @@ int main(int argc, char *argv[])
app.setApplicationVersion(QStringLiteral("0.1.0")); app.setApplicationVersion(QStringLiteral("0.1.0"));
app.setQuitOnLastWindowClosed(false); app.setQuitOnLastWindowClosed(false);
const QString locale = QLocale::system().name();
auto *translator = new QTranslator(&app);
const QStringList translationDirs = {
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)
+ QStringLiteral("/translations"),
QStringLiteral("/usr/share/gxde-ai-see/translations"),
QStringLiteral("/usr/local/share/gxde-ai-see/translations"),
};
bool translated = false;
for (const QString &dir : translationDirs) {
if (translator->load(QStringLiteral("gxde-ai-see_") + locale, dir) ||
translator->load(QStringLiteral("gxde-ai-see_") + locale.section(QLatin1Char('_'), 0, 0),
dir)) {
app.installTranslator(translator);
translated = true;
break;
}
}
Q_UNUSED(translated)
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("GXDE 小埃同学")); parser.setApplicationDescription(QStringLiteral("GXDE 小埃同学"));
parser.addHelpOption(); parser.addHelpOption();
+524
View File
@@ -0,0 +1,524 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>App</name>
<message>
<location filename="../src/app.cpp" line="76"/>
<source>小埃同学</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/app.cpp" line="77"/>
<source>当前桌面不支持传送门(需要 GXDE Wayland 合成器)。小埃同学会继续在后台待命,暂存区、内容工厂和对话仍然可用。</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatClient</name>
<message>
<location filename="../src/chat/chatclient.cpp" line="158"/>
<source>模型没有返回内容</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatclient.cpp" line="227"/>
<source>请求失败</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatclient.cpp" line="277"/>
<source>API Key 无效或已过期</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatWindow</name>
<message>
<location filename="../src/chat/chatwindow.cpp" line="211"/>
<location filename="../src/chat/chatwindow.cpp" line="334"/>
<source>小埃同学</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="250"/>
<source>工具调用次数过多,已停止</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="309"/>
<source>⚠️ %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="342"/>
<source>把内容拖进对话即可提问</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="355"/>
<source>+ 新建对话</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="363"/>
<source>⋯ 更多</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="370"/>
<source>压缩上下文</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="371"/>
<source>重试上一条</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="372"/>
<source>重新加载 MCP</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="375"/>
<source>没有配置 MCP 服务器</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="380"/>
<source>关于</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="384"/>
<source>设置</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="416"/>
<source>输入消息,Ctrl+Enter 发送;也可以把文件/文字拖进窗口</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="424"/>
<source>发送</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="535"/>
<location filename="../src/chat/chatwindow.cpp" line="652"/>
<source>新对话</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="605"/>
<source>你</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="605"/>
<location filename="../src/chat/chatwindow.cpp" line="630"/>
<source>小埃</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="826"/>
<source>正在压缩上下文…</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="845"/>
<source>编辑消息</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="906"/>
<source>%1 · 分支</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="944"/>
<source>关于小埃同学</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="945"/>
<source>&lt;h3&gt;GXDE 小埃同学&lt;/h3&gt;&lt;p&gt;版本 %1&lt;/p&gt;&lt;p&gt;GXDE 桌面的 AI 助手:传送门、内容工厂、待办与对话。&lt;/p&gt;&lt;p&gt;许可证:GPL-3.0-or-later&lt;br/&gt;仓库:&lt;a href=&apos;https://gitee.com/GXDE-OS/gxde-ai-see&apos;&gt;gitee.com/GXDE-OS/gxde-ai-see&lt;/a&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="963"/>
<source>小埃同学设置</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="975"/>
<source>DeepSeek API Key</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="982"/>
<source>API 地址</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="992"/>
<source>模型</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="998"/>
<source>上下文条数</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="1000"/>
<source>超出后自动压缩旧消息</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="1013"/>
<source>MCP 服务器(JSON)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="1020"/>
<source>系统提示</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="1025"/>
<source>还没有账号?前往 &lt;a href=&apos;https://platform.deepseek.com&apos;&gt;platform.deepseek.com&lt;/a&gt; 开通并创建 API Key。Key 只保存在本机配置文件中。</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/chat/chatwindow.cpp" line="1055"/>
<source>MCP 配置不是合法的 JSON 数组,已忽略</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ContentFactory</name>
<message>
<location filename="../src/factory/contentfactory.cpp" line="151"/>
<location filename="../src/factory/contentfactory.cpp" line="181"/>
<source>拖到这里,按类型处理</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/factory/contentfactory.cpp" line="154"/>
<source>拖到动作上松手</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Downloader</name>
<message>
<location filename="../src/actions/downloader.cpp" line="78"/>
<source>另存为</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/actions/downloader.cpp" line="89"/>
<source>无法写入:%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/actions/downloader.cpp" line="99"/>
<source>开始下载:%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/actions/downloader.cpp" line="108"/>
<source>下载失败:%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/actions/downloader.cpp" line="111"/>
<source>下载完成:%1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PortalPanel</name>
<message>
<location filename="../src/ui/portalpanel.cpp" line="761"/>
<source>GXDE 小埃同学</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="781"/>
<location filename="../src/ui/portalpanel.cpp" line="889"/>
<source>%1 项</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="859"/>
<source>移动到目标窗口上松手</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="861"/>
<source>当前无已打开的窗口</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="862"/>
<source>当前环境不支持传送门</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="880"/>
<source>暂存区</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="913"/>
<source>全选</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="914"/>
<source>反选</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="915"/>
<source>删除选中</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1031"/>
<source>复制</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1035"/>
<location filename="../src/ui/portalpanel.cpp" line="1043"/>
<source>已复制</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1097"/>
<source>真实地址</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1102"/>
<source>分词(%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1107"/>
<source>提取链接(%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1113"/>
<source>未找到语音朗读程序</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1117"/>
<source>开始朗读</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1124"/>
<source>已添加到待办</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1129"/>
<source>没有可保存的图片</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1133"/>
<source>保存图片</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1133"/>
<source>图片 (*.png *.jpg)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1136"/>
<source>已保存:%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1145"/>
<source>翻译接口将在小埃同学接入后可用</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1150"/>
<source>该能力尚未接入 GXDE Daemon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1154"/>
<source>未知操作:%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1297"/>
<source>固定位置</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1301"/>
<source>恢复默认位置</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1303"/>
<source>固定大小</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1307"/>
<source>全屏时隐藏</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1316"/>
<source>开机自启与后台保活</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1321"/>
<source>清空暂存区</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1322"/>
<source>关于</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1323"/>
<source>退出</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1370"/>
<source>已开启开机自启与保活</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1370"/>
<source>已关闭开机自启与保活</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1375"/>
<source>关于小埃同学</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="1376"/>
<source>&lt;h3&gt;GXDE 小埃同学&lt;/h3&gt;&lt;p&gt;版本 %1&lt;/p&gt;&lt;p&gt;传送门、内容工厂、待办与小埃同学对话。&lt;/p&gt;&lt;p&gt;GPL-3.0-or-later · &lt;a href=&apos;https://gitee.com/GXDE-OS/gxde-ai-see&apos;&gt;gxde-ai-see&lt;/a&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PortalZone</name>
<message>
<location filename="../src/ui/portalpanel.cpp" line="587"/>
<source>问问小埃 →</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="664"/>
<source>传送门</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="672"/>
<source>拖到这里
选择窗口跃迁</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="729"/>
<source>悬停在「传送门」上,再移动到目标窗口</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../src/chat/chatclient.cpp" line="24"/>
<source>还没有配置 API Key。请先点右上角设置,填入 DeepSeek API Key。</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="118"/>
<source>文件</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="120"/>
<source>图片</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/portalpanel.cpp" line="122"/>
<source>文字 · %1 字</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>StagingView</name>
<message>
<location filename="../src/ui/portalpanel.cpp" line="351"/>
<source>把文件、文字或图片拖到这里</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>TodoWindow</name>
<message>
<location filename="../src/todo/todowindow.cpp" line="145"/>
<location filename="../src/todo/todowindow.cpp" line="195"/>
<source>待办</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/todo/todowindow.cpp" line="158"/>
<location filename="../src/todo/todowindow.cpp" line="203"/>
<source>%1 项</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/todo/todowindow.cpp" line="208"/>
<source>+</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/todo/todowindow.cpp" line="212"/>
<location filename="../src/todo/todowindow.cpp" line="280"/>
<source>新建待办</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/todo/todowindow.cpp" line="220"/>
<source>×</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/todo/todowindow.cpp" line="224"/>
<source>隐藏</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/todo/todowindow.cpp" line="285"/>
<source>要做的事…</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>