Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-lingual support #242

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
47 changes: 36 additions & 11 deletions WFInfo/Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Data
public JObject equipmentData; // Contains equipmentData from Warframe PC Drops {<EQMT>: {"vaulted": true, "PARTS": {<NAME>:{"relic_name":<name>|"","count":<num>}, ...}}, ...}
public JObject nameData; // Contains relic to market name translation {<relic_name>: <market_name>}


private static List<Dictionary<int, List<int>>> korean = new List<Dictionary<int, List<int>>>() {
new Dictionary<int, List<int>>() {
{ 0, new List<int>{ 6, 7, 8, 16 } }, // ㅁ, ㅂ, ㅃ, ㅍ
Expand Down Expand Up @@ -55,6 +56,7 @@ class Data
private readonly string equipmentDataPath;
private readonly string relicDataPath;
private readonly string nameDataPath;

public string JWT; // JWT is the security key, store this as email+pw combo
private readonly WebSocket marketSocket = new WebSocket("wss://warframe.market/socket?platform=pc");
private readonly string filterAllJSON = "https://api.warframestat.us/wfinfo/filtered_items";
Expand Down Expand Up @@ -84,6 +86,7 @@ public Data(IReadOnlyApplicationSettings settings)
equipmentDataPath = applicationDirectory + @"\eqmt_data.json";
relicDataPath = applicationDirectory + @"\relic_data.json";
nameDataPath = applicationDirectory + @"\name_data.json";
translationDataPath = applicationDirectory + @"\translation_data.json";

Directory.CreateDirectory(applicationDirectory);

Expand Down Expand Up @@ -156,7 +159,9 @@ public int GetGithubVersion()
public void ReloadItems()
{
marketItems = new JObject();

WebClient webClient = createWfmClient();

JObject obj =
JsonConvert.DeserializeObject<JObject>(
webClient.DownloadString("https://api.warframe.market/v1/items"));
Expand All @@ -174,7 +179,7 @@ public void ReloadItems()
marketItems[item["id"].ToString()] = name + "|" + item["url_name"];
}
}

try
{
using (var request = new HttpRequestMessage()
Expand Down Expand Up @@ -210,7 +215,7 @@ public void ReloadItems()
{
Main.AddLog("GetTopListings: " + e.Message);
}


marketItems["version"] = Main.BuildVersion;
Main.AddLog("Item database has been downloaded");
Expand Down Expand Up @@ -329,7 +334,6 @@ private bool LoadEqmtData(JObject allFiltered, bool force = false)
relicData = File.Exists(relicDataPath) ? JsonConvert.DeserializeObject<JObject>(File.ReadAllText(relicDataPath)) : new JObject();
if (nameData == null)
nameData = File.Exists(nameDataPath) ? JsonConvert.DeserializeObject<JObject>(File.ReadAllText(nameDataPath)) : new JObject();

// fill in equipmentData (NO OVERWRITE)
// fill in nameData
// fill in relicData
Expand All @@ -342,7 +346,6 @@ private bool LoadEqmtData(JObject allFiltered, bool force = false)
equipmentData["timestamp"] = DateTime.Now;
relicData["timestamp"] = DateTime.Now;
nameData = new JObject();

foreach (KeyValuePair<string, JToken> era in allFiltered["relics"].ToObject<JObject>())
{
relicData[era.Key] = new JObject();
Expand Down Expand Up @@ -375,7 +378,6 @@ private bool LoadEqmtData(JObject allFiltered, bool force = false)
equipmentData[primeName]["parts"][partName]["count"] = part.Value["count"];
equipmentData[primeName]["parts"][partName]["ducats"] = part.Value["ducats"];


string gameName = part.Key;
if (prime.Value["type"].ToString() == "Archwing" && (part.Key.Contains("Systems") || part.Key.Contains("Harness") || part.Key.Contains("Wings")))
{
Expand All @@ -388,6 +390,7 @@ private bool LoadEqmtData(JObject allFiltered, bool force = false)
if (marketData.TryGetValue(partName, out _))
{
nameData[gameName] = partName;
string localeRelicName = Translator.TranslateParName(gameName, _settings.Locale);
marketData[partName]["ducats"] = Convert.ToInt32(part.Value["ducats"].ToString(), Main.culture);
}
}
Expand All @@ -397,15 +400,14 @@ private bool LoadEqmtData(JObject allFiltered, bool force = false)
foreach (KeyValuePair<string, JToken> ignored in allFiltered["ignored_items"].ToObject<JObject>())
{
nameData[ignored.Key] = ignored.Key;
translationData[ignored.Key] = ignored.Key;
}

Main.AddLog("Prime Database has been downloaded");
return true;
}
Main.AddLog("Prime Database is up to date");
return false;
}

private void RefreshMarketDucats()
{
//equipmentData[primeName]["parts"][partName]["ducats"]
Expand Down Expand Up @@ -511,6 +513,7 @@ public void SaveAllJSONs()
SaveDatabase(nameDataPath, nameData);
SaveDatabase(marketItemsPath, marketItems);
SaveDatabase(marketDataPath, marketData);
SaveDatabase(translationDataPath, translationData);
}

public void ForceEquipmentUpdate()
Expand Down Expand Up @@ -629,11 +632,19 @@ public int LevenshteinDistance(string s, string t)
case "ko":
// for korean
return LevenshteinDistanceKorean(s, t);
case "fr":
return LevenshteinDistanceFrench(s, t);
default:
return LevenshteinDistanceDefault(s, t);
}
}

