mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
Merge branch 'ui-card-translation' into 'master'
UI Card Translation (desktop and mobile) See merge request core-developers/forge!1987
This commit is contained in:
@@ -112,7 +112,7 @@ public class Localizer {
|
||||
|
||||
public static class Language {
|
||||
public String languageName;
|
||||
public String langaugeID;
|
||||
public String languageID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -536,27 +536,50 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return getText(getCurrentState());
|
||||
return getText(getCurrentState(), null);
|
||||
}
|
||||
public String getText(CardStateView state) {
|
||||
public String getText(CardStateView state, HashMap<String, String> translationsText) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
//final boolean isSplitCard = (state.getState() == CardStateName.LeftSplit);
|
||||
|
||||
String tname = "", toracle = "", taltname = "", taltoracle = "";
|
||||
|
||||
// If we have translations, use them
|
||||
if (translationsText != null) {
|
||||
tname = translationsText.get("name");
|
||||
taltname = translationsText.get("altname");
|
||||
|
||||
// Don't translate oracles if the card is a cloned one
|
||||
if (((String) get(TrackableProperty.Cloner)).isEmpty()) {
|
||||
toracle = translationsText.get("oracle");
|
||||
taltoracle = translationsText.get("altoracle");
|
||||
}
|
||||
}
|
||||
|
||||
tname = tname.isEmpty() ? state.getName() : tname;
|
||||
toracle = toracle.isEmpty() ? state.getOracleText() : toracle;
|
||||
if (isSplitCard()) {
|
||||
taltname = getAlternateState().getName();
|
||||
taltoracle = getAlternateState().getOracleText();
|
||||
}
|
||||
|
||||
if (getId() < 0) {
|
||||
if (isSplitCard()) {
|
||||
sb.append("(").append(state.getName()).append(") ");
|
||||
sb.append(state.getOracleText());
|
||||
sb.append("(").append(tname).append(") ");
|
||||
sb.append(toracle);
|
||||
sb.append("\r\n\r\n");
|
||||
sb.append("(").append(getAlternateState().getName()).append(") ");
|
||||
sb.append(getAlternateState().getOracleText());
|
||||
sb.append("(").append(taltname).append(") ");
|
||||
sb.append(taltoracle);
|
||||
return sb.toString().trim();
|
||||
} else {
|
||||
return state.getOracleText();
|
||||
return toracle;
|
||||
}
|
||||
}
|
||||
|
||||
final String rulesText = state.getRulesText();
|
||||
if (!rulesText.isEmpty()) {
|
||||
if (!toracle.isEmpty()) {
|
||||
sb.append(toracle).append("\r\n\r\n");
|
||||
} else if (!rulesText.isEmpty()) {
|
||||
sb.append(rulesText).append("\r\n\r\n");
|
||||
}
|
||||
if (isCommander()) {
|
||||
@@ -565,6 +588,7 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
|
||||
if (isSplitCard() && !isFaceDown()) {
|
||||
// TODO: Translation?
|
||||
CardStateView view = state.getState() == CardStateName.LeftSplit ? state : getAlternateState();
|
||||
if (getZone() != ZoneType.Stack) {
|
||||
sb.append("(");
|
||||
@@ -573,12 +597,12 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
sb.append(view.getAbilityText());
|
||||
} else {
|
||||
sb.append(state.getAbilityText());
|
||||
if (toracle.isEmpty()) sb.append(state.getAbilityText());
|
||||
}
|
||||
|
||||
if (isSplitCard() && !isFaceDown() && getZone() != ZoneType.Stack) {
|
||||
//ensure ability text for right half of split card is included unless spell is on stack
|
||||
sb.append("\r\n\r\n").append("(").append(getAlternateState().getName()).append(") ").append(getAlternateState().getOracleText());
|
||||
sb.append("\r\n\r\n").append("(").append(taltname).append(") ").append(taltoracle);
|
||||
}
|
||||
|
||||
String nonAbilityText = get(TrackableProperty.NonAbilityText);
|
||||
|
||||
@@ -21,6 +21,7 @@ import forge.CachedCardImage;
|
||||
import forge.FThreads;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardTranslation;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
@@ -690,7 +691,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
|
||||
}
|
||||
|
||||
// Card name overlay
|
||||
titleText.setText(card.getCurrentState().getName());
|
||||
titleText.setText(CardTranslation.getTranslatedName(card.getCurrentState().getName()));
|
||||
|
||||
final int damage = card.getDamage();
|
||||
damageText.setText(damage > 0 ? "\u00BB " + damage + " \u00AB" : "");
|
||||
|
||||
@@ -10,6 +10,7 @@ import forge.assets.AssetsDownloader;
|
||||
import forge.assets.FSkin;
|
||||
import forge.assets.FSkinFont;
|
||||
import forge.assets.ImageCache;
|
||||
import forge.card.CardTranslation;
|
||||
import forge.error.BugReporter;
|
||||
import forge.error.ExceptionHandler;
|
||||
import forge.interfaces.IDeviceAdapter;
|
||||
@@ -108,6 +109,9 @@ public class Forge implements ApplicationListener {
|
||||
splashScreen.getProgressBar().setDescription("Loading fonts...");
|
||||
FSkinFont.preloadAll();
|
||||
|
||||
splashScreen.getProgressBar().setDescription("Loading card translations...");
|
||||
CardTranslation.preloadTranslation(prefs.getPref(FPref.UI_LANGUAGE));
|
||||
|
||||
splashScreen.getProgressBar().setDescription("Finishing startup...");
|
||||
|
||||
Gdx.app.postRunnable(new Runnable() {
|
||||
|
||||
@@ -183,7 +183,7 @@ public class CardImageRenderer {
|
||||
//draw name for card
|
||||
x += padding;
|
||||
w -= 2 * padding;
|
||||
g.drawText(state.getName(), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true);
|
||||
g.drawText(CardTranslation.getTranslatedName(state.getName()), NAME_FONT, Color.BLACK, x, y, w - manaCostWidth - padding, h, false, Align.left, true);
|
||||
}
|
||||
|
||||
public static final FBufferedImage forgeArt;
|
||||
@@ -260,7 +260,7 @@ public class CardImageRenderer {
|
||||
g.drawImage(image, x + (w - iconSize) / 2, y + (h - iconSize) / 2, iconSize, iconSize);
|
||||
}
|
||||
else {
|
||||
final String text = card.getText(state);
|
||||
final String text = card.getText(state, CardTranslation.getTranslationTexts(state.getName(), ""));
|
||||
if (StringUtils.isEmpty(text)) { return; }
|
||||
|
||||
float padding = TEXT_FONT.getCapHeight() * 0.75f;
|
||||
|
||||
@@ -255,7 +255,7 @@ public class CardRenderer {
|
||||
state.getLoyalty(), count, suffix, x, y, w, h, compactMode);
|
||||
}
|
||||
else { //if fake card, just draw card name centered
|
||||
String name = state.getName();
|
||||
String name = CardTranslation.getTranslatedName(state.getName());
|
||||
if (count > 0) { //preface name with count if applicable
|
||||
name = count + " " + name;
|
||||
}
|
||||
@@ -317,7 +317,7 @@ public class CardRenderer {
|
||||
CardFaceSymbols.drawManaCost(g, mainManaCost, x + w - manaCostWidth, y, MANA_SYMBOL_SIZE);
|
||||
|
||||
x += cardArtWidth;
|
||||
String name = card.getCurrentState().getName();
|
||||
String name = CardTranslation.getTranslatedName(card.getCurrentState().getName());
|
||||
if (count > 0) { //preface name with count if applicable
|
||||
name = count + " " + name;
|
||||
}
|
||||
@@ -448,7 +448,7 @@ public class CardRenderer {
|
||||
//draw name and mana cost overlays if card is small or default card image being used
|
||||
if (h <= NAME_COST_THRESHOLD && canShow) {
|
||||
if (showCardNameOverlay(card)) {
|
||||
g.drawOutlinedText(details.getName(), FSkinFont.forHeight(h * 0.18f), Color.WHITE, Color.BLACK, x + padding, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
|
||||
g.drawOutlinedText(CardTranslation.getTranslatedName(details.getName()), FSkinFont.forHeight(h * 0.18f), Color.WHITE, Color.BLACK, x + padding, y + padding, w - 2 * padding, h * 0.4f, true, Align.left, false);
|
||||
}
|
||||
if (showCardManaCostOverlay(card)) {
|
||||
float manaSymbolSize = w / 4;
|
||||
|
||||
18283
forge-gui/res/languages/cardnames-de-DE.txt
Normal file
18283
forge-gui/res/languages/cardnames-de-DE.txt
Normal file
File diff suppressed because it is too large
Load Diff
18255
forge-gui/res/languages/cardnames-es-ES.txt
Normal file
18255
forge-gui/res/languages/cardnames-es-ES.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -160,11 +160,13 @@ public class CardDetailUtil {
|
||||
|
||||
public static String formatCardName(final CardView card, final boolean canShow, final boolean forAltState) {
|
||||
final String name = forAltState ? card.getAlternateState().getName() : card.getName();
|
||||
return StringUtils.isEmpty(name) || !canShow ? "???" : name.trim();
|
||||
String translatedname = CardTranslation.getTranslatedName(name);
|
||||
return StringUtils.isEmpty(translatedname) || !canShow ? "???" : translatedname.trim();
|
||||
}
|
||||
|
||||
public static String formatCardType(final CardStateView card, final boolean canShow) {
|
||||
return canShow ? card.getType().toString() : (card.getState() == CardStateName.FaceDown ? "Creature" : "---");
|
||||
String translatedtype = CardTranslation.getTranslatedType(card.getName(), card.getType().toString());
|
||||
return canShow ? translatedtype : (card.getState() == CardStateName.FaceDown ? "Creature" : "---");
|
||||
}
|
||||
|
||||
public static String formatPowerToughness(final CardStateView card, final boolean canShow) {
|
||||
@@ -276,7 +278,9 @@ public class CardDetailUtil {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
String text = card.getText(state);
|
||||
|
||||
String text = card.getText(state, CardTranslation.getTranslationTexts(state.getName(), ""));
|
||||
|
||||
// LEVEL [0-9]+-[0-9]+
|
||||
// LEVEL [0-9]+\+
|
||||
|
||||
|
||||
91
forge-gui/src/main/java/forge/card/CardTranslation.java
Normal file
91
forge-gui/src/main/java/forge/card/CardTranslation.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package forge.card;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import com.google.common.base.Charsets;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.LineReader;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CardTranslation {
|
||||
|
||||
private static Map <String, String> translatednames;
|
||||
private static Map <String, String> translatedtypes;
|
||||
private static Map <String, String> translatedoracles;
|
||||
private static String languageSelected;
|
||||
|
||||
private static void readTranslationFile(String language) {
|
||||
String filename = "cardnames-" + language + ".txt";
|
||||
|
||||
try (LineReader translationFile = new LineReader(new FileInputStream(ForgeConstants.LANG_DIR + filename), Charsets.UTF_8)) {
|
||||
for (String line : translationFile.readLines()) {
|
||||
String[] matches = line.split("\\|");
|
||||
if (matches.length >= 2) {
|
||||
translatednames.put(matches[0], matches[1]);
|
||||
}
|
||||
if (matches.length >= 3) {
|
||||
translatedtypes.put(matches[0], matches[2]);
|
||||
}
|
||||
if (matches.length >= 4) {
|
||||
translatedoracles.put(matches[0], matches[3].replace("\\n", "\n\n"));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.error("Error reading translation file: cardnames-" + language + ".txt");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTranslatedName(String name) {
|
||||
if (needsTranslation()) {
|
||||
String tname = translatednames.get(name);
|
||||
return tname == null ? name : tname;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public static String getTranslatedType(String name, String originaltype) {
|
||||
if (needsTranslation()) {
|
||||
String ttype = translatedtypes.get(name);
|
||||
return ttype == null ? originaltype : ttype;
|
||||
}
|
||||
|
||||
return originaltype;
|
||||
}
|
||||
|
||||
public static String getTranslatedOracle(String name) {
|
||||
if (needsTranslation()) {
|
||||
String toracle = translatedoracles.get(name);
|
||||
return toracle == null ? "" : toracle;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public static HashMap<String, String> getTranslationTexts(String cardname, String altcardname) {
|
||||
HashMap<String, String> translations = new HashMap<String, String>();
|
||||
translations.put("name", getTranslatedName(cardname));
|
||||
translations.put("oracle", getTranslatedOracle(cardname));
|
||||
translations.put("altname", getTranslatedName(altcardname));
|
||||
translations.put("altoracle", getTranslatedOracle(altcardname));
|
||||
return translations;
|
||||
}
|
||||
|
||||
private static boolean needsTranslation() {
|
||||
return !languageSelected.equals("en-US");
|
||||
}
|
||||
|
||||
public static void preloadTranslation(String language) {
|
||||
languageSelected = language;
|
||||
|
||||
if (needsTranslation()) {
|
||||
translatednames = new HashMap<>();
|
||||
translatedtypes = new HashMap<>();
|
||||
translatedoracles = new HashMap<>();
|
||||
readTranslationFile(languageSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import forge.CardStorageReader.ProgressObserver;
|
||||
import forge.achievement.*;
|
||||
import forge.ai.AiProfileUtil;
|
||||
import forge.card.CardPreferences;
|
||||
import forge.card.CardTranslation;
|
||||
import forge.card.CardType;
|
||||
import forge.deck.CardArchetypeLDAGenerator;
|
||||
import forge.deck.CardRelationMatrixGenerator;
|
||||
@@ -146,6 +147,7 @@ public final class FModel {
|
||||
final CardStorageReader tokenReader = new CardStorageReader(ForgeConstants.TOKEN_DATA_DIR, progressBarBridge,
|
||||
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
||||
magicDb = new StaticData(reader, tokenReader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR);
|
||||
CardTranslation.preloadTranslation(preferences.getPref(FPref.UI_LANGUAGE));
|
||||
|
||||
//create profile dirs if they don't already exist
|
||||
for (final String dname : ForgeConstants.PROFILE_DIRS) {
|
||||
|
||||
0
forge-gui/tools/cardnames-de-DE-patch.txt
Normal file
0
forge-gui/tools/cardnames-de-DE-patch.txt
Normal file
6
forge-gui/tools/cardnames-es-ES-patch.txt
Normal file
6
forge-gui/tools/cardnames-es-ES-patch.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Forest|Bosque|Tierra básica - Bosque|({T}: Agrega {G}.)
|
||||
Island|Isla|Tierra básica - Isla|({T}: Agrega {U}.)
|
||||
Mountain|Montaña|Tierra básica - Montaña|({T}: Agrega {R}.)
|
||||
Plains|Llanura|Tierra básica - Llanura|({T}: Agrega {W}.)
|
||||
Swamp|Pantano|Tierra básica - Pantano|({T}: Agrega {B}.)
|
||||
|
||||
164
forge-gui/tools/cardnamesTranslations.py
Normal file
164
forge-gui/tools/cardnamesTranslations.py
Normal file
@@ -0,0 +1,164 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import urllib.request
|
||||
|
||||
database = 'scryfall-all-cards.json'
|
||||
scryfalldburl = 'https://archive.scryfall.com/json/' + database
|
||||
languages = ['es', 'de']
|
||||
|
||||
urllib.request.urlretrieve(scryfalldburl, database)
|
||||
|
||||
# Sort file and remove duplicates
|
||||
def cleanfile(filename):
|
||||
names_seen = set()
|
||||
outfile = open(filename + ".tmp2", "w", encoding='utf8')
|
||||
with open(filename + ".tmp", "r", encoding='utf8') as r:
|
||||
for line in sorted(r):
|
||||
name = line.split('|')[0]
|
||||
if name not in names_seen:
|
||||
outfile.write(line)
|
||||
names_seen.add(name)
|
||||
outfile.close()
|
||||
os.remove(filename + ".tmp")
|
||||
|
||||
# Manual patch of file translations
|
||||
def patchtranslations(filename):
|
||||
ffinal = open(filename + '.txt', 'w', encoding='utf8')
|
||||
fpatch = open(filename + '-patch.txt', 'r', encoding='utf8')
|
||||
patchline = fpatch.readline()
|
||||
|
||||
with open(filename + '.tmp2', 'r', encoding='utf8') as temp:
|
||||
for templine in temp:
|
||||
tempname = templine.split('|')[0]
|
||||
patchname = patchline.split('|')[0]
|
||||
if patchname == tempname:
|
||||
ffinal.write(patchline)
|
||||
patchline = fpatch.readline()
|
||||
else:
|
||||
ffinal.write(templine)
|
||||
|
||||
ffinal.close()
|
||||
fpatch.close()
|
||||
os.remove(filename + '.tmp2')
|
||||
|
||||
with open(database, mode='r', encoding='utf8') as json_file:
|
||||
data = json.load(json_file)
|
||||
|
||||
feses = open('cardnames-es-ES.tmp', 'w', encoding='utf8')
|
||||
fdede = open('cardnames-de-DE.tmp', 'w', encoding='utf8')
|
||||
|
||||
for card in data:
|
||||
if card['lang'] in languages:
|
||||
try:
|
||||
name = card['name']
|
||||
except:
|
||||
pass
|
||||
|
||||
# Parse simple card
|
||||
if ' // ' not in name:
|
||||
tname = ttype = toracle = ''
|
||||
|
||||
try:
|
||||
tname = card['printed_name']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
ttype = card['printed_type_line']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
toracle = card['printed_text']
|
||||
except:
|
||||
pass
|
||||
|
||||
output = name + '|' + tname + '|' + ttype
|
||||
output = output + '|' + toracle
|
||||
output = output.replace('\n', '\\n')
|
||||
output = output + '\n'
|
||||
|
||||
if card['lang'] == 'es':
|
||||
feses.write(output)
|
||||
if card['lang'] == 'de':
|
||||
fdede.write(output)
|
||||
|
||||
# Parse double card
|
||||
else:
|
||||
tname0 = tname1 = ttype0 = ttype1 = toracle0 = toracle1 = ''
|
||||
|
||||
cardfaces = card['card_faces']
|
||||
|
||||
try:
|
||||
name0 = cardfaces[0]['name']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
name1 = cardfaces[1]['name']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
tname0 = cardfaces[0]['printed_name']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
tname1 = cardfaces[1]['printed_name']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
ttype0 = cardfaces[0]['printed_type_line']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
ttype1 = cardfaces[1]['printed_type_line']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
toracle0 = cardfaces[0]['printed_text']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
toracle1 = cardfaces[1]['printed_text']
|
||||
except:
|
||||
pass
|
||||
|
||||
# Output Card0
|
||||
|
||||
output0 = name0 + '|' + tname0 + '|' + ttype0
|
||||
output0 = output0 + '|' + toracle0
|
||||
output0 = output0.replace('\n', '\\n')
|
||||
|
||||
if card['lang'] == 'es':
|
||||
feses.write(output0 + '\n')
|
||||
if card['lang'] == 'de':
|
||||
fdede.write(output0 + '\n')
|
||||
|
||||
# Output Card1
|
||||
|
||||
output1 = name1 + '|' + tname1 + '|' + ttype1
|
||||
output1 = output1 + '|' + toracle1
|
||||
output1 = output1.replace('\n', '\\n')
|
||||
|
||||
if card['lang'] == 'es':
|
||||
feses.write(output1 + '\n')
|
||||
if card['lang'] == 'de':
|
||||
fdede.write(output1 + '\n')
|
||||
|
||||
feses.close()
|
||||
fdede.close()
|
||||
|
||||
# Sort file and remove duplicates
|
||||
cleanfile("cardnames-es-ES")
|
||||
cleanfile("cardnames-de-DE")
|
||||
|
||||
# Patch language files
|
||||
patchtranslations("cardnames-es-ES")
|
||||
patchtranslations("cardnames-de-DE")
|
||||
Reference in New Issue
Block a user