1809 字
9 分钟
Renpy AI翻译教程
2025-03-14
无标签

renpy和RPG的翻译环境太差了,教程和工具都相当落后,除了比较出名的T++(内嵌补丁制作,但什么字符串都提取,得删改好久)和MTool(外挂补丁,就是得挂着个翻译器才能运行补丁,但至少不会太多bug)外,其他项目在Github(开源社区)里没什么浏览,工具也落后。

Renpy的翻译可以说很简单,如果使用T++,只需要将游戏导入,翻译,导出到游戏文件夹即可,不过得排查bug,相当折磨。

还有一种更优的方法,那就是使用renpy的SDK进行翻译。但在这之前,你得把游戏拆包,至于是否只拆文本文件(如Scripts.rpa),取决于你是想要制作PC的翻译补丁,还是想进一步把这游戏的安卓安装包也制作出来。

通用翻译步骤#

注意: 确保SDK和游戏都在英文路径下

0. 解压rpa文件#

使用unrpa。

  • 安装:pip install unrpa
  • 解包:unrpa script.rpy
  • 解压rpa文件到游戏根目录。

1. 提取rpy翻译文件#

把整个游戏(包括目录)丢SDK目录(保证都是英文路径),打开renpy.exe,在项目里找到对应游戏,点击生成翻译,随便写个语言(我写的是CN),然后点击生成翻译(如果报错就把报错的文件删了),就获得了可用来翻译的文件,让我们点开看看。

示例

以上是几种文本情况,由此得出他们的文件形式

示例格式

太复杂了?简单来说,就是以下这两种基础结构不断重复而已。

结构一

# 文件路径.rpy:行数位置
translate lauguage 文件_id位置
#无人名或人名简写或"人名" 原始字符串
无人名或人名简写或"人名" 原始字符串

结构二

translate CN strings(还会提取字符串):
# 文件路径.rpy:行数位置
old "旧字符串"
new "新字符串"

2. 提取译文+翻译+注入译文#

根据上述结构,写正则表达式或python代码,来提取原文,同时还得写注回代码(就是把翻译后的文本,再写回对应位置),确保注入注回后,原rpy文件和被注回的rpy文件大致一致,之后你就可以开始AI翻译了(由于代码要调试,所以提取、翻译、注入放同一节)。

我采用的方式是提取json,参考代码放评论区,使用的是GalTransl翻译器进行翻译。

3. 替换语言#

你将sdk提取的翻译文件翻译完,还不够,你得让Renpy知道,这游戏要使用你的语言,不然就默认为英文。随便找个rpy文件,把define config.default_language = "CN"(你自己定义的语言名)写入,就是中文了。

4. 替换字体#

游戏使用中文了,但打开游戏,文字变成了方块。怎么回事?是因为这些Renpy游戏没有用中文字体,显示不了中文。好解决,先把自己准备的中文字体先丢game目录下,打开这个目录下的GUI.rpy文件,搜索ttf,把里面的字体修改为你的字体,OK,完美……了吗。

5. 错误修复#

翻译完后,游戏并非没了bug,但如果游戏能正常启动,就说明问题不大。问题通常是”rpy翻译文本中出现了出现了语法错误”,应该没什么大问题。在游戏因此报错时,玩家只需要选择跳过就行。当然,如果想错误修复,那就用SDK的”使用lint检查脚本”调出错误信息,对其进行修复。

常见的错误:[name]翻译成[名字]{i}你好{/i}翻译成{你好}\"翻译成"等导致错误。

6. 打包#

如果要让别人玩到,直接压缩整个游戏文件夹就行。如果只是想发布补丁,就只把rpy相关的文件和字体按目录结构放一起压缩。如果你想要打包到安卓玩,那就把rpa文件全部解压到game目录下,使用SDK打包。

可能的问题#

1. 上述完成后,启动游戏,结果还是英文,怎么回事?

答:搜索rpyc文件并删除,重新编译——用SDK和游戏自带的exe分别编译rpy文件(二者在启动时都会编译),反正一定是编译问题……非常离谱,反复试吧。

2. 如果打包到安卓安装包出错了怎么办?

