首页/新闻资讯/正文详情

Easydict 回译语言方向保留机制解析:Auto 检测、双 Auto 与偏好传播的完整实现

发布时间:2026/9/23 1:14:41 来源:云帆数科 栏目:资讯中心
Easydict 回译语言方向保留机制解析:Auto 检测、双 Auto 与偏好传播的完整实现
桌面应用AI 应用【免费下载链接】Easydict一个简洁优雅的词典翻译 macOS App。开箱即用支持离线 OCR 识别支持有道词典 苹果系统词典 苹果系统翻译OpenAIGeminiDeepLGoogleBing腾讯百度阿里小牛彩云和火山翻译。A concise and elegant Dictionary and Translator macOS App for looking up words and translating text.项目地址https://gitcode.com/gh_mirrors/ea/Easydict点击查看免费下载本文围绕 Easydict 查询窗口的“语言交换回译”功能深入解析一次针对 Auto 源语言方向丢失问题的完整修复从问题成因、核心门禁逻辑到双 Auto 采用译文重查、纯空白结果与共享偏好传播等边界处理并结合 EZBaseQueryViewController.m 源码与 ReverseTranslationTests.swift 测试矩阵还原一个可复现、可验证的实现方案。读者读完可掌握 Easydict 中toggleTranslationLanguages的完整行为契约以及如何在查询窗口层设计健壮的语言方向保留逻辑。背景Auto 源语言回译为什么会丢失方向Easydict 的查询窗口EZBaseQueryViewController在输入框下方提供语言选择条EZSelectLanguageCell用户可以点击中间的交换按钮⌘T交换源语言与目标语言将译文作为新输入进行“回译”。问题出在 Auto 源语言上。当用户将源语言设为自动Auto时实际查询方向由语言检测结果决定。例如输入日语文本源语言为 Auto系统检测出日语detectedLanguage japanese目标语言为中文译文输出“你好”此时点击交换按钮进行回译期望的结果是“你好 → 日语”即回到检测出的实际语言方向但修复前交换逻辑直接对用户设置的语言偏好userSourceLanguage Auto、userTargetLanguage 中文进行对调回译变成了“中文 → Auto”。按默认的中英偏好Auto 目标会被解析成英语最终回译方向错误地变成了“中文 → 英语”而不是“中文 → 日语”。这一问题的根因在于交换操作发生前检测出的实际语言方向queryFromLanguage/queryTargetLanguage没有被捕获并传递给交换路径。查询模型 QueryModel.swift 中有效查询方向由用户偏好与检测结果共同决定/// Effective query source language. var queryFromLanguage: Language { hasUserSourceLanguage ? userSourceLanguage : detectedLanguage } /// Effective query target language. var queryTargetLanguage: Language { let fromLanguage queryFromLanguage if hasUserTargetLanguage { return userTargetLanguage } return EZLanguageManager.shared().userTargetLanguage(withSourceLanguage: fromLanguage) }也就是说queryFromLanguage在 Auto 场景下会解析为检测语言但交换逻辑只操作userSourceLanguage/userTargetLanguage这两个原始偏好检测结果在交换时被丢弃从而“按中英偏好重新选择英语”。核心修复在采用完成译文前捕获实际语言方向修复思路记录于 2026-09-05-reverse-translation-auto-direction.md可概括为一句话在采用完成译文之前先捕获有效的源语言和目标语言并将其交给现有语言交换路径。若有效语言尚未确定或两端相同则不替换输入加载中、出错、无结果以及未完成的流式输出一律保留原行为。最终的toggleTranslationLanguages实现位于 EZBaseQueryViewController.m- (void)toggleTranslationLanguages { BOOL canSwap [self.selectLanguageCell canToggleTranslationLanguages]; BOOL bothAutomatic [self.queryModel.userSourceLanguage isEqualToString:EZLanguageAuto] [self.queryModel.userTargetLanguage isEqualToString:EZLanguageAuto]; if (canSwap || bothAutomatic) { BOOL isStreamingInProgress self.firstService.isStream !self.firstService.result.isStreamFinished; BOOL isResultReady !self.firstService.result.isLoading !isStreamingInProgress !self.firstService.result.error; NSString *translatedText isResultReady ? [self firstTranslatedText] : nil; if (bothAutomatic) { // Re-query the translation using Auto without changing language preferences. if ([translatedText ns_trim].length 0) { self.queryModel.ocrImage nil; [self startQueryText:translatedText actionType:EZActionTypeInputQuery]; } return; } // Use the completed results direction; shared language preferences may have changed. EZLanguage fromLanguage self.firstService.result.from; EZLanguage toLanguage self.firstService.result.to; BOOL hasReverseDirection fromLanguage.length 0 toLanguage.length 0 ![fromLanguage isEqualToString:EZLanguageAuto] ![toLanguage isEqualToString:EZLanguageAuto] ![fromLanguage isEqualToString:toLanguage]; if ([translatedText ns_trim].length 0 hasReverseDirection) { self.queryModel.userSourceLanguage fromLanguage; self.queryModel.userTargetLanguage toLanguage; self.inputText translatedText; self.queryModel.ocrImage nil; self.queryModel.actionType EZActionTypeInputQuery; } } [self.selectLanguageCell toggleTranslationLanguages]; }有效性门禁非空、非 Auto、两端不同非双 Auto 分支采用译文前会同时检查两个条件译文有效translatedText经ns_trim去除首尾空白后长度大于 0方向有效hasReverseDirection首服务完成结果的from与to均非空、均非 Auto、且两端不相同。译文来源firstTranslatedTextEZBaseQueryViewController.m本身也带有一道一致性校验——只有当首服务结果的queryText与当前查询文本一致时才返回结果优先返回copiedText否则返回translatedText- (nullable NSString *)firstTranslatedText { if (self.firstService [self.firstService.result.queryText isEqualToString:self.queryModel.queryText]) { NSString *copiedText self.firstService.result.copiedText; if (copiedText.length 0) { return copiedText; } return self.firstService.result.translatedText; } return nil; }当满足全部条件时交换路径会先把完成结果的方向写回用户偏好userSourceLanguage fromLanguage、userTargetLanguage toLanguage再把译文写入输入框、清空 OCR 图片、将动作类型改为EZActionTypeInputQuery普通文本查询最后才调用语言选择条的toggleTranslationLanguages完成交换与重查。这样回译方向严格跟随“上一次查询完成结果”的实际方向而不是当前可能已漂移的偏好。不可用结果的原行为保留对于以下场景代码不会替换输入与 OCR 状态仅保留“仅交换语言”的回退行为结果仍在加载isLoading YES流式输出未完成isStream !isStreamFinished查询出错result.error非空首服务结果缺失或与当前查询文本不匹配stale译文为空或纯空白。此时translatedText为nil或 trim 后为空非双 Auto 分支的直接交换仅更新userSourceLanguage/userTargetLanguage并调用 cell 的交换逻辑不会被执行到“替换输入”的路径ocrImage与actionType保持原状。双 Auto采用译文重新查询的简化方案首轮修复刻意未扩展默认Auto → Auto的 no-op 行为也未修改全局自动语言选择策略。随后在 2026-09-06 的迭代中用户确认了双 Auto 场景采用简化方案将首服务的完成译文放入输入框再按现有 Auto 规则查询不增加临时语言覆盖不保证严格返回原语言双 Auto 选项和默认语言偏好保持不变。对应代码即toggleTranslationLanguages中的bothAutomatic分支当userSourceLanguage与userTargetLanguage均为 Auto 时若译文 trim 后非空则清空ocrImage并以EZActionTypeInputQuery重新发起查询同时直接return绕过下方hasReverseDirection的 raw 语言相等门禁Auto 与 Auto 在canToggleTranslationLanguages中是不允许交换的因此必须单独开这条通道。这一设计意味着双 Auto 下不修改任何语言偏好MyConfiguration.shared.fromLanguage/toLanguage保持 Auto回译方向由“当前查询的 Auto 解析规则”决定而非死记上一次的方向加载中、出错、空结果、过期结果等不可用状态不会触发重查保持原输入、OCR 与查询动作不变。边界与回归纯空白译文与共享偏好传播在评审过程中PR #1276 的 review comment又暴露了两个需要单独处理的边界问题均在 2026-09-06 同日完成修复。拒绝纯空白回译结果非双 Auto 分支曾存在一个漏洞当译文为纯空白如 \n\t 时若hasReverseDirection成立输入会被替换为空字符串导致原始输入丢失。修复方式是在采用译文前统一检查ns_trim后的非空性纯空白结果保留原输入、OCR 和查询动作仅沿用原有的“仅交换语言”回退行为。使用完成结果保存的语言方向共享语言偏好MyConfiguration.shared.fromLanguage/toLanguage是全局的会通过 QueryModel.swift 的Defaults.publisher订阅自动传播到所有查询模型private func startObservingDefaults() { Defaults.publisher(.queryFromLanguage, options: [.initial]) .map(\.newValue) .removeDuplicates() .receive(on: DispatchQueue.main) .sink { [weak self] language in self?.userSourceLanguage language } .store(in: cancellables) Defaults.publisher(.queryToLanguage, options: [.initial]) .map(\.newValue) .removeDuplicates() .receive(on: DispatchQueue.main) .sink { [weak self] language in self?.userTargetLanguage language } .store(in: cancellables) }这带来了一个隐蔽问题如果用户在查询期间修改了共享偏好例如改成 英→法旧的译文回译时会按“新方向”被错误回译。修复方案是让非双 Auto 分支改用首服务完成结果的from/to而不是当前的queryModel偏好并保留“非空、非 Auto、非同语言”校验测试也同步使用结果语言快照并覆盖共享偏好变化后的回译方向。测试矩阵ReverseTranslationTests 的 21 个场景整个修复以 ReverseTranslationTests.swift 作为回归保障。该测试套件直接驱动真实的EZBaseQueryViewController与EZSelectLanguageCell而非 mock 替身但通过 KVC 注入私有协作对象、以临时子类捕获查询入口从而在不启动查询窗口、不发起任何翻译请求的前提下验证完整行为。测试基座真实 controller/cell 与捕获查询入口withFixtureReverseTranslationTests.swift完成以下装配通过NSClassFromString创建真实的EZBaseQueryViewController与EZSelectLanguageCellKVC 注入queryModel、inputText、firstService、selectLanguageCell及 cell 的enterActionBlock构造一个ReverseTranslationService继承QueryServiceserviceType()返回.apple通过streaming标志模拟普通与流式结果设置一个带 OCR 图片的日语查询detectedLanguage japanese、actionType .ocrQuery为“Auto 源语言 OCR 查询”场景建立基线测试结束前恢复MyConfiguration.shared的语言偏好避免污染其他测试。withCapturedQueryReverseTranslationTests.swift则利用 Objective-C 运行时为当前 controller 实例动态创建子类并替换startQueryText:actionType:的实现捕获回译发起的查询文本、动作类型与模型状态验证“不真正发起网络请求”的前提下调用链正确。7 个测试方法、21 个场景清单最终版本共 7 个测试方法、21 个参数化场景全部通过0 失败测试方法覆盖场景场景数detectedSourceAuto 源语言按检测语言回译日语→中文交换回日语1explicitSource显式语言 已完成流式输出仍正常回译1unavailableResultempty / whitespace / loading / streaming / error / stale 六种不可用状态6invalidResultLanguages结果两端相同same/ from 为 Auto / to 为 Auto3changedPreferences共享语言偏好传播后按完成结果方向回译1bothAutomatic双 Auto 流式false/true采用译文重查2bothAutomaticUnavailable双 Auto 下 missing / empty / whitespace / loading / streaming / error / stale 不触发重查7其中makeResultUnavailableReverseTranslationTests.swift统一构造各不可用状态case missing: controller.setValue(nil, forKey: firstService) case empty: service.result.translatedResults [] case whitespace: service.result.translatedResults [ \n\t ] case loading: service.result.isLoading true case streaming: service.streaming true service.result.isStreamFinished false case error: service.result.error QueryError(type: .api) case stale: service.result.queryText a different query有效结果的三个方向测试detectedSourceReverseTranslationTests.swift验证“Auto 源语言回译到检测语言而非 Auto 目标”——交换后userSourceLanguage .simplifiedChinese、userTargetLanguage .japanese输入变为译文“你好”OCR 图片清空、动作类型变为.inputQueryexplicitSourceL43-L57显式语言 已完成流式输出streaming true且isStreamFinished true仍能正确回译changedPreferencesL97-L134先等待共享偏好从默认值传播到模型英→法再触发交换断言回译方向仍取完成结果的日→中而不是被传播后的偏好干扰且共享配置被正确更新为 中→日。无效结果与双 Auto 的防护测试unavailableResult与invalidResultLanguages断言输入保持“こんにちは”、userSourceLanguage .simplifiedChinese、userTargetLanguage .auto、ocrImage非空、actionType .ocrQuery——即只做“仅交换语言”的回退绝不替换输入bothAutomaticL136-L162用withCapturedQuery验证恰好发起 1 次startQueryText(你好, .inputQuery)且偏好保持 AutobothAutomaticUnavailableL164-L189验证 7 种不可用状态下查询计数为 0输入、OCR、动作与偏好全部保持不变。验证与构建命令整个迭代的验证流程记录于历史文档包含静态检查与定向测试两部分# 静态检查 git diff --check swiftformat --lint EasydictTests/Feature/Query/ReverseTranslationTests.swift plutil -lint Easydict.xcodeproj/project.pbxproj # 工程文件语法检查 # 定向 Xcode 测试最终版本arm64 xcodebuild test -workspace Easydict.xcworkspace -scheme Easydict \ -destination platformmacOS,archarm64 \ -only-testing:EasydictTests/ReverseTranslationTests \ ENABLE_PREVIEWSYES CODE_SIGNING_ALLOWEDNO其中ENABLE_PREVIEWSYES用于跳过全仓格式化步骤测试文件单独执行swiftformat --lintCODE_SIGNING_ALLOWEDNO允许在无签名环境下运行测试。迭代中还出现过“旧版测试二进制导致结果失真”的问题——测试源码时间戳未刷新时 Xcode 会复用旧编译产物解决方案是刷新测试源码时间戳后重新编译并以日志确认新增场景确实被执行。小结Easydict 的这次回译方向修复本质上是把“回译方向”的决策依据从用户偏好迁移到了完成结果的实际语言方向单侧 Auto 与显式语言采用首服务完成结果的from/to带非空、非 Auto、非同语言三重门禁译文 trim 后非空才替换输入双 Auto不走交换门禁将完成译文放回输入框按现有 Auto 规则重查不触碰任何偏好所有不可用状态加载中、流式未完成、出错、无结果、过期、空白一律保留原输入与 OCR 状态仅回退到纯语言交换共享偏好传播不再污染回译方向方向快照来自结果而非当前配置。与之配套的 21 个测试场景覆盖了有效、无效、双 Auto 与偏好传播四类路径并通过真实 controller/cell 运行时方法替换的测试基座在不触碰网络与窗口的前提下锁定了全部行为契约。若读者需要在类似查询型应用中实现“保留实际语言方向的回译”上述门禁设计、快照策略与测试矩阵均可直接迁移复用。延伸阅读完整的迭代记录见 docs/histories/2026-09/2026-09-05-reverse-translation-auto-direction.md核心实现见 EZBaseQueryViewController.m 与 EZSelectLanguageCell.m语言解析模型见 QueryModel.swift回归测试见 ReverseTranslationTests.swift。赞分享桌面应用AI 应用【免费下载链接】Easydict一个简洁优雅的词典翻译 macOS App。开箱即用支持离线 OCR 识别支持有道词典 苹果系统词典 苹果系统翻译OpenAIGeminiDeepLGoogleBing腾讯百度阿里小牛彩云和火山翻译。A concise and elegant Dictionary and Translator macOS App for looking up words and translating text.项目地址https://gitcode.com/gh_mirrors/ea/Easydict点击查看免费下载相关推荐XUnity Auto TranslatorUnity游戏多语言翻译完整解决方案XUnity Auto TranslatorUnity游戏多语言翻译完整解决方案 XUnity Auto Translator 是一款强大的 Unity 游戏游戏开发本地部署AI 应用auto-novel国际化支持多语言界面与翻译方向切换实现auto novel国际化支持多语言界面与翻译方向切换实现 auto novel作为轻小说机翻网站提供了全面的国际化支持包括多语言界面切换和灵活的翻译方向后端前端AI 应用网页爬虫XUnity Auto TranslatorUnity游戏多语言翻译完整指南XUnity Auto TranslatorUnity游戏多语言翻译完整指南 XUnity Auto Translator是一款专为Unity游戏设计的强大多虚拟化图形学创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

