└── README.md /README.md: -------------------------------------------------------------------------------- 1 | [SDEZ160汉化](https://github.com/pikayan69/SDEZ1.60.ZH)|[音击汉化](https://gitea.tendokyu.moe/Chilor/mu3-chn-psds) 2 | --- 3 | 汉化教程已发布~ 4 | https://blog.csdn.net/wdjjsn/article/details/140917049 5 | --- 6 | 7 | ## 同时也适用于几乎所有的unity引擎游戏 8 | 9 | - 前往XUnity.AutoTranslator的github [releases页面](https://github.com/bbepis/XUnity.AutoTranslator/releases)下载XUnity.AutoTranslator-ReiPatcher-版本号.zip和TMP_Font_AssetBundles.zip。 10 | - 将两个压缩包解压到游戏目录。 11 | - 典型的Unity游戏文件结构可能如下: 12 | ``` 13 | Game/ 14 | ├── Game.exe 15 | ├── Game_Data/ 16 | │ ├── Managed/ 17 | │ │ ├── Assembly-CSharp.dll 18 | │ │ ├── UnityEngine.dll 19 | │ │ └── ... 20 | │ └── ... 21 | └── ... 22 | ``` 23 | - 将压缩包解压到Game目录下,以maimai作为的示例如图![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/64e912775f914d669cf741013c4f9f09.png#pic_center) 24 | - 运行SetupReiPatcherAndAutoTranslator.exe会在当前目录生成一个快捷方式:游戏名 (Patch and Run)和一个文件夹AutoTranslator打开该文件夹里的Config.ini配置信息,修改以下内容 25 | ``` 26 | [General](括号内是我写的注释) 27 | Language=zh(要翻译成的语言) 28 | FromLanguage=ja(原语言,ja是日语) 29 | [TextFrameworks](选择要抓捕文本的类型,建议全部True,防止漏抓捕) 30 | EnableIMGUI=True 31 | EnableUGUI=True 32 | EnableNGUI=True 33 | EnableTextMeshPro=True 34 | EnableTextMesh=True 35 | EnableFairyGUI=True 36 | [Behaviour](选择替换字体和备用字体,防止翻译后出现方块字) 37 | OverrideFontTextMeshPro=arialuni_sdf_u2018 38 | FallbackFontTextMeshPro=arialuni_sdf_u2019 39 | ``` 40 | - 运行游戏名 (Patch and Run),如果游戏需要强制从启动器启动则修改启动器内容:原游戏名.exe替换为游戏名 (Patch and Run),如maimai![](https://i-blog.csdnimg.cn/direct/d3a5a60e85f842c595ffe389c842b3e7.png#pic_center)红框内的原内容是Sinmai 41 | - 启动游戏,按ALT + 0:打开 XUnity自动翻译器 UI。(这是零,不是 O)![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dd96a9699d474acdab23f99721fc817c.png#pic_center) 42 | 选择翻译器和备用翻译器,建议这两种,其他的几乎都被墙了。这时候该插件会自动抓捕游戏中的文本并机翻(如果没有请尝试按ALT + T:此插件提供的所有文本的翻译版本和未翻译版本之间的交替。),你可以在如示例图片的位置修改翻译内容。![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/23193686d6f14679870173583d6df84f.png#pic_center) 43 | 等号(=)左边为插件抓捕到的文本,右边为翻译内容,你可以修改右边的内容来进行人工翻译。 44 | - 但是游戏有很多文本是以图片形式呈现的(如maimai),这个插件只能翻译文本。![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c194f707bcb34f70b9b1cd7a67c1e485.png#pic_center) 45 | 在如示例图片的路径下你能找到resources.assets,通过这个文件可以获得游戏的全部图片资源![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/88b92549528a441b9e380ce72d2a9d05.png#pic_center) 46 | - 通过软件[AssetStudioMod](https://github.com/astro75/AssetStudioMod)你能预览并导出图片(但是不能编辑和导入),通过软件[UABEA](https://github.com/nesrak1/UABEA/releases)可以批量导出导入图片。 47 | 打开AssetStudioMod点左上角的File再点Load file,选择resources.assets并确认就可以打开该文件。点击第二行的Asset List,再在第一行选择Filter Type,再选择TexTure2D类型,就可以看到该游戏的所有图片资源。![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/80181b90f8744bdeba9704ff9ca31455.png#pic_center) 48 | 使用UABEA打开resources.assets(注意UABEA打开resources.assets需要借用到同文件夹下的其他同类文件,只有一个resources.assets是打不开的,不要把其他文件删了)选中第一个TexTure2D类型文件,向下滚动页面,按住Shift,点击最后一个TexTure2D类型文件就可以选中所有图片![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d842c2660adb4a72bf69e95a5c52fdb7.png#pic_center) 49 | 点击右边的Plugins,再点Batch export textures,点OK,选择导出路径,所有图片就会导出,找到有文字的图片,把它ps成中文的,将所有有文字的图片都改为中文后,点击右边的Plugins,再点Batch import textures,批量覆盖掉原来的图片。 50 | - 但是一个一个ps工程量太大了,像maimai这种有国服的游戏,我们可以考虑把国服的图片拿过来替换。(不能直接把国服的resources.assets直接拿过来替换,会导致内存溢出,具体的原因我也不清楚)所以我们要把国服的图片导出进行替换,同样按照上文方法导出图片,可以看到不管是国服还是日服素材都很多而且数量不一样。![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/5e71afdefa3c41099f59b8e8d4fcadc0.png#pic_center) 51 | ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/4baec4e784bb43a1be7149b0d7ba9194.png#pic_center) 52 | 同一个文件导出后-后面的数字编号不同,不能直接替换。所以我们写一个程序:检查国服和日服-前面的内容是否一致,如果一致就用日服文件的名字替换国服文件的名字,这样就把同一个文件数字编号不同的问题解决了。 53 | ```python 54 | import os 55 | import shutil 56 | 57 | def rename_files(folder1, folder2): 58 | # 获取文件夹1中的所有文件名 59 | files1 = os.listdir(folder1) 60 | print(f"Files in folder1 ({folder1}):") 61 | for file in files1: 62 | print(f" - {file}") 63 | 64 | # 创建一个字典,用于存储文件夹1中"-"之前的部分和完整文件名的对应关系 65 | name_dict = {} 66 | for file in files1: 67 | prefix = file.split('-')[0] 68 | name_dict[prefix] = file 69 | print("\nMapping of prefixes to file names in folder1:") 70 | for prefix, filename in name_dict.items(): 71 | print(f" - {prefix}: {filename}") 72 | 73 | # 获取文件夹2中的所有文件名 74 | files2 = os.listdir(folder2) 75 | print(f"\nFiles in folder2 ({folder2}):") 76 | for file in files2: 77 | print(f" - {file}") 78 | 79 | renamed_files = 0 80 | for file in files2: 81 | prefix = file.split('-')[0] 82 | # 如果文件夹2中的文件名前缀在文件夹1中存在,则重命名文件夹2中的文件 83 | if prefix in name_dict: 84 | old_path = os.path.join(folder2, file) 85 | new_path = os.path.join(folder2, name_dict[prefix]) 86 | if old_path != new_path: 87 | print(f'Renaming "{old_path}" to "{new_path}"') 88 | shutil.move(old_path, new_path) 89 | renamed_files += 1 90 | else: 91 | print(f'Skipping renaming "{old_path}" to the same name.') 92 | else: 93 | print(f'No matching prefix found for "{file}" in folder1.') 94 | 95 | print(f"\nTotal files renamed: {renamed_files}") 96 | 97 | # 设置文件夹路径 98 | folder1 = r'E:\unityzheteng\hh\work\1\png\jp(替换成你的地址)' 99 | folder2 = r'E:\unityzheteng\hh\work\1\png\zh(替换成你的地址)' 100 | 101 | # 调用函数进行重命名 102 | rename_files(folder1, folder2) 103 | ``` 104 | 保留在两个文件夹中文件名相同的文件,删除其他文件 105 | ```python 106 | import os 107 | 108 | def compare_and_delete(folder1, folder2): 109 | # 获取文件夹1和文件夹2中的所有文件名 110 | files1 = set(os.listdir(folder1)) 111 | files2 = set(os.listdir(folder2)) 112 | 113 | print(f"Files in folder1 ({folder1}):") 114 | for file in files1: 115 | print(f" - {file}") 116 | 117 | print(f"\nFiles in folder2 ({folder2}):") 118 | for file in files2: 119 | print(f" - {file}") 120 | 121 | deleted_files = 0 122 | for file in files2: 123 | if file in files1: 124 | print(f'Keeping "{file}" as it exists in both folders.') 125 | else: 126 | file_path = os.path.join(folder2, file) 127 | print(f'Deleting "{file_path}" as it does not exist in folder1.') 128 | os.remove(file_path) 129 | deleted_files += 1 130 | 131 | print(f"\nTotal files deleted: {deleted_files}") 132 | 133 | # 设置文件夹路径 134 | folder1 = r'D:\huanhua\hh\png\jp(替换成你的地址)' 135 | folder2 = r'D:\huanhua\hh\png\zh(替换成你的地址)' 136 | 137 | # 调用函数进行比较和删除 138 | compare_and_delete(folder1, folder2) 139 | ``` 140 | 使用UABEA将图片批量导入回去即可! 141 | 142 | - 有些文字你会发现使用XUnity.AutoTranslator无法抓捕到,这时候必须使用[dnSpy](https://github.com/dnSpy/dnSpy)反编译Assembly-CSharp.dll,Assembly-CSharp.dll游戏的逻辑和功能,其中也有文字。打开dnSpy,打开游戏的Assembly-CSharp.dll,在选择导出到工程,就可以获得游戏的CSharp源码![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0956d11374b94630ad54046f6da4f066.png#pic_center) 143 | 导出为工程的是一个解决方案![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/45994a2e4b7e45d4add30fa0cfa65bfc.png#pic_center) 144 | 文件夹里面就是源码,但是用vs看不方便,我们历遍所有源码挑出日文 145 | ```py 146 | import os 147 | import re 148 | 149 | def extract_japanese_text_from_cs_files(folder_path, output_file): 150 | # 正则表达式匹配日文字符 151 | japanese_pattern = re.compile(r'[\u3040-\u30ff\u4e00-\u9faf\u3400-\u4dbf]+') 152 | 153 | with open(output_file, 'w', encoding='utf-8') as txt_file: 154 | for root, _, files in os.walk(folder_path): 155 | for file in files: 156 | if file.endswith('.cs'): 157 | cs_file_path = os.path.join(root, file) 158 | with open(cs_file_path, 'r', encoding='utf-8') as cs_file: 159 | content = cs_file.read() 160 | # 提取日文内容 161 | japanese_texts = japanese_pattern.findall(content) 162 | if japanese_texts: 163 | txt_file.write(f"--- {cs_file_path} ---\n") 164 | txt_file.write('\n'.join(japanese_texts)) 165 | txt_file.write("\n\n") 166 | 167 | # 使用方法 168 | folder_path = r'E:\unityzheteng\yizhi\work\jp\csharpexport\Assembly-CSharp' # 替换为你的文件夹路径 169 | output_file = r'E:\unityzheteng\yizhi\work\output.txt' # 替换为你想要的输出文件路径和文件名 170 | extract_japanese_text_from_cs_files(folder_path, output_file) 171 | 172 | ``` 173 | 在ouput.txt搜索要汉化的文字,打开dnSpy,右键要修改的部分,点击编辑类,然后进行修改。修改完成后点击编译后保存即可。![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c947f3c358564163be01f697ef8d8d2b.png#pic_center) 174 | ## 至此就是所有的汉化教程了! 175 | 176 | --------------------------------------------------------------------------------