移动端通过 python 使用 i18n 汉化插件

这个方法我很早就在用了,最近偶然看到 i18n 插件,所以又想了起来。由于 i18n 插件不支持移动端,所以只能迂回使用该方法。同时该方法需要安装 pydroid3 或其它可以执行 python 代码的 app。

i18n 插件

该方法会使用到 i18n 插件的部分文件。桌面端可直接使用该插件,有关 i18n 插件的内容详见站内介绍:
Obsidian插件翻译工具i18n:汉化插件、翻译词典编辑器、云端共享词典、内置沉浸式翻译

方法来源

该方法来源于站内:

经过改良,适配移动端与 i18n 插件文件。

使用步骤

  1. 下载 i18n 插件,该插件中 zh-cn 文件夹中有一个 plugins 文件夹。复制该文件夹路径,替换代码中 i18n_dir 单引号中的内容。
  2. 复制自己的 obsidian 的插件路径,替换代码中 plugins_dir 单引号中的内容。
  3. 执行代码。
  4. 插件更新后汉化会失效,需要重新执行一次代码。

已实现

  • 翻译所有插件 main.js (若有翻译文件)
  • 翻译所有插件 manifest.json (仅 description)(若有翻译文件)
  • 多个翻译版本中选择最新版本进行翻译
  • 自动备份一次原 main.js 和 原 manifest.json

总结

只能勉强用用,大家还是期待 i18n 插件支持移动端吧。

代码如下


import os
import json

class I18n:
    
    plugins_dir = r'/storage/emulated/0/notes/.obsidian/plugins/'
    i18n_dir = r'/storage/emulated/0/notes/.obsidian/zh-cn/plugins'
    
    def translate_plugins(self):
        """翻译所有插件"""
        plugins_names = os.listdir(f'{self.plugins_dir}')
        for plugin_name in plugins_names:
            self._translate_plugin(plugin_name)
    def _translate_plugin(self, plugin_name:str):
        """翻译指定插件
        :plugin_name: 插件名称
        """
        i18n_plugin_dir = f'{self.i18n_dir}{plugin_name}/'
        if not os.path.exists(i18n_plugin_dir):
            print(f'{plugin_name} 的翻译目录不存在')
            return
        i18n_name = self._get_latest_file(i18n_plugin_dir)
        i18n_path = f'{i18n_plugin_dir}{i18n_name}'
        if not os.path.exists(i18n_path):
            print(f'{plugin_name} 的翻译文件不存在')
            return
        with open(i18n_path,'r',encoding='utf-8') as i18n_file:
            i18n:dict = json.load(i18n_file)
        self._translate_main(plugin_name, i18n)
        self._translate_manifest(plugin_name, i18n)
        print(f'已翻译 {plugin_name}')
    def _translate_main(self, plugin_name:str, i18n:dict):
        """翻译插件 main 文件
        :plugin_name: 插件名称
        :i18n: 翻译字典
        """
        main_path = f'{self.plugins_dir}{plugin_name}/main.js'
        with open(main_path,'r',encoding='utf-8') as plugin_file:
            main:str = plugin_file.read()
        if not os.path.exists(f'{main_path}.bak'):
            os.rename(main_path,f'{main_path}.bak')
        for line in i18n["dict"].items():
            main = main.replace(*line)
        with open(main_path,'w',encoding='utf-8') as plugin_file:
            plugin_file.write(main)
    def _translate_manifest(self, plugin_name:str, i18n:dict):
        """翻译插件 manifest 文件
        :plugin_name: 插件名称
        :i18n: 翻译字典
        """
        manifest_path = f'{self.plugins_dir}{plugin_name}/manifest.json'
        with open(manifest_path,'r',encoding='utf-8') as manifest_file:
            manifest:dict = json.load(manifest_file)
        if not os.path.exists(f'{manifest_path}.bak'):
            os.rename(manifest_path,f'{manifest_path}.bak')
        manifest["description"] = i18n["description"]["translation"]
        with open(manifest_path,'w',encoding='utf-8') as manifest_file:
            json.dump(manifest, manifest_file, indent=4, ensure_ascii=False)
    def _get_latest_file(self, dir_:str)->str:
        """获取指定目录下版本最新文件名
        :dir_: 目录路径
        :return: 版本最新文件名
        """
        # 获取目录下所有.json文件
        files = [f for f in os.listdir(dir_) if f.endswith('.json')]
        if len(files) == 1: return files[0]
        version_files = []
        for file in files:
            # 去除.json后缀并分割版本号
            version_str = file.rsplit('.', 1)[0]
            parts = version_str.split('.')
            # 验证是否为三段式版本号
            if len(parts) != 3: continue
            try:
                # 将版本号转换为整数元组
                version_tuple = tuple(map(int, parts))
                version_files.append((version_tuple, file))
            except ValueError: continue
        # 如果没有有效文件返回None
        if not version_files: return None
        # 按版本号降序排序并返回最新文件名
        version_files.sort(reverse=True)
        return version_files[0][1]
    
if __name__=='__main__':
    I18n().translate_plugins()
     
1 个赞