knowledge-work-plugins 插件机制解析:斜杠命令与知识工作自动化
knowledge-work-plugins 插件机制解析:斜杠命令与知识工作自动化

1. 从零认识 knowledge-work-plugins:它到底解决什么问题第一次看到knowledge-work-plugins这个仓库名的时候,我下意识以为又是一个"把文档塞进向量库然后问答"的套壳项目。真正把代码拉下来跑了一遍之后才发现,它的定位比我想的要… · 2026/9/23 1:14:41

语音合成助手免费版新手避坑:3行代码打通TTS核心逻辑
语音合成助手免费版新手避坑:3行代码打通TTS核心逻辑

语音合成助手免费版新手避坑:3行代码打通TTS核心逻辑 看了一堆教程还是不会写项目?别慌,这通常是 新手避坑 没做到位。很多开发者卡在“理论懂了,代码跑不通”的阶段,其实是因为没看懂底层数据流。 今天咱们不整虚的,直接拆解一个开源的… · 2026/9/23 1:14:41

项目实施方案模板83009:从结构设计到自动化生成
项目实施方案模板83009:从结构设计到自动化生成

简介:这是一份面向项目管理者、实施工程师及方案撰写人员的项目实施方案模板,适用于信息化平台或软件系统交付类项目的启动、执行与验收全过程。文档以标准公文式结构组织,依次覆盖项目概述、实施阶段划分、项目管理机制与培训方案&#xff0… · 2026/9/23 1:14:35