int LevenshteinDistanceFrench(string firstWord, string secondWord)
{
firstWord = getLocaleNameData(firstWord);
firstWord = firstWord.Replace("Schéma", "").Replace("-", "").Trim();
secondWord = secondWord.Replace("Schéma", "").Trim();
return LevenshteinDistanceDefault(firstWord, secondWord);
}
public int LevenshteinDistanceDefault(string s, string t)
{
// Levenshtein Distance determines how many character changes it takes to form a known result
Expand Down Expand Up @@ -691,6 +702,10 @@ public static bool isKorean(String str)
if (0xAC00 <= c && c <= 0xD7A3) return true;
return false;
}
/*
* Returns the locale market name of s
* s is the market name in english
*/
public string getLocaleNameData(string s)
{
bool saveDatabases = false;
Expand All @@ -700,7 +715,7 @@ public string getLocaleNameData(string s)
if (marketItem.Key == "version")
continue;
string[] split = marketItem.Value.ToString().Split('|');
if (split[0] == s)
if (split[0].Replace("Blueprint", "").Trim() == s.Replace("Blueprint", "").Trim())
{
if (split.Length == 3)
{
Expand Down Expand Up @@ -887,7 +902,18 @@ public string GetPartName(string name, out int low, bool suppressLogging)
string lowest = null;
string lowest_unfiltered = null;
low = 9999;
foreach (KeyValuePair<string, JToken> prop in nameData)
JObject namesData;
switch (_settings.Locale)
{
case "fr":
namesData = translationData;
break;
default:
namesData = nameData;
break;
}

foreach (KeyValuePair<string, JToken> prop in namesData)
{
int val = LevenshteinDistance(prop.Key, name);
if (val < low)
Expand All @@ -902,7 +928,6 @@ public string GetPartName(string name, out int low, bool suppressLogging)
lowest_unfiltered = prop.Key;
}
}

if (!suppressLogging)
Main.AddLog("Found part(" + low + "): \"" + lowest_unfiltered + "\" from \"" + name + "\"");
return lowest;
Expand Down
14 changes: 12 additions & 2 deletions WFInfo/EquipmentWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using WFInfo.Settings;

namespace WFInfo
{
Expand Down Expand Up @@ -50,7 +51,6 @@ public void reloadItems()

public void populate()
{

primeTypes = new Dictionary<string, TreeNode>();
foreach (KeyValuePair<string, JToken> prime in Main.dataBase.equipmentData)
{
Expand Down Expand Up @@ -78,11 +78,21 @@ public void populate()
foreach (KeyValuePair<string, JToken> primePart in prime.Value["parts"].ToObject<JObject>())
{
string partName = primePart.Key;
switch (ApplicationSettings.GlobalReadonlySettings.Locale)
{
case "fr":
partName = Main.dataBase.getLocaleNameData(partName);
partName = partName.Replace("-", "").Trim();
break;
default:
break;
}

if (primePart.Key.IndexOf("Prime") + 6 < primePart.Key.Length)
partName = partName.Substring(primePart.Key.IndexOf("Prime") + 6);

if (partName.Contains("Kubrow"))
partName = partName.Substring(partName.IndexOf(" Blueprint") + 1);
partName = partName.Substring(partName.IndexOf(" "+ Properties.strings.Blueprint) + 1);
TreeNode partNode = new TreeNode(partName, primePart.Value["vaulted"].ToObject<bool>() ? "Vaulted" : "", false, 0);
partNode.MakeClickable(primePart.Key);
if (Main.dataBase.marketData.TryGetValue(primePart.Key.ToString(), out JToken marketValues))
Expand Down
9 changes: 7 additions & 2 deletions WFInfo/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using AutoUpdaterDotNET;
using System.Windows;
using System.Windows.Forms;
using System.Threading;
using System.Globalization;
using WebSocketSharp;
using WFInfo.Resources;
using WFInfo.Settings;
Expand Down Expand Up @@ -54,6 +56,9 @@ public Main()
AutoUpdater.Start("https://github.com/WFCD/WFinfo/releases/latest/download/update.xml");

Task.Factory.StartNew(ThreadedDataLoad);

//string str = Properties.strings.Primary; To localize all hard coded text

}

private void AutoUpdaterOnCheckForUpdateEvent(UpdateInfoEventArgs args)
Expand All @@ -65,7 +70,7 @@ public static void ThreadedDataLoad()
{
try
{
StatusUpdate("Updating Databases...", 0);
StatusUpdate(Properties.strings.Update_databases, 0);

dataBase.Update();

Expand All @@ -79,7 +84,7 @@ public static void ThreadedDataLoad()
OCR.VerifyWarframe();
LoggedIn();
}
StatusUpdate("WFInfo Initialization Complete", 0);
StatusUpdate(Properties.strings.Init_complete, 0);
AddLog("WFInfo has launched successfully");
FinishedLoading();

Expand Down
12 changes: 10 additions & 2 deletions WFInfo/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using WFInfo.Settings;

using System.Globalization;
using System.Threading;
namespace WFInfo
{
/// <summary>
Expand All @@ -39,7 +40,11 @@ public MainWindow()
Main.AddLog("Duplicate process found");
Close();
}

//Set default culture of all new Thread to the selected culture and chage the culture of this thread
CultureInfo culture = CultureInfo.GetCultureInfo(_settingsViewModel.Locale);
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;

INSTANCE = this;
main = new Main();
Expand Down Expand Up @@ -196,6 +201,9 @@ private void RelicsClick(object sender, RoutedEventArgs e)
private void EquipmentClick(object sender, RoutedEventArgs e)
{
if (Main.dataBase.equipmentData == null) { ChangeStatus("Equipment data not yet loaded in", 2); return; }
Main.equipmentWindow?.Close();
Main.equipmentWindow = new EquipmentWindow();
Main.equipmentWindow.populate();
Main.equipmentWindow.Show();
}

Expand Down
40 changes: 31 additions & 9 deletions WFInfo/Ocr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public enum WindowStyle
// Screen / Resolution Scaling - Used to adjust pixel values to each person's monitor
public static double screenScaling;

public static Regex RE = new Regex("[^a-z가-]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static Regex RE = new Regex("[^a-z가-힣éèêëîïàäâÉÈç]", RegexOptions.IgnoreCase | RegexOptions.Compiled);

// Pixel measurements for reward screen @ 1920 x 1080 with 100% scale https://docs.google.com/drawings/d/1Qgs7FU2w1qzezMK-G1u9gMTsQZnDKYTEU36UPakNRJQ/edit
public const int pixleRewardWidth = 968;
Expand Down Expand Up @@ -788,7 +788,9 @@ private static WFtheme GetClosestTheme(Color clr, out int threshold)
/// <returns>If part name is close enough to valid to actually process</returns>
internal static bool PartNameValid (string partName)
{
if ((partName.Length < 13 && _settings.Locale == "en") || (partName.Replace(" ", "").Length < 6 && _settings.Locale == "ko")) // if part name is smaller than "Bo prime handle" skip current part
if ((partName.Length < 13 && _settings.Locale == "en")
|| (partName.Replace(" ", "").Length < 6 && _settings.Locale == "ko")
|| partName.Length < 13 && _settings.Locale == "fr") // if part name is smaller than "Bo prime handle" skip current part
//TODO: Add a min character for other locale here.
return false;
return true;
Expand Down Expand Up @@ -825,7 +827,7 @@ internal static void ProcessSnapIt(Bitmap snapItImage, Bitmap fullShot, Point sn
continue;
}
Debug.WriteLine($"Part {foundParts.IndexOf(part)} out of {foundParts.Count}");
string name = Main.dataBase.GetPartName(part.Name, out firstProximity[0], false);
string name = Main.dataBase.GetPartName(part.Name, out firstProximity[0], false);//Gives the part name in the locale language, translation is done in GetPartName
part.Name = name;
foundParts[i] = part;
JObject job = Main.dataBase.marketData.GetValue(name).ToObject<JObject>();
Expand Down Expand Up @@ -1439,11 +1441,31 @@ internal static void ProcessProfileScreen(Bitmap fullShot)
for (int i = 0; i < foundParts.Count; i++)
{
InventoryItem part = foundParts[i];
if (!PartNameValid(part.Name + " Blueprint"))
continue;
string name = Main.dataBase.GetPartName(part.Name+" Blueprint", out int proximity, true); //add blueprint to name to check against prime drop table
string checkName = Main.dataBase.GetPartName(part.Name + " prime Blueprint", out int primeProximity, true); //also add prime to check if that gives better match. If so, this is a non-prime
Main.AddLog("Checking \"" + part.Name.Trim() +"\", (" + proximity +")\"" + name + "\", +prime (" + primeProximity + ")\"" + checkName + "\"");
string name;
string checkName;
int proximity;
int primeProximity;
switch (_settings.Locale)
{
case "fr":
if (!PartNameValid(part.Name + " Schéma"))
continue;
name = Main.dataBase.GetPartName(part.Name + " Schéma", out proximity, true); //add blueprint to name to check against prime drop table
checkName = Main.dataBase.GetPartName(part.Name + " prime Schéma", out primeProximity, true); //also add prime to check if that gives better match. If so, this is a non-prime
break;
case "en":
if (!PartNameValid(part.Name + " Blueprint"))
continue;
name = Main.dataBase.GetPartName(part.Name + " Blueprint", out proximity, true); //add blueprint to name to check against prime drop table
checkName = Main.dataBase.GetPartName(part.Name + " prime Blueprint", out primeProximity, true); //also add prime to check if that gives better match. If so, this is a non-prime
break;
default:
if (!PartNameValid(part.Name + " Blueprint"))
continue;
name = Main.dataBase.GetPartName(part.Name + " Blueprint", out proximity, true); //add blueprint to name to check against prime drop table
checkName = Main.dataBase.GetPartName(part.Name + " prime Blueprint", out primeProximity, true); //also add prime to check if that gives better match. If so, this is a non-prime
break;
}

//Decide if item is an actual prime, if so mark as mastered
if (proximity < 3 && proximity < primeProximity && part.Name.Length > 6 && name.Contains("Prime"))
Expand Down Expand Up @@ -1704,7 +1726,7 @@ private static List<InventoryItem> FindOwnedItems(Bitmap ProfileImage, string ti


//do OCR
_tesseractService.FirstEngine.SetVariable("tessedit_char_whitelist", " ABCDEFGHIJKLMNOPQRSTUVWXYZ&");
_tesseractService.FirstEngine.SetVariable("tessedit_char_whitelist", " ABCDEFGHIJKLMNOPQRSTUVWXYZ&îïÎÏôÔöÖâäàÂêëËÊéèÉÈ");
using (var page = _tesseractService.FirstEngine.Process(cloneBitmap, PageSegMode.SingleLine))
{
using (var iterator = page.GetIterator())
Expand Down
Loading