Add Japanese localization and fix split card view

This commit is contained in:
Alumi
2021-02-19 04:05:22 +00:00
committed by Michael Kamensky
parent cbc72ef2e5
commit 92d9bebc58
10 changed files with 23457 additions and 35 deletions

View File

@@ -37,8 +37,14 @@ public class CardTranslation {
public static String getTranslatedName(String name) {
if (needsTranslation()) {
if (name.contains(" // ")) {
int splitIndex = name.indexOf(" // ");
String leftname = name.substring(0, splitIndex);
String rightname = name.substring(splitIndex + 4, name.length());
return translatednames.get(leftname) + " // " + translatednames.get(rightname);
}
String tname = translatednames.get(name);
return tname == null ? name : tname;
return (tname == null || tname.isEmpty()) ? name : tname;
}
return name;
@@ -63,6 +69,7 @@ public class CardTranslation {
}
public static HashMap<String, String> getTranslationTexts(String cardname, String altcardname) {
if (!needsTranslation()) return null;
HashMap<String, String> translations = new HashMap<>();
translations.put("name", getTranslatedName(cardname));
translations.put("oracle", getTranslatedOracle(cardname));

View File

@@ -585,8 +585,6 @@ public class CardView extends GameEntityView {
}
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
@@ -594,37 +592,54 @@ public class CardView extends GameEntityView {
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()) {
// TODO: Translate for cloned or mutated cards
// For now, don't translate oracles if the card is a cloned or mutated
if (((String) get(TrackableProperty.Cloner)).isEmpty() &&
((String) get(TrackableProperty.MergedCards)).isEmpty()) {
toracle = translationsText.get("oracle");
taltoracle = translationsText.get("altoracle");
}
}
tname = tname.isEmpty() ? state.getName() : tname;
if (isSplitCard()) {
taltname = getAlternateState().getName();
taltoracle = getAlternateState().getOracleText();
tname = tname.isEmpty() ? getLeftSplitState().getName() : tname;
taltname = taltname.isEmpty() ? getRightSplitState().getName() : taltname;
if (getId() < 0) {
toracle = toracle.isEmpty() ? getLeftSplitState().getOracleText() : toracle;
taltoracle = taltoracle.isEmpty() ? getRightSplitState().getOracleText() : taltoracle;
}
} else {
tname = tname.isEmpty() ? state.getName() : tname;
if (getId() < 0) {
toracle = toracle.isEmpty() ? state.getOracleText() : toracle;
}
}
if (getId() < 0) {
toracle = toracle.isEmpty() ? state.getOracleText() : toracle;
if (isSplitCard() && !toracle.isEmpty()) {
sb.append("(").append(tname).append(") ");
sb.append(toracle);
sb.append("\r\n\r\n");
sb.append("(").append(taltname).append(") ");
sb.append(taltoracle);
} else {
sb.append(toracle);
}
return sb.toString();
}
final String rulesText = state.getRulesText();
if (!toracle.isEmpty() && !isFaceDown()) {
if (isSplitCard()) {
sb.append("(").append(tname).append(") ");
sb.append(toracle);
sb.append("\r\n\r\n");
sb.append("(").append(taltname).append(") ");
sb.append(taltoracle);
return sb.toString().trim();
} else {
return toracle;
sb.append(toracle);
}
}
final String rulesText = state.getRulesText();
if (!toracle.isEmpty()) {
sb.append(toracle).append("\r\n\r\n");
sb.append("\r\n\r\n");
} else if (!rulesText.isEmpty()) {
sb.append(rulesText).append("\r\n\r\n");
}
@@ -633,23 +648,15 @@ public class CardView extends GameEntityView {
sb.append(getOwner().getCommanderInfo(this)).append("\r\n");
}
if (isSplitCard() && !isFaceDown()) {
// TODO: Translation?
if (getZone() != ZoneType.Stack) {
sb.append("(");
sb.append(getLeftSplitState().getName());
sb.append(") ");
if (toracle.isEmpty()) {
if (isSplitCard() && !isFaceDown() && getZone() != ZoneType.Stack) {
sb.append("(").append(getLeftSplitState().getName()).append(") ");
sb.append(getLeftSplitState().getAbilityText());
} else {
sb.append("\r\n\r\n").append("(").append(getRightSplitState().getName()).append(") ");
sb.append(getRightSplitState().getAbilityText());
} else {
sb.append(state.getAbilityText());
}
} else {
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(taltname).append(") ").append(taltoracle);
}
String nonAbilityText = get(TrackableProperty.NonAbilityText);

View File

@@ -25,12 +25,16 @@ import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import forge.model.FModel;
import forge.properties.ForgePreferences.FPref;
/**
* <p>
* GuiUtils class.
@@ -66,6 +70,13 @@ public final class GuiUtils {
} catch (final IOException e) {
System.err.println("GuiUtils > newFont: can't find \"" + filename + "\"");
}
if ("ja-JP".equals(FModel.getPreferences().getPref(FPref.UI_LANGUAGE)) && !ttf.canDisplay('鍮') ||
"zh-CN".equals(FModel.getPreferences().getPref(FPref.UI_LANGUAGE)) && !ttf.canDisplay('鹫')) {
// Use the system default font if can't display the above character
ttf = new JLabel().getFont();
}
return ttf;
}

View File

@@ -77,7 +77,7 @@ public class FSkinFont {
//pre-load all supported font sizes
public static void preloadAll(String language) {
//todo:really check the language glyph is a lot
MAX_FONT_SIZE = (language.equals("zh-CN")) ? MAX_FONT_SIZE_MANY_GLYPHS : MAX_FONT_SIZE_LESS_GLYPHS;
MAX_FONT_SIZE = (language.equals("zh-CN") || language.equals("ja-JP")) ? MAX_FONT_SIZE_MANY_GLYPHS : MAX_FONT_SIZE_LESS_GLYPHS;
for (int size = MIN_FONT_SIZE; size <= MAX_FONT_SIZE; size++) {
_get(size);
}

View File

@@ -276,7 +276,8 @@ public class CardImageRenderer {
g.drawImage(image, x + (w - iconSize) / 2, y + (h - iconSize) / 2, iconSize, iconSize);
}
else {
final String text = card.getText(state, CardTranslation.getTranslationTexts(state.getName(), ""));
final String text = !card.isSplitCard() ? card.getText(state, CardTranslation.getTranslationTexts(state.getName(), "")) :
card.getText(state, CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()));
if (StringUtils.isEmpty(text)) { return; }
float padding = TEXT_FONT.getCapHeight() * 0.75f;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -280,7 +280,8 @@ public class CardDetailUtil {
area.append("\n");
}
String text = card.getText(state, CardTranslation.getTranslationTexts(state.getName(), ""));
String text = !card.isSplitCard() ? card.getText(state, CardTranslation.getTranslationTexts(state.getName(), "")) :
card.getText(state, CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()));
// LEVEL [0-9]+-[0-9]+
// LEVEL [0-9]+\+

View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python3
import os
import urllib.request
sets = [
'4ED', 'CHR', 'ICE', 'ALL', 'MIR', 'VIS', '5ED', 'POR', 'WTH', 'TMP',
'STH', 'EXO', 'P02', 'USG', 'ULG', '6ED', 'PTK', 'UDS', 'MMQ', 'NEM',
'PCY', 'INV', 'PLS', '7ED', 'APC', 'ODY', 'TOR', 'JUD', 'ONS', 'LGN',
'SCG', '8ED', 'MRD', 'DST', '5DN', 'CHK', 'BOK', 'SOK', '9ED', 'RAV',
'GPT', 'DIS', 'CSP', 'TSP', 'PLC', 'FUT', '10E', 'LRW', 'MOR', 'SHM',
'EVE', 'ALA', 'CFX', 'ARB', 'M10', 'HOP', 'ZEN', 'WWK', 'ROE', 'M11',
'SOM', 'MBS', 'NPH', 'CMD', 'M12', 'ISD', 'DKA', 'AVR', 'PC2', 'M13',
'RTR', 'GTC', 'DGM', 'M14', 'THS', 'C13', 'BNG', 'JOU', 'CNS', 'M15',
'KTK', 'C14', 'FRF', 'DTK', 'ORI', 'BFZ', 'C15', 'OGW', 'SOI', 'EMA',
'EMN', 'CN2', 'KLD', 'C16', 'AER', 'AKH', 'HOU', 'C17', 'XLN', 'IMA',
'RIX', 'A25', 'DOM', 'BBD', 'M19', 'C18', 'GRN', 'UMA', 'RNA', 'WAR',
'MH1', 'M20', 'C19', 'ELD', 'THB', 'IKO', 'C20', 'M21', 'JMP', '2XM',
'ZNR', 'ZNC', 'CMR', 'KHM', 'KHC',
]
costmap = [
('(白)', '{W}'),
('(青)', '{U}'),
('(黒)', '{B}'),
('(赤)', '{R}'),
('(緑)', '{G}'),
('(◇)', '{C}'),
('()', '{0}'),
('()', '{1}'),
('()', '{2}'),
('()', '{3}'),
('()', '{4}'),
('()', '{5}'),
('()', '{6}'),
('()', '{7}'),
('()', '{8}'),
('()', '{9}'),
('()', '{10}'),
('()', '{11}'),
('()', '{12}'),
('()', '{13}'),
('()', '{14}'),
('()', '{15}'),
('()', '{16}'),
('()', '{X}'),
('(白/青)', '{W/U}'),
('(白/黒)', '{W/B}'),
('(青/黒)', '{U/B}'),
('(青/赤)', '{U/R}'),
('(黒/赤)', '{B/R}'),
('(黒/緑)', '{B/G}'),
('(赤/緑)', '{R/G}'),
('(赤/白)', '{R/W}'),
('(緑/白)', '{G/W}'),
('(緑/青)', '{G/U}'),
('(/白)', '{2/W}'),
('(/青)', '{2/U}'),
('(/黒)', '{2/B}'),
('(/赤)', '{2/R}'),
('(/緑)', '{2/G}'),
('(白/Φ)', '{W/P}'),
('(青/Φ)', '{U/P}'),
('(黒/Φ)', '{B/P}'),
('(赤/Φ)', '{R/P}'),
('(緑/Φ)', '{G/P}'),
('(氷)', '{S}'),
('()', '{T}'),
('()', '{Q}'),
('()', '{E}'),
]
def remove_engtype(text):
text = text.replace('(Urzas)', 'ウルザの')
while text.rfind('(') != -1:
left_index = text.rindex('(')
right_index = text.rindex(')')
if text[left_index + 1:right_index].isascii():
text = text[:left_index] + text[right_index + 1:]
else:
break
return text
def replace_cost(text):
for cm in costmap:
text = text.replace(cm[0], cm[1])
return text
def writecard(cardfile, cardname, jap_name, jap_type, jap_text):
if jap_name.rfind('') != -1:
jap_name = jap_name[:jap_name.rindex('')]
elif len(jap_name) == 0:
jap_name = cardname
jap_type = jap_type.replace(' --- ', '')
jap_type = remove_engtype(jap_type)
jap_text = replace_cost(jap_text)
jap_text = remove_engtype(jap_text)
jap_text = jap_text.replace('\n', '\\n')
cardfile.write(cardname + '|' + jap_name + '|' + jap_type + '|' + jap_text + '\n')
def processcards(cardfile, cur_set):
datafilename = f'ja-JP/{cur_set}.txt'
if not os.path.exists(datafilename):
if cur_set == 'CFX':
cur_set = 'CON'
card_data = urllib.request.urlopen(f'http://whisper.wisdom-guild.net/cardlist/{cur_set}.txt').read().decode('shift_jis')
with open(datafilename, 'w', encoding='utf8') as datafile:
datafile.write(card_data)
with open(datafilename, 'r', encoding='utf8') as datafile:
cardname = ''
jap_name = ''
jap_type = ''
jap_text = ''
text_mode = False
for line in datafile:
if line.startswith(' 英語名:'):
if text_mode is True:
text_mode = False
jap_text = jap_text.rstrip('\n')
writecard(cardfile, cardname, jap_name, jap_type, jap_text)
jap_name = ''
jap_type = ''
jap_text = ''
cardname = line[len(' 英語名:'):].rstrip('\n')
cardname = cardname.replace('AEther', 'Aether')
elif line.startswith('日本語名:'):
jap_name = line[len('日本語名:'):].rstrip('\n')
elif line.startswith(' タイプ:'):
jap_type = line[len(' タイプ:'):].rstrip('\n')
text_mode = True
elif line.startswith('イラスト:'):
text_mode = False
jap_text = jap_text.rstrip('\n')
writecard(cardfile, cardname, jap_name, jap_type, jap_text)
cardname = ''
jap_name = ''
jap_type = ''
jap_text = ''
elif line.startswith(' '):
continue
elif text_mode and not line.isspace():
jap_text += line
def cleanfile(filename, extension1, extension2):
names_seen = set()
outfile = open(filename + extension2, 'w', encoding='utf8')
with open(filename + extension1, '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 + extension1)
def main():
if not os.path.exists('ja-JP'):
os.makedirs('ja-JP')
with open('cardnames-ja-JP.tmp', 'w', encoding='utf8') as cardfile:
for cur_set in sets:
print (f'Processing {cur_set} ...')
processcards(cardfile, cur_set)
# Sort file and remove duplicates
cleanfile('cardnames-ja-JP', '.tmp', ".txt")
if __name__ == '__main__':
main()

View File

@@ -228,3 +228,7 @@ for lang in languages.keys():
# Sort file and remove duplicates
for lang in languages.keys():
cleanfile("cardnames-{0}".format(languages[lang]), ".tmp3", ".txt")
# Call the Japanese translation script
import JapaneseTranslations
JapaneseTranslations.main()