最新炫舞挂揭秘:面试必问的内存读写最佳实践
最新炫舞挂揭秘:面试必问的内存读写最佳实践

最新炫舞挂揭秘:面试必问的内存读写最佳实践 面试被问到“如何监控进程内存”却答不上来?这不仅是尴尬,更是技术底色的暴露。很多开发者把“最新炫舞挂”这类话题只当八卦,却忽略了其背后隐藏的 内存读写 与 进程注入… · 2026/9/23 6:34:36

meid是什么?3个致命坑让你的项目直接崩盘
meid是什么?3个致命坑让你的项目直接崩盘

meid是什么?3个致命坑让你的项目直接崩盘 看了一堆教程还是不会写项目?别怪代码,是你没搞懂底层的 meid 机制。很多新手在搭后台时,看到数据库字段里有个 meid ,或者接口返回里带着 meid ,一脸懵圈:这玩意儿到底是主键 ID… · 2026/9/23 6:34:30

3天搞定g盘环境,附速查手册避坑指南
3天搞定g盘环境,附速查手册避坑指南

3天搞定g盘环境,附速查手册避坑指南 配置环境就卡半天,是不是你的常态?别急,今天这篇 g盘 入门教程,就是为你准备的 速查手册 。咱们不整虚的,直接解决你搭建环境时遇到的那些头疼问题,让你从“卡半天”变成“半小时搞定”。… · 2026/9/23 6:34:24