答:就直接找这个游戏的安卓版,apk也是一种压缩包,解压,打开,点击里面的assets目录,里面有个x-game目录,去除里面文件的”x-”前缀,就相当于game目录了。这时游戏就能在SDK里运行了。把你PC版游戏翻译好的的tl翻译文件,丢到这里的tl文件夹中,用SDK进行安卓打包,OVER!

3. 如何更新补丁?

答:打开新版本游戏的game文件夹,把tl文件夹丢里面,然后进行第0步和第1步操作,之后你就会发现,tl补丁文件内容下面出现了新增的未翻译英文,对它们进行翻译即可,建议使用github项目里的Renpy-Translator进行补翻……Renpy-Translator主要是性能很差,不可视,不可操控,api差的时候更是一点都翻译不动,所以只建议在补丁更新的时候使用。

示例代码

# parsejson.py
import json
import sys

if len(sys.argv) != 3:
    print("Usage: python parsejson.py <input_file> <output_file>")
    sys.exit(1)

input_file = sys.argv[1]  # 输入文件路径
output_file = sys.argv[2] # 输出JSON文件路径

parsed_entries = []  # 存储解析后的条目
with open(input_file, "r", encoding="utf-8") as lines:
    for line_index, line in enumerate(lines):
        # 跳过特殊行
        if line.startswith("#") or line.startswith("translate") or line.startswith("game"):
            continue
        line = line.strip()
        if not line.startswith("#") and not line.startswith("old"):
            continue
         
        line = line.strip("# ")
        if line.startswith("game"):
            continue
        
        # 解析文本行
        if not line.startswith('"'):
            # 处理 name "text" 格式
            space_pos = line.index(" ")
            speaker_name = line[:space_pos]
            message_text = line[space_pos + 1:].strip('"')
        else:
            # 处理 "name" "text" 格式
            parts = line.split('"')
            if len(parts) == 3:
                message_text = parts[1]
                speaker_name = None
            elif len(parts) == 5:
                speaker_name = '"' + parts[1] + '"'
                message_text = parts[3]
                
        entry = {
            "name": speaker_name,
            "message": message_text,
            "originMessage": message_text,
            "line": line_index + 1
        }
        
        parsed_entries.append(entry)
        speaker_name = None
        
# 写入JSON文件
with open(output_file, "w", encoding="utf-8") as f:
    json.dump(parsed_entries, f, indent=4, ensure_ascii=False)
# toScript.py
import json
import sys

if len(sys.argv) != 4:
    print("Usage: python toScript.py <input_file> <injson_file> <output_file>")
    sys.exit(1)

input_file = sys.argv[1]     # 原始文本文件
json_file = sys.argv[2]      # JSON翻译文件
output_file = sys.argv[3]    # 输出文件

def count_leading_spaces(text_line):
    """计算行首空格数"""
    space_count = 0
    for char in text_line:
        if char == " ":
            space_count += 1
        else:
             break
    return space_count
            
# 读取JSON数据
with open(json_file, "r", encoding="utf-8") as f:
    translations = json.load(f)
    line_mapping = {}  # 行号到翻译的映射
    for translation in translations:
        line_mapping[translation["line"]] = translation
    
# 处理文本文件
with open(input_file, "r", encoding="utf-8") as input_f:
    with open(output_file, "w", encoding="utf-8") as output_f:
        for line_num, line in enumerate(input_f):
            speaker_name = None
            if line_num in line_mapping:
                # 获取该行的翻译
                current_translation = line_mapping[line_num]
                output_text = current_translation["message"]
                
                speaker_name = current_translation["name"]
                
                if speaker_name == "old":
                    speaker_name = "new"
                
                if speaker_name is not None:
                    output_text = speaker_name + ' "' + output_text + '"'
                else:
                    output_text = '"' + output_text + '"'
                    
                # 保持原有缩进
                space_count = count_leading_spaces(line)
                spaces = ' ' * space_count
                output_f.write(spaces + output_text + "\n")
            else:
                output_f.write(line)
Renpy AI翻译教程
https://lublog.netlify.app/posts/renpy/
作者
凛游Blog
发布于
2025-03-14
许可协议
CC BY-NC-SA 4.0