如果你想要使用离线 AI 翻译,请前往:
https://github.com/anonymousException/renpy-translator/blob/feature/ai-translate/README_zh.md
Translator | Supported Language number | Charge | Service | Country | Reference |
---|---|---|---|---|---|
108 | Free | Token Required | America | pygtrans | ||
Youdao | 11 | 114 | Free | Token Required | Netease | China | |
Deepl | 29 | Token Required | Deepl | Germany | |
OpenAI | 108 | Token Required | OpenAI | America | openai-python |
Alibaba | 214 | Free | damo | China | translators |
ModernMt | 200 | Free | Modernmt / Translated | Italy | translators |
Bing | 133 | Free | Modernmt / Translated | Italy | translators |
CloudTranslation | 8 | Free | Xiamen University / CloudTranslation | China | translators |
Caiyun | 3 | Token Required | ColorfulClouds | China |
支持一键翻译,只需要选择目标语言和字体文件然后游戏就会被自动翻译
这等价于后面的完整教程
one_key_translate.mp4
complete_tutorial.mp4
本视频按顺序展示了:
- 展示原始的游戏
- 解包游戏
- 运行时抽取(只抽取对话)
- 官方抽取(等价于使用 renpy-sdk 里的提取翻译)
- 抽取
- 替换字体
- 在偏好设置中添加一个更改语言的入口
- 翻译
- 展示翻译后的游戏
支持打包游戏包体基于 rpatool , 你现在可以制作一个翻译 Mod 了
pack_game.mp4
web_translate_editor.mp4
web_brower_translate2.mp4
web_brower_translate.mp4
export_import_html.mp4
export_import_html2.mp4
export_import_html3.mp4
convert_to_html.mp4
支持不包含原始内容的rpy文件格式 格式化后即可翻译内容
format_rpy_files.mp4
add_entrance.mp4
extract_runtime.mp4
official_extract.mp4
unpack_game.mp4
renpy_editor.mp4
对于高级编辑,建议使用 EXCEL 等专业软件
该工具仅支持加载和简单修改
local_glossary.mp4
你应该提供 2 列:
原文:替换前的单词
当前(替换的) : 被替换后的单词
第一行应该是标题(只是为了展示,你可以重命名它们)
translate_files.mp4
translate_directory.mp4
replace_font_new.mp4
extract_files.mp4
extract_directories.mp4
抽取所有内容到整个 tl 目录并重命名(如果 tl name 为空则直接抽取到输入的 tl 目录下):
extract_tl_directory_rename.mp4
正如你所见上方翻译后的内容,在翻译后被翻译的原文也会被保留
如果要被翻译的内容缺少原内容(在"#"后作为注释),那么这些内容将被跳过不被翻译
我做这个工具的目的不是为了替代人工翻译,而是帮助
翻译的内容通常因为机翻而不够准确
因此这时原文的作用就来了,你可以根据原文重写翻译,并且你修改后的内容也不会在后面的翻译里再被替换掉
本工具也处理了被翻译文本种包含特殊符号的场景,像 "{}" "[]" 和 "<>" ,具体可以前往 特性的第 2 点
你可以通过 https://github.com/anonymousException/renpy-translator/releases/latest 下载最新版本
-
完全免费且开源
-
支持编辑器模式来编辑文件
-
支持本地术语以改进翻译
-
支持要翻译的文本中包含特殊符号,像 "{}" "[]" 和 "<>" , 特殊符号里的内容不会被翻译,例子:
# 未翻译的文本: # "Your name is [povname], right?" # 中文翻译: "你的名字是 [povname] ,对吗?" # 日语翻译: "あなたの名前は [povname] ですよね?"
-
支持抽取出 ren'py 未能发现的未翻译文本
-
支持输入、浏览、拖拽文件或者目录
-
支持针对单个 rpy 文件进行翻译/抽取未翻译文本
-
支持遍历某目录下的所有 rpy 进行翻译/抽取
-
支持替换要被翻译的语言的字体
-
兼容未被该工具的翻译的翻译过的游戏 已经翻译过的原文会被保留,只翻译未翻译的内容
-
支持跳过已抽取过的内容 , 不用担心重复抽取
举个例子 ,如果翻译内容已经存在于 tl 目录下
如:
old "Hello" new "こんにちは"
或:
# game/xxx.rpy:1352 translate japanese role_lose_7489b947: # "Hello" "こんにちは"
那么 "Hello" 将不会再被抽取
-
支持 108 种语言(默认的谷歌翻译),具体的语言列表可参考: 谷歌源语言 和 谷歌目标(翻译后)的语言
-
支持保留未被翻译的原文本作为注释
-
支持实时日志输出 , 你可以随时观察当前的翻译进度
-
支持本地代理,如果你不能正常访问谷歌|有道|Deepl,可以尝试使用 VPN (如 V2ray) 并配置本地代理
-
支持多种翻译引擎
complete_tutorial.mp4
一个好的网络环境,如果网络不好,可能无法正常调用翻译引擎来翻译
当你发现无法正常访问谷歌翻译时,可以试试:https://github.com/GoodCoder666/GoogleTranslate_IPFinder
使用工具替换完 host 以后再尝试翻译 (该方法不一定有效,很大程度上取决于当地网络运营商)
或者使用本地代理功能:
使用前:
使用:
一个未被打包的 ren'py 游戏 (能够在游戏文件夹底下找到 .rpa 文件) ,如:
你可以试试跟着这篇教程翻译我提供的 教程游戏
如果游戏被打包了,需要解包:
我一般是用从 https://f95zone.to/ 整来的 UnRen-ultrahack.bat 来解包
UnRen.mp4
强烈不推荐翻译者通过解包翻译后未经原作者授权就私自发布翻译包,请尊重原作者
下载 UnRen-ultrahack.bat 并把它拷贝到游戏目录底下 (里面含有 游戏名.exe)
现在解包功能已支持:支持通过运行时 hook 解包(能绕过大多数加密)
解包出来的文件格式是 rpyc,因此会自动调用 UnRen-forall(v9.4) 转换为 rpy
unpack_game.mp4
如果游戏完全没被翻译过,你可以接着看下面的内容
如果游戏已经被翻译过了(你想要再更新一下这个翻译) 可以直接跳转到 翻译更新
要翻译的第一件事是抽取出要翻译的游戏文本
有 2 种方式:
- 官方抽取 (由 ren'py 引擎提供的官方方式)
- 本工具抽取 (由本工具提供的抽取方式)
推荐先 官方抽取 翻译后发现翻译的文本不全时再使用本工具抽取补全后再翻译
官方抽取现在已在本工具中支持
official_extract.mp4
你可以前往 https://www.renpy.org/ 去下载最新版本的 ren'py 引擎
解压你下载的 ren'py 引擎
在右下角点击 "preferences" (设置)
重新把 "Projects Directory" 设置为你要翻译游戏的上一层文件夹
返回上一个界面然后点击 "Generate Translations" (生成翻译)
填写 "Lanauge" , 这只是一个用来翻译的标签 (它相当于本工具里的 "tl name") ,你可以随便起名,但是最好用要被翻译的语言的名称来命名。比如 "japanese" 或 "chinese" 等
其他设置保持默认即可,注意 在 "Generate Translations" (生成翻译) 里的 "Generate empty strings for translations" (为翻译生成空子串) 不要勾选
当这些都设置好以后,点击 "Generate Translations" (生成翻译) 然后等待翻译完成
翻译完成以后就可以关闭软件了,官方抽取已经完成了
你可以看到生成好的文件夹,记住这个路径
教程里的路径是:
F:\Games\RenPy\DemoGame\game\tl\japanese
extract_runtime.mp4
运行时抽取可以在不解包下只抽取对话并且为和官方一样的块格式
填写(输入,拖拽或浏览) 前面 官方抽取 生成的文件路径,然后点一下 extract 按钮,等待抽取完成即可
很简单,对吧?这就是我为什么做这个工具
add_entrance.mp4
已支持通过本工具自动添加入口
你需要在游戏里添加一个用来切换语言的入口
打开游戏文件夹,然后再进入子目录 "game" ,打开 "screen.rpy" 文件 (可以用记事本打开)
搜索关键字 : "game_menu(_("Preferences")" (一般这个关键字都是能搜到的,不然你就得自己找一下游戏用来替代 "Preferences" 的词)
通常来说你会找到很多 "vbox",像这样:
尝试找到一个包含 "Language" 或其他 "Language" 的近义词的 vbox
如果没找到,把下面的代码粘贴到包含 "skip" 的 vbox 下面
vbox:
style_prefix "radio"
label _("Language")
textbutton "English" action Language(None)
textbutton "LanguageName" action Language("The Tag you fill in official Extract")
#比如 textbutton "turn to Japanese" action Language("japanese")
注意缩进保持对齐,在编辑后应该能得到类似:
如果找到了,直接加一行到对应的 vbox 里
textbutton "LanguageName" action Language("The Tag you fill in official Extract")
#比如 textbutton "turn to Japanese" action Language("japanese")
当文件保存以后,打开游戏确保设置里面已经有了我们添加的切换语言的选项
replace_font_new.mp4
翻译前你需要有一个能够正常显示你想要翻译的语言字体文件 (*.ttf or *.otf)
默认字体路径填充您的默认系统字体,该字体与您现在使用的语言兼容
例如:如果您是日文用户,并且您的系统字体是日文,那么默认字体文件就可以支持日文,那么您无需从网上下载日文字体来解决乱码问题
one_key_translate_2.mp4
对于日语,我一般是用 hanazomefont 这个字体
对于中文,我一般是用 SourceHanSansCN 这个字体
对于其它语言,你可以很简单地在网上搜到相关的字体
打开本工具然后填写下面的内容
directory : 前面在官方抽取生成得到的路径
font: 你下载的字体文件
target : 你想要翻译的语言 (支持输入单词的前缀字母来搜索 ,不用一直滑滚轮找)
source:默认 Auto Detect (自动检测) 就行
等待翻译完成:
到目前为止,翻译都完成了,你可以打开游戏检查一下
抽取后,只需要输入前面在官方抽取里生成的文件夹和选一下要翻译的语言
对于字体,如果更新后的游戏新增了特殊的 style (有不同的字体设置) , 你可以使用字体替换功能时不输入字体的路径来涵盖新增的那部分特殊 styles (字体先前已经替换过了)
如果你想要基于本项目开发,你需要有一个 python3 的环境
然后安装的包体可以参考:requirements.txt
确保在 官方抽取 环节, 对于 "Generate Translations" (生成翻译) 选项, 不要勾选 "Generate empty strings for translations"(为翻译生成空子串)
翻译只会在满足下面格式时生效:
# game/script.rpy:553
translate schinese naming_0f7b6e71:
# r "Do name yourself like that and I'll break your face..."
r "Do name yourself like that and I'll break your face..."
or
# game/script.rpy:30886
old "Win or Lose?"
new "Win or Lose?"
注意原始文本(在 # 或 old 后) 应该和未翻译的文本(在非 # 或 new 后)完全一样
你可能会遇到像这样的错误 :
2024-01-30 14:55:19 D:\Download\Nova-Pasta\SunshineLoveCH2-1.01-pc\game\tl\Portugues/10_week10_00.rpy Error in line:1320
It’s [s_name]. And [y_name].
It’s [0] . And [1] .
É [0] . E 1] .
Error
这取决于翻译的结果,为了跳过翻译特殊符号像 '[]' '{}' '<>' , 这个工具将用按顺序的数字替换特殊字符 举个例子: "It’s [s_name]. And [y_name]." 将会被替换为 "It’s [0] . And [1] ."
通常来说,这种格式将不会被翻译且会保留 '[0]' 和 '[1]' ,并且这个工具将会根据这个有序数字还原原本的内容
然而,有时这种格式会在翻译后被破坏,正如前面提到的: "É [0] . E 1] ." 你会发现 '[' 丢了,所以本工具无法还原原本的内容,因此这行文本不会被翻译 你可能需要手动修正这些行 幸运的是这种情况很少发生,你不会花费很多时间在修正这些行上
目前谷歌是用来翻译这些包含特殊符号内容的最好的选择 , 你可以用谷歌重新翻译这些内容
这个软件是用 pyinstaller 打包的,且因为有文件操作(打开 读 写) ,因此可能会有误检测
如果你对此表示担忧,你可以自己下载源码并运行
Pyinstaller 构建的版本很容易被误检测为病毒
如果你对此表示担忧 , 可以尝试 Nuitka 构建的版本(Nuitka.Build 或 RenpyTranslatorInstaller)
正式包 | 解释 | 优点 | 缺点 |
---|---|---|---|
RenpyTranslator-Win.Pyinstaller.Build.zip | 由 Pyinstaller 构建 | 可执行文件只有一个更轻量 | 可能会被误检测为 病毒 |
RenpyTranslator-Win.Nuitka.Build.zip | 由 Nuitka 构建 | 不会被绝大多数杀毒软件误检测为病毒 | 可执行文件包含很多额外的库 |
RenpyTranslatorInstaller.exe | Nuitka 版本的安装包 | 更易于安装使用 | 需要安装 |
当你翻译完游戏运行后可以会遇到类似下面的报错
不难发现在翻译过后 "[[XXXX]" 被翻译成了"[ [XXXX]" , 多了个空格 (你遇到的可能是其它形式的错误,但重点在于在翻译后特殊符号的结构被破坏了)
这个结果是翻译引擎翻译导致的 , 有时翻译引擎对特殊符号像 '[]' '<>' ... 不太友好 特别是当特殊符号叠加使用时
但仍然有个好消息 , 通常这种情况不会很频繁发生,你只需要手动处理这些错误的句子 (对于我在上面提到的这个案例 , 只需要删除多余的空格即可)
对于在 tooltip 里的特殊文本,明明翻译了却没生效
原始的代码看起来像这样 :
tooltip "this is a tooltip"
参考来自 https://f95zone.to/threads/translation-of-text-in-screen.90781/ 的教程
只需要打开 rpy 文件 (随便一个在 tl 文件夹底下的或你可以自己创一个) 并添加以下代码
#如果是自己创建的话,需要补上下面这行去掉 # , tl_name 填 tl 目录下的文件夹名称如 schinese
#translate tl_name strings:
old "[tooltip]"
new "[tooltip!t]"
对于在 notify 里的特殊文本 ,明明翻译了却没生效
你需要定位没被翻译的句子在原本代码里的位置 (不是在翻译的 tl 文件夹下)
你将会找到看起来像这样的原始代码 :
show screen notify(_("Find old Man Gibson"), None)
替换为
show screen notify(__("Find old Man Gibson"), None)
操作只是在notify( 后添加 '_'
很简单对吧? 然后翻译就能生效了
可能会有其它情况,有些句子在翻译后仍然没生效
这取决于原始的代码,抽取可能不能完全覆盖如果原始代码写得不是那么得对翻译友好
为了避免多余的无效抽取,本工具的抽取功能不会抽取满足以下格式的句子:
句子长度小于 8 (空格和特殊符号长度被记为 0)
举个例子 :
"I know [special symbol]"
实际有效的内容是:
Iknow
长度只有 5 , 所以它不会被本工具的抽取功能抽取
除此之外,在 ConditionSwitch() 里的内容同样不会被翻译, 因为分支选择的代码可能会被包含在里面
这是正常的,因为目标文件没有可抽取的内容
别删除 0 KB 的文件,它被用来占位
如果 0 KB 文件不存在可能会导致错误产生
在抽取后你会看到类似下面的内容
old "old:1709016761.706834_0.8853030081254853"
new "new:1709016761.706834_0.8853030081254853"
这是一个不重复的时间戳用来标记你抽取的时间,格式是 时间戳_随机数(随机数是为了确保生成的内容不重复否则会引起错误)
你可以转换在 '_' 前的部分 (本例是1709016761.706834) 为可读的时间通过 https://www.epochconverter.com/ 或其它能转换时间戳的网站
你可以在 : rate-limits 查看速率限制并设置一个合理的限制
如果是 OpenAI 新用户,每分钟请求数最多 3 个,基本无法使用
默认设置就是针对这种情况 ,如果你不在意多花点时间 ,你可以使用默认设置
对于更高权限的用户 , 推荐提高配置 推荐配置如下
参数 | 推荐 |
---|---|
RPM(requests per minute) | 250 |
RPS(requests per second) | 10 |
TPM(requests token limits) | rate-limits |
model | gpt-3.5-turbo | gpt-4 (更智能但更贵) |
OpenAI 似乎对像 '{}' '[]' 的特殊符号不太友好 , 你可能会遇到前面提到的问题 : 有些错误导致某些行无法被翻译
你可以用谷歌翻译重新翻译这些错误的行 (已经被翻译过的行将会被自动跳过,只需要重新翻译之前翻译过的整个目录或文件即可)
(谷歌翻译会对这些特殊符号更友好但仍然可能留下一点点错误)
最后你需要手动处理剩下的一点点错误
除此之外如果你在使用 openai 时遇到 traceback 错误 , 尝试禁用 Multi-Translate 选项,并重新翻译 (更稳定但会慢一些)
你可能遇到如下错误:
2024-02-22 11:18:28 Traceback (most recent call last):
File "openai_translate.py", line 180, in translate_limit
File "json\__init__.py", line 346, in loads
File "json\decoder.py", line 337, in decode
File "json\decoder.py", line 353, in raw_decode
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 1613 (char 1612)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "openai_translate.py", line 187, in translate_limit
Exception: Unterminated string starting at: line 1 column 1613 (char 1612)
这是由于 openai 返回了错误的数据格式 , 幸运的是这并不会频繁发生并且只会导致一个文件部分内容未被成功翻译,你可以用其它翻译引擎像谷歌翻译来重新翻译这些未被翻译的行
你可能会遇到如下报错:
2024-02-23 10:19:34 translated result can not match the untranslated contents
原因在于 open-ai 可能会偶然把多个翻译结果混合成一个 , 例如 :
#untranslated
["Hello","Good"]
#translated
["こんにちは,良い"]
这会导致翻译结果的不匹配 , 因此这部分内容的翻译不会生效 , 你可能需要用其它翻译引擎翻译这些未翻译的内容
你可能会遇到如下报错:
2024-02-23 10:19:34 Another non-200-range status code was received:400 <Response [400 Bad Request]>
2024-02-23 10:19:34 Error code: 400 - {'error': {'message': 'Connection error (request id: 20240223101928248984704uoEfBgvh)', 'type': '', 'param': '', 'code': 'connection_error'}}
2024-02-23 10:19:34 ['untranslated1','untranslated2','....']
这取决于你的网络环境,重新翻译可能就可以了
你可以写一个提供翻译 api 的 python 脚本,并把它导入到此工具
你应该参照 _caiyun.py 和 _baidu.py 来写 (api 函数名必须是 tranlate_single 或 tranlate_queue)
除此之外你还应该为 source 和 target 提供 2 个语言文件像 caiyun.source.rst 和 caiyun.target.rst
一个好的例子看起来是这样的 :