Implement better in-game localization/translation

This commit is contained in:
Lyu Zong-Hong
2021-02-25 21:34:52 +09:00
parent e428e66623
commit aa1da0c652
25 changed files with 233 additions and 119 deletions

View File

@@ -1,17 +1,20 @@
package forge.util;
import com.google.common.base.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public class CardTranslation {
private static Map <String, String> translatednames;
private static Map <String, String> translatedtypes;
private static Map <String, String> translatedoracles;
private static Map <String, List <Pair <String, String> > > oracleMappings;
private static Map <String, String> translatedCaches;
private static String languageSelected = "en-US";
private static void readTranslationFile(String language, String languagesDirectory) {
@@ -27,7 +30,7 @@ public class CardTranslation {
translatedtypes.put(matches[0], matches[2]);
}
if (matches.length >= 4) {
translatedoracles.put(matches[0], matches[3].replace("\\n", "\n\n"));
translatedoracles.put(matches[0], matches[3].replace("\\n", "\r\n\r\n"));
}
}
} catch (IOException e) {
@@ -89,7 +92,101 @@ public class CardTranslation {
translatednames = new HashMap<>();
translatedtypes = new HashMap<>();
translatedoracles = new HashMap<>();
oracleMappings = new HashMap<>();
translatedCaches = new HashMap<>();
readTranslationFile(languageSelected, languagesDirectory);
}
}
}
private static String replaceCardName(String language, String name, String toracle) {
String nickName = language.equals("en-US") ? Lang.getEnglishInstance().getNickName(name) : Lang.getInstance().getNickName(name);
String result = TextUtil.fastReplace(toracle, name, "CARDNAME");
if (!nickName.equals(name)) {
result = TextUtil.fastReplace(result, nickName, "NICKNAME");
}
return result;
}
public static void buildOracleMapping(String faceName, String oracleText) {
if (!needsTranslation() || oracleMappings.containsKey(faceName)) return;
String translatedName = getTranslatedName(faceName);
String translatedText = getTranslatedOracle(faceName);
List <Pair <String, String> > mapping = new ArrayList<>();
String [] splitOracleText = oracleText.split("\\\\n");
String [] splitTranslatedText = translatedText.split("\r\n\r\n");
for (int i = 0; i < splitOracleText.length && i < splitTranslatedText.length; i++) {
String toracle = replaceCardName("en-US", faceName, splitOracleText[i]);
String ttranslated = replaceCardName(languageSelected, translatedName, splitTranslatedText[i]);
// Remove reminder text in English oracle text unless entire line is reminder text
if (!toracle.startsWith("(")) {
toracle = toracle.replaceAll("\\(.*\\)", "");
}
mapping.add(Pair.of(toracle, ttranslated));
}
oracleMappings.put(faceName, mapping);
}
public static String translateMultipleDescriptionText(String descText, String cardName) {
if (!needsTranslation()) return descText;
String [] splitDescText = descText.split("\r\n");
String result = descText;
for (String text : splitDescText) {
if (text.isEmpty()) continue;
String translated = translateSingleDescriptionText(text, cardName);
if (!text.equals(translated)) {
result = TextUtil.fastReplace(result, text, translated);
} else {
// keywords maybe combined into one line, split them and try translate again
String [] splitKeywords = text.split(", ");
if (splitKeywords.length <= 1) continue;
for (String keyword : splitKeywords) {
if (keyword.contains(" ")) continue;
translated = translateSingleDescriptionText(keyword, cardName);
if (!keyword.equals(translated)) {
result = TextUtil.fastReplace(result, keyword, translated);
}
}
}
}
return result;
}
public static String translateSingleDescriptionText(String descText, String cardName) {
if (!needsTranslation()) return descText;
if (translatedCaches.containsKey(descText)) return translatedCaches.get(descText);
List <Pair <String, String> > mapping = oracleMappings.get(cardName);
if (mapping == null) return descText;
String result = descText;
if (!mapping.isEmpty()) {
result = translateSingleIngameText(descText, mapping);
}
translatedCaches.put(descText, result);
return result;
}
private static String translateSingleIngameText(String descText, List <Pair <String, String> > mapping) {
String tcompare = descText.startsWith("(") ? descText : descText.replaceAll("\\(.*\\)", "");
// Use Levenshtein Distance to find matching oracle text and replace it with translated text
int candidateIndex = mapping.size();
int minDistance = tcompare.length();
for (int i = 0; i < mapping.size(); i++) {
String toracle = mapping.get(i).getLeft();
int threshold = Math.min(toracle.length(), tcompare.length()) / 3;
int distance = StringUtils.getLevenshteinDistance(toracle, tcompare, threshold);
if (distance != -1 && distance < minDistance) {
minDistance = distance;
candidateIndex = i;
}
}
if (candidateIndex < mapping.size()) {
return mapping.get(candidateIndex).getRight();
}
return descText;
}
}

View File

@@ -18,6 +18,7 @@ import com.google.common.collect.Lists;
public abstract class Lang {
private static Lang instance;
private static Lang englishInstance;
protected String languageCode;
protected String countryCode;
@@ -41,11 +42,20 @@ public abstract class Lang {
}
instance.languageCode = language;
instance.countryCode = country;
// Create english instance for internal usage
englishInstance = new LangEnglish();
englishInstance.languageCode = "en";
englishInstance.countryCode = "US";
}
public static Lang getInstance() {
return instance;
}
public static Lang getEnglishInstance() {
return englishInstance;
}
protected Lang() {
}
@@ -170,4 +180,6 @@ public abstract class Lang {
}
return Integer.toString(n);
}
public abstract String getNickName(final String name);
}

View File

@@ -19,4 +19,9 @@ public class LangChinese extends Lang {
return getPossesive(owner) + object;
}
@Override
public String getNickName(final String name) {
return name;
}
}

View File

@@ -30,4 +30,8 @@ public class LangEnglish extends Lang {
return getPossesive(owner) + " " + object;
}
@Override
public String getNickName(final String name) {
return name.split(", ")[0];
}
}

View File

@@ -26,4 +26,10 @@ public class LangGerman extends Lang {
return getPossesive(owner) + " " + object;
}
@Override
public String getNickName(final String name) {
return name.split(", ")[0];
}
}

View File

@@ -23,4 +23,9 @@ public class LangItalian extends Lang {
return getPossesive(owner) + " " + object;
}
@Override
public String getNickName(final String name) {
return name.split(", ")[0];
}
}

View File

@@ -19,4 +19,11 @@ public class LangJapanese extends Lang {
return getPossesive(owner) + object;
}
@Override
public String getNickName(final String name) {
String [] splitName = name.split("");
if (splitName.length > 1) return splitName[1];
return name;
}
}

View File

@@ -23,4 +23,9 @@ public class LangSpanish extends Lang {
return getPossesive(owner) + " " + object;
}
@Override
public String getNickName(final String name) {
return name.split(", ")[0];
}
}