It is currently 25 Aug 2025, 16:41
   
Text Size

Automatic translation of card names and card information

Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins

Automatic translation of card names and card information

Postby klaxnek » 06 Jul 2019, 15:00

Hi all,

I'm trying to translate card names and info of all the cards into spanish (or other languages automatically using the info from scryfall).

I have done a python script to automatically translate the card info contained in each .txt card file in cardsfolder. zip with the info retrieved from scryfall. I want to translate the following fields: Name, Types and Oracle. Here is the result of abbot_of_keral_keep.txt:

Code: Select all
Name:Abad de la Fortaleza Keral           
ManaCost:1 R
Types:Criatura - Monje humano
PT:2/1
K:Prowess
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ When CARDNAME enters the battlefield, exile the top card of your library. Until end of turn, you may play that card.
SVar:TrigMill:DB$ Mill | Defined$ You | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | SubAbility$ DBCleanup | ExileOnMoved$ Exile
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play remembered card.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:PlayMain1:ALWAYS
SVar:Picture:...
Oracle:Destreza. (Siempre que lances un hechizo que no sea de criatura, esta criatura obtiene +1/+1 hasta el final del turno.)Cuando el Abad de la Fortaleza Keral entre al campo de batalla, exilia la primera carta de tu biblioteca. Hasta el final del turno, puedes jugar esa carta.
But when I launch the game it crashes... I don't know where it checks the card names when launching.
Could you please help me? It would be nice to have automatically translated cards.

Here is the python script I have done (remove the spaces in the url, 1st post here and I cannot post urls...):
Code: Select all
import urllib.request
import re
import os

def sanitize(text):
    # remove initial \n and spaces and final \n
    text = text[3:]
    text = text.strip()
    text = text[:-2]

    # replace '
    text = text.replace("\\xe2\\x80\\x99", "'")

    # replace dash
    text = text.replace("\\xe2\\x80\\x94", "-")

    # replace ""
    text = text.replace("\\xe2\\x80\\x9c", "\"")
    text = text.replace("\\xe2\\x80\\x9d", "\"")

    # replace ·
    text = text.replace("\\xe2\\x80\\xa2", "·")

    # remove .
    text = text.replace("\\xe2\\x80\\xa8", "·")

    # replace ñ
    text = text.replace("\\xc3\\xb1", "ny")

    # replace accute vowels
    text = text.replace("\\xc3\\x81", "A")
    text = text.replace("\\xc3\\x89", "E")
    text = text.replace("\\xc3\\x8d", "I")
    text = text.replace("\\xc3\\x93", "O")
    text = text.replace("\\xc3\\x9a", "U")

    text = text.replace("\\xc3\\xa1", "a")
    text = text.replace("\\xc3\\xa9", "e")
    text = text.replace("\\xc3\\xad", "i")
    text = text.replace("\\xc3\\xb3", "o")
    text = text.replace("\\xc3\\xba", "u")
   
    return text

def remove_abbr(result):
    result = re.sub("<abbr.*\">", "", result)
    result = result.replace("</abbr>", "")
    return result

def extract_title(idx):
    found = re.findall(r'class="card-text-title" lang="es">(.*?)</h1>',str(respData))

    if not found:
        return ""

    result = found[idx]

    # remove mana cost
    result = result.replace("<span class=\"card-text-mana-cost\">", "")
    result = result.replace("</span>", "")

    result = re.sub("{.*}", "", result)
    result = result.replace("\\n", "")
    result = remove_abbr(result)

    result = sanitize(result + "\\n")

    return result

def extract_types(idx):
    found = re.findall(r'class="card-text-type-line" lang="es">(.*?)</p>',str(respData))

    if not found:
        return ""

    # remove card symbols
    result = remove_abbr(found[idx])

    if idx==1:
        result = result.replace("\\n", "")
        result = result + "\\n"

    result = sanitize(result)

    return result

