diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..02365cd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,71 @@
+# ==========================================
+# 1. Python 字节码与缓存文件
+# ==========================================
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+
+# ==========================================
+# 2. 虚拟环境 (Virtual Environments)
+# ==========================================
+.venv/
+venv/
+env/
+ENV/
+env.bak/
+venv.bak/
+
+# ==========================================
+# 3. PyPI 打包与构建产物 (Build & Distribution)
+# ==========================================
+build/
+bdist.*/
+sdist/
+dist/
+*.egg-info/
+.eggs/
+*.egg
+
+# ==========================================
+# 4. QPyPack 临时打包引擎缓存 (PyInstaller & Nuitka)
+# ==========================================
+*.build/
+*.dist/
+*.onefile-build/
+*.spec
+qpypack_*_version.txt
+qpypack_temp_*
+_qpypack_temp_*
+
+# ==========================================
+# 5. 本地运行日志、配置与生成的临时测试文件
+# ==========================================
+config.ini
+*.log
+qpypack_build_*.log
+*.qpypack
+dropdown_arrow.png
+sound_success.wav
+sound_failure.wav
+
+# ==========================================
+# 6. IDE 与编辑器配置文件
+# ==========================================
+.idea/
+.vscode/
+*.swp
+*.swo
+*~
+
+# ==========================================
+# 7. 操作系统自动生成文件 (macOS / Windows)
+# ==========================================
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..966f548
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,213 @@
+# 更新日志 (Changelog)
+
+QPyPack 项目的所有重要变更都将记录在此文件中。
+
+本日志格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/) 规范,
+且本项目严格遵循 [语义化版本 (Semantic Versioning)](https://semver.org/lang/zh-CN/) 规范。
+
+---
+
+## [2.6.2] - 2026-07-27
+
+* **新增界面推移动画**:重构切页逻辑,新增双图层平滑推移过渡动画,彻底消除从设置页返回主界面时的排版闪烁与图标跳动。
+* **非打断式通知系统**:全面剥离强侵入式的 `QMessageBox` 阻塞弹窗,改用底部状态栏 Toast 轻量级通知,保障打包工作流连贯。
+* **错误日志红色高亮**:遇致命异常、锁定文件或依赖缺失时,自动展开日志面板并以醒目红色 HTML 字体高亮呈现,便于快速定位。
+
+* **GNU gettext 引擎重构**:引入 `TranslationEngine`,将全软件中文硬编码重构为英文原文 Base Key 模式;内置 `zh_CN` 兜底字典,实现零外部依赖运行。
+* **全球主流多语言扩展**:新增日 (`ja_JP`)、韩 (`ko_KR`)、法 (`fr_FR`)、德 (`de_DE`)、西 (`es_ES`)、俄 (`ru_RU`)、葡 (`pt_BR`)、繁中 (`zh_TW`) 等语言包支持。
+* **智能语言选择器**:设置界面新增语言切换下拉框,支持各国原生母语名称显示(如 Deutsch, Français),并能精准动态识别当前系统默认语言。
+
+* **作用域变量冲突**:修复 `QFileDialog` 解构占位符 `fp, _` 意外覆盖全局翻译函数 `_()` 导致局部作用域崩溃的严重隐患。
+* **Qt 动画引擎警告**:修复旧版 geometry 动画作用于 `QStackedLayout` 时抛出 `starting an animation without end value` 的底层警告。
+* **PyPI 静态资源打包**:`pyproject.toml` 补全 `locales/*.json` 资源打包规则,確保 `pip install` 安装后多语言功能依然有效。
+
+---
+
+## [2.6.1] - 2026-07-25
+
+* **消除依赖污染**:修复开启“精简模式”时,AST 引擎错误遍历整个父目录的 Bug,解决打包产物被混入无关依赖的问题。
+* **构建沙盒隔离**:默认将目标脚本放入独立沙盒中解析,杜绝同目录其他脚本的依赖干扰;同时在 UI 界面新增“允许扫描同目录所有文件”开关。
+* **MSVC 智能检测**:引入本地 C++ 编译环境智能嗅探。若系统已安装 MSVC,Nuitka 将自动启用 `--msvc=latest`,优先使用系统原生编译器,提升兼容性并免除耗时的 MinGW 下载。
+* **UPX 警告静默**:自动识别并跳过系统核心动态库的 UPX 压缩报错,并小幅提升构建速度。
+
+---
+
+## [2.6.0] - 2026-07-24
+
+### 🎨 交互与 UI 架构重构
+* **重构 TAB 导航**:重新分配 TAB 布局,界面更加美观易用。
+* **布局与字体精细化**:紧凑化引擎描述框布局;引入跨平台安全字体栈,零侵权风险且全平台优雅回退。
+* **高分屏渲染优化**:优先提取 256px 高清图标图层,结合浮点精度绘制,解决 125%~200% 屏幕缩放下的图标模糊。
+
+### 🛠️ 打包引擎与稳定性修复
+* **防穿透包名兜底**:内置 C 扩展模块硬编码映射,避免出现找不到包的错误。
+* **拦截虚假切源警报**:智能识别 pip 包名错误,避免在镜像源网络正常时误报“主镜像源连接超时”。
+* **只读目录权限防护**:控制台防闪退注入脚本改写至系统临时目录,防止因源目录只读导致打包中断。
+
+---
+
+## [2.5.7] - 2026-07-23
+
+* **完善日志右键菜单**:新增清空日志、导出日志功能。
+* **下拉菜单与样式优化**:修复非编辑模式下拉框导致的文本重叠 Bug。
+* **按钮与布局规格统一**:统一“浏览”、“AST 扫描”等工具按钮的长度和大小。
+* **细节提升**:拖拽区域的主标题与副标题均开启自动换行,防止载入长路径文件时挤压主面板。
+
+---
+
+## [2.5.6] - 2026-07-23
+
+* **PIP 双源备份**:新增备用镜像源配置与故障自动重试机制,内置国内主流 PyPI 镜像预设。
+* **UI 与视觉重构**:重构 `QComboBox` 悬浮下拉框,统一极简白色主题与滚动体验;Tab 标签栏改为居中对齐,优化窗口最小宽度(710px)。
+* **打包稳定性提升**:依赖安装全流程接管,支持主/备源自动降级切源;补全 PyInstaller Windows 版本元数据的 `VarFileInfo` 翻译头字段;优化精简模式下的排除模块策略。
+
+---
+
+## [2.5.5] - 2026-07-22
+
+* **消除误删风险**:优化目录 `build/dist` 智能清理,保护桌面及工作区安全。
+* **全沙盒隔离编译**:引入系统级临时目录隔离输出,彻底解决跨盘符与路径割裂异常。
+* **修复路径映射与 UX**:构建 cwd 重定向至源码目录以完美解析相对路径;修正 `open_dist` 打开逻辑。
+* **修复 pipreqs 崩溃**:解决依赖自动扫描过程中的编码中断问题。
+
+---
+
+## [2.5.4] - 2026-07-18
+
+* **新增精简模式**:自动剔除 `pip`、`unittest` 等冗余库并启用 Nuitka `-OO` 优化,大幅缩减产物体积;配套深度源码静态扫描(至多检索 500 个本地依赖),保障极致精简下的运行稳定性。
+* **修复 UPX 假性报错**:当未勾选 UPX 时,显式向引擎传递 `--noupx` 指令,阻断 PyInstaller 静默调用系统 UPX 造成的兼容性报错与日志干扰。
+* **修复日志中文乱码**:对底层命令行调用 (`subprocess`) 全面强制追加 `encoding="utf-8"` 参数,彻底解决 Windows 默认编码 (GBK) 解析字节流时引发的控制台乱码。
+
+---
+
+## [2.5.3] - 2026-07-17
+
+* **引擎版本锁定**:支持在高级设置中自定义 Nuitka / PyInstaller / pipreqs 的版本,杜绝上游更新导致的打包失败。
+* **动态进度 UI**:主界面大字标题实时同步并轮播当前构建步骤,告别卡顿感与等待焦虑。
+* **解决编译崩溃**:全局强制注入 UTF-8 环境,修复 Windows 中文系统下 Nuitka 解析 PyTorch 等库时因 GBK 编码导致的致命报错。
+* **高分屏渲染修复**:解决非整数缩放(如 125%)下的字体发虚问题,清理冗余代码消除 DPI 权限报错。
+* **依赖扫描自适应**:修复非 UTF-8 编码源码导致 `pipreqs` 分析崩溃的问题,支持自动回退本地编码。
+* **智能解释器检索**:合并 `.EXE/.exe` 重复路径,默认选中当前环境,并剥离 `\\?\` 路径前缀以防子进程调用异常。
+* **绝对无痕清理**:增加对 `nuitka-crash-report.xml` 与临时 `__pycache__` 的强制回收,绝不污染用户目录。
+* **修复图标未更新问题**:修复添加新的打包会导致图标未能更新的问题。
+
+---
+
+## [2.5.2] - 2026-07-15
+
+* **重构编译后端指令集**:将 PyInstaller 与 Nuitka 的底层打包参数生成逻辑重构为两套完全独立、互不干扰的构建分支,彻底消除参数污染隐患。
+* **优化 Nuitka 附加资源兼容性**:针对 Nuitka 编译器在处理单个附加数据文件时,若目标相对路径为 `.` 会导致编译崩溃的问题进行了自适应修复 (`os.path.normpath` 路径标准化)。
+* **修正 Nuitka 排除模块指令**:将排除特定模块参数修正为 Nuitka 官方标准的 `--nofollow-import-to`。
+* **完善 Windows 二进制属性映射**:修正应用描述正确映射至 `--file-description`,应用名称映射至 `--product-name`,补充 `--product-version` 参数。
+
+---
+
+## [2.5.1] - 2026-07-15
+
+* **多平台自动编译**:利用 GitHub Actions,在 windows-latest、macos-latest 和 ubuntu-latest 三种容器中并发运行,自动将产物打包归档并上传至 Tag Release 附件。
+* **扁平风格**:清除 Emoji 表情,全面换装扁平化 Google 风格矢量图标。
+* **修改版权信息显示**:删除标题栏作者信息,增加状态栏右侧常驻版权声明(自适应隐藏)。
+
+---
+
+## [2.5.0] - 2026-07-15
+
+* **安全去耦与跨平台稳定性优化**:完全隔离 Windows 的 `winreg` 注册表静态调用,保障 macOS 与 Linux 等非 Windows 环境下的静态加载安全性。
+* **自适应图像格式转换引擎**:引入全新的 `convert_image_to_format` 弹性图形转译机制,支持 SVG、PNG、JPG 转换为 `.ico` 与 `.icns`。
+* **应用元数据精准转译与写入**:Windows 下自动动态写入 `VSVersionInfo` 元数据,macOS 平台自动映射生成规范的 Bundle ID(如 `com.company.appname`)。
+* **多维异构构建产物精准提炼**:重构底层产物移动(Move)与重置逻辑,精准捕获 Windows `.exe`、macOS `.app` 以及 UNIX 无后缀二进制。
+
+---
+
+## [2.4.6] - 2026-07-12
+
+* **跨平台兼容性适配**:修复底层进程调用中 Windows 平台专有 `creationflags` 导致非 Windows 系统崩溃的异常;重构 UNIX 环境下的 Python 解释器探测器。
+* **UI 现代化重构与 UX 体验升级**:采用卡片式容器布局 (Card-based Layout),引入“胶囊式子导航 (Pill-Tabs)”,全面解决低分辨率缩放下的组件挤压问题。
+* **多版本内核独立编译沙盒**:支持全局指定或浏览任意版本的 Python 解释器,并在专属内核沙盒中完成代码解析与独立编译。
+* **关于界面视觉重构**:引入原生交互式动作行 (Action Row),支持无缝调用系统浏览器跳转开源仓库;修复 ICO 图标抗锯齿失真问题。
+
+---
+
+## [2.4.5] - 2026-07-06
+
+* **源码解析安全修复**:移除了进行依赖扫描和控制台防闪退注入时对源码换行符 (CRLF 强转 LF) 的干预,修复潜在的多行字符串解析错误及文件破坏问题。
+
+---
+
+## [2.4.4] - 2026-07-06
+
+* **附加资源可视化管理**:引入列表化管理组件,支持通过图形化对话框分别添加“文件”和“文件夹”,并支持双击列表条目修改相對釋放路徑。
+* **路径解析引擎升级**:重写底层存储与 `--add-data` 指令拼接逻辑,解决绝对路径被错误切割的解析隐患。
+
+---
+
+## [2.4.3] - 2026-07-01
+
+* **自定义依赖清单支持**:新增指定依赖文件路径配置,支持为不同脚本绑定不同的 `requirements.txt`。
+* **修复控制台模式下导入及资源读取异常**:调整控制台防闪退代码注入逻辑,将临时入口文件限制在源脚本同级目录下,规避相对导入 (Import) 失效。
+* **重构后台分析线程控制逻辑**:废弃不安全的 `QThread.terminate()`,采用解绑信号的处理机制,降低界面卡死或死锁概率。
+* **提升 Nuitka Standalone 打包兼容性**:改用动态扫描匹配 `.dist` 产物文件夹,避免入口重命名导致的构建异常。
+
+---
+
+## [2.4.2] - 2026-06-28
+
+* **编译语法异常诊断**:编译失败时,自动扫描构建日志,精准识别源码中的 `IndentationError`、`SyntaxError` 和 `TabError`。
+* **精准定位错误源**:解析并高亮展示语法错误的源文件名、行号及错误描述,区分“源码逻辑异常”与“打包工具故障”。
+
+---
+
+## [2.4.1] - 2026-06-25
+
+* **模块化配置重构**:重构设置面板,将参数分类为“应用元数据”、“编译控制”、“资源与沙盒”、“软件设置”四大模块。
+* **自定义归档目录**:支持将编译产物输出至全局指定的自定义目录,并支持对缺失的多级父目录进行自动递归创建。
+* **并发编译限制**:支持手动限制打包引擎并发编译时占用的最大 CPU 核心数。
+* **编译提示音效与自动日志保存**:引入系统级音频通知,并在任务结束后支持在目标输出路径自动保存 `.log` 运行日志。
+
+---
+
+## [2.4.0] - 2026-06-22
+
+* **控制台防闪退机制**:非 GUI 模式打包时,自动在临时脚本尾部注入防闪退控制代码。
+* **提升文件删除鲁棒性**:重构临时目录清理机制,引入带重试延时与只读属性解除的 `robust_rmtree` 回收机制。
+* **云盘锁状态前置拦截**:对处于 OneDrive 等云同步锁定状态的文件提供前置 I/O 异常检测。
+* **持续集成与自动发布**:新增 GitHub Actions 工作流,推送 Tag 自动触发 PyPI 发布与 GitHub Release 附件上传。
+
+---
+
+## [2.3.5] - 2026-06-17
+
+* **防漏包机制**:重构依赖拉取架构为“三重绝对安全网”(`Requirements -> Pipreqs -> AST 源码强扫`),彻底解决漏包闪退问题。
+* **工作区零污染**:配置文件迁移至全局路径 `~/.qpypack/config.ini`,所有中间产物转移至系统 Temp 目录。
+* **构建日志规范化**:引入规范的 CI/CD 生命周期状态标签 (`[Init]`, `[Env]`, `[Deps]`, `[Build]`, `[Pack]`)。
+* **虚拟环境沙盒智能自愈**:分配隔离 venv 后自动升级 pip,清除老旧模块的大段警告乱码。
+
+---
+
+## [2.3.0] - 2026-06-15
+
+* **UI 框架重构**:由 PyQt5 升级至 PySide6,优化高分屏 (High-DPI) 缩放表现。
+* **精简架构**:移除 cx_Freeze,专注打磨 PyInstaller 与 Nuitka 双核心引擎。
+* **Nuitka 最佳实践**:单文件模式下强制启用 `zstandard` 压缩;引入 `anti-bloat` 防膨胀插件;自动识别 `tkinter` 探针。
+
+---
+
+## [2.2.0] - 2026-06-14
+
+* **更名与规范**:正式更名为 **QPyPack**,规范化内部沙盒与临时文件命名。
+* **超时守护 (Watchdog)**:引入看门狗机制,为关键打包步骤设置超时限制,防止构建无限期挂起。
+* **智能命名输出**:自动解析脚本版本,预设输出文件名为 `{Name}_{Version}` 格式。
+
+---
+
+## [1.0.1] - 2026-05-31
+
+* **自定义 PIP 镜像源**:引入 PIP 镜像源配置,默认使用清华大学开源镜像源,支持修改即时生效与 QSettings 持久化存储。
+* **构建逻辑统一**:虚拟环境初始化与依赖解析统一走用户配置的自定义 PIP 镜像源。
+
+---
+
+## 📄 License
+
+Copyright (C) 2026 QwejayHuang.
+Licensed under the [GNU General Public License v3.0](LICENSE).
diff --git a/README.md b/README.md
index a4abf4f..c3aabe9 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,18 @@
# QPyPack
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
基于 PyInstaller 与 Nuitka 的跨平台 Python 应用可视化打包构建工具
@@ -26,9 +39,11 @@ QPyPack 是一款致力于简化 Python 应用打包流程的可视化工具。
## 📷 界面预览 (Screenshots)
-
-
-
+
+
+
+
+
---
@@ -40,6 +55,7 @@ QPyPack 是一款致力于简化 Python 应用打包流程的可视化工具。
### 1. 直观的可视化交互体验
* 📥 **拖放式载入 (Drag & Drop)**:无需手动拷贝路径,直接将 `.py` 或 `.pyw` 源代码文件拖放至软件窗口,系统将全自动解析并载入工作区。
* 🎨 **图标智能检索与自适应**:源程序导入后,系统会自动检索同级目录下的常用图像资产(如 `icon.ico` / `logo.ico` / `icon.svg` / `logo.svg`)进行自适应格式转换、自动绑定与高清预览。
+* 🌐 **多语言支持 (i18n)**:原生支持简体中文、英文等多语言界面自由切换,为全球开发者提供零门槛、无障碍的使用体验。
### 2. 依赖解析与虚拟环境沙盒
* 🛡️ **虚拟环境隔离 (Virtualenv Sandbox)**:支持一键在系统临时目录下创建隔离的虚拟沙盒,仅安装应用运行所需的最小依赖项,从而有效精简最终可执行产物的体积。
@@ -57,8 +73,6 @@ QPyPack 是一款致力于简化 Python 应用打包流程的可视化工具。
## ⚡ 快速上手 (Quick Start)
-您可以通过以下两种方式之使用 QPyPack:
-
### 方法一:通过 pip 安装并运行
在 Python >= 3.8 的环境中,执行以下命令进行安装与启动:
@@ -80,138 +94,7 @@ qpypack
## 📅 更新日志 (Changelog)
-### [2.6.1] - 2026-07-25
-
-* **消除依赖污染**:修复了开启“精简模式”时,AST 引擎错误遍历整个父目录的 Bug,解决打包产物被混入无关依赖的问题。
-* **构建沙盒隔离**:默认将目标脚本放入独立沙盒中解析,杜绝同目录其他脚本的依赖干扰;同时在 UI 界面新增“允许扫描同目录所有文件”开关。
-* **MSVC 智能检测**:引入本地 C++ 编译环境智能嗅探。若系统已安装 MSVC,Nuitka 将自动启用 --msvc=latest,优先使用系统原生编译器,提升兼容性并免除耗时的 MinGW 下载。
-* **UPX 警告静默**:自动识别并跳过系统核心动态库的 UPX 压缩报错,并小幅提升了构建速度。
-
-### [2.6.0] - 2026-07-24
-
-**交互与 UI 架构重构**
-* **重构 TAB 导航**:重新分配TAB,界面更加美观易用。
-* **布局与字体精细化**:紧凑化引擎描述框布局;引入跨平台安全字体栈,零侵权风险且全平台优雅回退。
-* **高分屏渲染优化**:优先提取 256px 高清图标图层,结合浮点精度绘制,解决 125%~200% 屏幕缩放下的图标模糊。
-
-**打包引擎与稳定性修复**
-* **防穿透包名兜底**:内置 C 扩展模块硬编码映射,避免出现找不到包的错误。
-* **拦截虚假切源警报**:智能识别 pip 包名错误,避免在镜像源网络正常时误报“主镜像源连接超时”。
-* **只读目录权限防护**:控制台防闪退注入脚本改写至系统临时目录,防止因源目录只读导致打包中断。
-
-
-展开查看历史版本更新记录
-
-### [2.5.7] - 2026-07-23
-
-**完善日志右键菜单**:新增清空日志、导出日志功能。
-**下拉菜单与样式优化**:修复非编辑模式下拉框导致的文本重叠Bug。
-**按钮与布局规格统一**:统一“浏览”、“AST扫描”等工具按钮的长度和大小。
-**细节提升**:拖拽区域的主标题与副标题均开启了自动换行,防止载入长路径文件时挤压主面板。
-
-
-### [2.5.6] - 2026-07-23
-
-- **PIP 双源备份**:新增备用镜像源配置与故障自动重试机制,内置国内主流 PyPI 镜像预设。
-- **UI 与视觉重构**:
- - 重构 `QComboBox` 悬浮下拉框,统一极简白色主题与滚动体验。
- - Tab 标签栏改为居中对齐,优化窗口最小宽度(710px)。
-- **打包稳定性提升**:
- - 依赖安装全流程接管,支持主/备源自动降级切源。
- - 补全 PyInstaller Windows 版本元数据的 `VarFileInfo` 翻译头字段。
- - 优化精简模式下的排除模块策略。
-
-### [2.5.5] - 2026-07-22
-
-* **消除误删风险**:优化目录 `build/dist` 智能清理,保护桌面及工作区安全。
-* **全沙盒隔离编译**:引入系统级临时目录隔离输出,彻底解决跨盘符与路径割裂异常。
-* **修复路径映射与 UX**:构建 cwd 重定向至源码目录以完美解析相对路径;修正 `open_dist` 打开逻辑。
-* **修复 pipreqs 崩溃**:尝试解决依赖自动扫描过程中的编码中断问题。
-
-### [2.5.4] - 2026-07-18
-
-* **新增精简模式**:自动剔除 `pip`、`unittest` 等冗余库并启用 Nuitka `-OO` 优化,大幅缩减产物体积;配套深度源码静态扫描(至多检索 500 个本地依赖),保障极致精简下的运行稳定性。
-* **修复 UPX 假性报错问题**:当未勾选 UPX 时,显式向引擎传递 `--noupx` 指令,阻断 PyInstaller 静默调用系统 UPX 造成的兼容性报错与日志干扰。
-* **修复日志中文乱码问题**:对底层命令行调用 (`subprocess`) 全面强制追加 `encoding="utf-8"` 参数,彻底解决 Windows 默认编码 (GBK) 解析字节流时引发的控制台乱码。。
-
-### [2.5.3] - 2026-07-17
-
-* **引擎版本锁定**:支持在高级设置中自定义 Nuitka/PyInstaller/pipreqs 的版本,杜绝上游更新导致的打包失败。
-* **动态进度 UI**:主界面大字标题实时同步并轮播当前构建步骤,告别卡顿感与等待焦虑。
-* **解决编译崩溃**:全局强制注入 UTF-8 环境,修复 Windows 中文系统下 Nuitka 解析 PyTorch 等库时因 GBK 编码导致的致命报错。
-* **高分屏渲染修复**:解决非整数缩放(如 125%)下的字体发虚问题,清理冗余代码消除 DPI 权限报错。
-* **依赖扫描自适应**:修复非 UTF-8 编码源码导致 `pipreqs` 分析崩溃的问题,支持自动回退本地编码。
-* **智能解释器检索**:合并 `.EXE/.exe` 重复路径,默认选中当前环境,并剥离 `\\?\` 路径前缀以防子进程调用异常。
-* **绝对无痕清理**:增加对 `nuitka-crash-report.xml` 与临时 `__pycache__` 的强制回收,绝不污染用户目录。
-* **修复图标未更新问题**:修复添加新的打包会导致图标未能更新的问题。
-* **综合细节提升**:重构设置面板排版以消除滚动条;移除高版本 API 完美向下兼容 Python 3.7;Nuitka 插件匹配忽略大小写。
-
-### [2.5.2] - 2026-07-15
-* **重构编译后端指令集**:将 PyInstaller 与 Nuitka 的底层打包参数生成逻辑重构为两套**完全独立、互不干扰的构建分支**。这在架构上彻底消除了历史版本中因共享部分参数生成逻辑而导致的参数污染隐患,确保各自在运行中均符合官方最新的标准规范。
-* **优化 Nuitka 附加资源兼容性**:针对 Nuitka 编译器在处理单个附加数据文件时,若目标相对路径为 `.` 会导致编译崩溃(*contains illegal suffix*)的问题进行了自适应修复(`os.path.normpath` 路径标准化)。
-* **修正 Nuitka 排除模块指令**:将 Nuitka 引擎下排除特定模块的参数由原先不规范的 `--exclude-module` 修正为 Nuitka 官方标准的 `--nofollow-import-to`,提升了打包时对冗余依赖库进行剪裁的安全性和有效性。
-* **完善 Windows 二进制属性映射**:修正并补充了 Nuitka 编译分支下 Windows PE 元数据的字段映射。将应用描述正确映射至 `--file-description`(此前误映射为产品名称)、应用名称映射至 `--product-name`,并同步补充了 `--product-version` 参数,使输出的可执行文件属性信息更趋完整。
-
-### [2.5.1] - 2026-07-15
-* **多平台自动编译**:利用 GitHub Actions,工作流将会在 windows-latest、macos-latest 和 ubuntu-latest 三种原生系统容器中并发运行,并自动将各自生成的原生可执行产物打包归档,最后统一上传至该 Tag 对应的 GitHub Release 附件中。
-* **扁平风格**:清除 Emoji 表情,全面换装扁平化 Google 风格矢量图标。
-* **修改版权信息显示**:删除标题栏作者信息,增加状态栏右侧常驻版权声明(自适应隐藏)。
-
-### [2.5.0]
-* **安全去耦与跨平台稳定性优化**:完全隔离 Windows 的 `winreg` 注册表静态调用,基于动态占位保障 macOS 与 Linux 等非 Windows 环境下的静态加载安全性。
-* **自适应图像格式转换引擎**:引入全新的 `convert_image_to_format` 弹性图形转译机制。可将 SVG、PNG、JPG 等格式自动适配转换为 Windows `.ico` 与 macOS `.icns`,内置“macOS sips -> Qt QImage -> Pillow”三重安全降级备份。
-* **应用元数据精准转译与写入**:在 Windows 系统下自动动态写入 `VSVersionInfo` 元数据,而在 macOS 平台下自动映射生成规范的 Bundle ID(如 `com.company.appname`),为后续的代码签名与公证提供标准数据格式。
-* **多维异构构建产物精准提炼**:重构底层产物移动(Move)与重置逻辑,精准捕获并转移不同编译模式下产生的文件实体或目录包(如 Windows 的 `.exe`,macOS 拥有独立目录结构的 `.app`,以及 UNIX 无后缀二进制),杜绝旧版本跨系统提取失败的问题。
-* **依赖声明与打包规范优化**:更新 `pyproject.toml`,补充标准 PyPI 分类器(Classifiers)及扩展项目链接(URLs),显式声明 `setuptools` 范围防止打包杂质,并将 Pillow 加入核心运行时依赖保障跨平台图标转译。
-
-### [2.4.6]
-* **跨平台兼容性适配**:修复了底层进程调用中 Windows 平台专有的 `creationflags` 导致非 Windows 系统执行崩溃的致命异常;重构 UNIX 环境(macOS/Linux)下的 Python 解释器静态探测与虚拟沙盒环境分析器;过滤及剔成了平台特异性编译参数(如 `version-file` 等)。
-* **UI 现代化重构与 UX 体验升级**:废弃传统密集型排版,采用卡片式容器布局(Card-based Layout)。为复杂的构建参数面板引入“胶囊式子导航(Pill-Tabs)”,在功能丰富度与视觉克制力之间取得平衡;全面解决因低分辨率缩放引发的组件挤压、文本截断等视觉缺陷。
-* **多版本内核独立编译沙盒**:支持全局指定或浏览任意版本的 Python 解释器(例如兼容极旧的 Python 3.5 内核),程序将在对应的专属内核沙盒中完成代码解析、依赖对齐与独立编译分发,彻底与主控宿主环境解耦。
-* **关于界面视觉重构**:引入原生交互式动作行(Action Row)替代传统低质感排版,支持无缝调用系统浏览器跳转开源仓库;修复了部分 ICO 图标渲染时的抗锯齿失真问题,保证界面资产高清锐利。
-
-### [2.4.5]
-* **源码解析安全修复**:移除了在进行依赖扫描(AST 语法树解析)和控制台防闪退代码注入时对源码换行符(CRLF 强转 LF)的干预操作,修复了由此导致的潜在多行字符串解析错误及文件破坏问题。
-
-### [2.4.4]
-* **附加资源可视化管理**:废弃了易输错的单行文本框输入,引入列表化管理组件。支持通过图形化对话框分别添加“文件”和“文件夹”,并支持双击列表条目直接修改打包后的相对释放路径。
-* **路径解析引擎升级**:配合新的可视化资源管理,重写了底层存储与引擎构建指令(`--add-data`)的拼接逻辑,解决了旧版本中绝对路径被错误切割的解析隐患。
-
-### [2.4.3]
-* **自定义依赖清单支持**:新增指定依赖文件路径配置。当多个 Python 脚本放置在同一文件夹管理时,可直接为各个脚本绑定不同的依赖清单(例如 `requirements1.txt`),无需再频繁手动重命名。
-* **修复控制台模式下导入及资源读取异常**:调整了控制台防闪退代码注入逻辑,将生成的临时入口文件限制在源脚本同级目录下,规避了原版本在系统临时文件夹编译导致本地相对导入(Import)及相对路径资源失效的错误。
-* **重构后台分析线程控制逻辑**:废弃了不安全的后台线程强制终止方式(`QThread.terminate()`),采用解绑信号的处理机制,降低了在快速拖拽或频繁切换脚本时程序偶发的界面卡死、闪退或死锁概率。
-* **提升 Nuitka Standalone 打包兼容性**:重构了 Nuitka 自动匹配逻辑,改用动态扫描匹配 `.dist` 产物文件夹,避免因打包入口临时重命名而引发“未定位到有效产物”的构建异常。
-* **优化 SVG 图标转换容错**:针对 Windows 平台下 PyInstaller 对图标格式的限制,在宿主环境缺少特定图片格式插件时,新增优雅降级与日志提示机制,预防编译器因传入不支持的格式而引发崩溃。
-
-### [2.4.2]
-* **编译语法异常诊断**:编译失败时,系统将自动扫描并重排完整的构建日志,精准识别源码中的 `IndentationError`、`SyntaxError` 和 `TabError` 语法错误。
-* **精准定位错误源**:解析并高亮展示发生语法错误的源文件名、行号及错误描述,区分“源码逻辑异常”与“打包工具故障”,避免混淆。
-
-### [2.4.1]
-* **模块化配置重构**:全面重构设置面板,将参数解耦并分类为“版本信息与元数据”、“编译控制”、“资源与沙盒”、“软件设置”四大模块。
-* **自定义归档目录**:支持将编译产物输出至全局指定的自定义输出目录,并支持对缺失的多级父目录进行全自动递归创建。
-* **并发编译限制**:支持手动限制打包引擎并发编译时所能占用的最大 CPU 核心数,避免由于硬件满载引发设备死机。
-* **编译提示音效**:引入系统级轻量化音频通知,在打包成功或发生异常中断时给予不同的声音提示。
-* **编译日志自动保存**:任务结束后支持在目标输出路径自动保存运行日志(.log 格式),极大提升异常分析排查的便利性。
-
-### [2.4.0]
-* **控制台防闪退机制**:在非 GUI 模式打包时,工具会自动在临时脚本尾部注入防闪退控制代码(针对 Windows 平台下双击运行的非交互式程序),提升用户体验。
-* **提升文件删除的鲁棒性**:重构了临时目录清理机制,引入带重试延时与只读属性解除的 `robust_rmtree` 回收机制,减少因防病毒软件或进程占用导致清理失败的情况。
-* **云盘锁状态前置拦截**:针对处于云盘同步锁定状态的文件,提供前置 I/O 异常检测,避免打包过程意外被系统挂起。
-* **完善引擎参数映射**:优化 Nuitka 的插件自动加载逻辑(自适应 PyQt/PySide 家族、numpy, matplotlib, tkinter 等常见重型库),使其配置更加贴合实际运行环境。
-* **持续集成与自动发布**:新增 GitHub Actions 工作流,推送版本标签自动触发 PyPI 发布,并同步编译运行包上传至 GitHub Release。
-
-
-
----
-
-## 🤝 参与贡献 (Contributing)
-
-我们非常欢迎您的参与!如果您在测试或使用过程中发现任何异常,或者有更好的优化建议,可以通过以下方式参与贡献:
-
-1. **提交反馈**:在项目主页提交 [GitHub Issues](https://github.com/Qwejay/QPyPack/issues),并附带异常日志。
-2. **提交代码**:欢迎提交 Pull Requests,请在提交前确保您的代码符合标准规范并完成了多平台本地测试。
+完整的版本更新历史与 Release 说明请参阅 [CHANGELOG.md](CHANGELOG.md) 或 [GitHub Releases 页面](https://github.com/Qwejay/QPyPack/releases)。
---
@@ -234,4 +117,3 @@ QPyPack 是一个完全开源且免费的项目,由个人利用业余时间开
> 使用 QPyPack 构建生成的二进制文件/应用程序,其版权和开源许可**完全由使用者自行决定**,QPyPack 的 GPL-3.0 协议不会影响用户打包后的程序。
Copyright (C) 2026 QwejayHuang.
-```
diff --git a/README_EN.md b/README_EN.md
new file mode 100644
index 0000000..4d072a9
--- /dev/null
+++ b/README_EN.md
@@ -0,0 +1,108 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ A Cross-Platform GUI Packaging & Compilation Suite based on PyInstaller and Nuitka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack is a visual GUI tool designed to simplify Python application packaging workflows. It deeply integrates two major compilation engines—PyInstaller and Nuitka—converting tedious CLI arguments into an intuitive graphical interface, helping developers generate cross-platform native executables efficiently.
+
+---
+
+## 📷 Screenshots
+
+
+
+
+
+
+
+
+
+
+---
+
+## 🚀 Key Features
+
+To reduce configuration overhead in traditional CLI builds and resolve cross-platform environment/dependency conflicts, QPyPack provides the following integrated engineering features:
+
+### 1. Intuitive Visual Interaction
+* 📥 **Drag & Drop Loading**: Simply drag `.py` or `.pyw` source files into the app window; QPyPack will automatically parse and load them into the workspace.
+* 🎨 **Smart Icon Search & Adaptation**: Automatically searches for common icon assets in the source directory (`icon.ico`, `logo.ico`, `icon.svg`, `logo.svg`), performs format conversion, binds them, and provides high-res previews.
+* 🌐 **Internationalization (i18n) Support**: Native multi-language interface support (English, Simplified Chinese, etc.) with real-time switching, providing a seamless, obstacle-free experience for global developers.
+
+### 2. Dependency Resolution & Sandbox Isolation
+* 🛡️ **Virtualenv Sandbox**: One-click creation of isolated temporary virtual environments to install only minimal required dependencies, significantly reducing output binary size.
+* 🔍 **Multi-dimensional Dependency Scanning**:
+ * **Config Sync**: Automatically detects and prioritizes `requirements.txt` in the project directory.
+ * **AST Static Analysis**: Provides deep Abstract Syntax Tree (AST) scanning to extract non-standard modules and automatically complete hidden imports.
+* ⚡ **Built-in PyPI Acceleration Mirrors**: Presets mainstream PyPI mirror channels to drastically speed up package downloading in the sandbox.
+
+### 3. Advanced Compilation Control
+* ⚙️ **Dual-Engine Adaptive Switch**: Seamlessly switch between PyInstaller and Nuitka. Parameter panels dynamically adapt based on the selected engine.
+* 📝 **Application Metadata Injection**: Write version numbers, company names, and descriptions directly in the UI without editing spec/version files (Supports Windows PE metadata & macOS Info.plist).
+* 📂 **Visual Data Files Management**: Add files or folders via GUI lists with double-click editing support for output relative destination paths.
+---
+
+## ⚡ Quick Start
+
+### Method 1: Install via pip
+
+```bash
+# Install QPyPack
+pip install qpypack
+
+# Launch
+qpypack
+```
+
+### Method 2: Download Standalone Binaries
+
+Download pre-compiled binaries directly from the [GitHub Releases](https://github.com/Qwejay/QPyPack/releases) page.
+
+---
+
+## 📅 Changelog
+
+For detailed release notes and update history, please refer to [CHANGELOG.md](CHANGELOG.md) or [GitHub Releases](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 🤝 Contributing
+
+Issues and Pull Requests are welcome on [GitHub Issues](https://github.com/Qwejay/QPyPack/issues)!
+
+---
+
+## 📄 License
+
+Open-sourced under [GNU General Public License v3.0](LICENSE).
+
+Copyright (C) 2026 QwejayHuang.
diff --git a/docs/readme/README_DE.md b/docs/readme/README_DE.md
new file mode 100644
index 0000000..675efb1
--- /dev/null
+++ b/docs/readme/README_DE.md
@@ -0,0 +1,83 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ Ein plattformübergreifendes GUI-Tool zur Paketierung von Python-Anwendungen basierend auf PyInstaller & Nuitka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack ist ein visuelles GUI-Tool zur Vereinfachung des Build-Prozesses von Python-Anwendungen. Es integriert PyInstaller und Nuitka nahtlos und wandelt komplexe Befehlszeilenparameter in eine intuitive grafische Benutzeroberfläche um.
+
+---
+
+## 📷 Screenshots
+
+
+
+
+
+
+---
+
+## 🚀 Hauptfunktionen
+
+* 📥 **Drag & Drop** : Ziehen Sie `.py`- oder `.pyw`-Skripte direkt in das Anwendungsfenster.
+* 🎨 **Automatische Icon-Erkennung** : Erkennt automatisch Bilddateien im Quellordner und wandelt diese um.
+* 🛡️ **Virtuelle Umgebung (Sandbox)** : Erstellt automatisch eine isolierte Umgebung zur Minimierung der Ausgabegröße.
+* 🔍 **Abhängigkeitsanalyse** : Erkennt `requirements.txt` und führt statische AST-Code-Analysen für verdeckte Importe durch.
+* ⚙️ **Dual-Engine-Unterstützung** : Wechseln Sie nahtlos zwischen PyInstaller und Nuitka.
+* 📝 **App-Metadaten-Injektion** : Fügen Sie Versionsnummern und Beschreibungen direkt in die Binärdatei ein.
+
+---
+
+## ⚡ Schnellstart
+
+```bash
+# Über pip installieren
+pip install qpypack
+
+# Anwendung starten
+qpypack
+```
+
+Alternativ können Sie vorkompilierte Dateien von der [GitHub Release-Seite](https://github.com/Qwejay/QPyPack/releases) herunterladen.
+
+---
+
+## 📅 Änderungsprotokoll (Changelog)
+
+Die vollständige Versionshistorie finden Sie in [CHANGELOG.md](CHANGELOG.md) oder auf der [GitHub Releases-Seite](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📄 Lizenz
+
+Lizenziert unter der [GNU General Public License v3.0](LICENSE).
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/docs/readme/README_ES.md b/docs/readme/README_ES.md
new file mode 100644
index 0000000..6915059
--- /dev/null
+++ b/docs/readme/README_ES.md
@@ -0,0 +1,83 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ Herramienta GUI multiplataforma para empaquetar aplicaciones Python basada en PyInstaller y Nuitka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack es una herramienta gráfica diseñada para simplificar el empaquetado de aplicaciones Python. Integra los motores PyInstaller y Nuitka, transformando comandos de consola en una interfaz intuitiva.
+
+---
+
+## 📷 Capturas de pantalla (Screenshots)
+
+
+
+
+
+
+---
+
+## 🚀 Características principales
+
+* 📥 **Arrastrar y Soltar** : Arrastre archivos `.py` o `.pyw` directamente a la ventana.
+* 🎨 **Detección de icono automática** : Convierte y previsualiza iconos del directorio fuente.
+* 🛡️ **Aislamiento en entorno virtual** : Crea entornos virtuales automáticamente para reducir el tamaño final.
+* 🔍 **Análisis de dependencias** : Lectura de `requirements.txt` y análisis estático de código AST para importaciones ocultas.
+* ⚙️ **Conmutación de motor doble** : Cambie libremente entre PyInstaller y Nuitka.
+* 📝 **Inyección de metadatos** : Configure la versión, autor y descripción directamente desde la interfaz.
+
+---
+
+## ⚡ Inicio rápido
+
+```bash
+# Instalar a través de pip
+pip install qpypack
+
+# Iniciar la aplicación
+qpypack
+```
+
+Descargue ejecutables precompilados directamente en la página de [Releases de GitHub](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📅 Historial de cambios (Changelog)
+
+Para ver las notas de versión completas, consulte [CHANGELOG.md](CHANGELOG.md) o la página de [Releases de GitHub](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📄 Licencia
+
+Licenciado bajo la [GNU General Public License v3.0](LICENSE).
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/docs/readme/README_FR.md b/docs/readme/README_FR.md
new file mode 100644
index 0000000..9a11990
--- /dev/null
+++ b/docs/readme/README_FR.md
@@ -0,0 +1,83 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ Un outil GUI multiplateforme pour empaqueter des applications Python basé sur PyInstaller & Nuitka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack est un outil graphique conçu pour simplifier le processus d'empaquetage des applications Python. Il intègre de manière transparente deux moteurs de compilation majeurs—PyInstaller et Nuitka—convertissant les arguments complexes en ligne de commande en une interface visuelle intuitive.
+
+---
+
+## 📷 Captures d'écran (Screenshots)
+
+
+
+
+
+
+---
+
+## 🚀 Fonctionnalités principales
+
+* 📥 **Glisser-déposer intuitif** : Glissez simplement vos fichiers `.py` ou `.pyw` dans l'application.
+* 🎨 **Détection d'icône automatique** : Conversion et aperçu automatique des icônes présentes dans le dossier source.
+* 🛡️ **Bac à sable d'environnement virtuel** : Crée un environnement virtuel isolé pour réduire la taille de l'exécutable final.
+* 🔍 **Analyse des dépendances** : Détection de `requirements.txt` et analyse statique du code (AST) pour inclure les dépendances implicites.
+* ⚙️ **Commutation de moteur double** : Basculez facilement entre PyInstaller et Nuitka.
+* 📝 **Injection de métadonnées** : Configurez la version, l'auteur et la description directement depuis l'interface (Windows PE & macOS Info.plist).
+
+---
+
+## ⚡ Démarrage rapide
+
+```bash
+# Installer via pip
+pip install qpypack
+
+# Lancer l'application
+qpypack
+```
+
+Ou téléchargez les exécutables autonomes directement sur la page des [GitHub Releases](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📅 Notes de version (Changelog)
+
+Pour consulter l'historique complet des mises à jour, veuillez vous référer à [CHANGELOG.md](CHANGELOG.md) ou à la page des [GitHub Releases](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📄 Licence
+
+Distribué sous la licence [GNU General Public License v3.0](LICENSE).
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/docs/readme/README_JA.md b/docs/readme/README_JA.md
new file mode 100644
index 0000000..b329eb6
--- /dev/null
+++ b/docs/readme/README_JA.md
@@ -0,0 +1,83 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ PyInstaller と Nuitka をベースにしたクロスプラットフォーム Python パッケージング GUI ツール
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack は、Python アプリケーションのパッケージング手順を簡素化するために設計された GUI ツールです。PyInstaller と Nuitka の 2 大コンパイルエンジンを高度に統合し、複雑な CLI コマンドライン引数を直感的な GUI 操作に変換します。
+
+---
+
+## 📷 スクリーンショット (Screenshots)
+
+
+
+
+
+
+---
+
+## 🚀 主な機能 (Key Features)
+
+* 📥 **ドラッグ&ドロップ対応**: `.py` / `.pyw` ソースファイルをウィンドウに直接ドラッグして読み込み。
+* 🎨 **アイコン自動検出**: ソースディレクトリ内の画像資産 (`icon.ico` / `logo.svg` 等) を自動検出しフォーマット変換。
+* 🛡️ **仮想環境サンドボックス**: 独立した仮想環境を自動生成し、最小限の依存関係でバイナリサイズを削減。
+* 🔍 **依存関係の自動解析**: `requirements.txt` 解析および AST 静的解析による暗黙のインポート (Hidden Imports) 補完。
+* ⚙️ **デュアルエンジン切替**: PyInstaller と Nuitka をワンクリックで切り替え。
+* 📝 **メタデータ注入**: バージョン情報や作者情報を実行ファイルプロパティに自動書き込み。
+
+---
+
+## ⚡ クイックスタート (Quick Start)
+
+```bash
+# pip によるインストール
+pip install qpypack
+
+# アプリケーションの起動
+qpypack
+```
+
+または [GitHub Releases ページ](https://github.com/Qwejay/QPyPack/releases) からビルド済みバイナリを直接ダウンロードできます。
+
+---
+
+## 📅 更新履歴 (Changelog)
+
+詳細なリリースノートおよび更新履歴については、[CHANGELOG.md](CHANGELOG.md) または [GitHub Releases ページ](https://github.com/Qwejay/QPyPack/releases) を参照してください。
+
+---
+
+## 📄 ライセンス (License)
+
+[GNU General Public License v3.0](LICENSE) のもとでオープンソース化されています。
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/docs/readme/README_KO.md b/docs/readme/README_KO.md
new file mode 100644
index 0000000..5e38792
--- /dev/null
+++ b/docs/readme/README_KO.md
@@ -0,0 +1,83 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ PyInstaller 및 Nuitka 기반의 크로스 플랫폼 Python 애플리케이션 패키징 GUI 도구
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack은 Python 애플리케이션 패키징 프로세스를 단순화하도록 설계된 GUI 도구입니다. PyInstaller와 Nuitka를 깊이 통합하여 복잡한 CLI 명령을 직관적인 그래픽 인터페이스로 변환합니다.
+
+---
+
+## 📷 스크린샷 (Screenshots)
+
+
+
+
+
+
+---
+
+## 🚀 주요 기능 (Key Features)
+
+* 📥 **드래그 앤 드롭 지원**: 소스 파일(.py / .pyw)을 창으로 끌어다 놓아 빠르게 로드.
+* 🎨 **아이콘 자동 추출**: 디렉터리 내 아이콘 자산(`icon.ico`, `logo.svg` 등) 자동 탐지 및 포맷 변환.
+* 🛡️ **가상 환경 샌드박스**: 독립된 가상 환경을 생성하여 최소 의존성만 설치, 실행 파일 용량 최적화.
+* 🔍 **의존성 자동 분석**: `requirements.txt` 파싱 및 AST 정적 스캔을 통한 암시적 임포트(Hidden Imports) 추출.
+* ⚙️ **듀얼 엔진 스위칭**: PyInstaller와 Nuitka 간의 손쉬운 전환.
+* 📝 **메타데이터 주입**: 버전 정보, 작성자, 설명을 UI에서 직접 실행 파일 속성에 주입.
+
+---
+
+## ⚡ 빠른 시작 (Quick Start)
+
+```bash
+# pip를 통한 설치
+pip install qpypack
+
+# 실행
+qpypack
+```
+
+또는 [GitHub Releases 페이지](https://github.com/Qwejay/QPyPack/releases)에서 사전 빌드된 실행 파일을 다운로드하세요.
+
+---
+
+## 📅 변경 사항 (Changelog)
+
+자세한 릴리스 노트 및 업데이트 기록은 [CHANGELOG.md](CHANGELOG.md) 또는 [GitHub Releases 페이지](https://github.com/Qwejay/QPyPack/releases)를 참조하세요.
+
+---
+
+## 📄 라이선스 (License)
+
+[GNU General Public License v3.0](LICENSE)에 따라 오픈 소스로 제공됩니다.
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/docs/readme/README_PT.md b/docs/readme/README_PT.md
new file mode 100644
index 0000000..24b1b48
--- /dev/null
+++ b/docs/readme/README_PT.md
@@ -0,0 +1,83 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ Uma ferramenta GUI multiplataforma para empacotar aplicações Python baseada no PyInstaller & Nuitka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+O QPyPack é uma ferramenta gráfica projetada para simplificar o empacotamento de aplicações Python. Ele integra perfeitamente os motores PyInstaller e Nuitka, convertendo argumentos complexos de linha de comando em uma interface intuitiva.
+
+---
+
+## 📷 Capturas de Tela (Screenshots)
+
+
+
+
+
+
+---
+
+## 🚀 Principais Recursos
+
+* 📥 **Arrastar e Soltar** : Arraste arquivos `.py` ou `.pyw` diretamente para a janela.
+* 🎨 **Detecção Automática de Ícone** : Converte e pré-visualiza ícones da pasta fonte.
+* 🛡️ **Ambiente Virtual Isolado** : Cria um ambiente virtual automaticamente para reduzir o tamanho do executável.
+* 🔍 **Análise de Dependências** : Leitura de `requirements.txt` e análise estática de código AST para importações ocultas.
+* ⚙️ **Troca de Motor Duplo** : Alterne facilmente entre PyInstaller e Nuitka.
+* 📝 **Injeção de Metadados** : Configure versão, autor e descrição diretamente na interface gráfica.
+
+---
+
+## ⚡ Início Rápido
+
+```bash
+# Instalar via pip
+pip install qpypack
+
+# Iniciar aplicação
+qpypack
+```
+
+Ou baixe executáveis pré-compilados diretamente na página de [Releases do GitHub](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📅 Registro de Alterações (Changelog)
+
+Para ver o histórico completo de atualizações, consulte [CHANGELOG.md](CHANGELOG.md) ou a página de [Releases do GitHub](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📄 Licença
+
+Distribuído sob a licença [GNU General Public License v3.0](LICENSE).
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/docs/readme/README_RU.md b/docs/readme/README_RU.md
new file mode 100644
index 0000000..ee52428
--- /dev/null
+++ b/docs/readme/README_RU.md
@@ -0,0 +1,83 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ Кроссплатформенный GUI-инструмент упаковки Python-приложений на базе PyInstaller и Nuitka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack — это визуальный инструмент, предназначенный для упрощения процесса сборки приложений Python. Он объединяет два основных компилятора — PyInstaller и Nuitka, преобразуя сложные консольные команды в интуитивный графический интерфейс.
+
+---
+
+## 📷 Скриншоты (Screenshots)
+
+
+
+
+
+
+---
+
+## 🚀 Основные возможности
+
+* 📥 **Drag & Drop** : Перетаскивайте файлы `.py` или `.pyw` прямо в окно программы.
+* 🎨 **Автоопределение иконки** : Автоматически находит иконки в папке исходников и конвертирует их.
+* 🛡️ **Виртуальное окружение (Sandbox)** : Создает изолированное окружение для минимизации размера итогового файла.
+* 🔍 **Анализ зависимостей** : Считывание `requirements.txt` и статический AST-анализ кода для скрытых импортов.
+* ⚙️ **Два движка сборки** : Быстрое переключение между PyInstaller и Nuitka.
+* 📝 **Внедрение метаданных** : Указывайте версию, автора и описание приложения прямо в интерфейсе.
+
+---
+
+## ⚡ Быстрый старт
+
+```bash
+# Установка через pip
+pip install qpypack
+
+# Запуск
+qpypack
+```
+
+Или скачайте готовые исполняемые файлы на странице [GitHub Releases](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📅 История изменений (Changelog)
+
+Полную историю версий и примечания к релизу см. в [CHANGELOG.md](CHANGELOG.md) или на странице [GitHub Releases](https://github.com/Qwejay/QPyPack/releases).
+
+---
+
+## 📄 Лицензия
+
+Распространяется под лицензией [GNU General Public License v3.0](LICENSE).
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/docs/readme/README_TW.md b/docs/readme/README_TW.md
new file mode 100644
index 0000000..0c477ea
--- /dev/null
+++ b/docs/readme/README_TW.md
@@ -0,0 +1,91 @@
+# QPyPack
+
+
+ 简体中文 |
+ English |
+ 繁體中文 |
+ 日本語 |
+ 한국어 |
+ Français |
+ Deutsch |
+ Español |
+ Русский |
+ Português
+
+
+
+ 基於 PyInstaller 與 Nuitka 的跨平台 Python 應用圖形化打包建置工具
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+QPyPack 是一款致力於簡化 Python 應用打包流程的圖形化工具。它深度整合了 PyInstaller 與 Nuitka 兩大主流編譯引擎,將繁瑣的命令列引數轉化為直觀、便利的圖形介面互動,幫助開發者更高效地產生跨平台原生可執行程式。
+
+---
+
+## 📷 介面預覽 (Screenshots)
+
+
+
+
+
+
+---
+
+## 🚀 核心特性 (Key Features)
+
+### 1. 直觀的圖形化互動體驗
+* 📥 **拖放式載入 (Drag & Drop)**:直接將 `.py` 或 `.pyw` 原始碼檔案拖放至視窗即可自動解析。
+* 🎨 **圖示智慧偵測與自適應**:自動檢索同級目錄下的圖示資產 (`icon.ico` / `logo.svg` 等) 並進行格式轉換與預覽。
+
+### 2. 依賴解析與虛擬環境沙盒
+* 🛡️ **虛擬環境隔離 (Virtualenv Sandbox)**:一鍵建立隔離的虛擬環境,僅安裝最小依賴項以縮減執行檔體積。
+* 🔍 **多維度依賴解析**:自動讀取 `requirements.txt` 並透過 AST 靜態掃描補全隱式匯入 (Hidden Imports)。
+
+### 3. 進階編譯參數精準控制
+* ⚙️ **雙引擎自適應切換**:支援 PyInstaller 與 Nuitka 的無縫切換。
+* 📝 **應用元資料寫入 (Metadata)**:直觀輸入版本號、公司名和描述,自動寫入 PE 屬性或 macOS Info.plist。
+* 📂 **附加資源視覺化管理**:清單化管理檔案與資料夾,支援雙擊修改釋放路徑。
+
+---
+
+## ⚡ 快速上手 (Quick Start)
+
+### 通過 pip 安裝並執行
+
+```bash
+# 安裝 QPyPack
+pip install qpypack
+
+# 啟動程式
+qpypack
+```
+
+亦可直接在 [GitHub Releases 頁面](https://github.com/Qwejay/QPyPack/releases) 下載預編譯的可執行檔。
+
+---
+
+## 📅 更新記錄 (Changelog)
+
+完整的版本更新歷史與 Release 說明請參閱 [CHANGELOG.md](CHANGELOG.md) 或 [GitHub Releases 頁面](https://github.com/Qwejay/QPyPack/releases)。
+
+---
+
+## 📄 開源協議 (License)
+
+基於 [GNU General Public License v3.0](LICENSE) 協議開源。
+
+Copyright (C) 2026 QwejayHuang.
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 47b32dd..80e6287 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
[project]
name = "qpypack"
-version = "2.6.1"
-description = "A cross-platform GUI packaging and compilation suite for PyInstaller and Nuitka."
+version = "2.6.2"
+description = "A cross-platform GUI packaging and compilation suite based on PyInstaller and Nuitka."
readme = "README.md"
authors = [
{ name = "QwejayHuang" }
]
-license = { text = "GPL-3.0" }
+license = { text = "GPL-3.0-only" }
requires-python = ">=3.8"
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -25,6 +25,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
]
dependencies = [
@@ -36,6 +37,7 @@ dependencies = [
"Homepage" = "https://github.com/qwejay/QPyPack"
"Bug Tracker" = "https://github.com/qwejay/QPyPack/issues"
"Source Code" = "https://github.com/qwejay/QPyPack"
+"Documentation" = "https://github.com/qwejay/QPyPack#readme"
[project.scripts]
qpypack = "qpypack.main:main"
@@ -44,4 +46,7 @@ qpypack = "qpypack.main:main"
packages = ["qpypack"]
[tool.setuptools.package-data]
-qpypack = ["icon.ico"]
\ No newline at end of file
+qpypack = [
+ "icon.ico",
+ "locales/*.json"
+]
\ No newline at end of file
diff --git a/qpypack/__init__.py b/qpypack/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/qpypack/__main__.py b/qpypack/__main__.py
new file mode 100644
index 0000000..c2f701a
--- /dev/null
+++ b/qpypack/__main__.py
@@ -0,0 +1,3 @@
+from qpypack.main import main
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/qpypack/locales/de_DE.json b/qpypack/locales/de_DE.json
new file mode 100644
index 0000000..f671566
--- /dev/null
+++ b/qpypack/locales/de_DE.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "Python-Skript (.py/.pyw) hierher ziehen\noder Klicken zum Durchsuchen...",
+ "Auto-parse dependencies, resources, and implicit imports": "Automatische Analyse von Abhängigkeiten, Ressourcen und impliziten Importen",
+ "Loaded: {filename}": "Geladen: {filename}",
+ "Parsing metadata...": "Metadaten werden analysiert...",
+ "Ready, waiting for build.": "Bereit, warte auf Build.",
+ "Initializing build...": "Build-Prozess wird initialisiert...",
+ "Preparing engine...": "Build-Engine wird vorbereitet...",
+ "Build Successful": "Build erfolgreich abgeschlossen",
+ "Open output directory or reset workspace.": "Öffnen Sie das Ausgabeverzeichnis oder setzen Sie den Arbeitsbereich zurück.",
+ "Build Failed": "Build fehlgeschlagen",
+ "Check log output below for troubleshooting.": "Überprüfen Sie das Protokoll unten zur Fehlerbehebung.",
+ "Status: Ready": " Status: Bereit",
+ "Status: Parsing {filename}...": " Status: Analysiere {filename}...",
+ "Status: Loaded {filename}{mode}": " Status: {filename}{mode} geladen",
+ "Status: Packaging ({engine}) ...": " Status: Paketierung läuft ({engine}) ...",
+ "Status: Build Completed": " Status: Build abgeschlossen",
+ "Status: Build Failed": " Status: Build fehlgeschlagen",
+ "Status: Workspace Reset": " Status: Arbeitsbereich zurückgesetzt",
+ "Start Build": " Build starten",
+ "Stop Build": " Build abbrechen",
+ "Open Directory": " Ordner öffnen",
+ "Rebuild": " Erneut bauen",
+ "Save & Return": " Speichern & Zurück",
+ "Build Settings": "Build-Einstellungen",
+ "Preferences": "Einstellungen",
+ "About": "Über QPyPack",
+ "🚀 Engine": "🚀 Engine",
+ "📦 Dependencies": "📦 Abhängigkeiten",
+ "📂 Resources": "📂 Ressourcen",
+ "⚡ Optimization": "⚡ Optimierung",
+ "🗺️ Package Map": "🗺️ Paket-Zuordnung",
+ "Engine & Environment": "Build-Engine und Umgebung",
+ "Execution Mode": "Ausführungsmodus",
+ "Mirrors & Scanner": "Spiegelserver und Scanner-Einstellungen",
+ "Additional Resources (Drag & Drop Supported)": "Zusätzliche Ressourcen (Drag & Drop unterstützt)",
+ "Performance Optimization": "Leistungs- und Kompilieroptimierung",
+ "Lock Core Dependencies": "Kernabhängigkeiten festlegen",
+ "Package Name Mappings": "Paketnamen-Zuordnungstabelle",
+ "UI Language:": "Oberflächensprache:",
+ "App Metadata & Presets": "App-Metadaten und Voreinstellungen",
+ "Output Location": "Ausgabeort der Anwendung",
+ "Preferences & System Behavior": "Einstellungen und Systemverhalten",
+ "Build Engine:": "Build-Engine:",
+ "Python Interpreter:": "Python-Interpreter:",
+ "Output Name:": "Name der Ausgabedatei:",
+ "App Icon:": "Anwendungssymbol:",
+ "Primary PIP Index:": "Primärer PIP-Spiegelserver:",
+ "Backup PIP Index:": "Sekundärer PIP-Spiegelserver:",
+ "Requirements File:": "Abhängigkeitsdatei (requirements.txt):",
+ "Hidden Imports:": "Implizite Importe (Hidden Imports):",
+ "Exclude Modules:": "Auszuschließende Module (Excludes):",
+ "CPU Cores:": "Parallele CPU-Kerne:",
+ "UPX Path:": "UPX-Pfad:",
+ "PyInstaller Version:": "PyInstaller-Version:",
+ "Nuitka Version:": "Nuitka-Version:",
+ "Pipreqs Version:": "Pipreqs-Version:",
+ "Version:": "Version:",
+ "Author/Company:": "Autor/Organisation:",
+ "Description:": "Beschreibung:",
+ "Output Rule:": "Ausgaberegel:",
+ "Target Directory:": "Zielverzeichnis:",
+ "One-File Mode (--onefile)": "Einzeldatei-Modus (--onefile)",
+ "Hide Console (--noconsole)": "Konsolenfenster ausblenden (--noconsole)",
+ "Use Virtual Environment (Recommended)": "Isolierte virtuelle Umgebung nutzen (Empfohlen)",
+ "Install requirements.txt": "Abhängigkeiten aus requirements.txt installieren",
+ "Analyze Dependencies (pipreqs)": "Projektabhängigkeiten automatisch analysieren (pipreqs)",
+ "Scan Entire Folder": "Gesamten Ordner scannen",
+ "Enable UPX Compression": "UPX-Komprimierung aktivieren",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "Lite-Modus (Dev-/Test-Abhängigkeiten ausschließen)",
+ "Concise Log Output": "Kompakte Protokollausgabe",
+ "Auto-save Build Log": "Build-Protokoll automatisch speichern",
+ "Auto Extract Icon": "Anwendungssymbol automatisch extrahieren",
+ "Clean Temporary Cache After Build": "Temporären Cache nach dem Build leeren",
+ "Sound Notification": "Benachrichtigungston bei Fertigstellung",
+ "Browse": "Durchsuchen...",
+ "AST Scan": "AST-Analyse",
+ "Add File": "Datei hinzufügen",
+ "Add Dir": "Ordner hinzufügen",
+ "Remove Selected": "Auswahl entfernen",
+ "Clear All": "Alles leeren",
+ "Add Mapping": "Zuordnung hinzufügen",
+ "Restore Defaults": "Standardwerte wiederherstellen",
+ "Export Preset...": "Voreinstellung exportieren...",
+ "Import Preset...": "Voreinstellung importieren...",
+ "Source File Directory": "Verzeichnis der Quelldatei",
+ "Custom Directory": "Benutzerdefiniertes Verzeichnis",
+ "Import Name": "Importname im Code",
+ "PyPI Package Name": "PyPI-Paketname",
+ "Reset to Default Config": "Auf Standardkonfiguration zurücksetzen",
+ "Cancel & Return": "Änderungen verwerfen & Zurück",
+ "Configure Build Settings": "Build-Parameter konfigurieren",
+ "Toggle Execution Log": "Protokoll anzeigen/ausblenden",
+ "Copy": "Kopieren",
+ "Select All": "Alles auswählen",
+ "Clear Log": "Protokoll löschen",
+ "Export Log...": "Protokoll exportieren...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "Ein plattformübergreifendes GUI-Tool zur Paketierung von Python-Anwendungen basierend auf PyInstaller & Nuitka",
+ " [Console]": " [Mit Konsole]",
+ " [No Console]": " [Ohne Konsole]",
+ "File": "Datei",
+ "Directory": "Verzeichnis",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: Schnelle Erstellung, hervorragende Kompatibilität. Ideal für schnelle Entwicklungszyklen.",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: Kompiliert in eine native C/C++ Binärdatei. Bessere Leistung und Schutz des Quellcodes.",
+ "Leave blank to auto-detect system default Python": "Leer lassen, um den Standard-Interpreter des Systems automatisch zu erkennen",
+ "Leave blank to auto-match script name": "Leer lassen, um automatisch den Namen des Skripts zu verwenden",
+ "Leave blank to auto-search requirements.txt in current directory": "Leer lassen, um automatisch nach requirements.txt im aktuellen Ordner zu suchen",
+ "Comma separated (e.g. pandas, PyQt5)": "Durch Kommas getrennt (z. B. pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "Durch Kommas getrennt (z. B. tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "Leer lassen, um automatisch aus der PATH-Umgebungsvariable zu ermitteln",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "Schließt überflüssige Abhängigkeiten aus, um die Ausführungsgeschwindigkeit zu erhöhen und die Dateigröße zu reduzieren.",
+ "Double-click to edit target path; Drag & drop supported": "Doppelklick zum Bearbeiten des Zielpfads; Drag & Drop wird unterstützt",
+ "Package mappings have been reset to defaults.": "Paket-Zuordnungen wurden auf Standardwerte zurückgesetzt.",
+ "Config preset exported to: {path}": "Konfigurations-Voreinstellung exportiert nach: {path}",
+ "Config preset imported successfully.": "Konfigurations-Voreinstellung erfolgreich importiert.",
+ "AST scan completed, found {count} dependencies.": "AST-Scan abgeschlossen, {count} Abhängigkeiten gefunden.",
+ "No log content.": "Kein Protokollinhalt zum Exportieren vorhanden.",
+ "Log saved to: {path}": "Protokoll gespeichert unter: {path}",
+ "Attention: Please check the log for details.": "Achtung: Bitte prüfen Sie das Protokoll für Details.",
+ "[INFO] Initializing isolated build environment...": "[INFO] Isolierte Build-Umgebung wird initialisiert...",
+ "[INFO] Python interpreter path: {path}": "[INFO] Verwendeter Python-Interpreter: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] Erstelle virtuelle Umgebung (venv)...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] Erstellung der virtuellen Umgebung fehlgeschlagen. Dem Python-System fehlen möglicherweise Module oder Rechte.",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] Synchronisiere und aktualisiere den Paketmanager pip...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] Installiere Build-Engine [{pkg}] und Kern-Abhängigkeiten...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Nuitka-Hinweis: Falls Sie beim ersten Build zum Download eines GCC/MinGW-Kompilers aufgefordert werden, stellen Sie eine stabile Internetverbindung sicher.",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] Abhängigkeits-Installation [1/3]: Installiere deklarierte Abhängigkeiten ({filename})...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] Abhängigkeits-Installation [2/3]: Führe pipreqs für statische Code-Analyse aus...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] Einzeldatei-Sandbox aktiviert: Analysiere nur das Zielskript, um Konflikte zu vermeiden.",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] Vollständiger Verzeichnisscan aktiviert: Scanne alle Python-Dateien im aktuellen Ordner...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] PyPI-Serveradresse für Analyse: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] Frage Paketversionen ab, bitte warten...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] Wechsel zum Backup-PIP-Index: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] Versuche erneuten Scan mit kompatibler Zeichenkodierung...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] Tiefenscan von pipreqs übersprungen, fehlende Abhängigkeiten werden per AST ergänzt.",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] Abhängigkeits-Installation [3/3]: Extrahiere implizite Importe mittels AST-Scan...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] Installiere erkannte implizite Abhängigkeiten: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ Warnung: Installation des Pakets [{pkg}] fehlgeschlagen! Kann zu Laufzeitabstürzen führen.",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] Starte {engine}-Engine für die Kompilierung...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] Lokale MSVC-Umgebung erkannt. Bevorzuge nativen C++ Kompiler.",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] Lite-Modus aktiv: Führe Optimierungen zur Größenreduzierung aus...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] Es wird dringend empfohlen, 'Virtuelle Umgebung' zu aktivieren, um die Größe optimal zu reduzieren.",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] Nuitka-Optimierungsflags (-OO) angewendet...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] Hauptkompilierung abgeschlossen, extrahiere und archiviere Ausgabedateien...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] Übertragung der Ausgabedatei fehlgeschlagen. Datei ist möglicherweise blockiert oder Rechte fehlen: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] Keine gültige ausführbare Datei im temporären Build-Verzeichnis gefunden: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] Validiere Ausgabedateien und erstelle das Endprodukt...",
+ "[INFO] Build log exported to: {path}": "[INFO] Build-Protokoll exportiert nach: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] Kompilierung erfolgreich abgeschlossen. Ausgabe: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[Syntaxfehler] Der Quellcode enthält einen Syntax- oder Einrückungsfehler!\n - Datei: {file}\n - Fehlertyp: {type}\n - Zeile: nahe Zeile {line}\n - Details: {desc}\n\nHinweis: Dies ist ein Fehler im Quellcode. Stellen Sie sicher, dass das Skript lokal fehlerfrei läuft.",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [Diagnose-Traceback: Vollständiges Protokoll aufgrund eines Ausführungsfehlers] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] Kompilierung aufgrund von Fehlern abgebrochen. Prüfen Sie das Protokoll.",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] Gebe Speicherplatz frei und bereinige temporäre Build-Umgebung...",
+ "\\nProgram execution completed, press Enter to exit...": "\\nAusführung beendet. Drücken Sie die Eingabetaste zum Beenden...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] Zieldatei ist gesperrt oder durch einen Cloud-Dienst verschlüsselt. Bitte entsperren und erneut versuchen.",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] Bitte laden Sie zuerst eine gültige Python-Quelldatei!",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] Ausnahme bei der AST-Analyse aufgetreten: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] Voreinstellungsdatei konnte nicht exportiert werden: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] Ungültiges oder beschädigtes Format der Voreinstellungsdatei: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] Protokolldatei konnte nicht exportiert werden: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] Build abgeschlossen, aber die Vorab-Installation folgender Abhängigkeiten ist fehlgeschlagen:\n\n 👉 {pkgs}\n\n⚠️ Hinweis: Die Anwendung könnte zur Laufzeit wegen fehlender Module abstürzen!"
+}
\ No newline at end of file
diff --git a/qpypack/locales/es_ES.json b/qpypack/locales/es_ES.json
new file mode 100644
index 0000000..122e0f7
--- /dev/null
+++ b/qpypack/locales/es_ES.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "Arrastra y suelta el script Python (.py/.pyw) aquí\no Haz clic para buscar...",
+ "Auto-parse dependencies, resources, and implicit imports": "Análisis automático de dependencias, recursos e importaciones implícitas",
+ "Loaded: {filename}": "Cargado: {filename}",
+ "Parsing metadata...": "Analizando metadatos...",
+ "Ready, waiting for build.": "Listo, esperando compilación.",
+ "Initializing build...": "Inicializando proceso de compilación...",
+ "Preparing engine...": "Preparando el motor...",
+ "Build Successful": "Compilación completada con éxito",
+ "Open output directory or reset workspace.": "Abre el directorio de salida o reinicia el espacio de trabajo.",
+ "Build Failed": "Compilación fallida",
+ "Check log output below for troubleshooting.": "Consulta el registro a continuación para solucionar el problema.",
+ "Status: Ready": " Estado: Listo",
+ "Status: Parsing {filename}...": " Estado: Analizando {filename}...",
+ "Status: Loaded {filename}{mode}": " Estado: {filename}{mode} cargado",
+ "Status: Packaging ({engine}) ...": " Estado: Empaquetando ({engine}) ...",
+ "Status: Build Completed": " Estado: Compilación completada",
+ "Status: Build Failed": " Estado: Compilación fallida",
+ "Status: Workspace Reset": " Estado: Espacio de trabajo reiniciado",
+ "Start Build": " Iniciar compilación",
+ "Stop Build": " Detener",
+ "Open Directory": " Abrir carpeta",
+ "Rebuild": " Recompilar",
+ "Save & Return": " Guardar y regresar",
+ "Build Settings": "Opciones de compilación",
+ "Preferences": "Preferencias",
+ "About": "Acerca de",
+ "🚀 Engine": "🚀 Motor",
+ "📦 Dependencies": "📦 Dependencias",
+ "📂 Resources": "📂 Recursos",
+ "⚡ Optimization": "⚡ Optimización",
+ "🗺️ Package Map": "🗺️ Mapeo",
+ "Engine & Environment": "Motor de compilación y entorno",
+ "Execution Mode": "Modo de ejecución",
+ "Mirrors & Scanner": "Servidores espejo y escáner",
+ "Additional Resources (Drag & Drop Supported)": "Recursos adicionales (Soporta arrastrar y soltar)",
+ "Performance Optimization": "Optimización de rendimiento y compilación",
+ "Lock Core Dependencies": "Bloquear versiones de dependencias clave",
+ "Package Name Mappings": "Tabla de mapeo de nombres de paquetes",
+ "UI Language:": "Idioma de la interfaz:",
+ "App Metadata & Presets": "Metadatos de la aplicación y ajustes preestablecidos",
+ "Output Location": "Ubicación del archivo ejecutable",
+ "Preferences & System Behavior": "Preferencias y comportamiento del sistema",
+ "Build Engine:": "Motor de compilación:",
+ "Python Interpreter:": "Intérprete de Python:",
+ "Output Name:": "Nombre del ejecutable:",
+ "App Icon:": "Icono de la aplicación:",
+ "Primary PIP Index:": "Índice PIP principal:",
+ "Backup PIP Index:": "Índice PIP secundario:",
+ "Requirements File:": "Archivo de dependencias (requirements.txt):",
+ "Hidden Imports:": "Importaciones implícitas (Hidden Imports):",
+ "Exclude Modules:": "Excluir módulos (Excludes):",
+ "CPU Cores:": "Núcleos CPU en paralelo:",
+ "UPX Path:": "Ruta del ejecutable UPX:",
+ "PyInstaller Version:": "Versión de PyInstaller:",
+ "Nuitka Version:": "Versión de Nuitka:",
+ "Pipreqs Version:": "Versión de Pipreqs:",
+ "Version:": "Versión:",
+ "Author/Company:": "Autor/Organización:",
+ "Description:": "Descripción:",
+ "Output Rule:": "Regla de ubicación:",
+ "Target Directory:": "Carpeta de destino:",
+ "One-File Mode (--onefile)": "Modo de archivo único (--onefile)",
+ "Hide Console (--noconsole)": "Ocultar ventana de la consola (--noconsole)",
+ "Use Virtual Environment (Recommended)": "Usar entorno virtual aislado (Recomendado)",
+ "Install requirements.txt": "Instalar dependencias de requirements.txt",
+ "Analyze Dependencies (pipreqs)": "Analizar dependencias automáticamente (pipreqs)",
+ "Scan Entire Folder": "Escanear toda la carpeta",
+ "Enable UPX Compression": "Habilitar compresión UPX",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "Modo ligero (Excluir dependencias dev/test)",
+ "Concise Log Output": "Salida de registro concisa",
+ "Auto-save Build Log": "Guardar registro de compilación automáticamente",
+ "Auto Extract Icon": "Extraer icono automáticamente",
+ "Clean Temporary Cache After Build": "Limpiar caché temporal al finalizar",
+ "Sound Notification": "Notificación sonora al finalizar",
+ "Browse": "Examinar...",
+ "AST Scan": "Análisis AST",
+ "Add File": "Añadir archivo",
+ "Add Dir": "Añadir carpeta",
+ "Remove Selected": "Eliminar selección",
+ "Clear All": "Limpiar todo",
+ "Add Mapping": "Añadir mapeo",
+ "Restore Defaults": "Restaurar valores predeterminados",
+ "Export Preset...": "Exportar ajustes...",
+ "Import Preset...": "Importar ajustes...",
+ "Source File Directory": "Carpeta del archivo fuente",
+ "Custom Directory": "Carpeta personalizada",
+ "Import Name": "Nombre de importación en código",
+ "PyPI Package Name": "Nombre del paquete en PyPI",
+ "Reset to Default Config": "Restablecer configuración predeterminada",
+ "Cancel & Return": "Descartar cambios y regresar",
+ "Configure Build Settings": "Configurar parámetros de compilación",
+ "Toggle Execution Log": "Mostrar/Ocultar registro de ejecución",
+ "Copy": "Copiar",
+ "Select All": "Seleccionar todo",
+ "Clear Log": "Limpiar registro",
+ "Export Log...": "Exportar registro...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "Una herramienta GUI multiplataforma para empaquetar aplicaciones Python basada en PyInstaller y Nuitka",
+ " [Console]": " [Con consola]",
+ " [No Console]": " [Sin consola]",
+ "File": "Archivo",
+ "Directory": "Carpeta",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: Compilación rápida y excelente compatibilidad. Ideal para desarrollo iterativo.",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: Compila a binario C/C++ nativo. Ofrece mayor rendimiento y protección del código fuente.",
+ "Leave blank to auto-detect system default Python": "Dejar en blanco para detectar automáticamente el intérprete del sistema",
+ "Leave blank to auto-match script name": "Dejar en blanco para usar automáticamente el nombre del script",
+ "Leave blank to auto-search requirements.txt in current directory": "Dejar en blanco para buscar automáticamente requirements.txt en la carpeta actual",
+ "Comma separated (e.g. pandas, PyQt5)": "Separados por comas (ej. pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "Separados por comas (ej. tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "Dejar en blanco para detectar desde las variables de entorno (PATH)",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "Excluye dependencias innecesarias dinámicamente para reducir el tamaño y acelerar la compilación.",
+ "Double-click to edit target path; Drag & drop supported": "Doble clic para editar la ruta relativa. Admite arrastrar y soltar",
+ "Package mappings have been reset to defaults.": "Los mapeos de paquetes se han restablecido a los valores predeterminados.",
+ "Config preset exported to: {path}": "Ajuste preestablecido exportado a: {path}",
+ "Config preset imported successfully.": "Ajuste preestablecido importado con éxito.",
+ "AST scan completed, found {count} dependencies.": "Análisis AST completado. Se encontraron {count} dependencias.",
+ "No log content.": "No hay contenido en el registro para exportar.",
+ "Log saved to: {path}": "Registro guardado en: {path}",
+ "Attention: Please check the log for details.": "Atención: Por favor, consulte el registro para obtener detalles.",
+ "[INFO] Initializing isolated build environment...": "[INFO] Inicializando entorno de compilación aislado...",
+ "[INFO] Python interpreter path: {path}": "[INFO] Ruta del intérprete Python: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] Creando entorno virtual (venv)...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] No se pudo crear el entorno virtual. Es posible que falten módulos requeridos o los permisos estén restringidos.",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] Sincronizando y actualizando el gestor de paquetes pip...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] Instalando motor [{pkg}] y dependencias clave...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Consejo de Nuitka: Si se le solicita descargar el compilador GCC/MinGW en la primera compilación, mantenga una conexión estable.",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] Instalación de dependencias [1/3]: Instalando dependencias declaradas ({filename})...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] Instalación de dependencias [2/3]: Ejecutando pipreqs para el análisis estático del proyecto...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] Modo sandbox habilitado: Analizando solo el script de entrada para evitar contaminación.",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] Modo de escaneo completo activado: Escaneando todos los archivos Python del directorio...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] Servidor PyPI para análisis de dependencias: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] Consultando versiones de los paquetes, por favor espere...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] Cambiando al índice PIP secundario para reintentar: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] Reintentando escaneo con codificación compatible...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] Se omitió el escaneo profundo de pipreqs. Las dependencias se completarán mediante AST.",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] Instalación de dependencias [3/3]: Extrayendo importaciones implícitas mediante análisis estático AST...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] Instalando dependencias implícitas detectadas: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ Advertencia: Error al instalar el paquete [{pkg}]. La aplicación podría fallar en ejecución.",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] Iniciando motor {engine} para la compilación binaria...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] Entorno MSVC local detectado, priorizando el compilador C++ nativo.",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] Modo ligero activado: Aplicando estrategias de reducción de tamaño...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] Se recomienda marcar 'Usar entorno virtual' para maximizar el efecto del modo ligero.",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] Aplicando directivas de optimización de Nuitka (-OO)...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] Compilación principal finalizada, extrayendo y archivando archivos...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] Error al transferir el ejecutable. El archivo puede estar ocupado por un proceso o faltan permisos: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] No se pudo encontrar un ejecutable válido en el directorio temporal: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] Validando ejecutable y generando producto final...",
+ "[INFO] Build log exported to: {path}": "[INFO] Registro de compilación exportado a: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] Compilación completada con éxito. Ruta de salida: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[Error de sintaxis] El código fuente contiene errores de sintaxis o sangría.\n - Archivo: {file}\n - Tipo de error: {type}\n - Línea: cerca de la línea {line}\n - Descripción: {desc}\n\nConsejo: Se trata de un error en el código fuente. Asegúrese de que funciona localmente antes de compilar.",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [Rastreo de diagnóstico: Registro completo por excepción de ejecución] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] Compilación interrumpida debido a errores. Verifique el registro.",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] Liberando espacio en disco y limpiando archivos temporales...",
+ "\\nProgram execution completed, press Enter to exit...": "\\nEjecución finalizada. Presione Enter para salir...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] El archivo está bloqueado o cifrado por un servicio en la nube. Desbloquéelo y reintente.",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] ¡Por favor, cargue primero un archivo de código fuente Python válido!",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] Ocurrió una excepción durante el análisis AST: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] No se pudo exportar el archivo de ajustes preestablecidos: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] El archivo de ajustes está corrupto o tiene un formato no válido: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] No se pudo exportar el archivo de registro: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] La compilación finalizó, pero falló la instalación previa de las siguientes dependencias:\n\n 👉 {pkgs}\n\n⚠️ Advertencia: El programa podría fallar en ejecución debido a la falta de módulos."
+}
\ No newline at end of file
diff --git a/qpypack/locales/fr_FR.json b/qpypack/locales/fr_FR.json
new file mode 100644
index 0000000..3afb47a
--- /dev/null
+++ b/qpypack/locales/fr_FR.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "Glissez-déposez un script Python (.py/.pyw) ici\nou cliquez pour parcourir...",
+ "Auto-parse dependencies, resources, and implicit imports": "Analyse automatique des dépendances, ressources et imports implicites",
+ "Loaded: {filename}": "Chargé : {filename}",
+ "Parsing metadata...": "Analyse des métadonnées...",
+ "Ready, waiting for build.": "Prêt, en attente de compilation.",
+ "Initializing build...": "Initialisation du processus de compilation...",
+ "Preparing engine...": "Préparation du moteur de compilation...",
+ "Build Successful": "Compilation réussie",
+ "Open output directory or reset workspace.": "Ouvrez le dossier de sortie ou réinitialisez l'espace de travail.",
+ "Build Failed": "Échec de la compilation",
+ "Check log output below for troubleshooting.": "Consultez le journal ci-dessous pour résoudre le problème.",
+ "Status: Ready": " Statut : Prêt",
+ "Status: Parsing {filename}...": " Statut : Analyse de {filename}...",
+ "Status: Loaded {filename}{mode}": " Statut : {filename}{mode} chargé",
+ "Status: Packaging ({engine}) ...": " Statut : Empaquetage en cours ({engine}) ...",
+ "Status: Build Completed": " Statut : Compilation terminée",
+ "Status: Build Failed": " Statut : Échec de la compilation",
+ "Status: Workspace Reset": " Statut : Espace de travail réinitialisé",
+ "Start Build": " Démarrer la compilation",
+ "Stop Build": " Interrompre",
+ "Open Directory": " Ouvrir le dossier",
+ "Rebuild": " Recompiler",
+ "Save & Return": " Enregistrer et revenir",
+ "Build Settings": "Options de compilation",
+ "Preferences": "Préférences",
+ "About": "À propos",
+ "🚀 Engine": "🚀 Moteur",
+ "📦 Dependencies": "📦 Dépendances",
+ "📂 Resources": "📂 Ressources",
+ "⚡ Optimization": "⚡ Optimisation",
+ "🗺️ Package Map": "🗺️ Mappage",
+ "Engine & Environment": "Moteur et environnement de compilation",
+ "Execution Mode": "Mode d'exécution",
+ "Mirrors & Scanner": "Miroirs et analyseur de dépendances",
+ "Additional Resources (Drag & Drop Supported)": "Ressources supplémentaires (Glisser-déposer pris en charge)",
+ "Performance Optimization": "Optimisation des performances et de la compilation",
+ "Lock Core Dependencies": "Verrouiller les versions des dépendances clés",
+ "Package Name Mappings": "Table de mappage des paquets",
+ "UI Language:": "Langue de l'interface :",
+ "App Metadata & Presets": "Métadonnées de l'application et préréglages",
+ "Output Location": "Emplacement de sortie de l'exécutable",
+ "Preferences & System Behavior": "Préférences et comportement système",
+ "Build Engine:": "Moteur de compilation :",
+ "Python Interpreter:": "Interprète Python :",
+ "Output Name:": "Nom de l'exécutable :",
+ "App Icon:": "Icône de l'application :",
+ "Primary PIP Index:": "Index PIP principal :",
+ "Backup PIP Index:": "Index PIP secondaire :",
+ "Requirements File:": "Fichier de dépendances (requirements.txt) :",
+ "Hidden Imports:": "Imports implicites (Hidden Imports) :",
+ "Exclude Modules:": "Exclure les modules (Excludes) :",
+ "CPU Cores:": "Cœurs CPU parallèles :",
+ "UPX Path:": "Chemin de l'exécutable UPX :",
+ "PyInstaller Version:": "Version de PyInstaller :",
+ "Nuitka Version:": "Version de Nuitka :",
+ "Pipreqs Version:": "Version de Pipreqs :",
+ "Version:": "Version :",
+ "Author/Company:": "Auteur/Organisation :",
+ "Description:": "Description :",
+ "Output Rule:": "Règle d'emplacement :",
+ "Target Directory:": "Dossier cible :",
+ "One-File Mode (--onefile)": "Mode fichier unique (--onefile)",
+ "Hide Console (--noconsole)": "Masquer la fenêtre de la console (--noconsole)",
+ "Use Virtual Environment (Recommended)": "Utiliser un environnement virtuel dédié (Recommandé)",
+ "Install requirements.txt": "Installer les dépendances de requirements.txt",
+ "Analyze Dependencies (pipreqs)": "Analyser automatiquement les dépendances (pipreqs)",
+ "Scan Entire Folder": "Analyser l'ensemble du dossier",
+ "Enable UPX Compression": "Activer la compression d'exécutable UPX",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "Mode allégé (Exclure les dépendances dev/test)",
+ "Concise Log Output": "Sortie de journal concise",
+ "Auto-save Build Log": "Sauvegarder automatiquement le journal",
+ "Auto Extract Icon": "Extraire automatiquement l'icône",
+ "Clean Temporary Cache After Build": "Nettoyer le cache temporaire après compilation",
+ "Sound Notification": "Notification sonore de fin",
+ "Browse": "Parcourir...",
+ "AST Scan": "Analyse AST",
+ "Add File": "Ajouter un fichier",
+ "Add Dir": "Ajouter un dossier",
+ "Remove Selected": "Supprimer la sélection",
+ "Clear All": "Tout effacer",
+ "Add Mapping": "Ajouter un mappage",
+ "Restore Defaults": "Restaurer par défaut",
+ "Export Preset...": "Exporter le préréglage...",
+ "Import Preset...": "Importer un préréglage...",
+ "Source File Directory": "Dossier du fichier source",
+ "Custom Directory": "Dossier personnalisé",
+ "Import Name": "Nom d'import dans le code",
+ "PyPI Package Name": "Nom du paquet PyPI",
+ "Reset to Default Config": "Réinitialiser la configuration",
+ "Cancel & Return": "Annuler les modifications et revenir",
+ "Configure Build Settings": "Configurer les paramètres de compilation",
+ "Toggle Execution Log": "Afficher/Masquer le journal d'exécution",
+ "Copy": "Copier",
+ "Select All": "Tout sélectionner",
+ "Clear Log": "Effacer le journal",
+ "Export Log...": "Exporter le journal...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "Un outil GUI multiplateforme pour empaqueter des applications Python basé sur PyInstaller & Nuitka",
+ " [Console]": " [Avec console]",
+ " [No Console]": " [Sans console]",
+ "File": "Fichier",
+ "Directory": "Dossier",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller : Vitesse de compilation rapide et excellente compatibilité. Idéal pour un développement itératif.",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka : Compile en binaire C/C++ natif. Offre de meilleures performances et protège le code source.",
+ "Leave blank to auto-detect system default Python": "Laissez vide pour détecter automatiquement l'interprète Python du système",
+ "Leave blank to auto-match script name": "Laissez vide pour utiliser automatiquement le nom du script source",
+ "Leave blank to auto-search requirements.txt in current directory": "Laissez vide pour rechercher automatiquement requirements.txt dans le dossier actuel",
+ "Comma separated (e.g. pandas, PyQt5)": "Séparés par des virgules (ex : pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "Séparés par des virgules (ex : tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "Laissez vide pour détecter automatiquement depuis la variable PATH",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "Exclut dynamiquement les dépendances inutiles de l'environnement pour réduire la taille et accélérer la compilation.",
+ "Double-click to edit target path; Drag & drop supported": "Double-cliquez pour modifier le chemin relatif. Glisser-déposer pris en charge",
+ "Package mappings have been reset to defaults.": "Le mappage des paquets a été réinitialisé aux valeurs par défaut.",
+ "Config preset exported to: {path}": "Le préréglage de configuration a été exporté vers : {path}",
+ "Config preset imported successfully.": "Le préréglage de configuration a été importé avec succès.",
+ "AST scan completed, found {count} dependencies.": "Analyse AST terminée : {count} dépendances trouvées.",
+ "No log content.": "Aucun journal à exporter.",
+ "Log saved to: {path}": "Journal sauvegardé sous : {path}",
+ "Attention: Please check the log for details.": "Attention : Veuillez consulter le journal pour plus de détails.",
+ "[INFO] Initializing isolated build environment...": "[INFO] Initialisation d'un environnement de compilation isolé...",
+ "[INFO] Python interpreter path: {path}": "[INFO] Chemin de l'interprète Python : {path}",
+ "[INFO] Creating virtual environment...": "[INFO] Création de l'environnement virtuel (venv)...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] Échec de la création de l'environnement virtuel. Des modules nécessaires peuvent manquer ou les permissions sont restreintes.",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] Synchronisation et mise à jour du gestionnaire de paquets pip...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] Installation du moteur de compilation [{pkg}] et des dépendances clés...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Astuce Nuitka : Si le téléchargement du compilateur GCC/MinGW est demandé lors du premier build, assurez-vous d'avoir une connexion réseau stable.",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] Installation des dépendances [1/3] : Installation des dépendances déclarées ({filename})...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] Installation des dépendances [2/3] : Exécution de pipreqs pour l'analyse statique du projet...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] Mode bac à sable activé : Analyse du script uniquement pour éviter toute pollution par d'autres fichiers.",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] Analyse du dossier complet activée : Analyse de tous les fichiers Python du dossier actuel...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] URL du serveur PyPI utilisé pour l'analyse : {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] Interrogation des versions des dépendances en cours, veuillez patienter...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] Basculement vers l'index PIP secondaire : {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] Tentative de nouvelle analyse avec un encodage compatible...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] L'analyse approfondie de pipreqs a été ignorée, les dépendances seront complétées par le moteur AST.",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] Installation des dépendances [3/3] : Extraction des imports implicites via l'analyse statique AST...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] Installation des dépendances implicites détectées : {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ Attention : Échec de l'installation du paquet [{pkg}] ! L'application pourrait planter à l'exécution.",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] Lancement du moteur {engine} pour la compilation des binationaux...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] Environnement MSVC local détecté, utilisation prioritaire du compilateur C++ natif.",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] Mode allégé activé : Appplication des stratégies de réduction de taille...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] Il est fortement recommandé d'activer l'option [Environnement virtuel] pour maximiser l'effet du mode allégé.",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] Application des directives d'optimisation Nuitka (-OO)...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] Compilation principale terminée, extraction et archivage des fichiers générés...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] Échec du transfert du fichier binaire. Le fichier est peut-être verrouillé par un processus ou les autorisations sont insuffisantes : {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] Impossible de localiser l'exécutable généré dans le dossier temporaire : {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] Validation du fichier de sortie et génération de l'exécutable final...",
+ "[INFO] Build log exported to: {path}": "[INFO] Le journal de compilation a été exporté sous : {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] Compilation terminée avec succès. Chemin de sortie : {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[Erreur de syntaxe] Le programme source contient une erreur de syntaxe ou d'indentation !\n - Fichier : {file}\n - Type d'erreur : {type}\n - Ligne : environ {line}\n - Description : {desc}\n\nAstuce : Il s'agit d'une erreur dans votre code source. Assurez-vous qu'il s'exécute correctement en local avant de recompiler.",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [Trace de diagnostic : Journal brut complet suite à une erreur d'exécution] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] Compilation interrompue en raison d'erreurs. Consultez le journal ci-dessus.",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] Libération de l'espace disque et nettoyage de l'environnement temporaire...",
+ "\\nProgram execution completed, press Enter to exit...": "\\nExécution terminée. Appuyez sur Entrée pour quitter...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] Le fichier cible est verrouillé ou chiffré par un service Cloud. Veuillez le déverrouiller et réessayer.",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] Veuillez d'abord charger un fichier source Python valide !",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] Une exception est survenue lors de l'analyse AST : {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] Impossible d'exporter le fichier de préréglage : {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] Format de fichier de préréglage invalide ou corrompu : {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] Impossible d'exporter le journal d'exécution : {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] La compilation s'est terminée, mais l'installation préalable des dépendances suivantes a échoué :\n\n 👉 {pkgs}\n\n⚠️ Attention : L'application pourrait planter au lancement en raison de modules manquants !"
+}
\ No newline at end of file
diff --git a/qpypack/locales/ja_JP.json b/qpypack/locales/ja_JP.json
new file mode 100644
index 0000000..9bc40d8
--- /dev/null
+++ b/qpypack/locales/ja_JP.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "Python スクリプト (.py/.pyw) をここにドラッグ&ドロップ\nまたはクリックして参照...",
+ "Auto-parse dependencies, resources, and implicit imports": "依存関係、リソース、および暗黙のインポートを自動解析",
+ "Loaded: {filename}": "読み込み完了: {filename}",
+ "Parsing metadata...": "メタデータを解析中...",
+ "Ready, waiting for build.": "準備完了。ビルド待機中。",
+ "Initializing build...": "ビルドプロセスを初期化中...",
+ "Preparing engine...": "ビルドエンジンを準備中...",
+ "Build Successful": "ビルドが正常に完了しました",
+ "Open output directory or reset workspace.": "出力ディレクトリを開くか、ワークスペースをリセットしてください。",
+ "Build Failed": "ビルドに失敗しました",
+ "Check log output below for troubleshooting.": "詳細については、以下のビルドログを確認してください。",
+ "Status: Ready": " ステータス: 準備完了",
+ "Status: Parsing {filename}...": " ステータス: {filename} を解析中...",
+ "Status: Loaded {filename}{mode}": " ステータス: {filename}{mode} を読み込みました",
+ "Status: Packaging ({engine}) ...": " ステータス: パッケージング中 ({engine}) ...",
+ "Status: Build Completed": " ステータス: ビルド完了",
+ "Status: Build Failed": " ステータス: ビルド失敗",
+ "Status: Workspace Reset": " ステータス: ワークスペースがリセットされました",
+ "Start Build": " ビルド開始",
+ "Stop Build": " ビルド中止",
+ "Open Directory": " 出力先を開く",
+ "Rebuild": " 再ビルド",
+ "Save & Return": " 保存して戻る",
+ "Build Settings": "ビルド設定",
+ "Preferences": "環境設定",
+ "About": "バージョン情報",
+ "🚀 Engine": "🚀 エンジン",
+ "📦 Dependencies": "📦 依存関係",
+ "📂 Resources": "📂 リソース",
+ "⚡ Optimization": "⚡ 最適化",
+ "🗺️ Package Map": "🗺️ パッケージマップ",
+ "Engine & Environment": "ビルドエンジンと環境",
+ "Execution Mode": "実行モード",
+ "Mirrors & Scanner": "ミラーソースとスキャナー設定",
+ "Additional Resources (Drag & Drop Supported)": "追加リソース (ドラッグ&ドロップ対応)",
+ "Performance Optimization": "パフォーマンスおよびコンパイル最適化",
+ "Lock Core Dependencies": "核心依存関係のバージョン固定",
+ "Package Name Mappings": "パッケージ名マッピング表",
+ "UI Language:": "表示言語:",
+ "App Metadata & Presets": "アプリのメタデータとプリセット",
+ "Output Location": "ビルド生成物の出力先",
+ "Preferences & System Behavior": "動作設定およびシステム動作",
+ "Build Engine:": "ビルドエンジン:",
+ "Python Interpreter:": "Python インタープリター:",
+ "Output Name:": "出力アプリケーション名:",
+ "App Icon:": "アプリケーションアイコン:",
+ "Primary PIP Index:": "メイン PIP インデックス:",
+ "Backup PIP Index:": "サブ PIP インデックス:",
+ "Requirements File:": "依存関係ファイル (requirements.txt):",
+ "Hidden Imports:": "暗黙のインポート (Hidden Imports):",
+ "Exclude Modules:": "除外モジュール (Excludes):",
+ "CPU Cores:": "ビルド並列 CPU コア数:",
+ "UPX Path:": "UPX 実行ファイルパス:",
+ "PyInstaller Version:": "PyInstaller バージョン:",
+ "Nuitka Version:": "Nuitka バージョン:",
+ "Pipreqs Version:": "Pipreqs バージョン:",
+ "Version:": "バージョン:",
+ "Author/Company:": "作者/組織名:",
+ "Description:": "詳細説明:",
+ "Output Rule:": "出力先ルール:",
+ "Target Directory:": "保存先ディレクトリ:",
+ "One-File Mode (--onefile)": "単一ファイルモード (--onefile)",
+ "Hide Console (--noconsole)": "コンソールウィンドウを非表示 (--noconsole)",
+ "Use Virtual Environment (Recommended)": "独立した仮想環境を使用 (推奨)",
+ "Install requirements.txt": "requirements.txt の依存関係をインストール",
+ "Analyze Dependencies (pipreqs)": "プロジェクトの依存関係を自動解析 (pipreqs)",
+ "Scan Entire Folder": "フォルダ全体をスキャン対象にする",
+ "Enable UPX Compression": "UPX 圧縮を有効化",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "軽量化モード (開発・テスト用依存関係を除外)",
+ "Concise Log Output": "ログ出力を簡略化",
+ "Auto-save Build Log": "ビルドログを自動保存",
+ "Auto Extract Icon": "アイコンを自動抽出",
+ "Clean Temporary Cache After Build": "ビルド完了後に一時キャッシュを削除",
+ "Sound Notification": "完了通知音を有効化",
+ "Browse": "参照...",
+ "AST Scan": "AST 構文解析",
+ "Add File": "ファイルを追加",
+ "Add Dir": "ディレクトリを追加",
+ "Remove Selected": "選択項目を削除",
+ "Clear All": "すべてクリア",
+ "Add Mapping": "マッピングを追加",
+ "Restore Defaults": "デフォルトに戻す",
+ "Export Preset...": "プリセットのエクスポート...",
+ "Import Preset...": "プリセットのインポート...",
+ "Source File Directory": "ソースファイルと同じディレクトリ",
+ "Custom Directory": "カスタムディレクトリ",
+ "Import Name": "コード内の import 名",
+ "PyPI Package Name": "PyPI パッケージ名",
+ "Reset to Default Config": "デフォルト設定にリセット",
+ "Cancel & Return": "変更を破棄して戻る",
+ "Configure Build Settings": "ビルドパラメータの構成",
+ "Toggle Execution Log": "実行ログの表示/非表示",
+ "Copy": "コピー",
+ "Select All": "すべて選択",
+ "Clear Log": "ログを消去",
+ "Export Log...": "ログを保存...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "PyInstaller および Nuitka をベースにしたクロスプラットフォーム Python パッケージング GUI ツール",
+ " [Console]": " [コンソールあり]",
+ " [No Console]": " [コンソールなし]",
+ "File": "ファイル",
+ "Directory": "ディレクトリ",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: ビルド速度が速く、互換性に優れています。迅速な反復開発や一般的なアプリケーションに最適です。",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: ネイティブ C/C++ バイナリへコンパイルします。実行パフォーマンスが向上し、ソースコードの保護が可能です。",
+ "Leave blank to auto-detect system default Python": "空欄の場合、システムデフォルトの Python を自動検出します",
+ "Leave blank to auto-match script name": "空欄の場合、スクリプト名を自動設定します",
+ "Leave blank to auto-search requirements.txt in current directory": "空欄の場合、現在のディレクトリから requirements.txt を自動検索します",
+ "Comma separated (e.g. pandas, PyQt5)": "カンマ区切りで入力 (例: pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "カンマ区切りで入力 (例: tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "空欄の場合、PATH 環境変数から自動検出します",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "ビルド環境から無駄な依存関係を動的に除外し、ビルド速度の向上とファイルサイズの削減を実現します。",
+ "Double-click to edit target path; Drag & drop supported": "ダブルクリックで展開先パスを編集。ドラッグ&ドロップによる追加に対応",
+ "Package mappings have been reset to defaults.": "パッケージマッピングが初期設定にリセットされました。",
+ "Config preset exported to: {path}": "設定プリセットを出力しました: {path}",
+ "Config preset imported successfully.": "設定プリセットを正常に読み込みました。",
+ "AST scan completed, found {count} dependencies.": "AST スキャンが完了しました。{count} 個の依存関係を検出しました。",
+ "No log content.": "出力可能なログが存在しません。",
+ "Log saved to: {path}": "ログを正常に保存しました: {path}",
+ "Attention: Please check the log for details.": "注意: 詳細についてはログ出力を確認してください。",
+ "[INFO] Initializing isolated build environment...": "[INFO] 隔離されたビルド環境を初期化中...",
+ "[INFO] Python interpreter path: {path}": "[INFO] 使用する Python インタープリター: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] 仮想環境 (venv) を作成中...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] 仮想環境の作成に失敗しました。Python 環境に必要なモジュールが不足しているか、実行権限が制限されている可能性があります。",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] pip パッケージマネージャーを同期・更新中...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] ビルドエンジン [{pkg}] およびコア依存関係をインストール中...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Nuitka ヒント: 初回ビルド時に GCC/MinGW コンパイラのダウンロードが求められた場合は、安定したネットワーク接続を維持してください。",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] 依存関係のインストール [1/3]: 宣言された依存関係 ({filename}) をインストール中...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] 依存関係のインストール [2/3]: pipreqs を呼び出してプロジェクトの依存関係を静的解析中...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] 単一ファイルサンドボックスモード有効: 他のファイルによる汚染を防ぐため、対象スクリプトのみを解析します。",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] 全ディレクトリ解析モード有効: カレントディレクトリ内のすべての Python ファイルをスキャン中...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] 依存関係解析用の PyPI サーバー: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] パッケージのバージョン情報を照会中。お待ちください...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] バックアップの PyPI インデックスに切り替えて再試行中: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] 互換性のある文字コードを使用してスキャンを再試行中...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] pipreqs による詳細スキャンをスキップしました。不足分は AST スキャンエンジンによって補完されます。",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] 依存関係のインストール [3/3]: AST 静的解析により暗黙の依存関係を抽出中...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] 暗黙的にインポートされている依存関係をインストール中: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ 警告: 依存パッケージ [{pkg}] のインストールに失敗しました。実行時にクラッシュする可能性があります。",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] {engine} エンジンを起動し、バイナリファイルのコンパイルを開始中...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] ローカルの MSVC 環境を検出しました。ネイティブ C++ コンパイラを優先使用します。",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] 軽量化モード有効: バイナリサイズの削減戦略を実行中...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] 軽量化効果を最大化するために、「仮想環境の使用」を有効にすることを強く推奨します。",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] Nuitka 最適化フラグ (-OO) を適用しました...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] コンパイルコアが完了しました。生成物を抽出およびアーカイブ中...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] 成果物の移動に失敗しました。ファイルが別のプロセスに使用されているか、権限が不足しています: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] 一時ビルドディレクトリ内に有効な実行可能ファイルが見つかりません: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] 出力ファイルを検証し、最終成果物を生成中...",
+ "[INFO] Build log exported to: {path}": "[INFO] ビルドログを出力しました: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] コンパイルが正常に完了しました。出力先: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[構文エラー] ソースコードに構文エラーまたはインデントエラーが存在します!\n - 対象ファイル: {file}\n - エラー種別: {type}\n - 発生位置: {line} 行目付近\n - 詳細: {desc}\n\nヒント: これはソースコード自体のエラーです。ローカルで正常に動作することを確認してから再実行してください。",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [診断トレースバック: 異常発生手順の完全な生ログ] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] 例外が発生したためコンパイルが中断されました。ログを確認してください。",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] ディスク容量を解放し、一時ビルド環境をクリーンアップ中...",
+ "\\nProgram execution completed, press Enter to exit...": "\\n処理が完了しました。Enter キーを押して終了します...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] 対象ファイルがクラウドストレージ等によりロックまたは暗号化されています。解除してから再試行してください。",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] 有効な Python ソースコードファイルを先に読み込んでください。",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] AST 構文解析中に例外が発生しました: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] プリセットファイルのエクスポートに失敗しました: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] プリセットファイルのフォーマットが不正か、破損しています: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] ログファイルのエクスポートに失敗しました: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] ビルドは完了しましたが、以下の依存パッケージの事前インストールに失敗しました:\n\n 👉 {pkgs}\n\n⚠️ 注意: 必要なモジュールが不足しているため、実行時に ModuleNotFoundError でクラッシュする可能性があります。"
+}
\ No newline at end of file
diff --git a/qpypack/locales/ko_KR.json b/qpypack/locales/ko_KR.json
new file mode 100644
index 0000000..44d05cd
--- /dev/null
+++ b/qpypack/locales/ko_KR.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "Python 스크립트 (.py/.pyw) 파일을 이곳에 끌어다 놓거나\n클릭하여 찾아보기...",
+ "Auto-parse dependencies, resources, and implicit imports": "의존성 패키지, 리소스 및 암시적 임포트 자동 분석",
+ "Loaded: {filename}": "로드 완료: {filename}",
+ "Parsing metadata...": "메타데이터 분석 중...",
+ "Ready, waiting for build.": "준비 완료. 빌드 대기 중.",
+ "Initializing build...": "빌드 프로세스 초기화 중...",
+ "Preparing engine...": "빌드 엔진 준비 중...",
+ "Build Successful": "빌드 성공",
+ "Open output directory or reset workspace.": "출력 디렉터리를 열거나 작업 영역을 리셋하세요.",
+ "Build Failed": "빌드 실패",
+ "Check log output below for troubleshooting.": "문제 해결을 위해 아래의 빌드 로그 출력을 확인하세요.",
+ "Status: Ready": " 상태: 준비 완료",
+ "Status: Parsing {filename}...": " 상태: {filename} 분석 중...",
+ "Status: Loaded {filename}{mode}": " 상태: {filename}{mode} 로드됨",
+ "Status: Packaging ({engine}) ...": " 상태: 패키징 진행 중 ({engine}) ...",
+ "Status: Build Completed": " 상태: 빌드 완료",
+ "Status: Build Failed": " 상태: 빌드 실패",
+ "Status: Workspace Reset": " 상태: 작업 영역 리셋됨",
+ "Start Build": " 빌드 시작",
+ "Stop Build": " 빌드 중단",
+ "Open Directory": " 출력 폴더 열기",
+ "Rebuild": " 다시 빌드",
+ "Save & Return": " 저장 후 돌아가기",
+ "Build Settings": "빌드 설정",
+ "Preferences": "환경 설정",
+ "About": "정보",
+ "🚀 Engine": "🚀 엔진",
+ "📦 Dependencies": "📦 의존성",
+ "📂 Resources": "📂 리소스",
+ "⚡ Optimization": "⚡ 최적화",
+ "🗺️ Package Map": "🗺️ 패키지 매핑",
+ "Engine & Environment": "빌드 엔진 및 환경",
+ "Execution Mode": "실행 모드",
+ "Mirrors & Scanner": "미러 서버 및 스캐너 설정",
+ "Additional Resources (Drag & Drop Supported)": "추가 리소스 (드래그 앤 드롭 지원)",
+ "Performance Optimization": "성능 및 컴파일 최적화",
+ "Lock Core Dependencies": "핵심 의존성 버전 고정",
+ "Package Name Mappings": "패키지 이름 매핑 테이블",
+ "UI Language:": "표시 언어:",
+ "App Metadata & Presets": "앱 메타데이터 및 프리셋",
+ "Output Location": "빌드 결과물 출력 위치",
+ "Preferences & System Behavior": "환경 설정 및 시스템 동작",
+ "Build Engine:": "빌드 엔진:",
+ "Python Interpreter:": "Python 인터프리터:",
+ "Output Name:": "출력 파일 이름:",
+ "App Icon:": "애플리케이션 아이콘:",
+ "Primary PIP Index:": "기본 PIP 인덱스:",
+ "Backup PIP Index:": "보조 PIP 인덱스:",
+ "Requirements File:": "의존성 목록 파일 (requirements.txt):",
+ "Hidden Imports:": "암시적 임포트 (Hidden Imports):",
+ "Exclude Modules:": "제외할 모듈 (Excludes):",
+ "CPU Cores:": "병렬 컴파일 CPU 코어 수:",
+ "UPX Path:": "UPX 실행 파일 경로:",
+ "PyInstaller Version:": "PyInstaller 버전:",
+ "Nuitka Version:": "Nuitka 버전:",
+ "Pipreqs Version:": "Pipreqs 버전:",
+ "Version:": "버전:",
+ "Author/Company:": "작성자/회사명:",
+ "Description:": "설명:",
+ "Output Rule:": "출력 규칙:",
+ "Target Directory:": "대상 디렉터리:",
+ "One-File Mode (--onefile)": "단일 파일 모드 (--onefile)",
+ "Hide Console (--noconsole)": "콘솔 창 숨기기 (--noconsole)",
+ "Use Virtual Environment (Recommended)": "독립된 가상 환경 사용 (권장)",
+ "Install requirements.txt": "requirements.txt 의존성 설치",
+ "Analyze Dependencies (pipreqs)": "프로젝트 의존성 자동 분석 (pipreqs)",
+ "Scan Entire Folder": "디렉터리 전체 스캔",
+ "Enable UPX Compression": "UPX 실행 파일 압축 활성화",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "경량화 모드 (개발/테스트 의존성 제외)",
+ "Concise Log Output": "간소화된 로그 출력",
+ "Auto-save Build Log": "빌드 로그 자동 저장",
+ "Auto Extract Icon": "아이콘 자동 추출",
+ "Clean Temporary Cache After Build": "빌드 완료 후 임시 캐시 삭제",
+ "Sound Notification": "빌드 완료 알림음",
+ "Browse": "찾아보기...",
+ "AST Scan": "AST 구문 분석",
+ "Add File": "파일 추가",
+ "Add Dir": "디렉터리 추가",
+ "Remove Selected": "선택 항목 삭제",
+ "Clear All": "전체 비우기",
+ "Add Mapping": "매핑 추가",
+ "Restore Defaults": "기본값 복원",
+ "Export Preset...": "프리셋 내보내기...",
+ "Import Preset...": "프리셋 가져오기...",
+ "Source File Directory": "소스 파일과 동일한 디렉터리",
+ "Custom Directory": "사용자 지정 디렉터리",
+ "Import Name": "코드 내 import 이름",
+ "PyPI Package Name": "PyPI 패키지 이름",
+ "Reset to Default Config": "기본 설정으로 리셋",
+ "Cancel & Return": "변경 사항 취소 후 돌아가기",
+ "Configure Build Settings": "빌드 파라미터 구성",
+ "Toggle Execution Log": "실행 로그 표시/숨기기",
+ "Copy": "복사",
+ "Select All": "전체 선택",
+ "Clear Log": "로그 지우기",
+ "Export Log...": "로그 내보내기...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "PyInstaller 및 Nuitka 기반의 크로스 플랫폼 Python 애플리케이션 패키징 GUI 도구",
+ " [Console]": " [콘솔 모드]",
+ " [No Console]": " [콘솔 없음]",
+ "File": "파일",
+ "Directory": "디렉터리",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: 빌드 속도가 빠르고 호환성이 뛰어납니다. 빠른 반복 개발 및 일반 애플리케이션에 적합합니다.",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: 네이티브 C/C++ 바이너리로 컴파일합니다. 더 뛰어난 실행 성능과 소스 코드 보안을 제공합니다.",
+ "Leave blank to auto-detect system default Python": "비워 두면 시스템 기본 Python 인터프리터를 자동 감지합니다",
+ "Leave blank to auto-match script name": "비워 두면 스크립트 이름을 자동으로 사용합니다",
+ "Leave blank to auto-search requirements.txt in current directory": "비워 두면 현재 디렉터리의 requirements.txt를 자동 검색합니다",
+ "Comma separated (e.g. pandas, PyQt5)": "쉼표로 구분하여 입력 (예: pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "쉼표로 구분하여 입력 (예: tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "비워 두면 PATH 환경 변수에서 자동 감지합니다",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "빌드 환경에서 불필요한 의존성을 동적으로 제외하여 빌드 속도를 높이고 결과물 용량을 줄입니다.",
+ "Double-click to edit target path; Drag & drop supported": "더블 클릭하여 배포 경로 수정. 드래그 앤 드롭 추가 지원",
+ "Package mappings have been reset to defaults.": "패키지 매핑이 기본값으로 리셋되었습니다.",
+ "Config preset exported to: {path}": "설정 프리셋을 내보냈습니다: {path}",
+ "Config preset imported successfully.": "설정 프리셋을 성공적으로 가져왔습니다.",
+ "AST scan completed, found {count} dependencies.": "AST 스캔 완료. 총 {count}개의 의존성을 발견했습니다.",
+ "No log content.": "내보낼 로그 내용이 없습니다.",
+ "Log saved to: {path}": "로그를 성공적으로 저장했습니다: {path}",
+ "Attention: Please check the log for details.": "주의: 자세한 내용은 로그 출력을 확인하세요.",
+ "[INFO] Initializing isolated build environment...": "[INFO] 격리된 빌드 환경 초기화 중...",
+ "[INFO] Python interpreter path: {path}": "[INFO] 사용할 Python 인터프리터 경로: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] 가상 환경 (venv) 생성 중...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] 가상 환경 생성에 실패했습니다. 현재 Python 환경에 필수 모듈이 누락되었거나 실행 권한이 제한되었을 수 있습니다.",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] pip 패키지 관리자 동기화 및 업그레이드 중...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] 빌드 엔진 [{pkg}] 및 핵심 컴파일 의존성 설치 중...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Nuitka 팁: 첫 빌드 시 GCC/MinGW 컴파일러 다운로드 요청이 나타나면 안정적인 네트워크 연결을 유지하세요.",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] 의존성 설치 [1/3]: 명시된 의존성 목록 ({filename}) 설치 중...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] 의존성 설치 [2/3]: pipreqs를 호출하여 프로젝트 의존성 정적 분석 중...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] 단일 파일 샌드박스 모드 활성화: 다른 파일에 의한 오염을 방지하기 위해 대상 스크립트만 분석합니다.",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] 전체 디렉터리 스캔 모드 활성화: 현재 폴더의 모든 Python 파일을 분석 중...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] 의존성 분석용 PyPI 서버 주소: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] 패키지 버전 정보를 조회 중입니다. 잠시만 기다려 주세요...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] 보조 PyPI 인덱스로 전환하여 재시도 중: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] 호환 인코딩을 사용하여 스캔 재시도 중...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] pipreqs 정밀 스캔을 건너뛰었습니다. 부족한 의존성은 AST 스캔 엔진이 보완합니다.",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] 의존성 설치 [3/3]: AST 정적 분석을 통해 암시적 의존성 추출 중...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] 암시적으로 임포트된 의존성 패키지 설치 중: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ 경고: 의존성 패키지 [{pkg}] 설치 실패! 실행 시 프로그램이 종료될 수 있습니다.",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] {engine} 엔진을 시작하여 바이너리 컴파일을 진행합니다...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] 로컬 MSVC 환경이 감지되었습니다. 네이티브 C++ 컴파일러를 우선 사용합니다.",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] 경량화 모드 활성화: 바이너리 용량 최적화 전략 실행 중...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] 경량화 효과를 극대화하려면 '가상 환경 사용' 옵션을 함께 활성화하는 것을 강력히 권장합니다.",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] Nuitka 최적화 플래그 (-OO) 적용 완료...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] 핵심 컴파일 완료. 결과물 추출 및 아카이빙 중...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] 결과물 이동 실패. 파일이 다른 프로세스에 의해 사용 중이거나 권한이 부족합니다: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] 임시 빌드 디렉터리에서 유효한 실행 파일을 찾을 수 없습니다: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] 출력 파일을 검증하고 최종 결과물을 생성 중...",
+ "[INFO] Build log exported to: {path}": "[INFO] 빌드 로그를 내보냈습니다: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] 컴파일이 성공적으로 완료되었습니다. 출력 경로: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[구문 오류] 소스 코드에 문법 또는 들여쓰기 오류가 존재합니다!\n - 대상 파일: {file}\n - 오류 유형: {type}\n - 발생 위치: 약 {line}번째 줄\n - 상세 내용: {desc}\n\n팁: 이는 소스 코드 자체의 오류입니다. 로컬에서 정상 실행되는지 확인 후 다시 시도하세요.",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [진단 트레이스백: 해당 단계의 진단용 원본 로그] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] 예외가 발생하여 컴파일이 중단되었습니다. 로그를 참조하세요.",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] 디스크 공간 확보를 위해 임시 빌드 환경을 정리하는 중...",
+ "\\nProgram execution completed, press Enter to exit...": "\\n실행이 완료되었습니다. 종료하려면 Enter 키를 누르세요...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] 대상 파일이 클라우드 드라이브 등에 의해 잠겨 있거나 암호화되어 있습니다. 잠금 해제 후 다시 시도하세요.",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] 먼저 유효한 Python 소스 코드 파일을 로드하세요.",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] AST 구문 분석 중 예외가 발생했습니다: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] 프리셋 내보내기에 실패했습니다: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] 프리셋 파일 형식이 올바르지 않거나 손상되었습니다: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] 로그 파일 내보내기에 실패했습니다: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] 빌드는 완료되었으나 다음 사전 의존성 설치에 실패했습니다:\n\n 👉 {pkgs}\n\n⚠️ 주의: 필수 모듈 누락으로 인해 실행 시 ModuleNotFoundError가 발생할 수 있습니다."
+}
\ No newline at end of file
diff --git a/qpypack/locales/pt_BR.json b/qpypack/locales/pt_BR.json
new file mode 100644
index 0000000..9274f26
--- /dev/null
+++ b/qpypack/locales/pt_BR.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "Arraste e solte o script Python (.py/.pyw) aqui\nou clique para navegar...",
+ "Auto-parse dependencies, resources, and implicit imports": "Análise automática de dependências, recursos e importações implícitas",
+ "Loaded: {filename}": "Carregado: {filename}",
+ "Parsing metadata...": "Analisando metadados...",
+ "Ready, waiting for build.": "Pronto, aguardando compilação.",
+ "Initializing build...": "Inicializando processo de compilação...",
+ "Preparing engine...": "Preparando o motor de compilação...",
+ "Build Successful": "Compilação concluída com sucesso",
+ "Open output directory or reset workspace.": "Abra o diretório de saída ou redefina o espaço de trabalho.",
+ "Build Failed": "Falha na compilação",
+ "Check log output below for troubleshooting.": "Verifique o log abaixo para solucionar o problema.",
+ "Status: Ready": " Status: Pronto",
+ "Status: Parsing {filename}...": " Status: Analisando {filename}...",
+ "Status: Loaded {filename}{mode}": " Status: {filename}{mode} carregado",
+ "Status: Packaging ({engine}) ...": " Status: Empacotando ({engine}) ...",
+ "Status: Build Completed": " Status: Compilação concluída",
+ "Status: Build Failed": " Status: Falha na compilação",
+ "Status: Workspace Reset": " Status: Espaço de trabalho redefinido",
+ "Start Build": " Iniciar Compilação",
+ "Stop Build": " Interromper",
+ "Open Directory": " Abrir Pasta",
+ "Rebuild": " Recompilar",
+ "Save & Return": " Salvar e Voltar",
+ "Build Settings": "Configurações de Build",
+ "Preferences": "Preferências",
+ "About": "Sobre",
+ "🚀 Engine": "🚀 Motor",
+ "📦 Dependencies": "📦 Dependências",
+ "📂 Resources": "📂 Recursos",
+ "⚡ Optimization": "⚡ Otimização",
+ "🗺️ Package Map": "🗺️ Mapeamento",
+ "Engine & Environment": "Motor de compilação e ambiente",
+ "Execution Mode": "Modo de execução",
+ "Mirrors & Scanner": "Servidores espelho e analisador",
+ "Additional Resources (Drag & Drop Supported)": "Recursos adicionais (Suporta arrastar e soltar)",
+ "Performance Optimization": "Otimização de desempenho e compilação",
+ "Lock Core Dependencies": "Fixar versões das dependências principais",
+ "Package Name Mappings": "Tabela de mapeamento de nomes de pacotes",
+ "UI Language:": "Idioma da interface:",
+ "App Metadata & Presets": "Metadados do aplicativo e predefinições",
+ "Output Location": "Local de saída do executável",
+ "Preferences & System Behavior": "Preferências e comportamento do sistema",
+ "Build Engine:": "Motor de compilação:",
+ "Python Interpreter:": "Intérprete Python:",
+ "Output Name:": "Nome do arquivo final:",
+ "App Icon:": "Ícone do aplicativo:",
+ "Primary PIP Index:": "Servidor PIP principal:",
+ "Backup PIP Index:": "Servidor PIP secundário:",
+ "Requirements File:": "Arquivo de dependências (requirements.txt):",
+ "Hidden Imports:": "Importações implícitas (Hidden Imports):",
+ "Exclude Modules:": "Excluir módulos (Excludes):",
+ "CPU Cores:": "Núcleos de CPU em paralelo:",
+ "UPX Path:": "Caminho do executável UPX:",
+ "PyInstaller Version:": "Versão do PyInstaller:",
+ "Nuitka Version:": "Versão do Nuitka:",
+ "Pipreqs Version:": "Versão do Pipreqs:",
+ "Version:": "Versão:",
+ "Author/Company:": "Autor/Organização:",
+ "Description:": "Descrição:",
+ "Output Rule:": "Regra de local de saída:",
+ "Target Directory:": "Diretório de destino:",
+ "One-File Mode (--onefile)": "Modo de arquivo único (--onefile)",
+ "Hide Console (--noconsole)": "Ocultar janela do console (--noconsole)",
+ "Use Virtual Environment (Recommended)": "Usar ambiente virtual isolado (Recomendado)",
+ "Install requirements.txt": "Instalar dependências do requirements.txt",
+ "Analyze Dependencies (pipreqs)": "Analisar dependências automaticamente (pipreqs)",
+ "Scan Entire Folder": "Escanear a pasta inteira",
+ "Enable UPX Compression": "Ativar compressão UPX",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "Modo leve (Excluir dependências dev/test)",
+ "Concise Log Output": "Saída de log concisa",
+ "Auto-save Build Log": "Salvar log de compilação automaticamente",
+ "Auto Extract Icon": "Extrair ícone automaticamente",
+ "Clean Temporary Cache After Build": "Limpar cache temporário após a compilação",
+ "Sound Notification": "Notificação sonora ao concluir",
+ "Browse": "Navegar...",
+ "AST Scan": "Análise AST",
+ "Add File": "Adicionar Arquivo",
+ "Add Dir": "Adicionar Pasta",
+ "Remove Selected": "Remover Selecionado",
+ "Clear All": "Limpar Tudo",
+ "Add Mapping": "Adicionar Mapeamento",
+ "Restore Defaults": "Restaurar Padrões",
+ "Export Preset...": "Exportar Predefinição...",
+ "Import Preset...": "Importar Predefinição...",
+ "Source File Directory": "Diretório do arquivo fonte",
+ "Custom Directory": "Diretório personalizado",
+ "Import Name": "Nome de importação no código",
+ "PyPI Package Name": "Nome do pacote no PyPI",
+ "Reset to Default Config": "Restaurar configurações padrão",
+ "Cancel & Return": "Descartar alterações e voltar",
+ "Configure Build Settings": "Configurar parâmetros de compilação",
+ "Toggle Execution Log": "Exibir/Ocultar log de execução",
+ "Copy": "Copiar",
+ "Select All": "Selecionar Tudo",
+ "Clear Log": "Limpar Log",
+ "Export Log...": "Exportar Log...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "Uma ferramenta GUI multiplataforma para empacotar aplicações Python baseada no PyInstaller & Nuitka",
+ " [Console]": " [Com console]",
+ " [No Console]": " [Sem console]",
+ "File": "Arquivo",
+ "Directory": "Diretório",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: Compilação rápida e excelente compatibilidade. Ideal para desenvolvimento iterativo.",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: Compila para binário C/C++ nativo. Melhora o desempenho e protege o código-fonte.",
+ "Leave blank to auto-detect system default Python": "Deixe em branco para detectar automaticamente o Python padrão do sistema",
+ "Leave blank to auto-match script name": "Deixe em branco para usar automaticamente o nome do script",
+ "Leave blank to auto-search requirements.txt in current directory": "Deixe em branco para buscar automaticamente o requirements.txt na pasta atual",
+ "Comma separated (e.g. pandas, PyQt5)": "Separados por vírgula (ex: pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "Separados por vírgula (ex: tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "Deixe em branco para detectar automaticamente pelas variáveis de ambiente",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "Exclui dependências desnecessárias para reduzir o tamanho final e acelerar a compilação.",
+ "Double-click to edit target path; Drag & drop supported": "Clique duplo para editar o caminho. Suporta arrastar e soltar",
+ "Package mappings have been reset to defaults.": "O mapeamento de pacotes foi redefinido para os padrões.",
+ "Config preset exported to: {path}": "Predefinição exportada para: {path}",
+ "Config preset imported successfully.": "Predefinição importada com sucesso.",
+ "AST scan completed, found {count} dependencies.": "Análise AST concluída: {count} dependências encontradas.",
+ "No log content.": "Não há conteúdo de log para exportar.",
+ "Log saved to: {path}": "Log salvo em: {path}",
+ "Attention: Please check the log for details.": "Atenção: Por favor, verifique o log para mais detalhes.",
+ "[INFO] Initializing isolated build environment...": "[INFO] Inicializando ambiente de compilação isolado...",
+ "[INFO] Python interpreter path: {path}": "[INFO] Caminho do intérprete Python: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] Criando ambiente virtual (venv)...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] Falha ao criar o ambiente virtual. O Python do sistema pode estar sem módulos essenciais ou com permissões restritas.",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] Sincronizando e atualizando o gerenciador de pacotes pip...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] Instalando motor de compilação [{pkg}] e dependências principais...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Dica do Nuitka: Se for solicitado o download do compilador GCC/MinGW, mantenha uma conexão de rede estável.",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] Instalação de dependências [1/3]: Instalando dependências declaradas ({filename})...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] Instalação de dependências [2/3]: Executando pipreqs para análise estática do projeto...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] Modo sandbox ativado: Analisando apenas o script principal para evitar interferência de outros arquivos.",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] Escaneamento completo ativado: Analisando todos os arquivos Python da pasta...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] Servidor PyPI utilizado para análise: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] Consultando versões dos pacotes, por favor aguarde...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] Alternando para o servidor PIP secundário: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] Tentando escanear novamente com codificação compatível...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] A análise profunda do pipreqs foi ignorada. As dependências serão completadas via AST.",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] Instalação de dependências [3/3]: Extraindo importações implícitas via análise estática AST...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] Instalando dependências implícitas detectadas: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ Aviso: Falha ao instalar o pacote [{pkg}]! A aplicação pode falhar na execução.",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] Iniciando motor {engine} para compilação binária...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] Ambiente MSVC local detectado. Priorizando o compilador C++ nativo.",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] Modo leve ativado: Aplicando estratégias para redução de tamanho...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] É altamente recomendável ativar 'Usar ambiente virtual' para maximizar o efeito do modo leve.",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] Aplicando diretivas de otimização do Nuitka (-OO)...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] Compilação principal concluída. Extraindo e arquivando executáveis...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] Falha ao mover o executável final. O arquivo pode estar em uso ou faltam permissões: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] Não foi possível localizar um executável válido na pasta temporária: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] Validando arquivos e gerando o produto final...",
+ "[INFO] Build log exported to: {path}": "[INFO] Log de compilação exportado para: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] Compilação concluída com sucesso. Local de saída: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[Erro de sintaxe] O código fonte contém um erro de sintaxis ou indentação!\n - Arquivo: {file}\n - Tipo de erro: {type}\n - Linha: próximo à linha {line}\n - Detalhes: {desc}\n\nDica: Este é um erro no seu código fonte. Certifique-se de que ele execute localmente antes de compilar.",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [Rastreamento de diagnóstico: Log completo devido à exceção de execução] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] Compilação interrompida devido a erros. Verifique o log.",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] Liberando espaço em disco e limpando arquivos temporários...",
+ "\\nProgram execution completed, press Enter to exit...": "\\nExecução concluída. Pressione Enter para sair...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] O arquivo está bloqueado ou criptografado por um serviço de nuvem. Desbloqueie-o e tente novamente.",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] Por favor, carregue primeiro um arquivo de código fonte Python válido!",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] Ocorreu uma exceção durante a análise AST: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] Não foi possível exportar o arquivo de predefinição: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] O arquivo de predefinição está corrompido ou em formato inválido: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] Não foi possível exportar o arquivo de log: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] A compilação foi concluída, mas falhou a instalação prévia das seguintes dependências:\n\n 👉 {pkgs}\n\n⚠️ Aviso: O programa pode falhar na execução devido à falta de módulos."
+}
\ No newline at end of file
diff --git a/qpypack/locales/ru_RU.json b/qpypack/locales/ru_RU.json
new file mode 100644
index 0000000..65c1add
--- /dev/null
+++ b/qpypack/locales/ru_RU.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "Перетащите Python-скрипт (.py/.pyw) сюда\nили нажмите для обзора...",
+ "Auto-parse dependencies, resources, and implicit imports": "Автоматический анализ зависимостей, ресурсов и неявных импортов",
+ "Loaded: {filename}": "Загружено: {filename}",
+ "Parsing metadata...": "Анализ метаданных...",
+ "Ready, waiting for build.": "Готово, ожидание сборки.",
+ "Initializing build...": "Инициализация процесса сборки...",
+ "Preparing engine...": "Подготовка движка сборки...",
+ "Build Successful": "Сборка успешно завершена",
+ "Open output directory or reset workspace.": "Откройте целевую папку или сбросьте рабочую область.",
+ "Build Failed": "Сборка не удалась",
+ "Check log output below for troubleshooting.": "Ознакомьтесь с журналом ниже для устранения неполадок.",
+ "Status: Ready": " Статус: Готово",
+ "Status: Parsing {filename}...": " Статус: Анализ {filename}...",
+ "Status: Loaded {filename}{mode}": " Статус: Загружен {filename}{mode}",
+ "Status: Packaging ({engine}) ...": " Статус: Упаковка ({engine}) ...",
+ "Status: Build Completed": " Статус: Сборка завершена",
+ "Status: Build Failed": " Статус: Сборка не удалась",
+ "Status: Workspace Reset": " Статус: Рабочая область сброшена",
+ "Start Build": " Начать сборку",
+ "Stop Build": " Остановить",
+ "Open Directory": " Открыть папку",
+ "Rebuild": " Пересобрать",
+ "Save & Return": " Сохранить и вернуться",
+ "Build Settings": "Параметры сборки",
+ "Preferences": "Настройки",
+ "About": "О программе",
+ "🚀 Engine": "🚀 Движок",
+ "📦 Dependencies": "📦 Зависимости",
+ "📂 Resources": "📂 Ресурсы",
+ "⚡ Optimization": "⚡ Оптимизация",
+ "🗺️ Package Map": "🗺️ Сопоставление",
+ "Engine & Environment": "Движок сборки и окружение",
+ "Execution Mode": "Режим выполнения",
+ "Mirrors & Scanner": "Зеркала и сканер зависимостей",
+ "Additional Resources (Drag & Drop Supported)": "Дополнительные ресурсы (Поддерживается Drag & Drop)",
+ "Performance Optimization": "Оптимизация производительности и компиляции",
+ "Lock Core Dependencies": "Фиксация версий ключевых зависимостей",
+ "Package Name Mappings": "Таблица сопоставления имен пакетов",
+ "UI Language:": "Язык интерфейса:",
+ "App Metadata & Presets": "Метаданные приложения и пресеты",
+ "Output Location": "Расположение итогового файла",
+ "Preferences & System Behavior": "Предпочтения и системное поведение",
+ "Build Engine:": "Движок сборки:",
+ "Python Interpreter:": "Интерпретатор Python:",
+ "Output Name:": "Имя итогового файла:",
+ "App Icon:": "Иконка приложения:",
+ "Primary PIP Index:": "Основное зеркало PIP:",
+ "Backup PIP Index:": "Резервное зеркало PIP:",
+ "Requirements File:": "Файл зависимостей (requirements.txt):",
+ "Hidden Imports:": "Неявные импорты (Hidden Imports):",
+ "Exclude Modules:": "Исключить модули (Excludes):",
+ "CPU Cores:": "Параллельные ядра ЦП:",
+ "UPX Path:": "Путь к утилите UPX:",
+ "PyInstaller Version:": "Версия PyInstaller:",
+ "Nuitka Version:": "Версия Nuitka:",
+ "Pipreqs Version:": "Версия Pipreqs:",
+ "Version:": "Версия:",
+ "Author/Company:": "Автор/Организация:",
+ "Description:": "Описание:",
+ "Output Rule:": "Правило размещения:",
+ "Target Directory:": "Целевая папка:",
+ "One-File Mode (--onefile)": "Режим одного файла (--onefile)",
+ "Hide Console (--noconsole)": "Скрыть окно консоли (--noconsole)",
+ "Use Virtual Environment (Recommended)": "Использовать изолированное виртуальное окружение (Рекомендуется)",
+ "Install requirements.txt": "Установить зависимости из requirements.txt",
+ "Analyze Dependencies (pipreqs)": "Автоматически проанализировать зависимости (pipreqs)",
+ "Scan Entire Folder": "Сканировать всю папку",
+ "Enable UPX Compression": "Включить UPX-сжатие",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "Облегченный режим (Исключить dev/test зависимости)",
+ "Concise Log Output": "Краткий вывод журнала",
+ "Auto-save Build Log": "Автоматически сохранять журнал сборки",
+ "Auto Extract Icon": "Автоматически извлекать иконку",
+ "Clean Temporary Cache After Build": "Очищать временный кэш после сборки",
+ "Sound Notification": "Звуковое уведомление о завершении",
+ "Browse": "Обзор...",
+ "AST Scan": "AST-анализ",
+ "Add File": "Добавить файл",
+ "Add Dir": "Добавить папку",
+ "Remove Selected": "Удалить выбранное",
+ "Clear All": "Очистить все",
+ "Add Mapping": "Добавить правило",
+ "Restore Defaults": "Сбросить настройки",
+ "Export Preset...": "Экспортировать пресет...",
+ "Import Preset...": "Импортировать пресет...",
+ "Source File Directory": "Папка исходного файла",
+ "Custom Directory": "Пользовательская папка",
+ "Import Name": "Имя импорта в коде",
+ "PyPI Package Name": "Имя пакета в PyPI",
+ "Reset to Default Config": "Сбросить к настройкам по умолчанию",
+ "Cancel & Return": "Отменить изменения и вернуться",
+ "Configure Build Settings": "Настройка параметров сборки",
+ "Toggle Execution Log": "Показать/скрыть журнал выполнения",
+ "Copy": "Копировать",
+ "Select All": "Выделить все",
+ "Clear Log": "Очистить журнал",
+ "Export Log...": "Сохранить журнал...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "Кроссплатформенный GUI-инструмент упаковки Python-приложений на базе PyInstaller и Nuitka",
+ " [Console]": " [С консолью]",
+ " [No Console]": " [Без консоли]",
+ "File": "Файл",
+ "Directory": "Папка",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: Высокая скорость сборки и отличная совместимость. Идеально для быстрой разработки.",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: Компилирует в нативный C/C++ бинарный файл. Выше производительность и защита исходного кода.",
+ "Leave blank to auto-detect system default Python": "Оставьте пустым для автоматического определения системного Python",
+ "Leave blank to auto-match script name": "Оставьте пустым для автоматического использования имени скрипта",
+ "Leave blank to auto-search requirements.txt in current directory": "Оставьте пустым для автоматического поиска requirements.txt в текущей папке",
+ "Comma separated (e.g. pandas, PyQt5)": "Через запятую (например: pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "Через запятую (например: tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "Оставьте пустым для автоматического поиска в переменной PATH",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "Динамически исключает избыточные зависимости для уменьшения размера и ускорения сборки.",
+ "Double-click to edit target path; Drag & drop supported": "Двойной клик для редактирования пути. Поддерживается Drag & Drop",
+ "Package mappings have been reset to defaults.": "Сопоставления пакетов сброшены к значениям по умолчанию.",
+ "Config preset exported to: {path}": "Пресет конфигурации экспортирован в: {path}",
+ "Config preset imported successfully.": "Пресет конфигурации успешно импортирован.",
+ "AST scan completed, found {count} dependencies.": "AST-сканирование завершено, найдено зависимостей: {count}.",
+ "No log content.": "Журнал пуст.",
+ "Log saved to: {path}": "Журнал сохранен в: {path}",
+ "Attention: Please check the log for details.": "Внимание: Пожалуйста, проверьте журнал для деталей.",
+ "[INFO] Initializing isolated build environment...": "[INFO] Инициализация изолированного окружения сборки...",
+ "[INFO] Python interpreter path: {path}": "[INFO] Путь к интерпретатору Python: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] Создание виртуального окружения (venv)...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] Не удалось создать виртуальное окружение. Возможно, отсутствуют модули или ограничены права.",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] Синхронизация и обновление менеджера пакетов pip...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] Установка движка сборки [{pkg}] и ключевых зависимостей...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Совет Nuitka: Если при первой сборке потребуется загрузка компилятора GCC/MinGW, убедитесь в стабильности сети.",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] Установка зависимостей [1/3]: Установка объявленных зависимостей ({filename})...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] Установка зависимостей [2/3]: Вызов pipreqs для статического анализа проекта...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] Режим песочницы включен: Анализируется только целевой скрипт во избежание конфликтов.",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] Включено сканирование всей папки: Анализ всех Python-файлов в текущем каталоге...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] Адрес сервера PyPI для анализа зависимостей: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] Запрос версий пакетов, пожалуйста, подождите...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] Переключение на резервное зеркало PIP: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] Повторная попытка сканирования с совместимой кодировкой...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] Глубокое сканирование pipreqs пропущено, недостающие зависимости дополняются через AST.",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] Установка зависимостей [3/3]: Извлечение неявных импортов через AST-анализ...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] Установка обнаруженных неявных зависимостей: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ Предупреждение: Не удалось установить пакет [{pkg}]! Это может вызвать сбой при запуске.",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] Запуск движка {engine} для компиляции...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] Обнаружено локальное окружение MSVC, используется нативный компилятор C++.",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] Включен облегченный режим: Применение стратегий оптимизации размера...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] Настоятельно рекомендуется включить 'Виртуальное окружение' для максимального эффекта.",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] Применены флаги оптимизации Nuitka (-OO)...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] Основная компиляция завершена, извлечение и архивация файлов...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] Ошибка перемещения итогового файла. Файл занят другим процессом или недостаточно прав: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] Не удалось найти исполняемый файл во временной папке сборки: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] Проверка итоговых файлов и генерация приложения...",
+ "[INFO] Build log exported to: {path}": "[INFO] Журнал сборки экспортирован в: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] Компиляция успешно завершена. Выходной путь: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[Синтаксическая ошибка] Исходный код содержит синтаксическую ошибку или ошибку отступов!\n - Файл: {file}\n - Тип ошибки: {type}\n - Строка: около {line}\n - Детали: {desc}\n\nСовет: Это ошибка в вашем исходном коде. Убедитесь, что скрипт работает локально перед сборкой.",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [Диагностический трассировочный журнал: Полный лог из-за ошибки выполнения] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] Компиляция прервана из-за ошибок. Проверьте журнал.",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] Освобождение места и очистка временного окружения сборки...",
+ "\\nProgram execution completed, press Enter to exit...": "\\nВыполнение завершено. Нажмите Enter для выхода...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] Целевой файл заблокирован или зашифрован облачным сервисом. Разблокируйте его и повторите попытку.",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] Сначала загрузите корректный файл исходного кода Python!",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] Исключение во время AST-анализа: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] Не удалось экспортировать файл пресета: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] Файл пресета поврежден или имеет неверный формат: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] Не удалось экспортировать файл журнала: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] Сборка завершена, но не удалось предварительно установить следующие зависимости:\n\n 👉 {pkgs}\n\n⚠️ Внимание: Приложение может завершиться с ошибкой при запуске из-за отсутствия модулей!"
+}
\ No newline at end of file
diff --git a/qpypack/locales/zh_TW.json b/qpypack/locales/zh_TW.json
new file mode 100644
index 0000000..6f80c93
--- /dev/null
+++ b/qpypack/locales/zh_TW.json
@@ -0,0 +1,162 @@
+{
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "將 Python 腳本 (.py/.pyw) 拖放至此\n或 點擊瀏覽檔案",
+ "Auto-parse dependencies, resources, and implicit imports": "自動解析依賴項、附加資源及隱式匯入",
+ "Loaded: {filename}": "已載入: {filename}",
+ "Parsing metadata...": "正在解析元資料...",
+ "Ready, waiting for build.": "就緒,等待建置。",
+ "Initializing build...": "初始化建置流程...",
+ "Preparing engine...": "準備建置引擎...",
+ "Build Successful": "建置成功",
+ "Open output directory or reset workspace.": "前往輸出目錄檢視,或重置當前工作區。",
+ "Build Failed": "建置失敗",
+ "Check log output below for troubleshooting.": "請參閱下方記錄檔以排查錯誤。",
+ "Status: Ready": " 狀態: 準備就緒",
+ "Status: Parsing {filename}...": " 狀態: 正在解析 {filename}...",
+ "Status: Loaded {filename}{mode}": " 狀態: 已載入 {filename}{mode}",
+ "Status: Packaging ({engine}) ...": " 狀態: 打包中 ({engine}) ...",
+ "Status: Build Completed": " 狀態: 建置完成",
+ "Status: Build Failed": " 狀態: 建置失敗",
+ "Status: Workspace Reset": " 狀態: 工作區已重置",
+ "Start Build": " 開始建置",
+ "Stop Build": " 停止建置",
+ "Open Directory": " 開啟輸出目錄",
+ "Rebuild": " 重新建置",
+ "Save & Return": " 儲存並返回",
+ "Build Settings": "建置設定",
+ "Preferences": "偏好設定",
+ "About": "關於",
+ "🚀 Engine": "🚀 建置引擎",
+ "📦 Dependencies": "📦 依賴管理",
+ "📂 Resources": "📂 附加資源",
+ "⚡ Optimization": "⚡ 效能優化",
+ "🗺️ Package Map": "🗺️ 包名對映",
+ "Engine & Environment": "建置引擎與環境",
+ "Execution Mode": "執行模式",
+ "Mirrors & Scanner": "鏡像源與掃描設定",
+ "Additional Resources (Drag & Drop Supported)": "附加資源檔案與目錄 (支援桌面拖放)",
+ "Performance Optimization": "效能與編譯優化",
+ "Lock Core Dependencies": "鎖定核心依賴版本",
+ "Package Name Mappings": "第三方庫包名對映表",
+ "UI Language:": "介面語言:",
+ "App Metadata & Presets": "應用元資料與工程預設",
+ "Output Location": "建置產物輸出位置",
+ "Preferences & System Behavior": "建置偏好與系統行為",
+ "Build Engine:": "建置引擎:",
+ "Python Interpreter:": "Python 解譯器:",
+ "Output Name:": "輸出程式名稱:",
+ "App Icon:": "應用程式圖示:",
+ "Primary PIP Index:": "PIP 主鏡像源:",
+ "Backup PIP Index:": "PIP 備用源:",
+ "Requirements File:": "依賴清單 (requirements.txt):",
+ "Hidden Imports:": "隱式匯入 (Hidden Imports):",
+ "Exclude Modules:": "排除模組 (Excludes):",
+ "CPU Cores:": "編譯並行 CPU 核心數:",
+ "UPX Path:": "UPX 工具路徑:",
+ "PyInstaller Version:": "PyInstaller 版本:",
+ "Nuitka Version:": "Nuitka 版本:",
+ "Pipreqs Version:": "Pipreqs 版本:",
+ "Version:": "版本號:",
+ "Author/Company:": "開發者/公司:",
+ "Description:": "程式描述:",
+ "Output Rule:": "輸出規則:",
+ "Target Directory:": "目標目錄:",
+ "One-File Mode (--onefile)": "單一檔案模式 (--onefile)",
+ "Hide Console (--noconsole)": "隱藏主控台視窗 (--noconsole)",
+ "Use Virtual Environment (Recommended)": "使用獨立虛擬環境 (推薦)",
+ "Install requirements.txt": "安裝 requirements.txt 依賴",
+ "Analyze Dependencies (pipreqs)": "自動分析依賴項 (pipreqs)",
+ "Scan Entire Folder": "掃描整個所在目錄",
+ "Enable UPX Compression": "啟用 UPX 執行檔壓縮",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "精簡模式 (排除開發與測試依賴)",
+ "Concise Log Output": "精簡日誌輸出",
+ "Auto-save Build Log": "自動儲存建置日誌",
+ "Auto Extract Icon": "自動擷取圖示",
+ "Clean Temporary Cache After Build": "建置後清理暫存快取",
+ "Sound Notification": "建置完成提示音",
+ "Browse": "瀏覽...",
+ "AST Scan": "AST 語法樹解析",
+ "Add File": "新增檔案",
+ "Add Dir": "新增目錄",
+ "Remove Selected": "移除選取",
+ "Clear All": "清除全部",
+ "Add Mapping": "新增對映",
+ "Restore Defaults": "還原預設值",
+ "Export Preset...": "匯出預設設定...",
+ "Import Preset...": "匯入預設設定...",
+ "Source File Directory": "原始碼檔案所在目錄",
+ "Custom Directory": "自訂目錄",
+ "Import Name": "程式碼匯入名 (import)",
+ "PyPI Package Name": "PyPI 套件名 (pip)",
+ "Reset to Default Config": "重置為預設設定",
+ "Cancel & Return": "放棄修改並返回",
+ "Configure Build Settings": "設定建置參數",
+ "Toggle Execution Log": "顯示/隱藏執行日誌",
+ "Copy": "複製",
+ "Select All": "全選",
+ "Clear Log": "清除日誌",
+ "Export Log...": "匯出日誌...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "基於 PyInstaller 與 Nuitka 的 Python 跨平台打包 GUI 工具",
+ " [Console]": " [主控台]",
+ " [No Console]": " [無主控台]",
+ "File": "檔案",
+ "Directory": "目錄",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: 建置速度快,相容性極佳。適合快速迭代與常規應用。",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: 編譯為原生 C/C++ 二進位檔。執行效能更好,且具備原始碼防反編譯保護。",
+ "Leave blank to auto-detect system default Python": "留空則自動偵測系統預設 Python",
+ "Leave blank to auto-match script name": "留空則自動比對腳本名稱",
+ "Leave blank to auto-search requirements.txt in current directory": "留空則自動搜尋當前目錄下的 requirements.txt",
+ "Comma separated (e.g. pandas, PyQt5)": "逗號分隔 (如: pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "逗號分隔 (如: tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "留空則從 PATH 環境變數自動偵測",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "動態排除建置環境中的冗餘依賴,提升建置速度並縮減產物容量。",
+ "Double-click to edit target path; Drag & drop supported": "雙擊修改目標路徑;支援直接從桌面拖放檔案或資料夾",
+ "Package mappings have been reset to defaults.": "套件名稱對映已重置為預設值。",
+ "Config preset exported to: {path}": "設定預設檔已匯出至: {path}",
+ "Config preset imported successfully.": "設定預設檔已成功匯入。",
+ "AST scan completed, found {count} dependencies.": "AST 解析完成,共發現 {count} 項依賴。",
+ "No log content.": "當前無日誌內容。",
+ "Log saved to: {path}": "日誌已儲存至: {path}",
+ "Attention: Please check the log for details.": "注意:發生異常,請檢視日誌細節。",
+ "[INFO] Initializing isolated build environment...": "[INFO] 正在初始化隔離建置環境...",
+ "[INFO] Python interpreter path: {path}": "[INFO] Python 解譯器路徑: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] 正在建立虛擬環境 (venv)...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] 虛擬環境建立失敗。當前 Python 環境可能缺少必要模組或權限受限。",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] 正在同步並升級 pip 套件管理器...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] 正在安裝建置引擎 [{pkg}] 及核心編譯依賴...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Nuitka 提示:若首次建置提示下載 GCC/MinGW 編譯器,請保持網路連線穩定。",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] 依賴安裝 [1/3]: 正在安裝聲明依賴 ({filename})...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] 依賴安裝 [2/3]: 正在調用 pipreqs 分析專案依賴...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] 已啟用單一檔案沙盒模式:僅對當前腳本解析,防止同目錄其他檔案污染。",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] 已允許全目錄掃描模式:正在掃描當前目錄下所有 Python 檔案...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] 依賴分析服務源地址: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] 正在查詢各依賴庫的版本,請稍候...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] 正在切換至備用 PyPI 查詢源重新檢索: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] 正在嘗試利用相容編碼進行掃描...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] pipreqs 已跳過深度掃描,將由 AST 掃描引擎補全依賴。",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] 依賴安裝 [3/3]: 正在透過 AST 靜態掃描擷取隱式依賴...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] 正在解析並安裝隱式匯入依賴: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ 警告:依賴庫 [{pkg}] 安裝失敗!可能導致打包後的軟體執行時崩潰。",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] 正在啟動 {engine} 引擎,開始編譯二進位檔案...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] 偵測到本地 MSVC 環境,優先使用原生 C++ 編譯器。",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] 已開啟精簡模式,正在執行體積縮減策略...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] 強烈建議勾選 [虛擬環境] 以最大化精簡效果。",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] 已啟用 Nuitka 優化指令 (-OO)...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] 編譯核心完成,正在擷取並歸檔建置檔案...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] 產物移交失敗,檔案可能被系統程序佔用或權限不足: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] 未在暫存建置目錄中定位到有效可執行產物: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] 正在校驗輸出檔案並產生最終產物...",
+ "[INFO] Build log exported to: {path}": "[INFO] 建置日誌已匯出至: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] 編譯已完成,輸出路徑: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[語法異常] 原始程式碼存在語法不合規或縮排異常錯誤!\n - 錯誤檔案: {file}\n - 異常類型: {type}\n - 異常位置: 第 {line} 行附近\n - 詳細描述: {desc}\n\n提示: 此問題為原始碼邏輯本身錯誤。請在確保本地執行無誤後,再次執行編譯流程。",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [診斷追蹤: 以下是由於該步驟執行異常產生的完整原始日誌] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] 編譯異常中斷,請參閱日誌以定位排查。",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] 正在釋放空間,清理暫存建置環境...",
+ "\\nProgram execution completed, press Enter to exit...": "\\n程式執行完畢,按下 Enter 鍵退出...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] 目標檔案被雲端硬碟鎖定或加密,請解密後重試。",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] 請先載入有效的 Python 原始碼檔案!",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] AST 語法樹解析過程中發生異常: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] 無法匯出預設檔: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] 預設檔格式錯誤或已被損壞: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] 無法匯出日誌檔案: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] 建置已完成,但預建置階段以下依賴安裝失敗:\n\n 👉 {pkgs}\n\n⚠️ 提示:程式執行時可能會因缺少模組導致崩潰!"
+}
\ No newline at end of file
diff --git a/qpypack/main.py b/qpypack/main.py
index d7a4846..c8563f2 100644
--- a/qpypack/main.py
+++ b/qpypack/main.py
@@ -7,12 +7,12 @@ import re
import time
import stat
import json
-import locale
import math
import wave
import struct
import threading
import configparser
+import locale
from pathlib import Path
if os.name == 'nt':
@@ -35,7 +35,7 @@ from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayo
QHeaderView)
from PySide6.QtCore import (Qt, QThread, Signal, QPropertyAnimation, QEasingCurve,
QParallelAnimationGroup, QFileInfo, QVariantAnimation,
- QTimer, QPointF, QRectF, QRect, QSize, QUrl)
+ QTimer, QPointF, QRectF, QRect, QSize, QUrl, QLocale, QObject)
from PySide6.QtGui import (QFont, QDragEnterEvent, QDropEvent, QIcon, QPixmap,
QPainter, QColor, QPen, QImage, QImageWriter)
from PySide6.QtSvg import QSvgRenderer
@@ -47,10 +47,339 @@ except ImportError:
HAS_QT_AUDIO = False
__app_name__ = "QPyPack"
-__version__ = "2.6.1"
+__version__ = "2.6.2"
__author__ = "QwejayHuang"
__company__ = "QwejayHuang"
-__description__ = "基于 PyInstaller 与 Nuitka 的跨平台 Python 应用打包构建工具"
+__description__ = "Cross-platform Python Application Packaging Tool"
+
+_CONFIG_DIR = Path.home() / ".qpypack"
+_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
+CONFIG_FILE = (_CONFIG_DIR / "config.ini").as_posix()
+
+ZH_CN_DICT = {
+ "Drag & Drop Python script (.py/.pyw) here\nor Click to Browse": "将 Python 脚本 (.py/.pyw) 拖放至此\n或 点击浏览文件",
+ "Auto-parse dependencies, resources, and implicit imports": "自动解析依赖、附加资源及隐式导入",
+ "Loaded: {filename}": "已载入: {filename}",
+ "Parsing metadata...": "正在解析元数据...",
+ "Ready, waiting for build.": "就绪,等待构建。",
+ "Initializing build...": "初始化构建...",
+ "Preparing engine...": "准备引擎...",
+ "Build Successful": "构建成功",
+ "Open output directory or reset workspace.": "前往输出目录查看,或重置工作区。",
+ "Build Failed": "构建失败",
+ "Check log output below for troubleshooting.": "请查看下方日志以排查错误。",
+ "Status: Ready": " 状态: 准备就绪",
+ "Status: Parsing {filename}...": " 状态: 正在解析 {filename}...",
+ "Status: Loaded {filename}{mode}": " 状态: 已载入 {filename}{mode}",
+ "Status: Packaging ({engine}) ...": " 状态: 构建中 ({engine}) ...",
+ "Status: Build Completed": " 状态: 构建完成",
+ "Status: Build Failed": " 状态: 构建失败",
+ "Status: Workspace Reset": " 状态: 工作区已重置",
+ "Start Build": " 开始构建",
+ "Stop Build": " 停止构建",
+ "Open Directory": " 打开目录",
+ "Rebuild": " 重新构建",
+ "Save & Return": " 保存并返回",
+ "Build Settings": "构建设置",
+ "Preferences": "首选项",
+ "About": "关于",
+ "🚀 Engine": "🚀 构建引擎",
+ "📦 Dependencies": "📦 依赖管理",
+ "📂 Resources": "📂 附加资源",
+ "⚡ Optimization": "⚡ 性能优化",
+ "🗺️ Package Map": "🗺️ 包名映射",
+ "Engine & Environment": "构建引擎与环境",
+ "Execution Mode": "运行模式",
+ "Mirrors & Scanner": "镜像源与扫描配置",
+ "Additional Resources (Drag & Drop Supported)": "附加资源文件与目录 (支持桌面拖拽)",
+ "Performance Optimization": "性能与编译优化",
+ "Lock Core Dependencies": "锁定核心依赖版本",
+ "Package Name Mappings": "第三方库包名映射表",
+ "UI Language:": "界面语言:",
+ "App Metadata & Presets": "应用元数据与工程预设",
+ "Output Location": "构建产物输出位置",
+ "Preferences & System Behavior": "构建偏好与系统行为",
+ "Build Engine:": "构建引擎:",
+ "Python Interpreter:": "Python 解释器:",
+ "Output Name:": "输出名称:",
+ "App Icon:": "程序图标:",
+ "Primary PIP Index:": "PIP 主镜像源:",
+ "Backup PIP Index:": "PIP 备用源:",
+ "Requirements File:": "依赖清单 (requirements):",
+ "Hidden Imports:": "隐式导入 (Hidden Imports):",
+ "Exclude Modules:": "排除模块 (Excludes):",
+ "CPU Cores:": "编译并发核心数:",
+ "UPX Path:": "UPX 路径:",
+ "PyInstaller Version:": "PyInstaller 版本:",
+ "Nuitka Version:": "Nuitka 版本:",
+ "Pipreqs Version:": "Pipreqs 版本:",
+ "Version:": "版本号:",
+ "Author/Company:": "开发者/公司:",
+ "Description:": "程序描述:",
+ "Output Rule:": "输出规则:",
+ "Target Directory:": "目标目录:",
+ "One-File Mode (--onefile)": "单文件模式 (--onefile)",
+ "Hide Console (--noconsole)": "隐藏控制台 (--noconsole)",
+ "Use Virtual Environment (Recommended)": "使用独立虚拟环境 (推荐)",
+ "Install requirements.txt": "安装 requirements.txt",
+ "Analyze Dependencies (pipreqs)": "自动分析依赖 (pipreqs)",
+ "Scan Entire Folder": "扫描整个所在目录",
+ "Enable UPX Compression": "启用 UPX 压缩",
+ "Lite Mode (Exclude Dev/Test Dependencies)": "精简模式 (排除开发/测试依赖)",
+ "Concise Log Output": "精简日志输出",
+ "Auto-save Build Log": "自动保存构建日志",
+ "Auto Extract Icon": "自动提取图标",
+ "Clean Temporary Cache After Build": "构建后清理临时缓存",
+ "Sound Notification": "构建完成提示音",
+ "Browse": "浏览",
+ "AST Scan": "AST分析",
+ "Add File": "添加文件",
+ "Add Dir": "添加目录",
+ "Remove Selected": "移除选中",
+ "Clear All": "清空",
+ "Add Mapping": "添加映射",
+ "Restore Defaults": "恢复默认",
+ "Export Preset...": "导出预设...",
+ "Import Preset...": "导入预设...",
+ "Source File Directory": "源文件所在目录",
+ "Custom Directory": "自定义目录",
+ "Import Name": "代码导入名",
+ "PyPI Package Name": "PyPI 包名",
+ "Reset to Default Config": "重置为默认配置",
+ "Cancel & Return": "放弃修改并返回",
+ "Configure Build Settings": "配置构建参数",
+ "Toggle Execution Log": "显示/隐藏执行日志",
+ "Copy": "复制",
+ "Select All": "全选",
+ "Clear Log": "清空日志",
+ "Export Log...": "导出日志...",
+ "A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka": "基于 PyInstaller 和 Nuitka 的 Python 跨平台打包 GUI 工具",
+ " [Console]": " [控制台]",
+ " [No Console]": " [无控制台]",
+ "File": "文件",
+ "Directory": "目录",
+ "PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration.": "PyInstaller: 构建速度快,兼容性极佳。适合快速迭代与常规应用。",
+ "Nuitka: Compiles to native C/C++ binary. Better performance and source code protection.": "Nuitka: 编译为原生 C/C++ 二进制。运行性能更好,且具有源码防反编译保护。",
+ "Leave blank to auto-detect system default Python": "留空则自动检测系统默认 Python",
+ "Leave blank to auto-match script name": "留空则自动匹配脚本名称",
+ "Leave blank to auto-search requirements.txt in current directory": "留空则自动检索当前目录 requirements.txt",
+ "Comma separated (e.g. pandas, PyQt5)": "逗号分隔 (如: pandas, PyQt5)",
+ "Comma separated (e.g. tkinter, matplotlib)": "逗号分隔 (如: tkinter, matplotlib)",
+ "Leave blank to auto-detect from environment variables": "留空则从环境变量自动检测",
+ "Dynamically exclude redundant dependencies in build environment, improving speed and reducing size.": "动态排除构建环境冗余依赖,提升构建速度并缩减产物体积。",
+ "Double-click to edit target path; Drag & drop supported": "双击修改目标路径;支持直接从桌面拖拽文件或文件夹至此区域",
+ "Package mappings have been reset to defaults.": "包名映射已重置为默认值。",
+ "Config preset exported to: {path}": "配置预设已导出至: {path}",
+ "Config preset imported successfully.": "配置预设已成功导入。",
+ "AST scan completed, found {count} dependencies.": "AST 解析完成,共发现 {count} 项依赖。",
+ "No log content.": "当前无日志内容。",
+ "Log saved to: {path}": "日志已保存至: {path}",
+ "Attention: Please check the log for details.": "注意:发生异常,请查看日志详情。",
+ "[INFO] Initializing isolated build environment...": "[INFO] 正在初始化隔离构建环境...",
+ "[INFO] Python interpreter path: {path}": "[INFO] Python 解释器路径: {path}",
+ "[INFO] Creating virtual environment...": "[INFO] 正在创建虚拟环境...",
+ "[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions.": "[ERROR] 虚拟环境创建失败。当前 Python 环境可能缺失必要模块或权限受限。",
+ "[INFO] Synchronizing and upgrading pip package manager...": "[INFO] 正在同步并升级 pip 包管理器...",
+ "[INFO] Installing build engine [{pkg}] and core compilation dependencies...": "[INFO] 正在安装构建引擎 [{pkg}] 及核心编译依赖...",
+ "[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection.": "[INFO] Nuitka 提示:若首次构建提示下载 GCC/MinGW 编译器,请保持网络正常。",
+ "[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...": "[INFO] 依赖安装 [1/3]: 正在安装声明依赖 ({filename})...",
+ "[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies...": "[INFO] 依赖安装 [2/3]: 正在调用 pipreqs 分析项目依赖...",
+ "[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files.": "[INFO] 已启用单文件沙盒模式:仅对当前脚本解析,防止同目录其他文件污染。",
+ "[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory...": "[WARN] 已允许全目录扫描模式:正在扫描当前目录下所有 Python 文件...",
+ "[INFO] Dependency analysis service source address: {server}": "[INFO] 依赖分析服务源地址: {server}",
+ "[INFO] Querying versions of dependency libraries, please wait...": "[INFO] 正在查询各依赖库的版本,请稍候...",
+ "[INFO] Switching to backup PyPI source for retrieval: {url}": "[INFO] 正在切换至备用 PyPI 查询源重新检索: {url}",
+ "[INFO] Attempting to scan using compatible encoding...": "[INFO] 正在尝试利用兼容编码进行扫描...",
+ "[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine.": "[WARN] pipreqs 已跳过深度扫描,将由 AST 扫描引擎补全依赖。",
+ "[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan...": "[INFO] 依赖安装 [3/3]: 正在通过 AST 静态扫描提取隐式依赖...",
+ "[INFO] Parsing and installing implicit import dependencies: {pkgs}": "[INFO] 正在解析并安装隐式导入依赖: {pkgs}",
+ "[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.": "[ERROR] ⚠️ 警告:依赖库 [{pkg}] 安装失败!可能导致打包后的软件运行时崩溃。",
+ "[INFO] Starting {engine} engine to compile binary files...": "[INFO] 正在启动 {engine} 引擎,开始编译二进制文件...",
+ "[INFO] Found local MSVC environment, prioritizing native C++ compiler.": "[INFO] 发现本地 MSVC 环境,优先使用原生 C++ 编译器。",
+ "[INFO] Lite mode enabled, executing size reduction strategy...": "[INFO] 已开启精简模式,正在执行体积缩减策略...",
+ "[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect.": "[WARN] 强烈建议勾选 [虚拟环境] 以最大化精简效果。",
+ "[INFO] Enabled Nuitka optimization directives...": "[INFO] 已启用 Nuitka 优化指令...",
+ "[INFO] Core compilation completed, extracting and archiving built files...": "[INFO] 编译核心完成,正在提取并归档编译文件...",
+ "[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}": "[ERROR] 产物移交失败,文件可能被系统进程占用或权限不足: {error}",
+ "[ERROR] Could not locate valid executable product in temporary build directory: {path}": "[ERROR] 未在临时构建目录中定位到有效可执行产物: {path}",
+ "[INFO] Validating output files and generating final product...": "[INFO] 正在校验输出文件并生成最终产物...",
+ "[INFO] Build log exported to: {path}": "[INFO] 编译日志已导出至: {path}",
+ "[SUCCESS] Compilation completed, output path: {path}": "[SUCCESS] 编译已完成,输出路径: {path}",
+ "[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.": "[语法异常] 源程序存在语法不合规或缩进异常错误!\n - 错误源文件: {file}\n - 异常类型: {type}\n - 异常位置: 第 {line} 行附近\n - 错误描述: {desc}\n\n提示: 此问题为源码逻辑本身错误。请在确保本地运行无误后,再次执行编译流程。",
+ "\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!": "\n!!!!!!!!!! [诊断回溯: 以下是由于该环节执行异常产生的完整原始日志] !!!!!!!!!!",
+ "[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.": "[FAILED] 编译异常中断,请参阅日志以定位排查。",
+ "[INFO] Freeing up space, cleaning temporary build environment...": "[INFO] 正在释放空间,清理临时构建环境...",
+ "\\nProgram execution completed, press Enter to exit...": "\\n程序执行完毕,按回车键退出...",
+ "[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again.": "[ERROR] 目标文件被云盘锁定或加密,请解密后重试。",
+ "[ERROR] Please load a valid Python source file first!": "[ERROR] 请先加载有效的 Python 源代码文件!",
+ "[ERROR] Exception occurred during AST parsing: {error}": "[ERROR] AST 语法树解析过程中发生异常: {error}",
+ "[ERROR] Failed to export preset file: {error}": "[ERROR] 无法导出预设文件: {error}",
+ "[ERROR] Preset file format error or corrupted: {error}": "[ERROR] 预设文件格式错误或已被损坏: {error}",
+ "[ERROR] Failed to export log file: {error}": "[ERROR] 无法导出日志文件: {error}",
+ "[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!": "[ERROR] 构建已完成,但预构建阶段以下依赖安装失败:\n\n 👉 {pkgs}\n\n⚠️ 提示:程序运行时可能会因缺少模块导致崩溃!"
+}
+
+class TranslationEngine(QObject):
+ language_changed = Signal(str)
+ DEFAULT_LOCALE = "en_US"
+
+ LANG_META = {
+ "en_US": {"native": "English", "flag": "🇺🇸"},
+ "zh_CN": {"native": "简体中文", "flag": "🇨🇳"},
+ "zh_TW": {"native": "繁體中文", "flag": "🇹🇼"},
+ "ja_JP": {"native": "日本語", "flag": "🇯🇵"},
+ "ko_KR": {"native": "한국어", "flag": "🇰🇷"},
+ "de_DE": {"native": "Deutsch", "flag": "🇩🇪"},
+ "fr_FR": {"native": "Français", "flag": "🇫🇷"},
+ "es_ES": {"native": "Español", "flag": "🇪🇸"},
+ "ru_RU": {"native": "Русский", "flag": "🇷🇺"},
+ "pt_BR": {"native": "Português (Brasil)", "flag": "🇧🇷"},
+ "it_IT": {"native": "Italiano", "flag": "🇮🇹"},
+ "nl_NL": {"native": "Nederlands", "flag": "🇳🇱"},
+ "pl_PL": {"native": "Polski", "flag": "🇵🇱"},
+ "tr_TR": {"native": "Türkçe", "flag": "🇹🇷"},
+ "vi_VN": {"native": "Tiếng Việt", "flag": "🇻🇳"},
+ "th_TH": {"native": "ไทย", "flag": "🇹🇭"},
+ "ar_SA": {"native": "العربية", "flag": "🇸🇦"},
+ }
+
+ def __init__(self, locales_dir: Path):
+ super().__init__()
+ self.locales_dir = locales_dir
+ self.current_lang = self.DEFAULT_LOCALE
+ self.translations = {}
+ self.fallback_zh_cn = ZH_CN_DICT
+
+ def init_locale(self):
+ self.load_all_locales()
+ self.current_lang = self.detect_system_language()
+
+ def normalize_locale(self, locale_str: str) -> str:
+ if not locale_str or locale_str == "auto":
+ return self.detect_system_language()
+ clean = locale_str.replace("-", "_")
+ parts = clean.split("_")
+ if len(parts) >= 2:
+ return f"{parts[0].lower()}_{parts[1].upper()}"
+ elif len(parts) == 1:
+ lang = parts[0].lower()
+ mapping = {"zh": "zh_CN", "en": "en_US", "ja": "ja_JP", "ko": "ko_KR", "de": "de_DE", "fr": "fr_FR", "es": "es_ES", "ru": "ru_RU", "pt": "pt_BR"}
+ return mapping.get(lang, self.DEFAULT_LOCALE)
+ return self.DEFAULT_LOCALE
+
+ def detect_system_language(self) -> str:
+ try:
+ sys_locale = QLocale.system().name()
+ except Exception:
+ sys_locale = ""
+
+ if sys_locale.startswith("zh"):
+ if any(k in sys_locale for k in ("TW", "HK", "MO", "Hant")): return "zh_TW"
+ return "zh_CN"
+ elif sys_locale.startswith("ja"): return "ja_JP"
+ elif sys_locale.startswith("ko"): return "ko_KR"
+ elif sys_locale.startswith("de"): return "de_DE"
+ elif sys_locale.startswith("fr"): return "fr_FR"
+ elif sys_locale.startswith("es"): return "es_ES"
+ elif sys_locale.startswith("ru"): return "ru_RU"
+ elif sys_locale.startswith("pt"): return "pt_BR"
+ return "en_US"
+
+ def load_all_locales(self):
+ self.translations.clear()
+ if self.locales_dir.exists():
+ for p in self.locales_dir.glob("*.json"):
+ try:
+ data = json.loads(p.read_text(encoding='utf-8'))
+ code = self.normalize_locale(p.stem)
+ self.translations[code] = data
+ except Exception as e:
+ print(f"[i18n] Failed to load {p.name}: {e}")
+
+ def get_available_languages(self) -> dict:
+ sys_code = self.detect_system_language()
+ if sys_code in self.LANG_META:
+ sys_native = self.LANG_META[sys_code]["native"].split(" (")[0]
+ else:
+ qloc = QLocale(sys_code)
+ sys_native = qloc.nativeLanguageName().capitalize() or sys_code
+
+ langs = {"auto": f"System Default ({sys_native})"}
+
+ all_codes = set(self.translations.keys()) | {self.DEFAULT_LOCALE, "zh_CN"}
+
+ for code in sorted(all_codes):
+ if code in self.LANG_META:
+ meta = self.LANG_META[code]
+ flag = meta.get("flag", "")
+ prefix = f"{flag} " if flag and flag != "🌐" else ""
+ langs[code] = f"{prefix}{meta['native']}"
+ else:
+ qloc = QLocale(code)
+ native_name = qloc.nativeLanguageName()
+ if not native_name or native_name == code:
+ native_name = qloc.languageToString(qloc.language())
+ native_name = native_name.capitalize() if native_name else code
+ langs[code] = native_name
+
+ return langs
+
+ def set_language(self, lang_code: str):
+ target = self.normalize_locale(lang_code)
+ if target != self.current_lang:
+ self.current_lang = target
+ self.language_changed.emit(self.current_lang)
+
+ def t(self, text: str, **kwargs) -> str:
+ val = text
+ if self.current_lang in self.translations and text in self.translations[self.current_lang]:
+ val = self.translations[self.current_lang][text]
+ elif self.current_lang == "zh_CN" and text in self.fallback_zh_cn:
+ val = self.fallback_zh_cn[text]
+ if kwargs:
+ try: return val.format(**kwargs)
+ except Exception: pass
+ return val
+
+_LOCALES_DIR = Path(__file__).parent / "locales"
+if not _LOCALES_DIR.exists():
+ _LOCALES_DIR = _CONFIG_DIR / "locales"
+
+I18N = TranslationEngine(_LOCALES_DIR)
+I18N.init_locale()
+
+def _(text: str, **kwargs) -> str:
+ return I18N.t(text, **kwargs)
+
+PYPI_MIRRORS_GLOBAL = [
+ ("PyPI Official (Global Default)", "https://pypi.org/simple"),
+ ("Python TestPyPI", "https://test.pypi.org/simple"),
+ ("AWS PyPI Mirror (US/EU)", "https://pypi.org/simple"),
+ ("Tsinghua University (China)", "https://pypi.tuna.tsinghua.edu.cn/simple"),
+ ("Aliyun Cloud (China)", "https://mirrors.aliyun.com/pypi/simple/"),
+ ("Tencent Cloud (China)", "https://mirrors.cloud.tencent.com/pypi/simple/"),
+ ("Huawei Cloud (China)", "https://repo.huaweicloud.com/repository/pypi/simple/"),
+ ("USTC (China)", "https://mirrors.ustc.edu.cn/pypi/simple/"),
+]
+
+DEFAULT_MAPPINGS = {
+ 'acoustid': 'pyacoustid', 'cv2': 'opencv-python', 'PIL': 'pillow', 'Pillow': 'pillow',
+ 'fitz': 'pymupdf', 'skimage': 'scikit-image', 'vlc': 'python-vlc', 'pyzbar': 'pyzbar',
+ 'docx': 'python-docx', 'pptx': 'python-pptx', 'bs4': 'beautifulsoup4', 'barcode': 'python-barcode',
+ 'pdfplumber': 'pdfplumber', 'win32com': 'pywin32', 'win32api': 'pywin32', 'win32con': 'pywin32',
+ 'win32gui': 'pywin32', 'win32clipboard': 'pywin32', 'win32print': 'pywin32', 'win32file': 'pywin32',
+ 'win32security': 'pywin32', 'win32process': 'pywin32', 'win32evtlog': 'pywin32', 'win32service': 'pywin32',
+ 'win32pipe': 'pywin32', 'win32net': 'pywin32', 'win32crypt': 'pywin32', 'pythoncom': 'pywin32',
+ 'pywintypes': 'pywin32', 'serial': 'pyserial', 'usb': 'pyusb', 'bluetooth': 'pybluez',
+ 'sklearn': 'scikit-learn', 'yaml': 'pyyaml', 'dateutil': 'python-dateutil', 'jwt': 'PyJWT',
+ 'Crypto': 'pycryptodome', 'crypto': 'pycryptodome', 'OpenGL': 'PyOpenGL', 'dns': 'dnspython',
+ 'wx': 'wxPython', 'desktop_notifier': 'desktop-notifier', 'dotenv': 'python-dotenv',
+ 'telegram': 'python-telegram-bot', 'websocket': 'websocket-client', 'git': 'GitPython',
+ 'github': 'PyGithub', 'gitlab': 'python-gitlab', 'discord': 'discord.py', 'paho': 'paho-mqtt',
+ 'socketio': 'python-socketio', 'engineio': 'python-engineio', 'kafka': 'kafka-python',
+ 'OpenSSL': 'pyOpenSSL', 'ldap': 'python-ldap', 'magic': 'python-magic', 'slugify': 'python-slugify',
+ 'snappy': 'python-snappy'
+}
MATERIAL_ICONS = {
'settings': 'M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.06-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.73,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.06,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.43-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.49-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z',
@@ -68,86 +397,22 @@ MATERIAL_ICONS = {
'close': 'M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'
}
-_CONFIG_DIR = Path.home() / ".qpypack"
-_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
-CONFIG_FILE = (_CONFIG_DIR / "config.ini").as_posix()
-
-PYPI_MIRRORS = [
- ("清华源", "https://pypi.tuna.tsinghua.edu.cn/simple"),
- ("阿里源", "https://mirrors.aliyun.com/pypi/simple/"),
- ("腾讯源", "https://mirrors.cloud.tencent.com/pypi/simple/"),
- ("华为源", "https://repo.huaweicloud.com/repository/pypi/simple/"),
- ("中科大源", "https://mirrors.ustc.edu.cn/pypi/simple/"),
- ("网易源", "https://mirrors.163.com/pypi/simple/"),
- ("PyPI 官方源", "https://pypi.org/simple"),
- ("Test PyPI 源", "https://test.pypi.org/simple"),
-]
-
-DEFAULT_MAPPINGS = {
- 'acoustid': 'pyacoustid',
- 'cv2': 'opencv-python',
- 'PIL': 'pillow',
- 'Pillow': 'pillow',
- 'fitz': 'pymupdf',
- 'skimage': 'scikit-image',
- 'vlc': 'python-vlc',
- 'pyzbar': 'pyzbar',
-
- 'docx': 'python-docx',
- 'pptx': 'python-pptx',
- 'bs4': 'beautifulsoup4',
- 'barcode': 'python-barcode',
- 'pdfplumber': 'pdfplumber',
-
- 'win32com': 'pywin32', 'win32api': 'pywin32', 'win32con': 'pywin32',
- 'win32gui': 'pywin32', 'win32clipboard': 'pywin32', 'win32print': 'pywin32',
- 'win32file': 'pywin32', 'win32security': 'pywin32', 'win32process': 'pywin32',
- 'win32evtlog': 'pywin32', 'win32service': 'pywin32', 'win32pipe': 'pywin32',
- 'win32net': 'pywin32', 'win32crypt': 'pywin32', 'pythoncom': 'pywin32',
- 'pywintypes': 'pywin32',
- 'serial': 'pyserial',
- 'usb': 'pyusb',
- 'bluetooth': 'pybluez',
-
- 'sklearn': 'scikit-learn',
- 'yaml': 'pyyaml',
- 'dateutil': 'python-dateutil',
- 'jwt': 'PyJWT',
- 'Crypto': 'pycryptodome',
- 'crypto': 'pycryptodome',
- 'OpenGL': 'PyOpenGL',
- 'dns': 'dnspython',
-
- 'wx': 'wxPython',
- 'desktop_notifier': 'desktop-notifier',
-
- 'dotenv': 'python-dotenv',
- 'telegram': 'python-telegram-bot',
- 'websocket': 'websocket-client',
- 'git': 'GitPython',
- 'github': 'PyGithub',
- 'gitlab': 'python-gitlab',
- 'discord': 'discord.py',
- 'paho': 'paho-mqtt',
- 'socketio': 'python-socketio',
- 'engineio': 'python-engineio',
- 'kafka': 'kafka-python',
-
- 'OpenSSL': 'pyOpenSSL',
- 'ldap': 'python-ldap',
- 'magic': 'python-magic',
- 'slugify': 'python-slugify',
- 'snappy': 'python-snappy'
-}
-
def load_config():
config = configparser.ConfigParser()
+ default_mirror = "https://pypi.org/simple"
+ default_backup = "https://test.pypi.org/simple"
+
+ if I18N.detect_system_language() == "zh_CN":
+ default_mirror = "https://pypi.tuna.tsinghua.edu.cn/simple"
+ default_backup = "https://mirrors.aliyun.com/pypi/simple/"
+
if not os.path.exists(CONFIG_FILE):
config['Mappings'] = DEFAULT_MAPPINGS
config['Settings'] = {
+ 'language': 'auto',
'engine': 'PyInstaller',
- 'pip_index': 'https://pypi.tuna.tsinghua.edu.cn/simple',
- 'pip_index_backup': 'https://mirrors.aliyun.com/pypi/simple/',
+ 'pip_index': default_mirror,
+ 'pip_index_backup': default_backup,
'onefile': 'True', 'noconsole': 'True', 'clean_all': 'True',
'auto_icon': 'True', 'use_venv': 'True', 'use_reqs': 'True',
'use_pipreqs': 'True', 'use_pipreqs_dir': 'False', 'upx': 'False', 'concise_log': 'True',
@@ -177,9 +442,12 @@ def load_config():
except: pass
if 'Settings' not in config: config['Settings'] = {}
+
+ updated = False
default_updates = {
- 'pip_index': 'https://pypi.tuna.tsinghua.edu.cn/simple',
- 'pip_index_backup': 'https://mirrors.aliyun.com/pypi/simple/',
+ 'language': 'auto',
+ 'pip_index': default_mirror,
+ 'pip_index_backup': default_backup,
'concise_log': 'True',
'cpu_cores': str(os.cpu_count() or 2),
'upx_path': '',
@@ -196,14 +464,17 @@ def load_config():
'pipreqs_version': '',
'lite_mode': 'False'
}
- updated = False
for k, v in default_updates.items():
if k not in config['Settings']:
config['Settings'][k] = v
updated = True
+
if updated:
try: save_config(config)
except: pass
+
+ lang_pref = config['Settings'].get('language', 'auto')
+ I18N.set_language(lang_pref)
return config
def save_config(config):
@@ -275,7 +546,6 @@ def create_pleasant_audio_files():
_AUDIO_EFFECT_REF = None
def play_alert(success=True):
- """优雅播放音频构建提示音"""
global _AUDIO_EFFECT_REF
try:
config = load_config()
@@ -397,7 +667,6 @@ class TableItemDelegate(QStyledItemDelegate):
def setup_combo_white_theme(combo: QComboBox, min_view_width: int = None):
ensure_arrow_icon()
-
list_view = QListView(combo)
combo.setView(list_view)
combo.setItemDelegate(ComboItemDelegate(combo))
@@ -409,71 +678,29 @@ def setup_combo_white_theme(combo: QComboBox, min_view_width: int = None):
if combo.view():
combo.view().setStyleSheet("""
QListView {
- background-color: #ffffff;
- color: #111827;
- border: 1px solid #d1d5db;
- border-radius: 6px;
- outline: none;
- padding: 4px;
- font-size: 12px;
+ background-color: #ffffff; color: #111827; border: 1px solid #d1d5db;
+ border-radius: 6px; outline: none; padding: 4px; font-size: 12px;
font-family: Consolas, "Segoe UI", sans-serif;
- selection-background-color: #2563eb;
- selection-color: #ffffff;
- }
- QListView::item {
- background-color: #ffffff;
- color: #111827;
- padding: 4px 8px;
- border-radius: 4px;
- }
- QListView::item:hover, QListView::item:selected {
- background-color: #2563eb;
- color: #ffffff;
+ selection-background-color: #2563eb; selection-color: #ffffff;
}
+ QListView::item { background-color: #ffffff; color: #111827; padding: 4px 8px; border-radius: 4px; }
+ QListView::item:hover, QListView::item:selected { background-color: #2563eb; color: #ffffff; }
""")
arrow_url = _ARROW_ICON_PATH.replace("\\", "/")
combo.setStyleSheet(f"""
QComboBox {{
- combobox-popup: 0;
- background-color: #ffffff;
- color: #111827;
- border: 1px solid #d1d5db;
- border-radius: 6px;
- padding: 5px 26px 5px 10px;
- font-size: 12px;
- min-height: 22px;
- font-family: Consolas, "Segoe UI", sans-serif;
- selection-background-color: #2563eb;
- selection-color: #ffffff;
- }}
- QComboBox:hover {{
- border-color: #9ca3af;
- }}
- QComboBox:focus {{
- border-color: #2563eb;
- }}
- QComboBox::drop-down {{
- subcontrol-origin: padding;
- subcontrol-position: top right;
- width: 24px;
- border: none;
- background: transparent;
- }}
- QComboBox::down-arrow {{
- image: url("{arrow_url}");
- width: 14px;
- height: 14px;
- }}
- QComboBox QLineEdit {{
- background-color: #ffffff;
- color: #111827;
- font-size: 12px;
- selection-background-color: #2563eb;
- selection-color: #ffffff;
+ combobox-popup: 0; background-color: #ffffff; color: #111827;
+ border: 1px solid #d1d5db; border-radius: 6px; padding: 5px 26px 5px 10px;
+ font-size: 12px; min-height: 22px; font-family: Consolas, "Segoe UI", sans-serif;
+ selection-background-color: #2563eb; selection-color: #ffffff;
}}
+ QComboBox:hover {{ border-color: #9ca3af; }}
+ QComboBox:focus {{ border-color: #2563eb; }}
+ QComboBox::drop-down {{ subcontrol-origin: padding; subcontrol-position: top right; width: 24px; border: none; background: transparent; }}
+ QComboBox::down-arrow {{ image: url("{arrow_url}"); width: 14px; height: 14px; }}
+ QComboBox QLineEdit {{ background-color: #ffffff; color: #111827; font-size: 12px; selection-background-color: #2563eb; selection-color: #ffffff; }}
""")
-
combo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
combo.setMinimumWidth(150)
@@ -485,19 +712,16 @@ class DropListWidget(QListWidget):
self.setAcceptDrops(True)
def dragEnterEvent(self, event: QDragEnterEvent):
- if event.mimeData().hasUrls():
- event.acceptProposedAction()
+ if event.mimeData().hasUrls(): event.acceptProposedAction()
def dragMoveEvent(self, event):
- if event.mimeData().hasUrls():
- event.acceptProposedAction()
+ if event.mimeData().hasUrls(): event.acceptProposedAction()
def dropEvent(self, event: QDropEvent):
urls = event.mimeData().urls()
if urls:
paths = [u.toLocalFile() for u in urls if u.toLocalFile()]
- if paths:
- self.itemsDropped.emit(paths)
+ if paths: self.itemsDropped.emit(paths)
def get_stdlib_names():
libs = {'os', 'sys', 're', 'math', 'time', 'datetime', 'json', 'urllib', 'sqlite3', 'csv',
@@ -519,7 +743,8 @@ def get_stdlib_names():
'unittest', 'uu', 'venv', 'wave', '__future__',
'weakref', 'webbrowser', 'winsound', 'wsgiref', 'xdrlib',
'xmlrpc', 'zipapp', 'zipimport', 'zlib', 'zoneinfo'}
- if sys.version_info >= (3, 10): libs.update(sys.stdlib_module_names)
+ if sys.version_info >= (3, 10):
+ libs.update(sys.stdlib_module_names)
return libs
STD_LIBS = get_stdlib_names()
@@ -535,7 +760,6 @@ def get_resource_path(relative_path):
def find_system_python():
candidates = []
-
for name in ('python', 'python3', 'pythonw'):
p = shutil.which(name)
if p: candidates.append(p)
@@ -553,9 +777,8 @@ def find_system_python():
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
text=True, encoding="utf-8", env=clean_env, creationflags=subprocess.CREATE_NO_WINDOW
)
- out, _ = proc.communicate(timeout=3)
- if out and os.path.exists(out.strip()):
- candidates.append(out.strip())
+ out, _err = proc.communicate(timeout=3)
+ if out and os.path.exists(out.strip()): candidates.append(out.strip())
except: pass
try:
@@ -620,8 +843,7 @@ def find_system_python():
for f in os.listdir(base):
if f.startswith("python3") or f == "python":
exe = os.path.join(base, f)
- if os.path.isfile(exe) and os.access(exe, os.X_OK):
- candidates.append(exe)
+ if os.path.isfile(exe) and os.access(exe, os.X_OK): candidates.append(exe)
except: pass
seen = set()
@@ -634,7 +856,6 @@ def find_system_python():
for cand in unique_candidates:
if not os.path.exists(cand): continue
-
if os.name == 'nt' and "WindowsApps" in cand:
try:
if os.path.getsize(cand) == 0: continue
@@ -648,10 +869,8 @@ def find_system_python():
kwargs = {"stdout": subprocess.PIPE, "stderr": subprocess.PIPE, "text": True, "encoding": "utf-8", "env": clean_env, "timeout": 3}
if os.name == 'nt': kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW
proc = subprocess.run([cand, "-V"], **kwargs)
- if proc.returncode == 0:
- return cand
- except:
- continue
+ if proc.returncode == 0: return cand
+ except: continue
return "python"
@@ -661,8 +880,7 @@ def get_python_executable():
custom_path = config['Settings'].get('custom_python_path', '').strip()
if custom_path and os.path.exists(custom_path) and os.path.isfile(custom_path):
return custom_path
- except Exception:
- pass
+ except Exception: pass
if getattr(sys, 'frozen', False) or '__compiled__' in globals():
return find_system_python()
@@ -674,7 +892,9 @@ def get_python_executable():
return find_system_python()
def remove_readonly(func, path, exc_info):
- try: os.chmod(path, stat.S_IWRITE); func(path)
+ try:
+ os.chmod(path, stat.S_IWRITE)
+ func(path)
except: pass
def robust_rmtree(path: Path, retries=15, delay=0.8):
@@ -686,21 +906,6 @@ def robust_rmtree(path: Path, retries=15, delay=0.8):
except: time.sleep(delay)
return False
-def parse_add_data(add_data_str):
- datas = []
- for d in add_data_str.split(','):
- d = d.strip()
- if not d: continue
- parts = d.rsplit(':', 1)
- if len(parts) == 2 and not (os.name == 'nt' and len(parts[0]) == 1):
- src_path = parts[0].strip()
- dst_path = parts[1].strip()
- else:
- src_path = d.strip()
- dst_path = "."
- datas.append((src_path, dst_path))
- return datas
-
def convert_image_to_format(src_path, dest_path, dest_format):
src = Path(src_path).resolve()
dst = Path(dest_path).resolve()
@@ -708,28 +913,18 @@ def convert_image_to_format(src_path, dest_path, dest_format):
if sys.platform == "darwin" and fmt == "icns":
try:
- proc = subprocess.run(
- ["sips", "-s", "format", "icns", src.as_posix(), "--out", dst.as_posix()],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
- )
- if proc.returncode == 0 and dst.exists():
- return True
- except:
- pass
+ proc = subprocess.run(["sips", "-s", "format", "icns", src.as_posix(), "--out", dst.as_posix()], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ if proc.returncode == 0 and dst.exists(): return True
+ except: pass
try:
img = QImage(src.as_posix())
if not img.isNull():
- if fmt == "ico":
- img = img.scaled(256, 256, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
- elif fmt == "icns":
- img = img.scaled(512, 512, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
-
+ if fmt == "ico": img = img.scaled(256, 256, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
+ elif fmt == "icns": img = img.scaled(512, 512, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
writer = QImageWriter(dst.as_posix(), fmt.upper().encode('utf-8'))
- if writer.write(img):
- return True
- except:
- pass
+ if writer.write(img): return True
+ except: pass
try:
from PIL import Image
@@ -743,13 +938,12 @@ def convert_image_to_format(src_path, dest_path, dest_format):
else:
img.save(dst.as_posix(), format=dest_format.upper())
return True
- except:
- pass
+ except: pass
return False
class AnimatedButton(QPushButton):
- def __init__(self, text, parent=None):
+ def __init__(self, text="", parent=None):
super().__init__(text, parent)
self.opacity_effect = QGraphicsOpacityEffect(self)
self.setGraphicsEffect(self.opacity_effect)
@@ -763,7 +957,6 @@ class AnimatedButton(QPushButton):
self.op_anim = QPropertyAnimation(self.opacity_effect, b"opacity")
self.op_anim.setDuration(200)
self.op_anim.setEasingCurve(QEasingCurve.Type.InOutCubic)
-
self.is_hovered = False
def enterEvent(self, event):
@@ -909,9 +1102,7 @@ class TargetIconWidget(QWidget):
self.update()
def paintEvent(self, event):
- if not self.pixmap or self.pixmap.isNull():
- return
-
+ if not self.pixmap or self.pixmap.isNull(): return
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
@@ -936,11 +1127,9 @@ class TargetIconWidget(QWidget):
elif self.burst_value > 0.0:
pop_scale = 1.0 + math.sin(self.burst_value * math.pi) * 0.15
draw_size = int(self.current_size * pop_scale)
-
if self.burst_value < 1.0:
alpha = int(255 * (1.0 - self.burst_value))
painter.setPen(Qt.PenStyle.NoPen)
-
painter.setBrush(QColor(26, 115, 232, alpha))
burst_radius_1 = (self.current_size / 2) + 10 + self.burst_value * 40
dot_size_1 = 8 * (1.0 - self.burst_value)
@@ -959,12 +1148,7 @@ class TargetIconWidget(QWidget):
dy = center.y() + math.sin(angle) * burst_radius_2
painter.drawEllipse(QPointF(dx, dy), dot_size_2, dot_size_2)
- pix_rect = QRectF(
- center_x - draw_size / 2.0,
- icon_center_y - draw_size / 2.0,
- float(draw_size),
- float(draw_size)
- )
+ pix_rect = QRectF(center_x - draw_size / 2.0, icon_center_y - draw_size / 2.0, float(draw_size), float(draw_size))
scaled_pix = self.pixmap.scaled(
int(draw_size * self.devicePixelRatioF()),
int(draw_size * self.devicePixelRatioF()),
@@ -984,25 +1168,25 @@ class DropArea(QFrame):
self.setAcceptDrops(True)
self.setFrameStyle(QFrame.Shape.NoFrame)
self.setCursor(Qt.CursorShape.PointingHandCursor)
+ self.current_filename = None
self.setStyleSheet("""
#DropArea { background-color: #f8f9fa; border: 2px dashed #dadce0; border-radius: 12px; }
#DropArea:hover { background-color: #f1f3f4; border: 2px dashed #bdc1c6; }
""")
self.init_ui()
+ I18N.language_changed.connect(self.retranslate_ui)
def _get_default_pixmap(self, size=88):
icon_path = get_resource_path("icon.ico")
if os.path.exists(icon_path):
pixmap = QIcon(icon_path).pixmap(256, 256)
- if not pixmap.isNull():
- return pixmap
+ if not pixmap.isNull(): return pixmap
return get_svg_pixmap('python', color="#9AA0A6", size=256)
def init_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(10, 20, 10, 20)
layout.setSpacing(0)
-
layout.addStretch(1)
self.icon_widget = TargetIconWidget(self)
@@ -1013,10 +1197,9 @@ class DropArea(QFrame):
h_layout.addWidget(self.icon_widget)
h_layout.addStretch(1)
layout.addLayout(h_layout)
-
layout.addSpacing(18)
- self.label = QLabel("将 Python 源代码文件 (.py/.pyw) 拖放到此处\n或 点击手动选择文件")
+ self.label = QLabel()
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.label.setWordWrap(True)
self.label.setStyleSheet("QLabel { background: transparent; color: #5F6368; font-size: 16px; font-weight: bold; border: none; }")
@@ -1024,13 +1207,21 @@ class DropArea(QFrame):
layout.addSpacing(8)
- self.sub_label = QLabel("自动解析模块依赖、资源文件与隐式导入")
+ self.sub_label = QLabel()
self.sub_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.sub_label.setWordWrap(True)
self.sub_label.setStyleSheet("QLabel { background: transparent; color: #9AA0A6; font-size: 13px; border: none; }")
layout.addWidget(self.sub_label)
-
layout.addStretch(1)
+
+ self.retranslate_ui()
+
+ def retranslate_ui(self):
+ if not self.current_filename:
+ self.label.setText(_("Drag & Drop Python script (.py/.pyw) here\nor Click to Browse"))
+ self.sub_label.setText(_("Auto-parse dependencies, resources, and implicit imports"))
+ else:
+ self.label.setText(_("Loaded: {filename}", filename=self.current_filename))
def dragEnterEvent(self, event: QDragEnterEvent):
if event.mimeData().hasUrls():
@@ -1052,41 +1243,41 @@ class DropArea(QFrame):
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
- fp, _ = QFileDialog.getOpenFileName(
+ fp, _filter = QFileDialog.getOpenFileName(
self,
- "选择 Python 源代码文件",
+ _("Drag & Drop Python script (.py/.pyw) here\nor Click to Browse"),
"",
"Python Scripts (*.py *.pyw);;All Files (*)"
)
if fp: self.fileDropped.emit(fp)
def set_loading(self, filename):
+ self.current_filename = filename
pixmap = get_svg_pixmap('package', color="#1A73E8", size=88)
self.icon_widget.set_file_pixmap(pixmap, 88)
- self.label.setText(f"已载入源文件:{filename}")
+ self.label.setText(_("Loaded: {filename}", filename=filename))
self.label.setStyleSheet("QLabel { background: transparent; color: #1A73E8; font-size: 16px; font-weight: bold; border: none; }")
- self.sub_label.setText("正在解析依赖与项目元数据,请稍候...")
+ self.sub_label.setText(_("Parsing metadata..."))
def set_success(self, filename, custom_icon_path=None):
+ self.current_filename = filename
pixmap = None
if custom_icon_path and Path(custom_icon_path).exists():
pixmap = QIcon(str(custom_icon_path)).pixmap(256, 256)
- if pixmap.isNull():
- pixmap = None
+ if pixmap.isNull(): pixmap = None
if not pixmap:
pixmap = get_svg_pixmap('package', color="#1A73E8", size=256)
self.icon_widget.set_file_pixmap(pixmap, 88)
-
- self.label.setText(f"已载入源文件:{filename}")
+ self.label.setText(_("Loaded: {filename}", filename=filename))
self.label.setStyleSheet("QLabel { background: transparent; color: #1A73E8; font-size: 16px; font-weight: bold; border: none; }")
- self.sub_label.setText("环境配置就绪,可执行构建")
+ self.sub_label.setText(_("Ready, waiting for build."))
def start_build_anim(self):
- self.label.setText("正在初始化构建流程...")
+ self.label.setText(_("Initializing build..."))
self.label.setStyleSheet("QLabel { background: transparent; color: #1A73E8; font-size: 16px; font-weight: bold; border: none; }")
- self.sub_label.setText("构建引擎准备中...")
+ self.sub_label.setText(_("Preparing engine..."))
self.icon_widget.start_building()
def stop_build_anim(self):
@@ -1108,32 +1299,30 @@ class DropArea(QFrame):
self.icon_widget.set_custom_pixmap(get_svg_pixmap('check', color="#1E8E3E", size=size), size)
self.icon_widget.start_success()
-
- self.label.setText("构建任务执行成功")
+ self.label.setText(_("Build Successful"))
self.label.setStyleSheet("QLabel { background: transparent; color: #1E8E3E; font-size: 20px; font-weight: bold; border: none; }")
- self.sub_label.setText("可打开输出目录查看构建产物,或重置当前工作区")
+ self.sub_label.setText(_("Open output directory or reset workspace."))
def show_failure(self):
size = 128
self.icon_widget.set_custom_pixmap(get_svg_pixmap('close', color="#D93025", size=size), size)
self.icon_widget.start_failure()
- self.label.setText("构建异常中断")
+ self.label.setText(_("Build Failed"))
self.label.setStyleSheet("QLabel { background: transparent; color: #D93025; font-size: 20px; font-weight: bold; border: none; }")
- self.sub_label.setText("请参阅下方编译日志以排查构建异常")
+ self.sub_label.setText(_("Check log output below for troubleshooting."))
def reset(self):
+ self.current_filename = None
self.icon_widget.reset()
- self.label.setText("将 Python 源代码文件 (.py/.pyw) 拖放到此处\n或 点击手动选择文件")
+ self.retranslate_ui()
self.label.setStyleSheet("QLabel { background: transparent; color: #5F6368; font-size: 16px; font-weight: bold; border: none; }")
- self.sub_label.setText("自动解析模块依赖、资源文件与隐式导入")
class PythonScannerThread(QThread):
scan_done = Signal(dict)
def run(self):
candidates = set()
-
for name in ('python', 'python3', 'pythonw'):
p = shutil.which(name)
if p: candidates.add(os.path.normpath(p))
@@ -1252,214 +1441,52 @@ class SettingsPanel(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.parent_win = parent
-
self.upx_check = None
self.upx_path_container = None
self.out_dir_container = None
self.setStyleSheet("""
- SettingsPanel {
- background-color: #f9fafb;
- }
- QLabel {
- color: #111827;
- font-size: 13px;
- font-weight: 600;
- background: transparent;
- }
-
- QLineEdit, QSpinBox {
- color: #111827;
- font-size: 12px;
- padding: 6px 10px;
- border: 1px solid #d1d5db;
- border-radius: 6px;
- background: #ffffff;
- min-height: 22px;
- }
- QLineEdit:hover, QSpinBox:hover {
- border-color: #9ca3af;
- }
- QLineEdit:focus, QSpinBox:focus {
- border: 1px solid #2563eb;
- background: #ffffff;
- }
-
- QCheckBox {
- font-size: 13px;
- color: #1f2937;
- spacing: 8px;
- background: transparent;
- }
- QCheckBox::indicator {
- width: 16px;
- height: 16px;
- border: 1px solid #d1d5db;
- border-radius: 4px;
- background: #ffffff;
- }
- QCheckBox::indicator:hover {
- border-color: #2563eb;
- }
- QCheckBox::indicator:checked {
- background: #2563eb;
- border-color: #2563eb;
- }
-
- QFrame#SettingCard {
- background-color: #ffffff;
- border: 1px solid #e5e7eb;
- border-radius: 8px;
- }
- QFrame#SettingCard:hover {
- border-color: #cbd5e1;
- }
-
- QPushButton.ToolBtn {
- background: #f3f4f6;
- border: 1px solid #e5e7eb;
- border-radius: 6px;
- padding: 6px 12px;
- color: #374151;
- font-weight: 600;
- font-size: 12px;
- min-width: 68px;
- }
- QPushButton.ToolBtn:hover {
- background: #e5e7eb;
- color: #111827;
- border-color: #d1d5db;
- }
- QPushButton.ToolBtn:pressed {
- background: #d1d5db;
- }
-
- /* === 主 TAB 样式 (居中对齐) === */
- QTabWidget#MainTabWidget::pane {
- border: none;
- background: transparent;
- }
- QTabWidget#MainTabWidget::tab-bar {
- alignment: center; /* ✅ 居中对齐 */
- }
- QTabBar#MainTabBar::tab {
- background: transparent;
- border: none;
- padding: 10px 24px;
- color: #6b7280;
- font-weight: 600;
- font-size: 14px;
- border-bottom: 2px solid transparent;
- margin: 0 16px;
- }
- QTabBar#MainTabBar::tab:selected {
- color: #2563eb;
- font-weight: 700;
- border-bottom: 2px solid #2563eb;
- }
- QTabBar#MainTabBar::tab:hover:!selected {
- color: #111827;
- }
-
- /* === 次 TAB 样式 (胶囊型 - 居中对齐) === */
- QTabWidget#SubTabWidget::pane {
- border: none;
- background: transparent;
- }
- QTabWidget#SubTabWidget::tab-bar {
- alignment: center; /* ✅ 居中对齐 */
- }
- QTabBar#SubTabBar::tab {
- background: #f3f4f6;
- border: 1px solid #e5e7eb;
- padding: 6px 16px;
- color: #4b5563;
- font-weight: 600;
- font-size: 12px;
- margin: 0 4px 10px 4px;
- border-radius: 6px;
- }
- QTabBar#SubTabBar::tab:selected {
- background: #eff6ff;
- color: #2563eb;
- border: 1px solid #bfdbfe;
- font-weight: 700;
- }
- QTabBar#SubTabBar::tab:hover:!selected {
- background: #e5e7eb;
- color: #111827;
- }
-
- QListWidget, QTableWidget {
- border: 1px solid #e5e7eb;
- border-radius: 6px;
- background-color: #ffffff;
- outline: none;
- font-size: 12px;
- gridline-color: #f3f4f6;
- }
- QListWidget::item {
- padding: 6px 10px;
- border-bottom: 1px solid #f3f4f6;
- color: #1f2937;
- }
- QListWidget::item:selected {
- background-color: #eff6ff;
- color: #2563eb;
- font-weight: 600;
- }
-
- /* === 修复 QTableWidget 单元格与重影问题 === */
- QTableWidget::item {
- padding: 4px 8px;
- color: #1f2937;
- border-bottom: 1px solid #f3f4f6;
- background-color: #ffffff;
- }
- QTableWidget::item:selected {
- background-color: #eff6ff;
- color: #2563eb;
- font-weight: 600;
- }
-
- /* === 彻底修复表头黑色块 Bug === */
- QHeaderView {
- background-color: #f8fafc;
- }
- QHeaderView::section {
- background-color: #f8fafc;
- color: #475569;
- font-size: 12px;
- font-weight: 700;
- padding: 6px 10px;
- border: none;
- border-bottom: 1px solid #e2e8f0;
- border-right: 1px solid #f1f5f9;
- }
- QHeaderView::section:horizontal {
- border-top: none;
- }
-
- QScrollBar:vertical {
- border: none;
- background: transparent;
- width: 6px;
- margin: 0px;
- }
- QScrollBar::handle:vertical {
- background: #d1d5db;
- min-height: 20px;
- border-radius: 3px;
- }
- QScrollBar::handle:vertical:hover {
- background: #9ca3af;
- }
- QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
- height: 0px;
- }
+ SettingsPanel { background-color: #f9fafb; }
+ QLabel { color: #111827; font-size: 13px; font-weight: 600; background: transparent; }
+ QLineEdit, QSpinBox { color: #111827; font-size: 12px; padding: 6px 10px; border: 1px solid #d1d5db; border-radius: 6px; background: #ffffff; min-height: 22px; }
+ QLineEdit:hover, QSpinBox:hover { border-color: #9ca3af; }
+ QLineEdit:focus, QSpinBox:focus { border: 1px solid #2563eb; background: #ffffff; }
+ QCheckBox { font-size: 13px; color: #1f2937; spacing: 8px; background: transparent; }
+ QCheckBox::indicator { width: 16px; height: 16px; border: 1px solid #d1d5db; border-radius: 4px; background: #ffffff; }
+ QCheckBox::indicator:hover { border-color: #2563eb; }
+ QCheckBox::indicator:checked { background: #2563eb; border-color: #2563eb; }
+ QFrame#SettingCard { background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 8px; }
+ QFrame#SettingCard:hover { border-color: #cbd5e1; }
+ QPushButton.ToolBtn { background: #f3f4f6; border: 1px solid #e5e7eb; border-radius: 6px; padding: 6px 12px; color: #374151; font-weight: 600; font-size: 12px; min-width: 68px; }
+ QPushButton.ToolBtn:hover { background: #e5e7eb; color: #111827; border-color: #d1d5db; }
+ QPushButton.ToolBtn:pressed { background: #d1d5db; }
+ QTabWidget#MainTabWidget::pane { border: none; background: transparent; }
+ QTabWidget#MainTabWidget::tab-bar { alignment: center; }
+ QTabBar#MainTabBar::tab { background: transparent; border: none; padding: 10px 24px; color: #6b7280; font-weight: 600; font-size: 14px; border-bottom: 2px solid transparent; margin: 0 16px; }
+ QTabBar#MainTabBar::tab:selected { color: #2563eb; font-weight: 700; border-bottom: 2px solid #2563eb; }
+ QTabBar#MainTabBar::tab:hover:!selected { color: #111827; }
+ QTabWidget#SubTabWidget::pane { border: none; background: transparent; }
+ QTabWidget#SubTabWidget::tab-bar { alignment: center; }
+ QTabBar#SubTabBar::tab { background: #f3f4f6; border: 1px solid #e5e7eb; padding: 6px 16px; color: #4b5563; font-weight: 600; font-size: 12px; margin: 0 4px 10px 4px; border-radius: 6px; }
+ QTabBar#SubTabBar::tab:selected { background: #eff6ff; color: #2563eb; border: 1px solid #bfdbfe; font-weight: 700; }
+ QTabBar#SubTabBar::tab:hover:!selected { background: #e5e7eb; color: #111827; }
+ QListWidget, QTableWidget { border: 1px solid #e5e7eb; border-radius: 6px; background-color: #ffffff; outline: none; font-size: 12px; gridline-color: #f3f4f6; }
+ QListWidget::item { padding: 6px 10px; border-bottom: 1px solid #f3f4f6; color: #1f2937; }
+ QListWidget::item:selected { background-color: #eff6ff; color: #2563eb; font-weight: 600; }
+ QTableWidget::item { padding: 4px 8px; color: #1f2937; border-bottom: 1px solid #f3f4f6; background-color: #ffffff; }
+ QTableWidget::item:selected { background-color: #eff6ff; color: #2563eb; font-weight: 600; }
+ QHeaderView { background-color: #f8fafc; }
+ QHeaderView::section { background-color: #f8fafc; color: #475569; font-size: 12px; font-weight: 700; padding: 6px 10px; border: none; border-bottom: 1px solid #e2e8f0; border-right: 1px solid #f1f5f9; }
+ QHeaderView::section:horizontal { border-top: none; }
+ QScrollBar:vertical { border: none; background: transparent; width: 6px; margin: 0px; }
+ QScrollBar::handle:vertical { background: #d1d5db; min-height: 20px; border-radius: 3px; }
+ QScrollBar::handle:vertical:hover { background: #9ca3af; }
+ QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }
""")
self.init_ui()
self.load_from_config()
+
+ I18N.language_changed.connect(self.retranslate_ui)
self.scanner_thread = PythonScannerThread()
self.scanner_thread.scan_done.connect(self.populate_python_combo)
@@ -1473,6 +1500,7 @@ class SettingsPanel(QWidget):
lay.setSpacing(12)
if title_text:
lbl = QLabel(title_text)
+ lbl.setObjectName("CardTitle")
lbl.setStyleSheet("font-size: 15px; font-weight: 800; color: #1a73e8; margin-bottom: 6px;")
lay.addWidget(lbl)
return card, lay
@@ -1482,11 +1510,13 @@ class SettingsPanel(QWidget):
scroll.setWidgetResizable(True)
scroll.setFrameShape(QFrame.Shape.NoFrame)
scroll.setStyleSheet("background: transparent;")
+
content = QWidget()
content.setStyleSheet("background: transparent;")
lay = QVBoxLayout(content)
lay.setContentsMargins(0, 5, 0, 15)
lay.setSpacing(15)
+
scroll.setWidget(content)
return scroll, content, lay
@@ -1496,10 +1526,8 @@ class SettingsPanel(QWidget):
data = combo.itemData(idx)
if data: return data
text = combo.currentText().strip()
- if "http://" in text or "https://" in text:
- m = re.search(r'https?://[^\s]+', text)
- if m: return m.group(0)
- return text
+ m = re.search(r'https?://[^\s]+', text)
+ return m.group(0) if m else text
def _set_combo_value(self, combo, url_val):
url_val = (url_val or '').strip()
@@ -1522,17 +1550,16 @@ class SettingsPanel(QWidget):
self.tabs.tabBar().setExpanding(False)
self.tab_build = QWidget()
- self.tab_pref_scroll, _, self.lay_pref = self._create_scroll_tab()
+ self.tab_pref_scroll, _cnt_pref, self.lay_pref = self._create_scroll_tab()
self.tab_about = QWidget()
- self.tabs.addTab(self.tab_build, get_svg_icon('package', "#5F6368", 16), "打包配置")
- self.tabs.addTab(self.tab_pref_scroll, get_svg_icon('settings', "#5F6368", 16), "全局设置")
- self.tabs.addTab(self.tab_about, get_svg_icon('info', "#5F6368", 16), "关于")
+ self.tabs.addTab(self.tab_build, get_svg_icon('package', "#5F6368", 16), _("Build Settings"))
+ self.tabs.addTab(self.tab_pref_scroll, get_svg_icon('settings', "#5F6368", 16), _("Preferences"))
+ self.tabs.addTab(self.tab_about, get_svg_icon('info', "#5F6368", 16), _("About"))
self.build_build_master_tab()
self.build_pref_tab()
self.build_about_tab()
-
layout.addWidget(self.tabs)
btn_lay = QHBoxLayout()
@@ -1542,12 +1569,12 @@ class SettingsPanel(QWidget):
self.btn_reset = AnimatedButton("")
self.btn_reset.setFixedSize(44, 44)
self.btn_reset.setIcon(get_svg_icon('refresh', "#5F6368"))
- self.btn_reset.setToolTip("重置为默认配置")
+ self.btn_reset.setToolTip(_("Reset to Default Config"))
self.btn_reset.setStyleSheet(self.parent_win.icon_btn_style)
self.btn_reset.clicked.connect(self.parent_win.reset_all)
btn_lay.addWidget(self.btn_reset)
- self.btn_save = AnimatedButton(" 保存并返回")
+ self.btn_save = AnimatedButton(_("Save & Return"))
self.btn_save.setFixedHeight(44)
self.btn_save.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.btn_save.setIcon(get_svg_icon('check', "white"))
@@ -1558,7 +1585,7 @@ class SettingsPanel(QWidget):
self.btn_back = AnimatedButton("")
self.btn_back.setFixedSize(44, 44)
self.btn_back.setIcon(get_svg_icon('back', "#5F6368"))
- self.btn_back.setToolTip("放弃修改并返回")
+ self.btn_back.setToolTip(_("Cancel & Return"))
self.btn_back.setStyleSheet(self.parent_win.icon_btn_style)
self.btn_back.clicked.connect(self.parent_win.show_main)
btn_lay.addWidget(self.btn_back)
@@ -1574,22 +1601,22 @@ class SettingsPanel(QWidget):
self.sub_tabs.tabBar().setObjectName("SubTabBar")
self.sub_tabs.tabBar().setExpanding(False)
- sub_scroll1, _, lay_sub1 = self._create_scroll_tab()
- sub_scroll2, _, lay_sub2 = self._create_scroll_tab()
- sub_scroll3, _, lay_sub3 = self._create_scroll_tab()
- sub_scroll4, _, lay_sub4 = self._create_scroll_tab()
- sub_scroll5, _, lay_sub5 = self._create_scroll_tab()
+ sub_scroll1, _cnt1, lay_sub1 = self._create_scroll_tab()
+ sub_scroll2, _cnt2, lay_sub2 = self._create_scroll_tab()
+ sub_scroll3, _cnt3, lay_sub3 = self._create_scroll_tab()
+ sub_scroll4, _cnt4, lay_sub4 = self._create_scroll_tab()
+ sub_scroll5, _cnt5, lay_sub5 = self._create_scroll_tab()
- self.sub_tabs.addTab(sub_scroll1, "🚀 引擎与模式")
- self.sub_tabs.addTab(sub_scroll2, "📦 依赖分析")
- self.sub_tabs.addTab(sub_scroll3, "📂 附加资源")
- self.sub_tabs.addTab(sub_scroll4, "⚡ 性能与优化")
- self.sub_tabs.addTab(sub_scroll5, "🗺️ 包名映射")
+ self.sub_tabs.addTab(sub_scroll1, _("🚀 Engine"))
+ self.sub_tabs.addTab(sub_scroll2, _("📦 Dependencies"))
+ self.sub_tabs.addTab(sub_scroll3, _("📂 Resources"))
+ self.sub_tabs.addTab(sub_scroll4, _("⚡ Optimization"))
+ self.sub_tabs.addTab(sub_scroll5, _("🗺️ Package Map"))
- card_engine, c_lay_engine = self._create_card("构建引擎与环境")
- form_engine = QFormLayout()
- form_engine.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
- form_engine.setSpacing(15)
+ self.card_engine, c_lay_engine = self._create_card(_("Engine & Environment"))
+ self.form_engine = QFormLayout()
+ self.form_engine.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
+ self.form_engine.setSpacing(15)
self.engine_combo = QComboBox()
self.engine_combo.addItems(["PyInstaller", "Nuitka"])
@@ -1599,16 +1626,7 @@ class SettingsPanel(QWidget):
self.engine_desc_lbl = QLabel()
self.engine_desc_lbl.setWordWrap(True)
self.engine_desc_lbl.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
- self.engine_desc_lbl.setStyleSheet("""
- QLabel {
- background-color: #f8fafc;
- color: #475569;
- border: 1px solid #e2e8f0;
- border-radius: 6px;
- padding: 9px 18px;
- font-size: 12px;
- }
- """)
+ self.engine_desc_lbl.setStyleSheet("QLabel { background-color: #f8fafc; color: #475569; border: 1px solid #e2e8f0; border-radius: 6px; padding: 9px 18px; font-size: 12px; }")
engine_cont = QWidget()
lay_eng = QVBoxLayout(engine_cont)
@@ -1617,242 +1635,379 @@ class SettingsPanel(QWidget):
lay_eng.addWidget(self.engine_combo)
lay_eng.addWidget(self.engine_desc_lbl)
- self.python_path_combo = QComboBox(); self.python_path_combo.setEditable(True)
- self.python_path_combo.setPlaceholderText("留空则自动检索当前环境下的默认 Python 解释器")
+ self.python_path_combo = QComboBox()
+ self.python_path_combo.setEditable(True)
+ self.python_path_combo.setPlaceholderText(_("Leave blank to auto-detect system default Python"))
setup_combo_white_theme(self.python_path_combo, min_view_width=520)
- btn_python_path = QPushButton("浏览"); btn_python_path.setProperty("class", "ToolBtn"); btn_python_path.clicked.connect(self.select_python_path)
- py_cont = QWidget(); h_py = QHBoxLayout(py_cont); h_py.setContentsMargins(0,0,0,0)
- h_py.addWidget(self.python_path_combo, 1); h_py.addWidget(btn_python_path)
+ self.btn_python_path = QPushButton(_("Browse"))
+ self.btn_python_path.setProperty("class", "ToolBtn")
+ self.btn_python_path.clicked.connect(self.select_python_path)
+
+ py_cont = QWidget()
+ h_py = QHBoxLayout(py_cont)
+ h_py.setContentsMargins(0,0,0,0)
+ h_py.addWidget(self.python_path_combo, 1)
+ h_py.addWidget(self.btn_python_path)
- self.name_edit = QLineEdit(); self.name_edit.setPlaceholderText("默认自适应源文件名")
+ self.name_edit = QLineEdit()
+ self.name_edit.setPlaceholderText(_("Leave blank to auto-match script name"))
self.icon_edit = QLineEdit()
- self.icon_preview = QLabel(); self.icon_preview.setFixedSize(24, 24); self.icon_preview.setScaledContents(True)
+ self.icon_preview = QLabel()
+ self.icon_preview.setFixedSize(24, 24)
+ self.icon_preview.setScaledContents(True)
self.icon_edit.textChanged.connect(self.update_icon_preview)
- btn_icon = QPushButton("浏览"); btn_icon.setProperty("class", "ToolBtn"); btn_icon.clicked.connect(self.select_icon)
- icon_cont = QWidget(); h_icon = QHBoxLayout(icon_cont); h_icon.setContentsMargins(0,0,0,0)
- h_icon.addWidget(self.icon_edit, 1); h_icon.addWidget(self.icon_preview); h_icon.addWidget(btn_icon)
+
+ self.btn_icon = QPushButton(_("Browse"))
+ self.btn_icon.setProperty("class", "ToolBtn")
+ self.btn_icon.clicked.connect(self.select_icon)
+
+ icon_cont = QWidget()
+ h_icon = QHBoxLayout(icon_cont)
+ h_icon.setContentsMargins(0,0,0,0)
+ h_icon.addWidget(self.icon_edit, 1)
+ h_icon.addWidget(self.icon_preview)
+ h_icon.addWidget(self.btn_icon)
- form_engine.addRow("构建引擎:", engine_cont)
- form_engine.addRow("Python 解释器:", py_cont)
- form_engine.addRow("输出程序名称:", self.name_edit)
- form_engine.addRow("应用图标 (Icon):", icon_cont)
- c_lay_engine.addLayout(form_engine)
+ self.lbl_eng_title = QLabel(_("Build Engine:"))
+ self.lbl_py_title = QLabel(_("Python Interpreter:"))
+ self.lbl_app_title = QLabel(_("Output Name:"))
+ self.lbl_icon_title = QLabel(_("App Icon:"))
- card_mode, c_lay_mode = self._create_card("运行模式")
+ self.form_engine.addRow(self.lbl_eng_title, engine_cont)
+ self.form_engine.addRow(self.lbl_py_title, py_cont)
+ self.form_engine.addRow(self.lbl_app_title, self.name_edit)
+ self.form_engine.addRow(self.lbl_icon_title, icon_cont)
+ c_lay_engine.addLayout(self.form_engine)
+
+ self.card_mode, c_lay_mode = self._create_card(_("Execution Mode"))
h_mode = QHBoxLayout()
- self.onefile_check = QCheckBox("单文件打包模式 (Standalone)")
- self.noconsole_check = QCheckBox("无控制台模式 (隐藏终端窗口)")
+ self.onefile_check = QCheckBox(_("One-File Mode (--onefile)"))
+ self.noconsole_check = QCheckBox(_("Hide Console (--noconsole)"))
h_mode.addWidget(self.onefile_check)
h_mode.addWidget(self.noconsole_check)
h_mode.addStretch()
c_lay_mode.addLayout(h_mode)
- lay_sub1.addWidget(card_engine)
- lay_sub1.addWidget(card_mode)
+ lay_sub1.addWidget(self.card_engine)
+ lay_sub1.addWidget(self.card_mode)
lay_sub1.addStretch()
- card_deps, c_lay_deps = self._create_card("镜像源与扫描配置")
- form_deps = QFormLayout()
- form_deps.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
- form_deps.setSpacing(15)
+ self.card_deps, c_lay_deps = self._create_card(_("Mirrors & Scanner"))
+ self.form_deps = QFormLayout()
+ self.form_deps.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
+ self.form_deps.setSpacing(15)
- self.pip_source_combo = QComboBox(); self.pip_source_combo.setEditable(True)
+ self.pip_source_combo = QComboBox()
+ self.pip_source_combo.setEditable(True)
setup_combo_white_theme(self.pip_source_combo, min_view_width=520)
- self.pip_backup_combo = QComboBox(); self.pip_backup_combo.setEditable(True)
+ self.pip_backup_combo = QComboBox()
+ self.pip_backup_combo.setEditable(True)
setup_combo_white_theme(self.pip_backup_combo, min_view_width=520)
- for name, url in PYPI_MIRRORS:
+ for name, url in PYPI_MIRRORS_GLOBAL:
display_text = f"{name}: {url}"
self.pip_source_combo.addItem(display_text, url)
self.pip_backup_combo.addItem(display_text, url)
+
self.pip_source_combo.currentTextChanged.connect(self._check_pip_mirrors)
self.pip_backup_combo.currentTextChanged.connect(self._check_pip_mirrors)
- self.reqs_file_edit = QLineEdit(); self.reqs_file_edit.setPlaceholderText("留空则自动检索当前目录下的 requirements.txt")
- btn_reqs = QPushButton("浏览"); btn_reqs.setProperty("class", "ToolBtn"); btn_reqs.clicked.connect(self.select_reqs_file)
- reqs_cont = QWidget(); h_reqs = QHBoxLayout(reqs_cont); h_reqs.setContentsMargins(0,0,0,0)
- h_reqs.addWidget(self.reqs_file_edit, 1); h_reqs.addWidget(btn_reqs)
+ self.reqs_file_edit = QLineEdit()
+ self.reqs_file_edit.setPlaceholderText(_("Leave blank to auto-search requirements.txt in current directory"))
+ self.btn_reqs = QPushButton(_("Browse"))
+ self.btn_reqs.setProperty("class", "ToolBtn")
+ self.btn_reqs.clicked.connect(self.select_reqs_file)
- self.hidden_edit = QLineEdit(); self.hidden_edit.setPlaceholderText("显式指定未被隐式导入的依赖,英文逗号分隔 (如: pandas, PyQt5)")
- btn_scan = QPushButton("AST扫描"); btn_scan.setProperty("class", "ToolBtn"); btn_scan.clicked.connect(self.auto_scan_hidden)
- hid_cont = QWidget(); h_hid = QHBoxLayout(hid_cont); h_hid.setContentsMargins(0,0,0,0)
- h_hid.addWidget(self.hidden_edit, 1); h_hid.addWidget(btn_scan)
+ reqs_cont = QWidget()
+ h_reqs = QHBoxLayout(reqs_cont)
+ h_reqs.setContentsMargins(0,0,0,0)
+ h_reqs.addWidget(self.reqs_file_edit, 1)
+ h_reqs.addWidget(self.btn_reqs)
+
+ self.hidden_edit = QLineEdit()
+ self.hidden_edit.setPlaceholderText(_("Comma separated (e.g. pandas, PyQt5)"))
+ self.btn_scan = QPushButton(_("AST Scan"))
+ self.btn_scan.setProperty("class", "ToolBtn")
+ self.btn_scan.clicked.connect(self.auto_scan_hidden)
+
+ hid_cont = QWidget()
+ h_hid = QHBoxLayout(hid_cont)
+ h_hid.setContentsMargins(0,0,0,0)
+ h_hid.addWidget(self.hidden_edit, 1)
+ h_hid.addWidget(self.btn_scan)
- self.exclude_edit = QLineEdit(); self.exclude_edit.setPlaceholderText("指定不进行打包的模块列表,英文逗号分隔 (如: tkinter, matplotlib)")
+ self.exclude_edit = QLineEdit()
+ self.exclude_edit.setPlaceholderText(_("Comma separated (e.g. tkinter, matplotlib)"))
- form_deps.addRow("PIP 主镜像源:", self.pip_source_combo)
- form_deps.addRow("PIP 备用源:", self.pip_backup_combo)
- form_deps.addRow("依赖清单 (requirements):", reqs_cont)
- form_deps.addRow("隐式导入 (Hidden Imports):", hid_cont)
- form_deps.addRow("排除模块 (Excludes):", self.exclude_edit)
- c_lay_deps.addLayout(form_deps)
+ self.lbl_pip_main = QLabel(_("Primary PIP Index:"))
+ self.lbl_pip_backup = QLabel(_("Backup PIP Index:"))
+ self.lbl_reqs = QLabel(_("Requirements File:"))
+ self.lbl_hidden = QLabel(_("Hidden Imports:"))
+ self.lbl_exclude = QLabel(_("Exclude Modules:"))
+
+ self.form_deps.addRow(self.lbl_pip_main, self.pip_source_combo)
+ self.form_deps.addRow(self.lbl_pip_backup, self.pip_backup_combo)
+ self.form_deps.addRow(self.lbl_reqs, reqs_cont)
+ self.form_deps.addRow(self.lbl_hidden, hid_cont)
+ self.form_deps.addRow(self.lbl_exclude, self.exclude_edit)
+ c_lay_deps.addLayout(self.form_deps)
c_lay_deps.addSpacing(5)
- g_dep = QGridLayout(); g_dep.setSpacing(10)
- self.venv_check = QCheckBox("启用独立虚拟环境打包 (推荐)")
- self.reqs_check = QCheckBox("同步安装 requirements.txt 声明")
- self.pipreqs_check = QCheckBox("自动分析并补全项目依赖 (pipreqs)")
- self.pipreqs_dir_check = QCheckBox("允许 pipreqs 扫描同目录所有文件")
+ g_dep = QGridLayout()
+ g_dep.setSpacing(10)
+
+ self.venv_check = QCheckBox(_("Use Virtual Environment (Recommended)"))
+ self.reqs_check = QCheckBox(_("Install requirements.txt"))
+ self.pipreqs_check = QCheckBox(_("Analyze Dependencies (pipreqs)"))
+ self.pipreqs_dir_check = QCheckBox(_("Scan Entire Folder"))
+
g_dep.addWidget(self.venv_check, 0, 0)
g_dep.addWidget(self.reqs_check, 0, 1)
g_dep.addWidget(self.pipreqs_check, 1, 0)
g_dep.addWidget(self.pipreqs_dir_check, 1, 1)
c_lay_deps.addLayout(g_dep)
- lay_sub2.addWidget(card_deps)
+ lay_sub2.addWidget(self.card_deps)
lay_sub2.addStretch()
- card_res, c_lay_res = self._create_card("附加资源文件与目录 (支持桌面拖拽进列表)")
+ self.card_res, c_lay_res = self._create_card(_("Additional Resources (Drag & Drop Supported)"))
self.add_data_list = DropListWidget()
self.add_data_list.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection)
self.add_data_list.setFixedHeight(180)
- self.add_data_list.setToolTip("双击修改目标路径;支持直接从桌面拖拽文件或文件夹至此区域")
+ self.add_data_list.setToolTip(_("Double-click to edit target path; Drag & drop supported"))
+
self.add_data_list.itemDoubleClicked.connect(self.edit_resource)
self.add_data_list.itemsDropped.connect(self.on_resources_dropped)
c_lay_res.addWidget(self.add_data_list)
btn_res_lay = QHBoxLayout()
- self.btn_add_file = QPushButton("添加文件"); self.btn_add_file.setProperty("class", "ToolBtn"); self.btn_add_file.clicked.connect(self.add_resource_files)
- self.btn_add_dir = QPushButton("添加目录"); self.btn_add_dir.setProperty("class", "ToolBtn"); self.btn_add_dir.clicked.connect(self.add_resource_dir)
- self.btn_del_res = QPushButton("删除选中"); self.btn_del_res.setProperty("class", "ToolBtn"); self.btn_del_res.clicked.connect(self.del_resource)
- self.btn_clear_res = QPushButton("清空"); self.btn_clear_res.setProperty("class", "ToolBtn"); self.btn_clear_res.clicked.connect(self.clear_resource)
- btn_res_lay.addWidget(self.btn_add_file); btn_res_lay.addWidget(self.btn_add_dir); btn_res_lay.addWidget(self.btn_del_res); btn_res_lay.addWidget(self.btn_clear_res); btn_res_lay.addStretch()
+ self.btn_add_file = QPushButton(_("Add File"))
+ self.btn_add_file.setProperty("class", "ToolBtn")
+ self.btn_add_file.clicked.connect(self.add_resource_files)
+
+ self.btn_add_dir = QPushButton(_("Add Dir"))
+ self.btn_add_dir.setProperty("class", "ToolBtn")
+ self.btn_add_dir.clicked.connect(self.add_resource_dir)
+
+ self.btn_del_res = QPushButton(_("Remove Selected"))
+ self.btn_del_res.setProperty("class", "ToolBtn")
+ self.btn_del_res.clicked.connect(self.del_resource)
+
+ self.btn_clear_res = QPushButton(_("Clear All"))
+ self.btn_clear_res.setProperty("class", "ToolBtn")
+ self.btn_clear_res.clicked.connect(self.clear_resource)
+
+ btn_res_lay.addWidget(self.btn_add_file)
+ btn_res_lay.addWidget(self.btn_add_dir)
+ btn_res_lay.addWidget(self.btn_del_res)
+ btn_res_lay.addWidget(self.btn_clear_res)
+ btn_res_lay.addStretch()
c_lay_res.addLayout(btn_res_lay)
- lay_sub3.addWidget(card_res)
+ lay_sub3.addWidget(self.card_res)
lay_sub3.addStretch()
- card_opt, c_lay_opt = self._create_card("性能与编译优化")
- form_opt = QFormLayout()
- form_opt.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
- form_opt.setSpacing(15)
- self.cores_spin = QSpinBox(); self.cores_spin.setRange(1, os.cpu_count() or 4); self.cores_spin.setValue(os.cpu_count() or 2)
- form_opt.addRow("并发构建 CPU 核心数:", self.cores_spin)
+ self.card_opt, c_lay_opt = self._create_card(_("Performance Optimization"))
+ self.form_opt = QFormLayout()
+ self.form_opt.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
+ self.form_opt.setSpacing(15)
- self.upx_check = QCheckBox("启用 UPX 可执行文件压缩")
+ self.cores_spin = QSpinBox()
+ self.cores_spin.setRange(1, os.cpu_count() or 4)
+ self.cores_spin.setValue(os.cpu_count() or 2)
+
+ self.lbl_cpu_cores = QLabel(_("CPU Cores:"))
+ self.form_opt.addRow(self.lbl_cpu_cores, self.cores_spin)
+
+ self.upx_check = QCheckBox(_("Enable UPX Compression"))
self.upx_check.toggled.connect(self.on_upx_toggled)
- self.upx_path_edit = QLineEdit(); self.upx_path_edit.setPlaceholderText("留空则自动检测环境变量")
- btn_upx = QPushButton("选择"); btn_upx.setProperty("class", "ToolBtn"); btn_upx.clicked.connect(self.select_upx_path)
- self.upx_path_container = QWidget(); h_upx = QHBoxLayout(self.upx_path_container); h_upx.setContentsMargins(0,0,0,0)
- h_upx.addWidget(self.upx_path_edit, 1); h_upx.addWidget(btn_upx)
+
+ self.upx_path_edit = QLineEdit()
+ self.upx_path_edit.setPlaceholderText(_("Leave blank to auto-detect from environment variables"))
+
+ self.btn_upx = QPushButton(_("Browse"))
+ self.btn_upx.setProperty("class", "ToolBtn")
+ self.btn_upx.clicked.connect(self.select_upx_path)
+
+ self.upx_path_container = QWidget()
+ h_upx = QHBoxLayout(self.upx_path_container)
+ h_upx.setContentsMargins(0,0,0,0)
+ h_upx.addWidget(self.upx_path_edit, 1)
+ h_upx.addWidget(self.btn_upx)
self.upx_path_container.setVisible(False)
h_upx_row = QHBoxLayout()
h_upx_row.addWidget(self.upx_check)
h_upx_row.addWidget(self.upx_path_container)
- form_opt.addRow("UPX 工具路径:", h_upx_row)
- self.lite_mode_check = QCheckBox("启用精简打包模式 (自动排除开发与测试依赖)")
+ self.lbl_upx_path = QLabel(_("UPX Path:"))
+ self.form_opt.addRow(self.lbl_upx_path, h_upx_row)
+
+ self.lite_mode_check = QCheckBox(_("Lite Mode (Exclude Dev/Test Dependencies)"))
self.lite_mode_check.setStyleSheet("color: #D93025; font-weight: bold;")
- self.lite_mode_check.setToolTip("动态排除构建环境冗余依赖,提升构建速度并缩减产物体积。")
- c_lay_opt.addLayout(form_opt)
+ self.lite_mode_check.setToolTip(_("Dynamically exclude redundant dependencies in build environment, improving speed and reducing size."))
+
+ c_lay_opt.addLayout(self.form_opt)
c_lay_opt.addWidget(self.lite_mode_check)
- card_ver, c_lay_ver = self._create_card("锁定核心依赖版本")
- form_ver = QFormLayout()
- form_ver.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
- form_ver.setSpacing(15)
- self.pyi_ver_edit = QLineEdit(); self.pyi_ver_edit.setPlaceholderText("默认最新版 (指定版本如 6.6.0)")
- self.nuitka_ver_edit = QLineEdit(); self.nuitka_ver_edit.setPlaceholderText("默认最新版 (指定版本如 4.1.3)")
- self.pipreqs_ver_edit = QLineEdit(); self.pipreqs_ver_edit.setPlaceholderText("默认最新版 (指定版本如 0.4.13)")
- form_ver.addRow("PyInstaller 版本:", self.pyi_ver_edit)
- form_ver.addRow("Nuitka 版本:", self.nuitka_ver_edit)
- form_ver.addRow("Pipreqs 版本:", self.pipreqs_ver_edit)
- c_lay_ver.addLayout(form_ver)
+ self.card_ver, c_lay_ver = self._create_card(_("Lock Core Dependencies"))
+ self.form_ver = QFormLayout()
+ self.form_ver.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
+ self.form_ver.setSpacing(15)
+
+ self.pyi_ver_edit = QLineEdit()
+ self.nuitka_ver_edit = QLineEdit()
+ self.pipreqs_ver_edit = QLineEdit()
+
+ self.lbl_pyi_ver = QLabel(_("PyInstaller Version:"))
+ self.lbl_nuitka_ver = QLabel(_("Nuitka Version:"))
+ self.lbl_pipreqs_ver = QLabel(_("Pipreqs Version:"))
+
+ self.form_ver.addRow(self.lbl_pyi_ver, self.pyi_ver_edit)
+ self.form_ver.addRow(self.lbl_nuitka_ver, self.nuitka_ver_edit)
+ self.form_ver.addRow(self.lbl_pipreqs_ver, self.pipreqs_ver_edit)
+ c_lay_ver.addLayout(self.form_ver)
- lay_sub4.addWidget(card_opt)
- lay_sub4.addWidget(card_ver)
+ lay_sub4.addWidget(self.card_opt)
+ lay_sub4.addWidget(self.card_ver)
lay_sub4.addStretch()
- card_map, c_lay_map = self._create_card("第三方库包名映射表")
+ self.card_map, c_lay_map = self._create_card(_("Package Name Mappings"))
self.mapping_table = QTableWidget()
self.mapping_table.setItemDelegate(TableItemDelegate(self.mapping_table))
self.mapping_table.setColumnCount(2)
- self.mapping_table.setHorizontalHeaderLabels(["代码导入名 (import)", "PyPI 包名 (pip)"])
+ self.mapping_table.setHorizontalHeaderLabels([_("Import Name"), _("PyPI Package Name")])
self.mapping_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
self.mapping_table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
self.mapping_table.setFixedHeight(260)
c_lay_map.addWidget(self.mapping_table)
btn_map_lay = QHBoxLayout()
- btn_add_map = QPushButton("添加映射"); btn_add_map.setProperty("class", "ToolBtn"); btn_add_map.clicked.connect(self.add_mapping_item)
- btn_del_map = QPushButton("删除选中"); btn_del_map.setProperty("class", "ToolBtn"); btn_del_map.clicked.connect(self.delete_mapping_item)
- btn_reset_map = QPushButton("重置默认"); btn_reset_map.setProperty("class", "ToolBtn"); btn_reset_map.clicked.connect(self.reset_mapping_default)
- btn_map_lay.addWidget(btn_add_map); btn_map_lay.addWidget(btn_del_map); btn_map_lay.addWidget(btn_reset_map); btn_map_lay.addStretch()
+ self.btn_add_map = QPushButton(_("Add Mapping"))
+ self.btn_add_map.setProperty("class", "ToolBtn")
+ self.btn_add_map.clicked.connect(self.add_mapping_item)
+
+ self.btn_del_map = QPushButton(_("Remove Selected"))
+ self.btn_del_map.setProperty("class", "ToolBtn")
+ self.btn_del_map.clicked.connect(self.delete_mapping_item)
+
+ self.btn_reset_map = QPushButton(_("Restore Defaults"))
+ self.btn_reset_map.setProperty("class", "ToolBtn")
+ self.btn_reset_map.clicked.connect(self.reset_mapping_default)
+
+ btn_map_lay.addWidget(self.btn_add_map)
+ btn_map_lay.addWidget(self.btn_del_map)
+ btn_map_lay.addWidget(self.btn_reset_map)
+ btn_map_lay.addStretch()
c_lay_map.addLayout(btn_map_lay)
- lay_sub5.addWidget(card_map)
+ lay_sub5.addWidget(self.card_map)
lay_sub5.addStretch()
-
+
main_lay.addWidget(self.sub_tabs)
self.on_engine_changed()
def build_pref_tab(self):
- card_meta, c_lay_meta = self._create_card("应用元数据与工程预设")
- form_meta = QFormLayout()
- form_meta.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
- form_meta.setSpacing(15)
+ self.card_lang, c_lay_lang = self._create_card(_("UI Language:"))
+ form_lang = QFormLayout()
+ self.lang_combo = QComboBox()
+
+ for code, name in I18N.get_available_languages().items():
+ self.lang_combo.addItem(name, code)
+
+ setup_combo_white_theme(self.lang_combo)
+
+ self.lbl_lang_title = QLabel(_("UI Language:"))
+ form_lang.addRow(self.lbl_lang_title, self.lang_combo)
+ c_lay_lang.addLayout(form_lang)
+
+ self.card_meta, c_lay_meta = self._create_card(_("App Metadata & Presets"))
+ self.form_meta = QFormLayout()
+ self.form_meta.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
+ self.form_meta.setSpacing(15)
+
self.ver_ver = QLineEdit("1.0.0")
self.ver_comp = QLineEdit("My Studio")
self.ver_desc = QLineEdit("Python Executable")
- form_meta.addRow("应用版本:", self.ver_ver)
- form_meta.addRow("公司/作者名称:", self.ver_comp)
- form_meta.addRow("应用描述:", self.ver_desc)
- c_lay_meta.addLayout(form_meta)
+
+ self.lbl_ver_title = QLabel(_("Version:"))
+ self.lbl_company_title = QLabel(_("Author/Company:"))
+ self.lbl_desc_title = QLabel(_("Description:"))
+
+ self.form_meta.addRow(self.lbl_ver_title, self.ver_ver)
+ self.form_meta.addRow(self.lbl_company_title, self.ver_comp)
+ self.form_meta.addRow(self.lbl_desc_title, self.ver_desc)
+ c_lay_meta.addLayout(self.form_meta)
c_lay_meta.addSpacing(10)
h_preset = QHBoxLayout()
- btn_exp_preset = QPushButton("保存工程预设..."); btn_exp_preset.setProperty("class", "ToolBtn"); btn_exp_preset.clicked.connect(self.export_preset)
- btn_imp_preset = QPushButton("加载工程预设..."); btn_imp_preset.setProperty("class", "ToolBtn"); btn_imp_preset.clicked.connect(self.import_preset)
- h_preset.addWidget(btn_exp_preset)
- h_preset.addWidget(btn_imp_preset)
+ self.btn_exp_preset = QPushButton(_("Export Preset..."))
+ self.btn_exp_preset.setProperty("class", "ToolBtn")
+ self.btn_exp_preset.clicked.connect(self.export_preset)
+
+ self.btn_imp_preset = QPushButton(_("Import Preset..."))
+ self.btn_imp_preset.setProperty("class", "ToolBtn")
+ self.btn_imp_preset.clicked.connect(self.import_preset)
+
+ h_preset.addWidget(self.btn_exp_preset)
+ h_preset.addWidget(self.btn_imp_preset)
h_preset.addStretch()
c_lay_meta.addLayout(h_preset)
- card1, lay1 = self._create_card("构建产物输出位置")
+ self.card1, lay1 = self._create_card(_("Output Location"))
self.out_mode_combo = QComboBox()
- self.out_mode_combo.addItems(["源文件同级目录", "自定义输出目录"])
+ self.out_mode_combo.addItems([_("Source File Directory"), _("Custom Directory")])
setup_combo_white_theme(self.out_mode_combo)
self.out_mode_combo.currentIndexChanged.connect(self.on_out_mode_changed)
- self.out_dir_edit = QLineEdit(); self.out_dir_edit.setPlaceholderText("选择具体的输出归档路径...")
- btn_out_dir = QPushButton("浏览..."); btn_out_dir.setProperty("class", "ToolBtn"); btn_out_dir.clicked.connect(self.select_out_dir)
- self.out_dir_container = QWidget(); h_out_dir = QHBoxLayout(self.out_dir_container); h_out_dir.setContentsMargins(0, 0, 0, 0)
- h_out_dir.addWidget(self.out_dir_edit, 1); h_out_dir.addWidget(btn_out_dir)
+ self.out_dir_edit = QLineEdit()
+ self.btn_out_dir = QPushButton(_("Browse"))
+ self.btn_out_dir.setProperty("class", "ToolBtn")
+ self.btn_out_dir.clicked.connect(self.select_out_dir)
+
+ self.out_dir_container = QWidget()
+ h_out_dir = QHBoxLayout(self.out_dir_container)
+ h_out_dir.setContentsMargins(0, 0, 0, 0)
+ h_out_dir.addWidget(self.out_dir_edit, 1)
+ h_out_dir.addWidget(self.btn_out_dir)
self.out_dir_container.setVisible(False)
- form1 = QFormLayout()
- form1.setVerticalSpacing(15)
- form1.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
- form1.addRow("输出路径规则:", self.out_mode_combo)
- form1.addRow("目标输出目录:", self.out_dir_container)
- lay1.addLayout(form1)
+ self.form_out = QFormLayout()
+ self.form_out.setVerticalSpacing(15)
+ self.form_out.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
+
+ self.lbl_out_rule_title = QLabel(_("Output Rule:"))
+ self.lbl_target_out_title = QLabel(_("Target Directory:"))
+
+ self.form_out.addRow(self.lbl_out_rule_title, self.out_mode_combo)
+ self.form_out.addRow(self.lbl_target_out_title, self.out_dir_container)
+ lay1.addLayout(self.form_out)
- card2, lay2 = self._create_card("构建偏好与系统行为")
+ self.card2, lay2 = self._create_card(_("Preferences & System Behavior"))
lay2.setSpacing(16)
- self.concise_log_check = QCheckBox("过滤冗余日志 (精简模式)")
- self.auto_save_log_check = QCheckBox("自动导出构建日志")
- self.auto_icon_check = QCheckBox("自动匹配项目图标")
- self.clean_all_check = QCheckBox("构建完成后清理临时缓存")
- self.sound_notify_check = QCheckBox("启用音效提示")
+ self.concise_log_check = QCheckBox(_("Concise Log Output"))
+ self.auto_save_log_check = QCheckBox(_("Auto-save Build Log"))
+ self.auto_icon_check = QCheckBox(_("Auto Extract Icon"))
+ self.clean_all_check = QCheckBox(_("Clean Temporary Cache After Build"))
+ self.sound_notify_check = QCheckBox(_("Sound Notification"))
for chk in (self.concise_log_check, self.auto_save_log_check, self.auto_icon_check, self.clean_all_check, self.sound_notify_check):
lay2.addWidget(chk)
- self.lay_pref.addWidget(card_meta)
- self.lay_pref.addWidget(card1)
- self.lay_pref.addWidget(card2)
+ self.lay_pref.addWidget(self.card_lang)
+ self.lay_pref.addWidget(self.card_meta)
+ self.lay_pref.addWidget(self.card1)
+ self.lay_pref.addWidget(self.card2)
self.lay_pref.addStretch()
def build_about_tab(self):
main_lay = QVBoxLayout(self.tab_about)
main_lay.setContentsMargins(40, 20, 40, 20)
main_lay.setSpacing(15)
-
main_lay.addStretch(1)
logo_lbl = QLabel()
@@ -1880,11 +2035,10 @@ class SettingsPanel(QWidget):
ver_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
main_lay.addWidget(ver_lbl)
- desc_lbl = QLabel(__description__)
- desc_lbl.setStyleSheet("font-size: 14px; color: #5f6368;")
- desc_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
- main_lay.addWidget(desc_lbl)
-
+ self.about_desc_lbl = QLabel(_("A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka"))
+ self.about_desc_lbl.setStyleSheet("font-size: 14px; color: #5f6368;")
+ self.about_desc_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ main_lay.addWidget(self.about_desc_lbl)
main_lay.addSpacing(25)
btn_lay = QHBoxLayout()
@@ -1895,29 +2049,16 @@ class SettingsPanel(QWidget):
btn = QPushButton(text)
btn.setCursor(Qt.CursorShape.PointingHandCursor)
btn.setStyleSheet("""
- QPushButton {
- background-color: #f1f3f4;
- color: #3c4043;
- border: none;
- border-radius: 8px;
- padding: 10px 20px;
- font-size: 13px;
- font-weight: bold;
- }
- QPushButton:hover {
- background-color: #e8eaed;
- color: #1A73E8;
- }
- QPushButton:pressed {
- background-color: #dadce0;
- }
+ QPushButton { background-color: #f1f3f4; color: #3c4043; border: none; border-radius: 8px; padding: 10px 20px; font-size: 13px; font-weight: bold; }
+ QPushButton:hover { background-color: #e8eaed; color: #1A73E8; }
+ QPushButton:pressed { background-color: #dadce0; }
""")
btn.clicked.connect(lambda: __import__('webbrowser').open(url))
return btn
- btn_github = create_link_btn("GitHub 仓库", "https://github.com/qwejay/QPyPack")
- btn_issue = create_link_btn("反馈与建议", "https://github.com/qwejay/QPyPack/issues")
- btn_pypi = create_link_btn("PyPI 主页", "https://pypi.org/project/qpypack/")
+ btn_github = create_link_btn("GitHub Repository", "https://github.com/qwejay/QPyPack")
+ btn_issue = create_link_btn("Issues & Feedback", "https://github.com/qwejay/QPyPack/issues")
+ btn_pypi = create_link_btn("PyPI Home", "https://pypi.org/project/qpypack/")
btn_lay.addWidget(btn_github)
btn_lay.addWidget(btn_issue)
@@ -1931,19 +2072,111 @@ class SettingsPanel(QWidget):
rights_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
main_lay.addWidget(rights_lbl)
+ def retranslate_ui(self):
+ self.tabs.setTabText(0, _("Build Settings"))
+ self.tabs.setTabText(1, _("Preferences"))
+ self.tabs.setTabText(2, _("About"))
+
+ self.sub_tabs.setTabText(0, _("🚀 Engine"))
+ self.sub_tabs.setTabText(1, _("📦 Dependencies"))
+ self.sub_tabs.setTabText(2, _("📂 Resources"))
+ self.sub_tabs.setTabText(3, _("⚡ Optimization"))
+ self.sub_tabs.setTabText(4, _("🗺️ Package Map"))
+
+ self.card_engine.findChild(QLabel, "CardTitle").setText(_("Engine & Environment"))
+ self.card_mode.findChild(QLabel, "CardTitle").setText(_("Execution Mode"))
+ self.card_deps.findChild(QLabel, "CardTitle").setText(_("Mirrors & Scanner"))
+ self.card_res.findChild(QLabel, "CardTitle").setText(_("Additional Resources (Drag & Drop Supported)"))
+ self.card_opt.findChild(QLabel, "CardTitle").setText(_("Performance Optimization"))
+ self.card_ver.findChild(QLabel, "CardTitle").setText(_("Lock Core Dependencies"))
+ self.card_map.findChild(QLabel, "CardTitle").setText(_("Package Name Mappings"))
+ self.card_lang.findChild(QLabel, "CardTitle").setText(_("UI Language:"))
+ self.card_meta.findChild(QLabel, "CardTitle").setText(_("App Metadata & Presets"))
+ self.card1.findChild(QLabel, "CardTitle").setText(_("Output Location"))
+ self.card2.findChild(QLabel, "CardTitle").setText(_("Preferences & System Behavior"))
+
+ self.lbl_eng_title.setText(_("Build Engine:"))
+ self.lbl_py_title.setText(_("Python Interpreter:"))
+ self.lbl_app_title.setText(_("Output Name:"))
+ self.lbl_icon_title.setText(_("App Icon:"))
+ self.lbl_pip_main.setText(_("Primary PIP Index:"))
+ self.lbl_pip_backup.setText(_("Backup PIP Index:"))
+ self.lbl_reqs.setText(_("Requirements File:"))
+ self.lbl_hidden.setText(_("Hidden Imports:"))
+ self.lbl_exclude.setText(_("Exclude Modules:"))
+ self.lbl_cpu_cores.setText(_("CPU Cores:"))
+ self.lbl_upx_path.setText(_("UPX Path:"))
+ self.lbl_pyi_ver.setText(_("PyInstaller Version:"))
+ self.lbl_nuitka_ver.setText(_("Nuitka Version:"))
+ self.lbl_pipreqs_ver.setText(_("Pipreqs Version:"))
+ self.lbl_lang_title.setText(_("UI Language:"))
+ self.lbl_ver_title.setText(_("Version:"))
+ self.lbl_company_title.setText(_("Author/Company:"))
+ self.lbl_desc_title.setText(_("Description:"))
+ self.lbl_out_rule_title.setText(_("Output Rule:"))
+ self.lbl_target_out_title.setText(_("Target Directory:"))
+
+ self.btn_save.setText(_("Save & Return"))
+ self.btn_python_path.setText(_("Browse"))
+ self.btn_icon.setText(_("Browse"))
+ self.btn_reqs.setText(_("Browse"))
+ self.btn_scan.setText(_("AST Scan"))
+ self.btn_upx.setText(_("Browse"))
+ self.btn_out_dir.setText(_("Browse"))
+ self.btn_add_file.setText(_("Add File"))
+ self.btn_add_dir.setText(_("Add Dir"))
+ self.btn_del_res.setText(_("Remove Selected"))
+ self.btn_clear_res.setText(_("Clear All"))
+ self.btn_add_map.setText(_("Add Mapping"))
+ self.btn_del_map.setText(_("Remove Selected"))
+ self.btn_reset_map.setText(_("Restore Defaults"))
+ self.btn_exp_preset.setText(_("Export Preset..."))
+ self.btn_imp_preset.setText(_("Import Preset..."))
+
+ self.python_path_combo.setPlaceholderText(_("Leave blank to auto-detect system default Python"))
+ self.name_edit.setPlaceholderText(_("Leave blank to auto-match script name"))
+ self.reqs_file_edit.setPlaceholderText(_("Leave blank to auto-search requirements.txt in current directory"))
+ self.hidden_edit.setPlaceholderText(_("Comma separated (e.g. pandas, PyQt5)"))
+ self.exclude_edit.setPlaceholderText(_("Comma separated (e.g. tkinter, matplotlib)"))
+ self.upx_path_edit.setPlaceholderText(_("Leave blank to auto-detect from environment variables"))
+
+ self.onefile_check.setText(_("One-File Mode (--onefile)"))
+ self.noconsole_check.setText(_("Hide Console (--noconsole)"))
+ self.venv_check.setText(_("Use Virtual Environment (Recommended)"))
+ self.reqs_check.setText(_("Install requirements.txt"))
+ self.pipreqs_check.setText(_("Analyze Dependencies (pipreqs)"))
+ self.pipreqs_dir_check.setText(_("Scan Entire Folder"))
+ self.upx_check.setText(_("Enable UPX Compression"))
+ self.lite_mode_check.setText(_("Lite Mode (Exclude Dev/Test Dependencies)"))
+
+ self.concise_log_check.setText(_("Concise Log Output"))
+ self.auto_save_log_check.setText(_("Auto-save Build Log"))
+ self.auto_icon_check.setText(_("Auto Extract Icon"))
+ self.clean_all_check.setText(_("Clean Temporary Cache After Build"))
+ self.sound_notify_check.setText(_("Sound Notification"))
+
+ self.out_mode_combo.setItemText(0, _("Source File Directory"))
+ self.out_mode_combo.setItemText(1, _("Custom Directory"))
+ self.mapping_table.setHorizontalHeaderLabels([_("Import Name"), _("PyPI Package Name")])
+ self.about_desc_lbl.setText(_("A Cross-platform Python Packaging GUI Tool based on PyInstaller & Nuitka"))
+
+ self.btn_reset.setToolTip(_("Reset to Default Config"))
+ self.btn_back.setToolTip(_("Cancel & Return"))
+ self.add_data_list.setToolTip(_("Double-click to edit target path; Drag & drop supported"))
+ self.lite_mode_check.setToolTip(_("Dynamically exclude redundant dependencies in build environment, improving speed and reducing size."))
+ self.on_engine_changed()
+
def populate_python_combo(self, py_dict):
current_text = self.python_path_combo.currentText().strip()
self.python_path_combo.clear()
for path, ver in py_dict.items():
self.python_path_combo.addItem(f"{path} (Python {ver})", path)
- if not current_text:
- current_text = get_python_executable()
+ if not current_text: current_text = get_python_executable()
if current_text:
clean_text = current_text
- if " (Python " in clean_text:
- clean_text = clean_text.split(" (Python ")[0].strip()
+ if " (Python " in clean_text: clean_text = clean_text.split(" (Python ")[0].strip()
clean_text = os.path.normpath(clean_text).lower()
found = False
@@ -1953,23 +2186,20 @@ class SettingsPanel(QWidget):
self.python_path_combo.setCurrentIndex(idx)
found = True
break
- if not found:
- self.python_path_combo.setCurrentText(current_text)
+ if not found: self.python_path_combo.setCurrentText(current_text)
def populate_mapping_table(self, mappings_dict):
self.mapping_table.setRowCount(0)
for imp_name, pypi_name in mappings_dict.items():
row = self.mapping_table.rowCount()
self.mapping_table.insertRow(row)
- item_imp = QTableWidgetItem(imp_name)
- item_pypi = QTableWidgetItem(pypi_name)
- self.mapping_table.setItem(row, 0, item_imp)
- self.mapping_table.setItem(row, 1, item_pypi)
+ self.mapping_table.setItem(row, 0, QTableWidgetItem(imp_name))
+ self.mapping_table.setItem(row, 1, QTableWidgetItem(pypi_name))
def add_mapping_item(self):
- imp_name, ok1 = QInputDialog.getText(self, "添加包名映射", "请输入代码中的 import 模块名 (如: cv2):")
+ imp_name, ok1 = QInputDialog.getText(self, _("Add Mapping"), "Import name (e.g. cv2):")
if not ok1 or not imp_name.strip(): return
- pypi_name, ok2 = QInputDialog.getText(self, "添加包名映射", f"请输入 [{imp_name.strip()}] 在 PyPI 的真实包名 (如: opencv-python):")
+ pypi_name, ok2 = QInputDialog.getText(self, _("Add Mapping"), f"PyPI package name for [{imp_name.strip()}]:")
if not ok2 or not pypi_name.strip(): return
row = self.mapping_table.rowCount()
@@ -1979,15 +2209,15 @@ class SettingsPanel(QWidget):
def delete_mapping_item(self):
rows = set(item.row() for item in self.mapping_table.selectedItems())
- for r in sorted(rows, reverse=True):
- self.mapping_table.removeRow(r)
+ for r in sorted(rows, reverse=True): self.mapping_table.removeRow(r)
def reset_mapping_default(self):
- if QMessageBox.question(self, "确认重置", "确定要将所有包名映射重置为系统默认设置吗?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.Yes:
- self.populate_mapping_table(DEFAULT_MAPPINGS)
+ self.populate_mapping_table(DEFAULT_MAPPINGS)
+ if hasattr(self.parent_win, "show_notification"):
+ self.parent_win.show_notification(_("Package mappings have been reset to defaults."))
def export_preset(self):
- fp, _ = QFileDialog.getSaveFileName(self, "导出工程预设文件", "project_config.qpypack", "QPyPack Presets (*.qpypack);;JSON Files (*.json)")
+ fp, _filter = QFileDialog.getSaveFileName(self, _("Export Preset..."), "project_config.qpypack", "QPyPack Presets (*.qpypack *.json)")
if fp:
try:
data = {
@@ -2012,12 +2242,14 @@ class SettingsPanel(QWidget):
"add_data_list": [self.add_data_list.item(i).data(Qt.ItemDataRole.UserRole) for i in range(self.add_data_list.count())]
}
Path(fp).write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding='utf-8')
- QMessageBox.information(self, "导出成功", f"工程配置预设已导出至:\n{fp}")
+ if hasattr(self.parent_win, "show_notification"):
+ self.parent_win.show_notification(_("Config preset exported to: {path}", path=fp))
except Exception as e:
- QMessageBox.warning(self, "导出失败", f"无法导出预设文件: {e}")
+ if hasattr(self.parent_win, "show_error_log"):
+ self.parent_win.show_error_log(_("[ERROR] Failed to export preset file: {error}", error=str(e)))
def import_preset(self):
- fp, _ = QFileDialog.getOpenFileName(self, "加载工程预设文件", "", "QPyPack Presets (*.qpypack *.json);;All Files (*)")
+ fp, _filter = QFileDialog.getOpenFileName(self, _("Import Preset..."), "", "QPyPack Presets (*.qpypack *.json);;All Files (*)")
if fp:
try:
data = json.loads(Path(fp).read_text(encoding='utf-8'))
@@ -2044,24 +2276,31 @@ class SettingsPanel(QWidget):
for item in data["add_data_list"]:
if isinstance(item, (list, tuple)) and len(item) == 3:
self._add_resource_item(item[0], item[1], item[2])
- QMessageBox.information(self, "加载成功", "工程配置预设已成功载入!")
+ if hasattr(self.parent_win, "show_notification"):
+ self.parent_win.show_notification(_("Config preset imported successfully."))
except Exception as e:
- QMessageBox.warning(self, "加载失败", f"预设文件格式错误或已被损坏: {e}")
+ if hasattr(self.parent_win, "show_error_log"):
+ self.parent_win.show_error_log(_("[ERROR] Preset file format error or corrupted: {error}", error=str(e)))
def load_from_config(self):
config = load_config()
if 'Settings' in config:
s = config['Settings']
+ lang_code = s.get('language', 'auto')
+ for i in range(self.lang_combo.count()):
+ if self.lang_combo.itemData(i) == lang_code:
+ self.lang_combo.setCurrentIndex(i); break
+
self.engine_combo.setCurrentText(s.get('engine', 'PyInstaller'))
self.onefile_check.setChecked(s.getboolean('onefile', True))
self.noconsole_check.setChecked(s.getboolean('noconsole', True))
self.clean_all_check.setChecked(s.getboolean('clean_all', True))
self.auto_icon_check.setChecked(s.getboolean('auto_icon', True))
- pip_main = s.get('pip_index', 'https://pypi.tuna.tsinghua.edu.cn/simple')
+ pip_main = s.get('pip_index', 'https://pypi.org/simple')
self._set_combo_value(self.pip_source_combo, pip_main)
- pip_backup = s.get('pip_index_backup', 'https://mirrors.aliyun.com/pypi/simple/')
+ pip_backup = s.get('pip_index_backup', 'https://test.pypi.org/simple')
self._set_combo_value(self.pip_backup_combo, pip_backup)
self.venv_check.setChecked(s.getboolean('use_venv', True))
@@ -2105,6 +2344,11 @@ class SettingsPanel(QWidget):
config = load_config()
if 'Settings' not in config: config['Settings'] = {}
s = config['Settings']
+
+ idx = self.lang_combo.currentIndex()
+ new_lang = self.lang_combo.itemData(idx)
+ s['language'] = new_lang
+
s['engine'] = self.engine_combo.currentText()
s['onefile'] = str(self.onefile_check.isChecked())
s['noconsole'] = str(self.noconsole_check.isChecked())
@@ -2121,12 +2365,10 @@ class SettingsPanel(QWidget):
s['use_reqs_file'] = self.reqs_file_edit.text().strip()
raw_py = self.python_path_combo.currentText().strip()
- if " (Python " in raw_py:
- raw_py = raw_py.split(" (Python ")[0].strip()
+ if " (Python " in raw_py: raw_py = raw_py.split(" (Python ")[0].strip()
s['custom_python_path'] = raw_py
- if self.upx_check:
- s['upx'] = str(self.upx_check.isChecked())
+ if self.upx_check: s['upx'] = str(self.upx_check.isChecked())
s['upx_path'] = self.upx_path_edit.text().strip()
s['cpu_cores'] = str(self.cores_spin.value())
s['exclude_modules'] = self.exclude_edit.text().strip()
@@ -2151,10 +2393,10 @@ class SettingsPanel(QWidget):
for r in range(self.mapping_table.rowCount()):
k = self.mapping_table.item(r, 0).text().strip()
v = self.mapping_table.item(r, 1).text().strip()
- if k and v:
- config['Mappings'][k] = v
+ if k and v: config['Mappings'][k] = v
save_config(config)
+ I18N.set_language(new_lang)
def _check_pip_mirrors(self):
sender = self.sender()
@@ -2180,13 +2422,9 @@ class SettingsPanel(QWidget):
def on_engine_changed(self):
engine = self.engine_combo.currentText()
if engine == "PyInstaller":
- self.engine_desc_lbl.setText(
- "PyInstaller:构建速度快、兼容性强,但单文件体积偏大。适合快速迭代与大多数常规应用。"
- )
+ self.engine_desc_lbl.setText(_("PyInstaller: Fast build speed, excellent compatibility. Ideal for rapid iteration."))
else:
- self.engine_desc_lbl.setText(
- "Nuitka:编译为原生 C/C++ 二进制,体积更小且性能更好、支持源码防反编译;但构建耗时较长。适合商业级项目发布。(容易被反病毒软件误杀)"
- )
+ self.engine_desc_lbl.setText(_("Nuitka: Compiles to native C/C++ binary. Better performance and source code protection."))
if getattr(self, 'upx_check', None) is not None and getattr(self, 'upx_path_container', None) is not None:
is_pyi = (engine == "PyInstaller")
@@ -2203,25 +2441,21 @@ class SettingsPanel(QWidget):
self.out_dir_container.setVisible(show_custom)
def select_out_dir(self):
- d = QFileDialog.getExistingDirectory(self, "选择输出归档目录")
- if d:
- self.out_dir_edit.setText(Path(d).resolve().as_posix())
+ d = QFileDialog.getExistingDirectory(self, _("Target Directory:"))
+ if d: self.out_dir_edit.setText(Path(d).resolve().as_posix())
def select_upx_path(self):
- d = QFileDialog.getExistingDirectory(self, "选择 UPX 工具根目录")
- if d:
- self.upx_path_edit.setText(Path(d).resolve().as_posix())
+ d = QFileDialog.getExistingDirectory(self, _("UPX Path:"))
+ if d: self.upx_path_edit.setText(Path(d).resolve().as_posix())
def select_reqs_file(self):
- f, _ = QFileDialog.getOpenFileName(self, "选择依赖清单文件", "", "Requirements Files (*.txt);;All Files (*)")
- if f:
- self.reqs_file_edit.setText(Path(f).resolve().as_posix())
+ f, _filter = QFileDialog.getOpenFileName(self, _("Requirements File:"), "", "Requirements Files (*.txt);;All Files (*)")
+ if f: self.reqs_file_edit.setText(Path(f).resolve().as_posix())
def select_python_path(self):
exe_filter = "Executable (*.exe);;All Files (*)" if os.name == 'nt' else "All Files (*)"
- f, _ = QFileDialog.getOpenFileName(self, "选择 Python 解释器", "", exe_filter)
- if f:
- self.python_path_combo.setCurrentText(Path(f).resolve().as_posix())
+ f, _filter = QFileDialog.getOpenFileName(self, _("Python Interpreter:"), "", exe_filter)
+ if f: self.python_path_combo.setCurrentText(Path(f).resolve().as_posix())
def update_icon_preview(self, path):
if path and Path(path).exists():
@@ -2232,23 +2466,26 @@ class SettingsPanel(QWidget):
self.icon_preview.clear()
def select_icon(self):
- p, _ = QFileDialog.getOpenFileName(self, "选择图标", "", "Icon Files (*.ico *.svg *.png *.icns)")
+ p, _filter = QFileDialog.getOpenFileName(self, _("App Icon:"), "", "Icon Files (*.ico *.svg *.png *.icns)")
if p: self.icon_edit.setText(Path(p).resolve().as_posix())
def auto_scan_hidden(self):
script_path = self.parent_win.script_path
- if not script_path: return QMessageBox.warning(self, "构建约束", "请先加载有效的 Python 源代码文件!")
+ if not hasattr(self.parent_win, "show_notification"): return
+
+ if not script_path:
+ return self.parent_win.show_error_log(_("[ERROR] Please load a valid Python source file first!"))
if is_cloud_locked(script_path):
- return QMessageBox.warning(self, "I/O 错误", "目标脚本处于云盘加密或锁定状态,请解密后重试。")
+ return self.parent_win.show_error_log(_("[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again."))
try:
python_exe = get_python_executable()
hidden = extract_imports_via_ast(script_path, python_exe)
hidden = [m for m in hidden if m not in STD_LIBS]
self.hidden_edit.setText(','.join(hidden))
- QMessageBox.information(self, "AST 分析完成", f"语法树解析成功,共定位到 {len(hidden)} 项非标准库依赖。")
+ self.parent_win.show_notification(_("AST scan completed, found {count} dependencies.", count=len(hidden)))
except Exception as e:
- QMessageBox.warning(self, "分析异常", f"AST 语法树解析过程中发生异常: {e}")
+ self.parent_win.show_error_log(_("[ERROR] Exception occurred during AST parsing: {error}", error=str(e)))
def on_resources_dropped(self, paths):
for p_str in paths:
@@ -2259,36 +2496,36 @@ class SettingsPanel(QWidget):
self._add_resource_item('dir', p.as_posix(), p.name)
def add_resource_files(self):
- files, _ = QFileDialog.getOpenFileNames(self, "选择附加文件", "", "All Files (*)")
+ files, _filter = QFileDialog.getOpenFileNames(self, _("Add File"), "", "All Files (*)")
for f in files:
src = Path(f).resolve().as_posix()
- dst = "."
- self._add_resource_item('file', src, dst)
+ self._add_resource_item('file', src, ".")
def add_resource_dir(self):
- folder = QFileDialog.getExistingDirectory(self, "选择附加文件夹")
+ folder = QFileDialog.getExistingDirectory(self, _("Add Dir"))
if folder:
src = Path(folder).resolve().as_posix()
dst = Path(folder).name
self._add_resource_item('dir', src, dst)
def _add_resource_item(self, r_type, src, dst):
- display_text = f"[{'文件' if r_type == 'file' else '目录'}] {src} -> {dst}"
+ tag = _("File") if r_type == 'file' else _("Directory")
+ display_text = f"[{tag}] {src} -> {dst}"
item = QListWidgetItem(display_text)
item.setData(Qt.ItemDataRole.UserRole, (r_type, src, dst))
self.add_data_list.addItem(item)
def edit_resource(self, item=None):
- if not item:
- item = self.add_data_list.currentItem()
+ if not item: item = self.add_data_list.currentItem()
if not item: return
r_type, src, dst = item.data(Qt.ItemDataRole.UserRole)
- new_dst, ok = QInputDialog.getText(self, "修改目标路径", "请输入打包后的相对目标路径\n(根目录请填 . ):", text=dst)
+ new_dst, ok = QInputDialog.getText(self, "Edit Path", "Target relative path:", text=dst)
if ok and new_dst:
new_dst = new_dst.strip().replace('\\', '/')
if not new_dst: new_dst = "."
item.setData(Qt.ItemDataRole.UserRole, (r_type, src, new_dst))
- item.setText(f"[{'文件' if r_type == 'file' else '目录'}] {src} -> {new_dst}")
+ tag = _("File") if r_type == 'file' else _("Directory")
+ item.setText(f"[{tag}] {src} -> {new_dst}")
def del_resource(self):
for item in self.add_data_list.selectedItems():
@@ -2316,32 +2553,26 @@ class ScriptAnalysisThread(QThread):
content = f.read(10240)
v_match = re.search(r'^(?:__version__|VERSION|version)\s*=\s*[\'"]([^\'"]+)[\'"]', content, re.M | re.I)
- if v_match:
- version = v_match.group(1)
+ if v_match: version = v_match.group(1)
c_match = re.search(r'^(?:__company__|COMPANY)\s*=\s*[\'"]([^\'"]+)[\'"]', content, re.M | re.I)
if c_match:
author = c_match.group(1)
else:
a_match = re.search(r'^(?:__author__|AUTHOR)\s*=\s*[\'"]([^\'"]+)[\'"]', content, re.M | re.I)
- if a_match:
- author = a_match.group(1)
+ if a_match: author = a_match.group(1)
n_match = re.search(r'^(?:__title__|__app_name__|APP_NAME)\s*=\s*[\'"]([^\'"]+)[\'"]', content, re.M | re.I)
- if n_match:
- app_name = n_match.group(1)
+ if n_match: app_name = n_match.group(1)
d_match = re.search(r'^(?:__description__|DESCRIPTION)\s*=\s*[\'"]([^\'"]+)[\'"]', content, re.M | re.I)
- if d_match:
- desc = d_match.group(1)
- except:
- pass
+ if d_match: desc = d_match.group(1)
+ except: pass
try:
python_exe = get_python_executable()
script_imports = extract_imports_via_ast(self.path, python_exe)
- except:
- pass
+ except: pass
self.analysis_done.emit(app_name, version, author, desc, script_imports)
@@ -2364,8 +2595,10 @@ class PackingThread(QThread):
self._is_cancelled = True
if self.process:
try:
- if os.name == "nt": subprocess.run(["taskkill", "/F", "/T", "/PID", str(self.process.pid)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)
- else: self.process.kill()
+ if os.name == "nt":
+ subprocess.run(["taskkill", "/F", "/T", "/PID", str(self.process.pid)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)
+ else:
+ self.process.kill()
except: pass
def run_cmd(self, cmd, cwd=None, timeout=None, silent_error=False):
@@ -2399,8 +2632,7 @@ class PackingThread(QThread):
stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)
else:
self.process.kill()
- except:
- pass
+ except: pass
timer = threading.Timer(timeout, kill_proc)
timer.start()
@@ -2409,8 +2641,7 @@ class PackingThread(QThread):
def is_noisy_line(l):
l_lower = l.lower()
- if "error:" in l_lower:
- return False
+ if "error:" in l_lower: return False
if "upx" in l_lower and ("subprocess.calledprocesserror" in l_lower or "notcompressibleexception" in l_lower):
return True
return any(kw in l_lower for kw in ["warning:", "info:", "deprecation:", "userwarning:", "futurewarning:"])
@@ -2433,46 +2664,36 @@ class PackingThread(QThread):
buffer.clear()
last_emit = time.time()
- if buffer:
- self.progress.emit('\n'.join(buffer))
-
+ if buffer: self.progress.emit('\n'.join(buffer))
self.process.wait()
if is_timeout[0]:
- self.progress.emit(f"[WARN] 子进程执行超时 (>{timeout}s),正在终止执行...")
+ self.progress.emit(f"[WARN] Command timeout (>{timeout}s)")
return False
success = self.process.returncode == 0
-
if not success and not silent_error and self.params.get('concise_log', True) and cmd_raw_lines:
- self.progress.emit("\n" + "!"*10 + " [诊断回溯: 以下是由于该环节执行异常产生的完整原始日志] " + "!"*10)
+ self.progress.emit(_("\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!"))
self.progress.emit('\n'.join(cmd_raw_lines))
self.progress.emit("!"*60 + "\n")
return success
except FileNotFoundError as e:
- cmd_name = cmd[0] if isinstance(cmd, list) and cmd else str(cmd)
- if "python" in str(cmd_name).lower():
- self.progress.emit(f"[ERROR] 环境异常:无法定位 Python 解释器,请检查环境变量配置。")
- else:
- self.progress.emit(f"[ERROR] 子进程调用失败:未检测到系统指令或外部程序 \"{cmd_name}\" ({e})")
+ self.progress.emit(f"[ERROR] Process error: command or binary missing ({e})")
return False
except Exception as e:
- self.progress.emit(f"[ERROR] 子进程执行发生 system 级异常: {e}")
+ self.progress.emit(f"[ERROR] System execution exception: {e}")
return False
finally:
- if timer:
- timer.cancel()
+ if timer: timer.cancel()
def run_pip_install(self, python_exe, pkgs_or_args):
primary_idx = self.params.get('pip_index_url', '').strip()
backup_idx = self.params.get('pip_index_backup', '').strip()
pip_args = []
- if primary_idx:
- pip_args.extend(["-i", primary_idx])
- if backup_idx and backup_idx != primary_idx:
- pip_args.extend(["--extra-index-url", backup_idx])
+ if primary_idx: pip_args.extend(["-i", primary_idx])
+ if backup_idx and backup_idx != primary_idx: pip_args.extend(["--extra-index-url", backup_idx])
cmd = [python_exe, "-m", "pip", "install"] + pkgs_or_args + pip_args
success = self.run_cmd(cmd)
@@ -2482,7 +2703,7 @@ class PackingThread(QThread):
if "no matching distribution found" in last_logs or "could not find a version" in last_logs:
return False
- self.progress.emit(f"[WARN] 主镜像源连接超时或失败,正在自动切换至备用源重试: {backup_idx}")
+ self.progress.emit(_("[INFO] Switching to backup PyPI source for retrieval: {url}", url=backup_idx))
fallback_cmd = [python_exe, "-m", "pip", "install"] + pkgs_or_args + ["-i", backup_idx]
success = self.run_cmd(fallback_cmd)
@@ -2490,7 +2711,7 @@ class PackingThread(QThread):
def sanitize_script(self, orig_path: Path):
if is_cloud_locked(orig_path):
- return None, False, "目标脚本处于云盘加密或锁定状态,请解密后重试。"
+ return None, False, _("Target file is locked or encrypted by cloud drive. Please decrypt and try again.")
if not self.params['noconsole']:
try:
@@ -2498,6 +2719,7 @@ class PackingThread(QThread):
try: code = raw.decode('utf-8-sig')
except: code = raw.decode(locale.getpreferredencoding(), errors='ignore')
+ pause_prompt_str = _("\\nProgram execution completed, press Enter to exit...")
pause_code = "\n" + "#"*30 + "\n" + (
"try:\n"
" import sys\n"
@@ -2507,7 +2729,7 @@ class PackingThread(QThread):
" process_list = (ctypes.c_uint * 10)()\n"
" num_processes = kernel32.GetConsoleProcessList(process_list, 10)\n"
" if num_processes <= 2:\n"
- " input('\\n执行完毕,按回车键退出...')\n"
+ f" input('{pause_prompt_str}')\n"
"except:\n"
" pass\n"
)
@@ -2516,7 +2738,7 @@ class PackingThread(QThread):
temp_file.write_text(code + pause_code, encoding='utf-8')
return temp_file, True, ""
except Exception as e:
- self.progress.emit(f"[WARN] 自动注入控制台防闪退阻塞机制失败: {e}")
+ self.progress.emit(f"[WARN] Pause code injection exception: {e}")
return orig_path, False, ""
@@ -2567,50 +2789,49 @@ class PackingThread(QThread):
failed_packages = []
try:
- self.progress.emit("[INFO] 正在初始化隔离构建环境...")
-
+ self.progress.emit(_("[INFO] Initializing isolated build environment..."))
script_path = Path(self.params['script_path']).resolve()
script_dir = script_path.parent
build_script_path, is_temp, err_msg = self.sanitize_script(script_path)
- if not build_script_path and err_msg: return self.finished.emit(False, f"[ERROR] I/O 异常: {err_msg}", [])
+ if not build_script_path and err_msg: return self.finished.emit(False, f"[ERROR] I/O Exception: {err_msg}", [])
script_posix = build_script_path.as_posix()
system_python_exe = get_python_executable()
- self.progress.emit(f"[INFO] Python 解释器路径: {system_python_exe}")
+ self.progress.emit(_("[INFO] Python interpreter path: {path}", path=system_python_exe))
script_imports = set()
try:
script_imports = extract_imports_via_ast(script_posix, system_python_exe)
except Exception as e:
- self.progress.emit(f"[WARN] 源码 AST 分析异常: {e}")
+ self.progress.emit(f"[WARN] AST Analysis Exception: {e}")
if self.params['use_venv']:
- self.progress.emit("[INFO] 正在创建虚拟环境...")
+ self.progress.emit(_("[INFO] Creating virtual environment..."))
self.venv_dir = Path(tempfile.mkdtemp(prefix="qpypack_env_")).resolve()
if not self.run_cmd([system_python_exe, "-m", "venv", self.venv_dir.as_posix()]):
- return self.finished.emit(False, "[ERROR] 虚拟环境 (virtualenv) 创建失败。当前 Python 环境可能缺失必要模块或权限受限。", [])
+ return self.finished.emit(False, _("[ERROR] Failed to create virtual environment. Current Python environment might be missing necessary modules or have restricted permissions."), [])
python_exe = (self.venv_dir / ("Scripts/python.exe" if os.name == "nt" else "bin/python")).as_posix()
- self.progress.emit("[INFO] 正在同步并升级 pip 包管理器...")
+ self.progress.emit(_("[INFO] Synchronizing and upgrading pip package manager..."))
self.run_pip_install(python_exe, ["--upgrade", "pip", "-q"])
else:
python_exe = system_python_exe
engine_pkg = "nuitka" if engine == "Nuitka" else "pyinstaller"
-
if engine == "Nuitka" and self.params.get('nuitka_version'):
engine_pkg = f"nuitka=={self.params['nuitka_version']}"
elif engine == "PyInstaller" and self.params.get('pyi_version'):
engine_pkg = f"pyinstaller=={self.params['pyi_version']}"
- self.progress.emit(f"[INFO] 正在安装构建引擎 [{engine_pkg}] 及核心编译依赖...")
+ self.progress.emit(_("[INFO] Installing build engine [{pkg}] and core compilation dependencies...", pkg=engine_pkg))
core_pkgs = [engine_pkg]
if engine == "PyInstaller":
core_pkgs.append("pillow")
elif engine == "Nuitka":
core_pkgs.append("zstandard")
- self.progress.emit("[INFO] Nuitka 提示:若首次构建提示下载 GCC/MinGW 编译器,请保持网络正常。")
+ self.progress.emit(_("[INFO] Nuitka Tip: If prompted to download GCC/MinGW compiler on first build, please ensure stable network connection."))
+
self.run_pip_install(python_exe, ["-q"] + core_pkgs)
if self.params.get('use_reqs'):
@@ -2621,9 +2842,10 @@ class PackingThread(QThread):
req_file = script_dir / "requirements.txt"
if req_file.exists():
- self.progress.emit(f"[INFO] 依赖安装 [1/3]: 正在安装声明依赖 ({req_file.name})...")
+ self.progress.emit(_("[INFO] Dependency installation [1/3]: Installing declared dependencies ({filename})...", filename=req_file.name))
try:
- if is_cloud_locked(req_file): raise ValueError("依赖清单文件已被系统锁定")
+ if is_cloud_locked(req_file):
+ raise ValueError("Requirements file is locked")
raw_req = req_file.read_bytes()
try: req_content = raw_req.decode('utf-8-sig')
except: req_content = raw_req.decode(locale.getpreferredencoding(), errors='ignore')
@@ -2632,20 +2854,20 @@ class PackingThread(QThread):
temp_req.write_text(req_content, encoding='utf-8')
self.run_pip_install(python_exe, ["-q", "-r", temp_req.as_posix()])
temp_req.unlink(missing_ok=True)
- except Exception as e: self.progress.emit(f"[WARN] 依赖清单读取或安装过程发生异常: {e}")
+ except Exception as e:
+ self.progress.emit(f"[WARN] Requirements install exception: {e}")
if self.params.get('use_pipreqs'):
- self.progress.emit("[INFO] 依赖安装 [2/3]: 正在调用 pipreqs 分析项目依赖...")
-
+ self.progress.emit(_("[INFO] Dependency installation [2/3]: Calling pipreqs to analyze project dependencies..."))
sandbox_dir = None
if not self.params.get('use_pipreqs_dir', False):
sandbox_dir = Path(tempfile.mkdtemp(prefix="qpypack_sandbox_")).resolve()
shutil.copy2(build_script_path, sandbox_dir / build_script_path.name)
target_scan_dir = sandbox_dir
- self.progress.emit("[INFO] 已启用单文件沙盒模式:仅对当前脚本解析,防止同目录其他文件污染。")
+ self.progress.emit(_("[INFO] Enabled single-file sandbox mode: parsing current script only to prevent pollution from other files."))
else:
target_scan_dir = script_dir
- self.progress.emit("[WARN] 已允许全目录扫描模式:正在扫描当前目录下所有 Python 文件...")
+ self.progress.emit(_("[WARN] Enabled full-directory scan mode: scanning all Python files in the current directory..."))
pipreqs_pkg = "pipreqs"
if self.params.get('pipreqs_version'):
@@ -2664,23 +2886,24 @@ class PackingThread(QThread):
]
if pypi_server:
pipreqs_cmd.extend(["--pypi-server", pypi_server])
- self.progress.emit(f"[INFO] 依赖分析服务源地址: {pypi_server}")
- self.progress.emit("[INFO] 正在查询各依赖库的版本,请稍候...")
+ self.progress.emit(_("[INFO] Dependency analysis service source address: {server}", server=pypi_server))
+
+ self.progress.emit(_("[INFO] Querying versions of dependency libraries, please wait..."))
success_pipreqs = self.run_cmd(pipreqs_cmd, timeout=120, silent_error=True)
if not success_pipreqs and pip_backup and pip_backup != pip_idx:
backup_pypi = re.sub(r'/simple/?$', '/pypi', pip_backup, flags=re.I).rstrip('/')
- self.progress.emit(f"[INFO] 正在切换至备用 PyPI 查询源重新检索: {backup_pypi}")
+ self.progress.emit(_("[INFO] Switching to backup PyPI source for retrieval: {url}", url=backup_pypi))
backup_pipreqs_cmd = [c if c != pypi_server else backup_pypi for c in pipreqs_cmd]
success_pipreqs = self.run_cmd(backup_pipreqs_cmd, timeout=120, silent_error=True)
if not success_pipreqs:
- self.progress.emit("[INFO] 正在尝试利用兼容编码进行扫描...")
+ self.progress.emit(_("[INFO] Attempting to scan using compatible encoding..."))
fallback_cmd = ["iso-8859-1" if c == "utf-8" else c for c in pipreqs_cmd]
success_pipreqs = self.run_cmd(fallback_cmd, timeout=120, silent_error=True)
if not success_pipreqs:
- self.progress.emit("[WARN] pipreqs 已跳过深度扫描,将由 AST 扫描引擎补全依赖。")
+ self.progress.emit(_("[WARN] pipreqs skipped deep scan, dependencies will be supplemented by AST scanning engine."))
if success_pipreqs and temp_pipreqs.exists():
self.run_pip_install(python_exe, ["-q", "-r", temp_pipreqs.as_posix()])
@@ -2703,9 +2926,8 @@ class PackingThread(QThread):
'win32clipboard': 'pywin32', 'win32print': 'pywin32', 'win32file': 'pywin32',
'win32security': 'pywin32', 'win32process': 'pywin32', 'win32evtlog': 'pywin32',
'win32service': 'pywin32', 'win32pipe': 'pywin32', 'win32net': 'pywin32',
- 'win32crypt': 'pywin32', 'pythoncom': 'pywin32', 'pywintypes': 'pywin32',
- 'cv2': 'opencv-python', 'pil': 'pillow', 'fitz': 'pymupdf', 'bs4': 'beautifulsoup4',
- 'sklearn': 'scikit-learn', 'yaml': 'pyyaml', 'dotenv': 'python-dotenv'
+ 'win32crypt': 'pywin32', 'cv2': 'opencv-python', 'pil': 'pillow', 'fitz': 'pymupdf',
+ 'bs4': 'beautifulsoup4', 'sklearn': 'scikit-learn', 'yaml': 'pyyaml', 'dotenv': 'python-dotenv'
}
for k, v in HARDCODED_SAFETY_MAPPINGS.items():
known_mappings_lower[k.lower()] = v
@@ -2721,24 +2943,23 @@ class PackingThread(QThread):
ast_pkgs_set = set()
for m in script_imports:
- if m in STD_LIBS or m.lower() in local_modules:
- continue
+ if m in STD_LIBS or m.lower() in local_modules: continue
mapped_name = known_mappings_lower.get(m.lower(), m)
ast_pkgs_set.add(mapped_name)
ast_pkgs = sorted(list(ast_pkgs_set))
if ast_pkgs:
- self.progress.emit(f"[INFO] 依赖安装 [3/3]: 正在通过 AST 静态扫描提取隐式依赖...")
- self.progress.emit(f"[INFO] 正在解析并安装隐式导入依赖: {', '.join(ast_pkgs)}")
+ self.progress.emit(_("[INFO] Dependency installation [3/3]: Extracting implicit dependencies via AST static scan..."))
+ self.progress.emit(_("[INFO] Parsing and installing implicit import dependencies: {pkgs}", pkgs=', '.join(ast_pkgs)))
for pkg in ast_pkgs:
if not self.run_pip_install(python_exe, ["-q", pkg]):
failed_packages.append(pkg)
- self.progress.emit(f"[ERROR] ⚠️ 警告:依赖库 [{pkg}] 安装失败!可能导致打包后的软件运行时崩溃。")
+ self.progress.emit(_("[ERROR] ⚠️ Warning: Failed to install dependency [{pkg}]! May cause runtime crash.", pkg=pkg))
- if self._is_cancelled: return self.finished.emit(False, "[INFO] 构建已被终止。", failed_packages)
+ if self._is_cancelled: return self.finished.emit(False, "[INFO] Build Cancelled.", failed_packages)
- self.progress.emit(f"[INFO] 正在启动 {engine} 引擎,开始编译二进制文件...")
+ self.progress.emit(_("[INFO] Starting {engine} engine to compile binary files...", engine=engine))
cmd = []
app_name = self.params['app_name']
icon_path = Path(self.params['icon']).resolve().as_posix() if self.params.get('icon') else None
@@ -2747,23 +2968,17 @@ class PackingThread(QThread):
self.temp_workpath = Path(tempfile.mkdtemp(prefix="qpypack_build_")).resolve()
self.temp_dist_dir = Path(tempfile.mkdtemp(prefix="qpypack_dist_")).resolve()
cmd = [
- python_exe, "-m", "PyInstaller",
- "--clean",
- "--noconfirm",
+ python_exe, "-m", "PyInstaller", "--clean", "--noconfirm",
f"--distpath={self.temp_dist_dir.as_posix()}",
f"--workpath={self.temp_workpath.as_posix()}",
f"--name={app_name}"
]
- if self.params['onefile']:
- cmd.append("--onefile")
- else:
- cmd.append("--onedir")
+ if self.params['onefile']: cmd.append("--onefile")
+ else: cmd.append("--onedir")
- if self.params['noconsole']:
- cmd.append("--noconsole")
- else:
- cmd.append("--console")
+ if self.params['noconsole']: cmd.append("--noconsole")
+ else: cmd.append("--console")
if icon_path:
cmd.extend(["--icon", icon_path])
@@ -2800,9 +3015,7 @@ class PackingThread(QThread):
elif engine == "Nuitka":
self.temp_out_dir = Path(tempfile.mkdtemp(prefix="nuitka_out_")).resolve()
cmd = [
- python_exe, "-m", "nuitka",
- "--remove-output",
- "--assume-yes-for-downloads",
+ python_exe, "-m", "nuitka", "--remove-output", "--assume-yes-for-downloads",
f"--output-dir={self.temp_out_dir.as_posix()}",
f"--output-filename={app_name}{ext}"
]
@@ -2818,36 +3031,29 @@ class PackingThread(QThread):
except: pass
if has_msvc:
cmd.append("--msvc=latest")
- self.progress.emit("[INFO] 发现本地 MSVC 环境,优先使用原生 C++ 编译器。")
+ self.progress.emit(_("[INFO] Found local MSVC environment, prioritizing native C++ compiler."))
+
cores = self.params.get('cpu_cores', os.cpu_count() or 2)
cmd.append(f"--jobs={cores}")
- if self.params['onefile']:
- cmd.append("--onefile")
- else:
- cmd.append("--standalone")
+ if self.params['onefile']: cmd.append("--onefile")
+ else: cmd.append("--standalone")
if self.params['noconsole']:
cmd.append("--windows-console-mode=disable")
- if sys.platform == "darwin":
- cmd.append("--macos-create-app-bundle")
+ if sys.platform == "darwin": cmd.append("--macos-create-app-bundle")
else:
cmd.append("--windows-console-mode=force")
if icon_path:
- if os.name == "nt":
- cmd.append(f"--windows-icon-from-ico={icon_path}")
- elif sys.platform == "darwin":
- cmd.append(f"--macos-app-icon={icon_path}")
+ if os.name == "nt": cmd.append(f"--windows-icon-from-ico={icon_path}")
+ elif sys.platform == "darwin": cmd.append(f"--macos-app-icon={icon_path}")
cmd.append(f"--include-data-files={Path(icon_path).resolve().as_posix()}={Path(icon_path).name}")
if os.name == "nt":
- if self.params.get('ver_comp'):
- cmd.append(f"--company-name={self.params['ver_comp']}")
- if self.params.get('ver_desc'):
- cmd.append(f"--file-description={self.params['ver_desc']}")
- if self.params.get('app_name'):
- cmd.append(f"--product-name={self.params['app_name']}")
+ if self.params.get('ver_comp'): cmd.append(f"--company-name={self.params['ver_comp']}")
+ if self.params.get('ver_desc'): cmd.append(f"--file-description={self.params['ver_desc']}")
+ if self.params.get('app_name'): cmd.append(f"--product-name={self.params['app_name']}")
if self.params.get('ver_ver'):
v_str = self.params['ver_ver'].strip()
v_nums = re.findall(r'\d+', v_str)
@@ -2855,10 +3061,8 @@ class PackingThread(QThread):
cmd.append(f"--file-version={v_clean}")
cmd.append(f"--product-version={v_clean}")
elif sys.platform == "darwin":
- if self.params.get('ver_comp'):
- cmd.append(f"--company-name={self.params['ver_comp']}")
- if self.params.get('ver_ver'):
- cmd.append(f"--macos-app-version={self.params['ver_ver']}")
+ if self.params.get('ver_comp'): cmd.append(f"--company-name={self.params['ver_comp']}")
+ if self.params.get('ver_ver'): cmd.append(f"--macos-app-version={self.params['ver_ver']}")
comp = self.params.get('ver_comp', 'mycompany').strip().lower().replace(" ", "")
bundle_id = f"com.{comp or 'anonymous'}.{app_name.lower().replace(' ', '')}"
cmd.append(f"--macos-signed-app-name={bundle_id}")
@@ -2881,37 +3085,33 @@ class PackingThread(QThread):
cmd.append(f"--include-data-dir={src_path}={dst}")
else:
filename = Path(src).name
- if dst == ".":
- nuitka_dst = filename
- else:
- nuitka_dst = os.path.normpath(os.path.join(dst, filename)).replace('\\', '/')
+ if dst == ".": nuitka_dst = filename
+ else: nuitka_dst = os.path.normpath(os.path.join(dst, filename)).replace('\\', '/')
cmd.append(f"--include-data-files={src_path}={nuitka_dst}")
for excl in self.params.get('exclude_modules', '').split(','):
if excl.strip(): cmd.append(f"--nofollow-import-to={excl.strip()}")
if self.params.get('lite_mode'):
- self.progress.emit("[INFO] 已开启精简模式,正在执行体积缩减策略...")
+ self.progress.emit(_("[INFO] Lite mode enabled, executing size reduction strategy..."))
if not self.params.get('use_venv'):
- self.progress.emit("[WARN] 强烈建议勾选 [虚拟环境] 以最大化精简效果。")
+ self.progress.emit(_("[WARN] Strongly recommend checking [Virtual Environment] to maximize lite mode effect."))
lite_excludes = ['pip', 'setuptools', 'distutils', 'wheel', 'pydoc']
-
for ex in lite_excludes:
if engine == "PyInstaller": cmd.append(f"--exclude-module={ex}")
elif engine == "Nuitka": cmd.append(f"--nofollow-import-to={ex}")
if engine == "Nuitka":
- self.progress.emit("[INFO] 已启用 Nuitka 优化指令...")
+ self.progress.emit(_("[INFO] Enabled Nuitka optimization directives..."))
cmd.append("--python-flag=-OO")
cmd.append(script_posix)
success = self.run_cmd(cmd, cwd=script_dir.as_posix())
- if self._is_cancelled: return self.finished.emit(False, "[INFO] 构建已被终止。", failed_packages)
+ if self._is_cancelled: return self.finished.emit(False, "[INFO] Build Cancelled.", failed_packages)
- self.progress.emit("[INFO] 编译核心完成,正在提取并归档编译文件...")
-
+ self.progress.emit(_("[INFO] Core compilation completed, extracting and archiving built files..."))
src_out = None
if engine == "PyInstaller":
if sys.platform == "darwin" and self.params['noconsole']:
@@ -2929,10 +3129,8 @@ class PackingThread(QThread):
src_out = self.temp_out_dir / f"{app_name}.app"
else:
dist_dirs = list(self.temp_out_dir.glob("*.dist"))
- if dist_dirs:
- src_out = dist_dirs[0]
- else:
- src_out = self.temp_out_dir / f"{app_name}.dist"
+ if dist_dirs: src_out = dist_dirs[0]
+ else: src_out = self.temp_out_dir / f"{app_name}.dist"
out_mode = int(self.params.get('out_mode', 0))
custom_out = self.params.get('custom_out_dir', '').strip()
@@ -2940,60 +3138,55 @@ class PackingThread(QThread):
try:
final_out_dir = Path(custom_out)
final_out_dir.mkdir(parents=True, exist_ok=True)
- except:
- final_out_dir = script_dir
+ except: final_out_dir = script_dir
else:
final_out_dir = script_dir
+
+ final_out = final_out_dir / (src_out.name if src_out else f"{app_name}{ext}")
- final_out = final_out_dir / src_out.name
if success and src_out and src_out.exists():
try:
if final_out.exists():
if final_out.is_dir(): shutil.rmtree(final_out, onerror=remove_readonly)
else: final_out.unlink(missing_ok=True)
shutil.move(src_out.as_posix(), final_out.as_posix())
- except Exception as e: self.progress.emit(f"[ERROR] 产物移交失败,文件可能被系统进程占用或权限不足: {e}")
- else: self.progress.emit(f"[ERROR] 未在临时构建目录中定位到有效可执行产物: {src_out}")
+ except Exception as e:
+ self.progress.emit(_("[ERROR] Product transfer failed, file might be occupied by system process or lack permission: {error}", error=str(e)))
+ else:
+ self.progress.emit(_("[ERROR] Could not locate valid executable product in temporary build directory: {path}", path=str(src_out)))
if success and final_out.exists():
- self.progress.emit("[INFO] 正在校验输出文件并生成最终产物...")
+ self.progress.emit(_("[INFO] Validating output files and generating final product..."))
if self.params.get('auto_save_log') and self.all_raw_logs:
try:
log_file = final_out_dir / f"qpypack_build_{app_name}.log"
log_file.write_text('\n'.join(self.all_raw_logs), encoding='utf-8')
- self.progress.emit(f"[INFO] 编译日志已导出至: {log_file.as_posix()}")
+ self.progress.emit(_("[INFO] Build log exported to: {path}", path=log_file.as_posix()))
except: pass
- self.finished.emit(True, f"[SUCCESS] 编译已完成,输出路径: {final_out.resolve().as_posix()}", failed_packages)
+ self.finished.emit(True, _("[SUCCESS] Compilation completed, output path: {path}", path=final_out.resolve().as_posix()), failed_packages)
else:
err_info = self.detect_python_syntax_errors()
if err_info["is_code_error"]:
- msg = (
- f"[语法异常] 源程序存在语法不合规或缩进异常错误!\n"
- f" - 错误源文件: {err_info['file']}\n"
- f" - 异常类型: {err_info['type']}\n"
- f" - 异常位置: 第 {err_info['line']} 行附近\n"
- f" - 错误描述: {err_info['desc']}\n"
- f"\n提示: 此问题为源码逻辑本身错误。请在确保本地运行无误后,再次执行编译流程。"
- )
+ msg = _("[Syntax Error] Source program has syntax or indentation errors!\n - File: {file}\n - Type: {type}\n - Line: near {line}\n - Desc: {desc}\n\nTip: This is an error in the source code logic. Ensure it runs locally before compiling.",
+ file=err_info['file'], type=err_info['type'], line=err_info['line'], desc=err_info['desc'])
else:
if self.params.get('concise_log', True) and self.all_raw_logs:
- self.progress.emit("\n" + "!"*10 + " [诊断回溯: 以下为最后段编译运行日志汇总] " + "!"*10)
+ self.progress.emit(_("\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!"))
self.progress.emit('\n'.join(self.all_raw_logs[-100:]))
- msg = "[FAILED] 编译异常中断,请参阅上方日志以定位构建异常。"
+ msg = _("[FAILED] Compilation interrupted with exceptions, refer to the log for troubleshooting.")
self.finished.emit(False, msg, failed_packages)
except Exception as e:
if self.params.get('concise_log', True) and self.all_raw_logs:
- self.progress.emit("\n" + "!"*10 + " [系统诊断: 原始日志] " + "!"*10)
+ self.progress.emit(_("\n!!!!!!!!!! [Diagnostic Traceback: Full raw log due to execution exception in this step] !!!!!!!!!!"))
self.progress.emit('\n'.join(self.all_raw_logs[-100:]))
- self.finished.emit(False, f"[ERROR] 构建流程发生致命异常: {str(e)}", failed_packages)
+ self.finished.emit(False, f"[ERROR] {str(e)}", failed_packages)
finally:
if is_temp and build_script_path and build_script_path.exists():
try:
build_script_path.unlink()
pycache_dir = script_dir / "__pycache__"
- if pycache_dir.exists():
- robust_rmtree(pycache_dir)
+ if pycache_dir.exists(): robust_rmtree(pycache_dir)
except: pass
if self.params.get('version_file'):
@@ -3009,13 +3202,11 @@ class PackingThread(QThread):
except: pass
if self.params['clean_all']:
- self.progress.emit("[INFO] 正在释放空间,清理临时构建环境...")
-
+ self.progress.emit(_("[INFO] Freeing up space, cleaning temporary build environment..."))
for p in [self.venv_dir, self.temp_workpath, self.temp_out_dir, self.temp_dist_dir]:
if p and p.exists(): robust_rmtree(p)
app_name = self.params.get('app_name', 'app')
-
for p in ["__pycache__", f"{app_name}.build", f"{app_name}.dist", f"{app_name}.onefile-build"]:
robust_rmtree(script_dir / p)
@@ -3024,6 +3215,34 @@ class PackingThread(QThread):
try: spec_file.unlink()
except: pass
+class SmoothSlideOverlay(QWidget):
+
+ def __init__(self, parent, pix_old, pix_new, direction):
+ super().__init__(parent)
+ self.pix_old = pix_old
+ self.pix_new = pix_new
+ self.direction = direction
+ self.progress = 0.0
+ self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+
+ def set_progress(self, p):
+ self.progress = p
+ self.update()
+
+ def paintEvent(self, event):
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
+
+ w = float(self.width())
+ p = self.progress
+
+ x_old = -w * p * self.direction
+ x_new = w * (1.0 - p) * self.direction
+
+ painter.drawPixmap(QPointF(x_old, 0), self.pix_old)
+ painter.drawPixmap(QPointF(x_new, 0), self.pix_new)
+
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
@@ -3033,12 +3252,13 @@ class MainWindow(QMainWindow):
self.current_state = "idle"
self.init_style()
self.init_ui()
+
+ I18N.language_changed.connect(self.retranslate_ui)
def init_style(self):
self.setWindowTitle(f"{__app_name__} {__version__}")
-
- self.setMinimumSize(720, 680)
- self.resize(760, 660)
+ self.setMinimumSize(740, 680)
+ self.resize(780, 680)
icon_path = get_resource_path("icon.ico")
if os.path.exists(icon_path):
@@ -3055,23 +3275,10 @@ class MainWindow(QMainWindow):
QStatusBar QLabel { color: #5f6368; font-size: 13px; padding: 2px; background: transparent; }
""")
- self.icon_btn_style = """
- QPushButton { background-color: #f1f3f4; border: 1px solid transparent; border-radius: 8px; }
- QPushButton:hover { background-color: #e8eaed; }
- QPushButton:pressed { background-color: #dadce0; }
- """
- self.primary_btn_style = """
- QPushButton { background-color: #1A73E8; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: bold; }
- QPushButton:hover { background-color: #1B66C9; } QPushButton:pressed { background-color: #174EA6; }
- """
- self.danger_btn_style = """
- QPushButton { background-color: #D93025; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: bold; }
- QPushButton:hover { background-color: #C5221F; } QPushButton:pressed { background-color: #A50E0E; }
- """
- self.success_btn_style = """
- QPushButton { background-color: #1E8E3E; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: bold; }
- QPushButton:hover { background-color: #188038; } QPushButton:pressed { background-color: #137333; }
- """
+ self.icon_btn_style = "QPushButton { background-color: #f1f3f4; border: 1px solid transparent; border-radius: 8px; } QPushButton:hover { background-color: #e8eaed; } QPushButton:pressed { background-color: #dadce0; }"
+ self.primary_btn_style = "QPushButton { background-color: #1A73E8; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: bold; } QPushButton:hover { background-color: #1B66C9; } QPushButton:pressed { background-color: #174EA6; }"
+ self.danger_btn_style = "QPushButton { background-color: #D93025; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: bold; } QPushButton:hover { background-color: #C5221F; } QPushButton:pressed { background-color: #A50E0E; }"
+ self.success_btn_style = "QPushButton { background-color: #1E8E3E; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: bold; } QPushButton:hover { background-color: #188038; } QPushButton:pressed { background-color: #137333; }"
def init_ui(self):
central = QWidget()
@@ -3118,7 +3325,6 @@ class MainWindow(QMainWindow):
self.btn_right = AnimatedButton("")
self.btn_right.setFixedSize(44, 44)
self.btn_right.setIcon(get_svg_icon('settings', "#5F6368"))
- self.btn_right.setToolTip("配置构建参数")
self.btn_right.setStyleSheet(self.icon_btn_style)
self.btn_right.clicked.connect(self.show_settings)
btn_layout.addWidget(self.btn_right)
@@ -3131,7 +3337,7 @@ class MainWindow(QMainWindow):
self.stacked_layout.setCurrentWidget(self.main_panel)
self.status_bar = self.statusBar()
- self.status_label = QLabel(" 状态: 准备就绪")
+ self.status_label = QLabel(_("Status: Ready"))
self.status_bar.addWidget(self.status_label)
self.copyright_label = QLabel(f"Copyright © {__company__}. ")
@@ -3140,6 +3346,16 @@ class MainWindow(QMainWindow):
self.update_ui_state("idle")
+ def show_notification(self, msg, timeout=4000):
+ msg = msg.replace('\n', ' ')
+ self.statusBar().showMessage(msg, timeout)
+
+ def show_error_log(self, msg):
+ if not self.log_container.isVisible():
+ self.toggle_log()
+ self.append_log(msg, is_error=True)
+ self.show_notification(_("Attention: Please check the log for details."), 6000)
+
def set_status(self, text):
self.status_label.setText(text)
self.adjust_status_bar()
@@ -3150,15 +3366,38 @@ class MainWindow(QMainWindow):
text = self.status_label.text()
metrics = self.status_label.fontMetrics()
text_width = metrics.horizontalAdvance(text)
-
- copyright_metrics = self.copyright_label.fontMetrics()
- copyright_width = copyright_metrics.horizontalAdvance(self.copyright_label.text())
+ copyright_width = self.copyright_label.fontMetrics().horizontalAdvance(self.copyright_label.text())
if text_width + copyright_width + 60 > self.status_bar.width():
self.copyright_label.hide()
else:
self.copyright_label.show()
+ def retranslate_ui(self):
+ self.btn_right.setToolTip(_("Configure Build Settings"))
+ is_log_open = self.log_container.isVisible()
+ if is_log_open:
+ self.btn_left.setToolTip(_("Toggle Execution Log"))
+ else:
+ if self.current_state in ("done", "failed"):
+ self.btn_left.setToolTip(_("Reset to Default Config"))
+ else:
+ self.btn_left.setToolTip(_("Toggle Execution Log"))
+
+ self.update_ui_state(self.current_state)
+
+ if self.current_state == "idle":
+ self.set_status(_("Status: Ready"))
+ elif self.current_state == "ready":
+ mode_suffix = _(" [Console]") if not self.settings_panel.noconsole_check.isChecked() else _(" [No Console]")
+ self.set_status(_("Status: Loaded {filename}{mode}", filename=Path(self.script_path).name, mode=mode_suffix))
+ elif self.current_state == "building":
+ self.set_status(_("Status: Packaging ({engine}) ...", engine=self.settings_panel.engine_combo.currentText()))
+ elif self.current_state == "done":
+ self.set_status(_("Status: Build Completed"))
+ elif self.current_state == "failed":
+ self.set_status(_("Status: Build Failed"))
+
def resizeEvent(self, event):
super().resizeEvent(event)
self.adjust_status_bar()
@@ -3172,7 +3411,7 @@ class MainWindow(QMainWindow):
self.analysis_thread.terminate()
self.analysis_thread.wait()
- if hasattr(self, 'settings_panel') and hasattr(self.settings_panel, 'scanner_thread'):
+ if hasattr(self,'settings_panel') and hasattr(self.settings_panel, 'scanner_thread'):
if self.settings_panel.scanner_thread and self.settings_panel.scanner_thread.isRunning():
self.settings_panel.scanner_thread.terminate()
self.settings_panel.scanner_thread.wait()
@@ -3181,7 +3420,6 @@ class MainWindow(QMainWindow):
def update_ui_state(self, state):
self.current_state = state
-
self.btn_right.setEnabled(state != "building")
self.drop_area.setAcceptDrops(state != "building")
@@ -3189,9 +3427,8 @@ class MainWindow(QMainWindow):
is_log_open = self.log_container.isVisible()
icon_name = 'expand_less' if is_log_open else 'expand_more'
self.btn_left.setIcon(get_svg_icon(icon_name, "#5F6368"))
- self.btn_left.setToolTip("显示/隐藏执行日志")
- self.btn_main.setText(" 开始构建")
+ self.btn_main.setText(_("Start Build"))
self.btn_main.setIcon(get_svg_icon('play', "white"))
self.btn_main.setStyleSheet(self.primary_btn_style)
@@ -3199,70 +3436,108 @@ class MainWindow(QMainWindow):
is_log_open = self.log_container.isVisible()
icon_name = 'expand_less' if is_log_open else 'expand_more'
self.btn_left.setIcon(get_svg_icon(icon_name, "#5F6368"))
- self.btn_left.setToolTip("显示/隐藏执行日志")
- self.btn_main.setText(" 停止构建")
+ self.btn_main.setText(_("Stop Build"))
self.btn_main.setIcon(get_svg_icon('stop', "white"))
self.btn_main.setStyleSheet(self.danger_btn_style)
elif state in ("done", "failed"):
self.btn_left.setIcon(get_svg_icon('refresh', "#5F6368"))
- self.btn_left.setToolTip("重置工作区")
-
if state == "done":
- self.btn_main.setText(" 打开输出目录")
+ self.btn_main.setText(_("Open Directory"))
self.btn_main.setIcon(get_svg_icon('folder', "white"))
self.btn_main.setStyleSheet(self.success_btn_style)
else:
- self.btn_main.setText(" 重新构建")
+ self.btn_main.setText(_("Rebuild"))
self.btn_main.setIcon(get_svg_icon('refresh', "white"))
self.btn_main.setStyleSheet(self.danger_btn_style)
def on_left_btn_clicked(self):
- if self.current_state in ("done", "failed"):
- self.reset_all()
- else:
- self.toggle_log()
+ if self.current_state in ("done", "failed"): self.reset_all()
+ else: self.toggle_log()
def on_main_btn_clicked(self):
- if self.current_state in ("idle", "ready", "failed"):
- self.start_pack()
- elif self.current_state == "building":
- self.cancel_pack()
- elif self.current_state == "done":
- self.open_dist()
+ if self.current_state in ("idle", "ready", "failed"): self.start_pack()
+ elif self.current_state == "building": self.cancel_pack()
+ elif self.current_state == "done": self.open_dist()
def toggle_log(self):
- if self.log_container.isVisible():
- self.log_container.hide()
- else:
- self.log_container.show()
+ if self.log_container.isVisible(): self.log_container.hide()
+ else: self.log_container.show()
self.update_ui_state(self.current_state)
- def show_settings(self):
- self._animate_switch(self.settings_panel)
+ def show_settings(self): self._animate_switch(self.settings_panel)
def show_main(self):
+ self.settings_panel.load_from_config()
self._animate_switch(self.main_panel)
def save_settings_and_return(self):
self.settings_panel.save_to_config()
- self.show_main()
+ self._animate_switch(self.main_panel)
def _animate_switch(self, target_widget):
- self.anim = QPropertyAnimation(self.stacked_layout.currentWidget(), b"geometry")
- self.anim.setDuration(250)
- self.anim.setEasingCurve(QEasingCurve.Type.OutCubic)
- self.anim.setStartValue(self.stacked_layout.currentWidget().geometry())
+ current_widget = self.stacked_layout.currentWidget()
+ if current_widget == target_widget:
+ return
+
+ if hasattr(self, 'anim_group') and self.anim_group.state() == QParallelAnimationGroup.State.Running:
+ self.anim_group.stop()
+ for attr in ('lbl_old', 'lbl_new'):
+ if hasattr(self, attr) and getattr(self, attr):
+ try: getattr(self, attr).deleteLater()
+ except RuntimeError: pass
+
+ pix_old = current_widget.grab()
+
self.stacked_layout.setCurrentWidget(target_widget)
- self.anim.setEndValue(target_widget.geometry())
- self.anim.start()
+ target_widget.setGeometry(current_widget.geometry())
+ QApplication.processEvents()
+ pix_new = target_widget.grab()
+
+ self.lbl_old = QLabel(self.centralWidget())
+ self.lbl_old.setPixmap(pix_old)
+ self.lbl_old.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+ self.lbl_old.show()
+
+ self.lbl_new = QLabel(self.centralWidget())
+ self.lbl_new.setPixmap(pix_new)
+ self.lbl_new.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+ self.lbl_new.show()
+
+ rect = current_widget.geometry()
+ x, y, w, h = rect.x(), rect.y(), rect.width(), rect.height()
+ direction = 1 if target_widget == self.settings_panel else -1
+
+ self.lbl_old.setGeometry(x, y, w, h)
+ self.lbl_new.setGeometry(x + w * direction, y, w, h)
+
+ self.anim_group = QParallelAnimationGroup(self)
+
+ anim_old = QPropertyAnimation(self.lbl_old, b"pos")
+ anim_old.setDuration(280)
+ anim_old.setEndValue(QPointF(x - w * direction, y))
+ anim_old.setEasingCurve(QEasingCurve.Type.OutCubic)
+
+ anim_new = QPropertyAnimation(self.lbl_new, b"pos")
+ anim_new.setDuration(280)
+ anim_new.setEndValue(QPointF(x, y))
+ anim_new.setEasingCurve(QEasingCurve.Type.OutCubic)
+
+ self.anim_group.addAnimation(anim_old)
+ self.anim_group.addAnimation(anim_new)
+
+ def cleanup():
+ if hasattr(self, 'lbl_old') and self.lbl_old: self.lbl_old.deleteLater()
+ if hasattr(self, 'lbl_new') and self.lbl_new: self.lbl_new.deleteLater()
+
+ self.anim_group.finished.connect(cleanup)
+ self.anim_group.start()
def on_script_selected(self, path):
path = Path(path).resolve().as_posix()
-
if is_cloud_locked(path):
- QMessageBox.critical(self, "加载失败", "目标脚本处于锁定状态,请稍后重试。")
+ self.show_error_log(_("[ERROR] Target file is locked or encrypted by cloud drive. Please decrypt and try again."))
return
if self.script_path and self.script_path != path:
@@ -3271,9 +3546,8 @@ class MainWindow(QMainWindow):
self.settings_panel.add_data_list.clear()
self.script_path = path
-
self.drop_area.set_loading(Path(path).name)
- self.set_status(f" 状态: 正在解析源文件 {Path(path).name} ...")
+ self.set_status(_("Status: Parsing {filename}...", filename=Path(path).name))
if self.analysis_thread and self.analysis_thread.isRunning():
self.analysis_thread.terminate()
@@ -3287,10 +3561,8 @@ class MainWindow(QMainWindow):
path = self.script_path
if not path: return
- if version:
- self.settings_panel.ver_ver.setText(version)
- else:
- self.settings_panel.ver_ver.setText("1.0.0")
+ if version: self.settings_panel.ver_ver.setText(version)
+ else: self.settings_panel.ver_ver.setText("1.0.0")
self.settings_panel.ver_comp.setText(author)
self.settings_panel.ver_desc.setText(desc)
@@ -3307,10 +3579,8 @@ class MainWindow(QMainWindow):
if self.settings_panel.auto_icon_check.isChecked():
preferred_extensions = [".ico", ".icns", ".png", ".svg"]
- if sys.platform == "darwin":
- preferred_extensions = [".icns", ".png", ".svg", ".ico"]
- elif sys.platform == "linux":
- preferred_extensions = [".png", ".svg", ".ico", ".icns"]
+ if sys.platform == "darwin": preferred_extensions = [".icns", ".png", ".svg", ".ico"]
+ elif sys.platform == "linux": preferred_extensions = [".png", ".svg", ".ico", ".icns"]
found = False
for ext in preferred_extensions:
@@ -3324,28 +3594,25 @@ class MainWindow(QMainWindow):
if found: break
self.drop_area.set_success(Path(path).name, custom_icon_path=auto_icon)
-
- status_suffix = ",已配置为控制台模式" if not has_gui else ",已配置为无控制台模式"
- self.set_status(f" 状态: 已载入源文件 {Path(path).name}{status_suffix}")
+ mode_suffix = _(" [Console]") if not has_gui else _(" [No Console]")
+ self.set_status(_("Status: Loaded {filename}{mode}", filename=Path(path).name, mode=mode_suffix))
if not self.log_container.isVisible(): self.toggle_log()
self.log.clear()
- self.append_log(f"[INFO] 载入应用入口文件成功: {path}")
- if not has_gui:
- self.append_log("[INFO] 静态扫描分析:未检测到图形框架。运行属性已自动配置为保留控制台。")
+ self.append_log(_("Loaded: {filename}", filename=path))
self.btn_main.setEnabled(True)
self.update_ui_state("ready")
def cancel_pack(self):
if self.thread and self.thread.isRunning():
self.thread.cancel()
- self.set_status(" 状态: 构建已终止")
+ self.set_status(_("Status: Workspace Reset"))
self.drop_area.stop_build_anim()
self.update_ui_state("ready")
def start_pack(self):
if not self.script_path or not Path(self.script_path).exists():
- QMessageBox.warning(self, "构建约束", "请先加载有效的 Python 源代码文件!")
+ self.show_error_log(_("[ERROR] Please load a valid Python source file first!"))
return
sp = self.settings_panel
@@ -3377,17 +3644,11 @@ class MainWindow(QMainWindow):
if icon_path.suffix.lower() != f".{needed_ext}":
temp_ico_name = f"qpypack_temp_icon_{int(time.time())}.{needed_ext}"
temp_ico = Path(tempfile.gettempdir()) / temp_ico_name
-
if convert_image_to_format(icon_path.as_posix(), temp_ico.as_posix(), needed_ext):
icon_path_str = temp_ico.as_posix()
temp_icon_file = temp_ico.as_posix()
- else:
- self.append_log(f"[WARN] 图像处理模块异常:目标图标转换失败,尝试使用源格式直接转交编译引擎。")
-
- add_data_items = []
- for i in range(sp.add_data_list.count()):
- add_data_items.append(sp.add_data_list.item(i).data(Qt.ItemDataRole.UserRole))
+ add_data_items = [sp.add_data_list.item(i).data(Qt.ItemDataRole.UserRole) for i in range(sp.add_data_list.count())]
main_pip = sp._get_url_from_combo(sp.pip_source_combo)
backup_pip = sp._get_url_from_combo(sp.pip_backup_combo)
@@ -3435,35 +3696,30 @@ class MainWindow(QMainWindow):
self.thread.finished.connect(self.on_pack_finished)
self.thread.start()
- self.set_status(f" 状态: 正在通过 {engine} 构建...")
+ self.set_status(_("Status: Packaging ({engine}) ...", engine=engine))
self.update_ui_state("building")
self.drop_area.start_build_anim()
def on_pack_finished(self, success, msg, failed_pkgs=None):
self.append_log("\n" + "━"*50 + "\n" + msg)
self.drop_area.stop_build_anim()
-
play_alert(success)
if success:
icon_path = self.settings_panel.icon_edit.text().strip()
self.drop_area.show_success(icon_path)
- self.set_status(" 状态: 构建完成")
+ self.set_status(_("Status: Build Completed"))
self.update_ui_state("done")
else:
self.drop_area.show_failure()
- self.set_status(" 状态: 构建失败")
+ self.set_status(_("Status: Build Failed"))
self.update_ui_state("failed")
if failed_pkgs:
- pkgs_str = ", ".join(failed_pkgs)
- QMessageBox.warning(
- self,
- "依赖缺失隐患警告",
- f"构建任务已结束,但在预构建阶段以下第三方依赖库安装失败:\n\n 👉 {pkgs_str}\n\n"
- f"⚠️ 提示:生成的可执行文件可能因缺少依赖,在运行时发生 ModuleNotFoundError 崩溃!\n"
- f"建议检查是否写错 import 名称,或在设置中补充正确的包名映射后重新构建。"
- )
+ warn_msg = _("Dependency Missing Warning: {pkgs} failed to install. Check log for details.", pkgs=", ".join(failed_pkgs))
+ self.show_notification(warn_msg, 6000)
+ err_log = _("[ERROR] Build completed, but the following dependencies failed to install during pre-build:\n\n 👉 {pkgs}\n\n⚠️ Tip: The program might crash at runtime due to missing modules!", pkgs=", ".join(failed_pkgs))
+ self.show_error_log(err_log)
def open_dist(self):
if self.settings_panel.out_mode_combo.currentIndex() == 1 and self.settings_panel.out_dir_edit.text().strip():
@@ -3485,19 +3741,24 @@ class MainWindow(QMainWindow):
self.settings_panel.hidden_edit.clear()
self.settings_panel.add_data_list.clear()
self.log.clear()
+
if self.log_container.isVisible(): self.toggle_log()
self.drop_area.reset()
- self.set_status(" 状态: 工作区已重置")
+ self.set_status(_("Status: Workspace Reset"))
self.update_ui_state("idle")
- def append_log(self, msg):
- self.log.append(msg)
+ def append_log(self, msg, is_error=False):
+ if is_error:
+ safe_msg = msg.replace('<', '<').replace('>', '>').replace('\n', '
')
+ self.log.append(f'{safe_msg}')
+ else:
+ self.log.append(msg)
+
self.log.ensureCursorVisible()
for line in msg.split('\n'):
line = line.strip()
- if not line:
- continue
+ if not line: continue
if line.startswith(("[INFO]", "[WARN]", "[SUCCESS]", "[FAILED]", "[ERROR]")):
clean_text = line
@@ -3507,52 +3768,32 @@ class MainWindow(QMainWindow):
break
if self.current_state == "building" and clean_text:
- if len(clean_text) > 35:
- clean_text = clean_text[:32] + "..."
+ if len(clean_text) > 35: clean_text = clean_text[:32] + "..."
self.drop_area.label.setText(clean_text)
self.drop_area.label.setStyleSheet("QLabel { background: transparent; color: #1A73E8; font-size: 16px; font-weight: bold; border: none; }")
def show_log_context_menu(self, pos):
menu = QMenu(self)
menu.setStyleSheet("""
- QMenu {
- background-color: #ffffff;
- color: #111827;
- border: 1px solid #d1d5db;
- border-radius: 8px;
- padding: 4px;
- }
- QMenu::item {
- padding: 6px 20px;
- border-radius: 4px;
- font-size: 12px;
- font-family: Consolas, "Segoe UI", sans-serif;
- }
- QMenu::item:selected {
- background-color: #eff6ff;
- color: #2563eb;
- font-weight: 600;
- }
- QMenu::separator {
- height: 1px;
- background-color: #e5e7eb;
- margin: 4px 2px;
- }
+ QMenu { background-color: #ffffff; color: #111827; border: 1px solid #d1d5db; border-radius: 8px; padding: 4px; }
+ QMenu::item { padding: 6px 20px; border-radius: 4px; font-size: 12px; font-family: Consolas, "Segoe UI", sans-serif; }
+ QMenu::item:selected { background-color: #eff6ff; color: #2563eb; font-weight: 600; }
+ QMenu::separator { height: 1px; background-color: #e5e7eb; margin: 4px 2px; }
""")
- act_copy = menu.addAction("复制")
+ act_copy = menu.addAction(_("Copy"))
act_copy.setEnabled(self.log.textCursor().hasSelection())
act_copy.triggered.connect(self.log.copy)
- act_select_all = menu.addAction("全选")
+ act_select_all = menu.addAction(_("Select All"))
act_select_all.triggered.connect(self.log.selectAll)
menu.addSeparator()
- act_clear = menu.addAction("清空日志")
+ act_clear = menu.addAction(_("Clear Log"))
act_clear.triggered.connect(self.log.clear)
- act_save = menu.addAction("导出日志到文件...")
+ act_save = menu.addAction(_("Export Log..."))
act_save.triggered.connect(self.save_log_file)
menu.exec(self.log.mapToGlobal(pos))
@@ -3560,39 +3801,32 @@ class MainWindow(QMainWindow):
def save_log_file(self):
content = self.log.toPlainText()
if not content.strip():
- return QMessageBox.information(self, "提示", "当前日志区域暂无内容,无需导出。")
+ return self.show_notification(_("No log content."))
default_name = "build.log"
if self.script_path:
default_name = f"qpypack_{Path(self.script_path).stem}.log"
- fp, _ = QFileDialog.getSaveFileName(self, "导出日志文件", default_name, "Log Files (*.log);;Text Files (*.txt);;All Files (*)")
+ fp, _filter = QFileDialog.getSaveFileName(self, _("Export Log..."), default_name, "Log Files (*.log);;Text Files (*.txt);;All Files (*)")
if fp:
try:
Path(fp).write_text(content, encoding='utf-8')
- QMessageBox.information(self, "导出成功", f"编译日志已成功保存至:\n{fp}")
+ self.show_notification(_("Log saved to: {path}", path=fp))
except Exception as e:
- QMessageBox.warning(self, "导出失败", f"写入日志文件失败: {e}")
+ self.show_error_log(_("[ERROR] Failed to export log file: {error}", error=str(e)))
def main():
try:
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
- except Exception:
- pass
+ except Exception: pass
app = QApplication(sys.argv)
app.setStyle("Fusion")
font = QFont()
font.setFamilies([
- "Segoe UI",
- "Microsoft YaHei",
- "PingFang SC",
- "Hiragino Sans GB",
- "Noto Sans SC",
- "Helvetica Neue",
- "Arial",
- "sans-serif"
+ "Segoe UI", "Microsoft YaHei", "PingFang SC",
+ "Hiragino Sans GB", "Noto Sans SC", "Helvetica Neue", "Arial", "sans-serif"
])
font.setPointSize(9)
app.setFont(font)
@@ -3602,4 +3836,4 @@ def main():
sys.exit(app.exec())
if __name__ == "__main__":
- main()
+ main()
\ No newline at end of file