3步搞定带字qq头像生成,面试必问的Canvas实战避坑指南
3步搞定带字qq头像生成,面试必问的Canvas实战避坑指南

3步搞定带字qq头像生成,面试必问的Canvas实战避坑指南 配置环境就卡半天,Node.js版本不兼容、字体加载失败、中文字体缺失,这简直是新手写脚本的噩梦。别急着骂娘,这其实是很多后端转全栈或者前端实习生在【面试必问】环节最容易翻车的地… · 2026/9/23 6:34:18

vc 教程源码解析:搞定环境配置,C++入门到精通
vc 教程源码解析:搞定环境配置,C++入门到精通

vc 教程源码解析:搞定环境配置,C++入门到精通 配置环境就卡半天?Visual Studio 安装包巨大,组件勾选眼花缭乱,编译报错满屏飘。很多转行做 C++ 开发的同行,还没写第一行代码,就在搭建 VC… · 2026/9/23 6:34:05

qq64位下载入门到精通:解决版本升级API全变痛点
qq64位下载入门到精通:解决版本升级API全变痛点

qq64位下载入门到精通:解决版本升级API全变痛点 版本升级后 API 全变了,很多老手都在这一步卡壳。 想从 qq64位下载 的入门到精通,光看文档根本不够。 必须搞懂底层协议,才能应对腾讯频繁的接口变动。 项目目标… · 2026/9/23 6:34:05

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码