def extract_oracle(idx):
    found = re.findall(r'class="card-text-oracle">(.*?)</div>',str(respData))

    if not found:
        return ""

    if idx < len(found):
        result = sanitize(found[idx])
    else:
        result = sanitize(found[0])
   
    # remove <p>
    result = result.replace("<p>", "")

    # remove \n
    result = result.replace("\\n", "")

    # replace </p> to \n
    result = result.replace("</p>", "\\n")

    # remove italics
    result = result.replace("<i>", "")
    result = result.replace("</i>", "")

    # remove card symbols
    result = remove_abbr(result)

    # remove trailing \n
    result = result[0:-2]

    return result


directory = "cards"

for filename in os.listdir(directory):
    if filename.endswith(".txt"):
        cardname = filename[0:-4]
        cardname = cardname.replace("_", "+")
        print(cardname)
        url = 'https :// scryfall. com/search?as=grid&order=name&q=' + cardname + '+lang:es'

        req = urllib.request.Request(url)
        resp = urllib.request.urlopen(req)
        respData = resp.read()

        fi = open("cards/" + filename)
        fo = open("cardstranslated/" + filename, "w")

        idx = 0

        for line in fi:
            if "Name" in line[0:4]:
                res = extract_title(idx)
                if res=="":
                    res = line[5:-1]
                fo.write("Name:" + res + "\n")
            elif "Types" in line[0:5]:
                res = extract_types(idx)
                if res=="":
                    res = line[6:-1]
                fo.write("Types:" + res + "\n")
            elif "Oracle" in line[0:6]:
                res = extract_oracle(idx)
                if res=="":
                    res = line[7:-1]
                fo.write("Oracle:" + res + "\n")
            elif "ALTERNATE" in line:
                idx = idx + 1
                fo.write(line)
            else:
                fo.write(line)

        fi.close()
        fo.close()
klaxnek
 
Posts: 11
Joined: 06 Jul 2019, 13:05
Has thanked: 6 times
Been thanked: 2 times

Re: Automatic translation of card names and card information

Postby klaxnek » 09 Jul 2019, 18:43

Hi all,

