Home🏡

Tradução Planescape Torment - Enhanced Edition

“python translate.py“

De vez em quando eu gosto de jogar alguns RPGs antigos estilo Fallout (1 e 2), essa semana encontrei um pessoal falando muito bem de um jogo chamado Planescape. É um jogo lançado em 1999 baseado nos conceitos de D&D e todo voltado para a história, afinal, se trata de um RPG de mesa adaptado para computador. Como o jogo é antigo, na Steam não tem a opção de jogar em pt-BR, pois sendo bem sincero, não estava afim de ficar lendo e traduzindo as palavras que eu não entendo, queria jogar aproveitando a história, apenas.

Como toda pessoa que jogou muitos jogos no começo dos anos 2000, comecei a procurar a tradução pela internet e não encontrei em lugar algum. Encontrei alguns posts de 2005~2009 em forums sobre cRPGs dizendo que o jogo era muito grande e tomaria muito tempo para traduzir. Então eu desisti... e fui atrás de entender como funciona a tradução de jogos.

TLK

Uma das empresas que desenvolveu Planescape é a Beamdog, se trata de uma empresa fundada pelo cofundador da Bioware. Essa informação é relevante pois os jogos da Bioware possuem arquivos com extensão .tlk. Nestes arquivos, são salvos os diálogos do jogo em diversas linguagens. Sabendo disso, comecei a pesquisar sobre como extrair estes textos dos arquivos. Em alguns forums bem antigos (2005), encontrei algumas ferramentas que começaram a me dar ideias de como eu iria traduzir o jogo. Uma delas se chama Inifinity Engine Talk Table Editor, através dela é possível carregar um arquivo .tlk, navegar entre os diálogos e substituir eles por outros.

alt

Porém, o jogo possui mais de 100k de diálogos, com essa ferramente eu iria precisar traduzir os diálogos um a um, isso daria muito trabalho. Fazendo mais algumas pesquisas, encontrei uma ferramenta (da mesma empresa que desenvolveu o Table Editor), se chama Weidu, é um executável que consegue transformar um arquivo .tlk em um arquivo da extensão .tra, essa extensão diferente da .tlk, pode ser lida como um arquivo texto comum.

TRA

alt

Cada @ representa um diálogo no jogo e o texto dos diálogos ficam entre TILS. Sabendo disso, usando NodeJS escrevi um código que faz um REGEX nesse arquivo e captura todos os textos que estão dentro dos TILS e salve em um json novo com a seguinte estrutura:

{
  "index": 0,
  "original": "text"
  "translated": ""
}

Código:

const fs = require("fs")

const REGEX = /(?<=~)(.*?)(?=~)/gs

const allFileContents = fs.readFileSync("dialog.tra", "utf-8")

const array = allFileContents.match(REGEX)

const isEven = (number) => number % 2 == 0

const newFile = []

array.map((text, index) => {
  if (!isEven(index)) return

  newFile.push({ index, original: text, translated: "" })
})

fs.writeFileSync("new-file.json", JSON.stringify(newFile), "utf-8")

Tradução

Eu queria algo automático, não queria desprender algum tempo pra traduzir manualmente o jogo. Usando python eu escrevi um código que le esse json com os diálogos extraidos do jogo e traduz utilizando uma lib.

import io
import json
from deep_translator import GoogleTranslator

f = open("new-file.json", "rb")

data = json.load(f)

array = []

def save_file(content):
    with io.open("output.txt",'w',encoding='utf8') as f:
        f.write(json.dumps(content))

for dialog in data['dialogs']:
    text = dialog['original']
    try:
        translated = GoogleTranslator(source='english', target='portuguese').translate(text)
        dict_dialogue = { "original": text, "translated": translated }
        array.append(dict_dialogue)
        save_file(array)
        print(f"finished dialogue")
    except Exception as e:
        print(f'ERRO={e} - DIALOGUE={text}')

print('finalizado')

Esse processo demorou cerca de 29hrs, eu não medi o tempo pelo código, mas levou cerca de 1 segundo por diálogo traduzido.

Depois de criar um json gigantesco com os diálogos originais e as traduções, escrevi um outro código em NodeJS para substituir no arquivo .tra os diálogos de en-US para pt-BR.

const fs = require("fs")
const dialogs = require("./output1.json")

let tra = fs.readFileSync("./dialog.tra", "utf-8")

const findInTraFile = (text) => tra.indexOf(`~${text}~`)
const replaceInTraFile = (originalText, translated) => tra.replace(`~${originalText}~`, `~${translated}~`)

console.time("translate")

for (let index = 0; index < dialogs.length; index++) {
  const dialog = dialogs[index]
  const text = dialog.original

  console.log(text)

  const exists = findInTraFile(text) > -1

  if (exists) {
    tra = replaceInTraFile(text, dialog.translated)
    fs.writeFileSync("dialog-new.tra", tra, "utf-8")
    console.log("ultimo trocado foi", index)
  } else {
    console.log("erro", index)
  }
}

console.timeEnd("translate")

Esse passo demorou cerca de 1hr. E para finalizar, utilzei a ferramenta Weidu para transformar o arquivo .tra em .tlk.

Resultado

alt

Faltam só alguns ajustes, como voltar o nome original dos personagens do jogo, mas para fazer isso basta editar o arquivo .tra e depois converter ele novamente.

Github: https://github.com/nulldreams/planescape