机翻Minecraft(我的世界)任务

对Minecraft(我的世界)任务进行

阅读量:986

点赞量:2

评论量:0
Life
标签
标签: JavascriptNodejs
摘要
对Minecraft(我的世界)任务进行机翻
正文


前言

最近迷上了我的世界,不过curseforge平台上面下载的整合包都没有汉化,而对于我这样的英语不咋地的玩家就非常不友好,等着其他大佬汉化完不知道要什么时候,而且整合包更新的有些比较快,于是就想着简单的机翻一些,能看懂个大概意思,并保留原内容。

项目使用nodejs完成,使用的百度翻译进行翻译。代码就不解释了,直接放上完整代码吧。(仅支持1.16,其他未测试,估计不能用)

完整代码

const path = require('path')
const fse = require('fs-extra')
const axios = require('axios')

// 游戏版本根目录
const baseUrl = 'D:/app/pcl2/.minecraft/versions/Ragnamod VI'

// ftb任务目录
const ftbQuests = path.join(baseUrl, 'config/ftbquests/quests/chapters')
// 自定义方块目录
const displayName = path.join(baseUrl, 'kubejs/startup_scripts')

startTranslate()

// 开始翻译
async function startTranslate() {
  await startFtbQuests()
  await startDisplayName()
}

// 开始汉化ftb任务
async function startFtbQuests() {
  // 获取任务文件
  const snbtFiles = fse.readdirSync(ftbQuests)
  if (snbtFiles.length) {
    for (let i = 0; i < snbtFiles.length; i++) {
      const fileName = snbtFiles[i]
      // 备份文件或者有备份文件的任务文件不再翻译
      if (
        fileName.endsWith('.bak') ||
        fse.existsSync(path.join(ftbQuests, fileName + '.bak'))
      ) {
        continue
      }
      // 读取任务文件内容
      const filePath = path.join(ftbQuests, fileName)
      const fileContent = fse.readFileSync(filePath, { encoding: 'utf-8' })
      // 获取每一行内容
      const lines = fileContent.split(/\r?\n/)
      // 存储当前行所属的字段
      let currentType = ''
      // 存储要翻译的信息(行号:文字)
      const translateObject = {}
      // 遍历行
      for (let j = 0; j < lines.length; j++) {
        const line = lines[j]
        let text = ''
        // 如果当前行含有冒号(:),则读取要翻译的文字,并存储当前行所属字段
        if (line.includes(':')) {
          const lineTrim = line.trim()
          // 存储当前行所属字段
          currentType = lineTrim.split(':')[0]
          // 只需对下面三个字段进行翻译,所以仅存储这些
          if (lineTrim.indexOf('title') === 0) {
            text = lineTrim.match(/"([^"]+)/)?.[1]
          } else if (lineTrim.indexOf('subtitle') === 0) {
            text = lineTrim.match(/"([^"]+)/)?.[1]
          } else if (lineTrim.indexOf('description') === 0) {
            text = lineTrim.match(/"([^"]+)/)?.[1]
          }
          // 否则如果当前行不包括特殊符号([]{}),则认为有可能是subtitle或者description的数据,判断后进行存储
        } else if (!/\}|\]|\{|\[/g.test(line)) {
          if (currentType === 'subtitle' || currentType === 'description') {
            text = line.match(/"([^"]+)/)?.[1]
          }
        }
        // 如果当前行有要翻译的文字,则进行存储
        if (text) {
          translateObject[j] = text
        }
      }
      // 处理要翻译的数据
      const translateObjectKeys = Object.keys(translateObject)
      const translateObjectValues = Object.values(translateObject)
      // 有则翻译
      if (translateObjectValues.length) {
        try {
          // 调用翻译方法,使用同步,防止接口请求频繁导致失败。
          const result = await translate(translateObjectValues)
          const res = result.flat()
          console.log(`文件 ${fileName} 获取翻译成功。`)
          const newContent = lines
            .map((line, index) => {
              if (translateObject[index]) {
                const lineIndex = translateObjectKeys.indexOf(index.toString())
                return line.replace(
                  /"[^"]+"/g,
                  `"${res[lineIndex].dst}(${res[lineIndex].src})"`
                )
              }
              return line
            })
            .join('\n')
          backupAndWrite(ftbQuests, fileName, newContent)
        } catch (err) {
          console.log('-------------ERROR START------------')
          console.log(`文件 ${fileName} 获取翻译失败。`)
          console.log(err)
          console.log('-------------ERROR END------------')
        }
      }
    }
  }
}