I've achieved to automatically translate all cards except the name and the card type, because Forge uses them internally. I've changed the script and now I use the offline .json file from Scryfall that contains all the info about All Cards (https: //archive.scryfall. com/json/scryfall-all-cards.json)

The script translates the different Description$ and Oracle text of all the cards into the selected language.

It compares the original text in english with some parts of the translated text if there is a translation available, and it gets the text that have the maximum likelihood with the original. Sometimes it fails with the chosen translation, but I think it works really well.

Here is the script:
Code: Select all
import json
import os
from difflib import SequenceMatcher

def similar(a, b):
    return SequenceMatcher(None, a, b).ratio()

database = "scryfall-all-cards.json"
directory = "cards"
translateddirectory = "cardstranslated"
language = 'es'

def sanitize(text):
    # replace accute vowels
    text = text.replace("Á", "A")
    text = text.replace("É", "E")
    text = text.replace("Í", "I")
    text = text.replace("Ó", "O")
    text = text.replace("Ú", "U")

    text = text.replace("á", "a")
    text = text.replace("é", "e")
    text = text.replace("í", "i")
    text = text.replace("ó", "o")
    text = text.replace("ú", "u")

    # replace ñ
    text = text.replace("ñ", "ny")

    return text

def removenewline(text):
    text = text.replace('\n', '\\n')
    return text

def removeinitialicon(text):
    initial = text.find(': ')
    if initial != -1:
        text = text[initial+2:]

    text = text.lstrip()

    return text

with open(database) as json_file: 
    data = json.load(json_file)
    found = False

    for folder in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'upcoming']:
        for filename in os.listdir(directory + '/' + folder):
            if filename.endswith('.txt'):
                fi = open(directory + "/" + folder + "/" + filename)
                os.system('mkdir -p ' + translateddirectory + "/" + folder)
                fo = open(translateddirectory + "/" + folder + "/" + filename, "w")

                line = fi.readline()
                name = line[5:-1]
                print("Card: " + name)

                printed_name = printed_text = ''
                found = False

                for card in data:
                    if name == card['name'] and language == card['lang']:
                        found = True
                        try:
                            printed_name = sanitize(card['printed_name'])
                        except:
                            pass

                        try:
                            printed_text = sanitize(removenewline(card['printed_text']))
                        except:
                            pass

                # Write Name: without translating
                fo.write(line)

                for line in fi:

                    # 1st check if there's no translation => copy file to output
                    if printed_text == '':
                        fo.write(line)

                    # Description$
                    elif line.rfind("Description$ ") != -1:
                        line = line[:-1] + ' |'
                        pieces = line.split('|')
                       
                        idx = 0

                        for piece in pieces:
                            if piece.find("Description$ ") != -1:
                                # Find Description$ at the end of the line and write it out
                                init = piece.rfind("Description$ ")
                                init = init + len("Description$ ")
                                fo.write(piece[0:init])

                                # Take the description and replace CARDNAME with the original cardname
                                descriptionenglish = piece[init:-1]
                                originalenglish = descriptionenglish

                                # Found printed_text
                                if len(printed_text) > 0:
                                    descriptionenglish = descriptionenglish.replace('CARDNAME', printed_name)
                                    ratio = 0
                                    outtext = ''

                                    translated = printed_text.split("\\n")
                                    for t in translated:
                                        if similar(t, descriptionenglish) > ratio:
                                            ratio = similar(t, descriptionenglish)
                                            outtext = t

                                    translated = printed_text.split(":")
                                    for t in translated:
                                        if similar(t, descriptionenglish) > ratio:
                                            ratio = similar(t, descriptionenglish)
                                            outtext = t

                                    translated = printed_text.split(".")
                                    for t in translated:
                                        if similar(t, descriptionenglish) > ratio:
                                            ratio = similar(t, descriptionenglish)
                                            outtext = t

                                    translated = printed_text.split(".|:|\\n")
                                    for t in translated:
                                        if similar(t, descriptionenglish) > ratio:
                                            ratio = similar(t, descriptionenglish)
                                            outtext = t

                                    # Remove initial icons
                                    outtext = outtext.replace('\\n', '')
                                    outtext = removeinitialicon(outtext) + ' '
                                    outtext = sanitize(outtext)
                                    if outtext[0] == ')':
                                        outtext = outtext[1:]
                                    fo.write(outtext)
                                                           
                            # Not Description in piece
                            else:
                                fo.write(piece)
                           
                            idx = idx + 1

                            if idx == len(pieces) - 1:
                                fo.write('\n')

                            if idx < len(pieces) - 1:
                                fo.write('|')

                    # Oracle
                    elif line.startswith('Oracle'):
                        if printed_text!='':
                            fo.write("Oracle:" + printed_text + "\n")
                        else:
                            # Translate
                            # output = hardcodedtranslations(line)
                            # output = translate(output)
                            fo.write(line)

                    else:
                        fo.write(line)

                fi.close()
                fo.close()
As you can see, you can choose other languages to translate modifying this line:
Code: Select all
language = 'es'
And then you should modify the sanitize function to include your language special characters and their equivalent to ASCII.

Here is the spanish translation of carddata.zip from Forge 1.6.27
Put that file into /res/cardsfolder/

Spanish carddata.zip Forge 1.6.27
https: //drive.google. com/file/d/1bfuRk9MdwzEZ99KF2kA_UEJH8brjOSa_/view?usp=sharing

Image of using Forge with this spanish translation:
https: //drive.google. com/file/d/1VH-XLW2VFWjqm-zD8B9-ElU5R0rvPIA9/view?usp=sharing

PS: Remove the spaces in all the links...
klaxnek
 
Posts: 11
Joined: 06 Jul 2019, 13:05
Has thanked: 6 times
Been thanked: 2 times


Return to Forge

Who is online

Users browsing this forum: No registered users and 29 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 29 users online :: 0 registered, 0 hidden and 29 guests (based on users active over the past 10 minutes)
Most users ever online was 7303 on 15 Jul 2025, 20:46

Users browsing this forum: No registered users and 29 guests

Login Form