GabrielTofvesson 100f5a32be Major changes
Refactorings:
  * BinaryCollector -> BitWriter
  * BinaryDistributor -> BitReader

Additions:
  * Output class for making serverside output pretty and more readable
  * Better RSA keys (private keys withheld)

Changes:
  * Minor changes to all views and their rendering
  * Added corrective resizing to resize listener to prevent errant window sizes
  * Removed "default" language in favour of a purely priority-based system
  * NetContext now attempts to verify server identity before continuing to next context
  * Simplified common operations in Context
  * Minor updates to some layouts
  * Completed translations for english and swedish
  * Promise system now supports internal processing before notifying original caller
  * Bank interactor methods are now async
  * Added support for multiple accounts per user (separate repositories for money)
  * Removed test code from client program
  * Updated Database to support multiple accounts
  * Reimplemented RSA on the server side purely as an identity verification system on top of the networking layer (rather than part of the layer)
  * Added Account management endpoints
  * Added full support for System-sourced transactions
  * Added Account availability endpoint
  * Added verbose error responses
2018-04-26 00:24:58 +02:00

144 lines
6.0 KiB
C#

using Client.Properties;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace Client.ConsoleForms
{
public sealed class LangManager
{
private const string MAPPING_PREFIX = "@string/";
public static readonly LangManager NO_LANG = new LangManager(true);
private readonly Dictionary<string, string> mappings;
public string Name { get; }
private LangManager(bool b)
{
mappings = new Dictionary<string, string>();
Name = "";
}
public LangManager(string langName = "en_US", bool fromResource = true)
{
string from = null;
if (fromResource)
{
PropertyInfo[] properties = typeof(Resources).GetProperties(BindingFlags.NonPublic | BindingFlags.Static);
foreach (var prop in properties)
if (prop.Name.Equals("strings_lang_" + langName) && prop.PropertyType.Equals(typeof(string)))
{
from = (string)prop.GetValue(null);
break;
}
if (from == null)
{
mappings = new Dictionary<string, string>();
Name = "";
return;
}
}
else from = langName;
mappings = DoMapping(from, out string name);
Name = name;
}
public bool HasMapping(string name) => name.StartsWith(MAPPING_PREFIX) && mappings.ContainsKey(StripPrefix(name));
public string GetMapping(string name) => mappings[StripPrefix(name)];
public string MapIfExists(string name) => HasMapping(name) ? GetMapping(name) : name;
private string StripPrefix(string from) => from.Substring(MAPPING_PREFIX.Length);
private Dictionary<string, string> DoMapping(string xml, out string label)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNode lang = doc.GetElementsByTagName("Strings").Item(0);
// Janky way of setting label since the compiler apparently doesn't accept it in an else-statement
label = null;
if (lang is XmlElement lang_el && lang_el.HasAttribute("label")) label = lang_el.GetAttribute("label");
// Read language mappings
Dictionary<string, string> map = new Dictionary<string, string>();
foreach(var node in lang.ChildNodes)
if(node is XmlElement el && el.Name.Equals("Entry") && el.HasAttribute("name"))
map.Add(el.GetAttribute("name"), el.InnerText);
return map;
}
public static LangManager LoadLang(string langMeta = "strings_meta", bool fromResource = true)
{
if (fromResource)
{
PropertyInfo[] properties = typeof(Resources).GetProperties(BindingFlags.NonPublic | BindingFlags.Static);
foreach (var prop in properties)
if (prop.Name.Equals(langMeta) && prop.PropertyType.Equals(typeof(string)))
return new LangManager(ProcessMeta((string)prop.GetValue(null)));
}
return new LangManager(ProcessMeta(langMeta));
}
private static string ProcessMeta(string metaString)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(metaString);
XmlNode lang = doc.GetElementsByTagName("Strings").Item(0);
List<XmlElement> priorities = new List<XmlElement>();
foreach(var node in lang.ChildNodes)
if (node is XmlElement el)
{
//if (el.Name.Equals("Default")) priorities.Insert(0, (XmlElement)node);
/*else*/ if (el.Name.Equals(/*"Fallback"*/"Lang"))
{
if (priorities.Count == 0) priorities.Add(el);
else
for (int i = 0; i < priorities.Count; ++i)
if (/*!priorities[i].Name.Equals("Default") && */ComparePriority(el, priorities[i]))
{
priorities.Insert(i, el);
break;
}
else if (i == priorities.Count - 1)
{
priorities.Add(el);
break;
}
}
}
PropertyInfo[] properties = typeof(Resources).GetProperties(BindingFlags.NonPublic | BindingFlags.Static);
// Check if we can use system language
string culture = System.Globalization.CultureInfo.InstalledUICulture.Name.Replace('-', '_');
foreach(var elt in priorities)
if (elt.InnerText.Equals(culture))
{
foreach (var prop in properties)
if (prop.Name.Equals("strings_lang_" + culture) && prop.PropertyType.Equals(typeof(string)))
return culture;
priorities.Remove(elt);
break;
}
// Use defaults and fallbacks
for (int i = 0; i<priorities.Count; ++i)
{
foreach (var prop in properties)
if (prop.Name.Equals("strings_lang_"+priorities[i].InnerText) && prop.PropertyType.Equals(typeof(string)))
return priorities[i].InnerText;
}
return "";
}
private static bool ComparePriority(XmlElement el1, XmlElement el2)
=> el1.HasAttribute("priority") && int.TryParse(el1.GetAttribute("priority"), out int el1_prio) && (!el2.HasAttribute("priority") || !int.TryParse(el2.GetAttribute("priority"), out int el2_prio) || el1_prio < el2_prio);
}
}