// 翻译自定义物品
async function startDisplayName() {
  // 获取自定义物品文件
  const files = fse.readdirSync(displayName)
  if (files.length) {
    for (let i = 0; i < files.length; i++) {
      const fileName = files[i]
      // 备份文件或者有备份文件的文件不再翻译
      if (
        fileName.endsWith('.bak') ||
        fse.existsSync(path.join(displayName, fileName + '.bak'))
      ) {
        continue
      }
      // 读取任务文件内容
      const filePath = path.join(displayName, fileName)
      const fileContent = fse.readFileSync(filePath, { encoding: 'utf-8' })
      // 获取每一行内容
      const lines = fileContent.split(/\r?\n/)
      // 存储要翻译的信息(行号:文字)
      const translateObject = {}
      // 遍历行
      for (let j = 0; j < lines.length; j++) {
        const line = lines[j]
        // 如果当前行含有.displayName(,则读取要翻译的文字
        if (line.includes('.displayName(')) {
          const text = line.match(/\.displayName\(['"]([^\)]+)['"]\)/)?.[1]
          translateObject[j] = text
        }
      }
      // 处理要翻译的数据
      const translateObjectKeys = Object.keys(translateObject)
      const translateObjectValues = Object.values(translateObject)
      // 有则翻译
      if (translateObjectValues.length) {
        try {
          // 调用翻译方法,使用同步,防止接口请求频繁导致失败。
          const result = await translate(translateObjectValues)
          const res = result.flat()
          console.log(`文件 ${fileName} 获取翻译成功。`)
          const newContent = lines
            .map((line, index) => {
              if (translateObject[index]) {
                const lineIndex = translateObjectKeys.indexOf(index.toString())
                return line.replace(
                  /\.displayName\(['"][^'"]+['"]\)/g,
                  `.displayName('${res[lineIndex].dst}(${res[lineIndex].src})')`
                )
              }
              return line
            })
            .join('\n')
          backupAndWrite(displayName, fileName, newContent)
        } catch (err) {
          console.log('-------------ERROR START------------')
          console.log(`文件 ${fileName} 获取翻译失败。`)
          console.log(err)
          console.log('-------------ERROR END------------')
        }
      }
    }
  }
}

// 翻译(因百度接口限制一次最多5000字,先处理,后调用接口)
function translate(texts) {
  if (!texts.length) {
    return ''
  }
  let textIndex = 0
  const textArr = ['']
  for (let i = 0; i < texts.length; i++) {
    if ((textArr[textIndex] + texts[i] + '\n').length < 5000) {
      textArr[textIndex] = textArr[textIndex] + texts[i] + '\n'
    } else {
      textIndex++
      textArr[textIndex] = texts[i] + '\n'
    }
  }
  return Promise.all(textArr.map((text) => translateBase(text)))
}

// 翻译接口
function translateBase(text) {
  return axios({
    method: 'post',
    url: 'https://fanyi.baidu.com/transapi',
    headers: {
      Accept: '*/*',
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
      Host: 'fanyi.baidu.com',
      Origin: 'https://fanyi.baidu.com',
      Referer: 'https://fanyi.baidu.com/'
    },
    data: {
      from: 'en',
      to: 'zh',
      source: 'txt',
      query: text
    }
  }).then((res) => {
    return res.data.data
  })
}

// 备份文件并写入新数据
function backupAndWrite(fileDir, fileName, text) {
  const filePath = path.join(fileDir, fileName)
  const bakPath = path.join(fileDir, fileName + '.bak')
  // 备份文件不存在时备份
  if (!fse.existsSync(bakPath)) {
    fse.renameSync(filePath, bakPath)
  }
  // 写入新数据
  fse.writeFile(filePath, text, { encoding: 'utf-8' }, () => {
    console.log(`文件 ${fileName} 翻译替换完成。`)
  })
}

项目依赖

"axios": "^1.2.0",
"fs-extra": "^10.1.0"

使用说明

1、创建一个目录,并新建文件main.js,将完整代码粘贴进去。

2、安装依赖包,修改整合包目录:baseUrl

3、运行脚本:node main.js

结语:适合尝鲜整合包,却看不懂英文的,可以有大致了解。

发布于: 12/12/2022, 4:34:40 PM
最后更新: 12/3/2024, 3:32:29 AM