using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; namespace OpenBveApi.Interface { public static partial class Translations { private class Language { /// The interface strings for this language internal readonly InterfaceString[] InterfaceStrings; /// The command information strings for this language internal readonly CommandInfo[] myCommandInfos; /// The key information strings for this language internal readonly KeyInfo[] KeyInfos; /// The quick-reference strings for this language internal readonly InterfaceQuickReference myQuickReferences; /// Returns the number of translated strings contained in the language internal int InterfaceStringCount => InterfaceStrings.Length; /// The language name private readonly string Name; /// The language flag internal readonly string Flag; /// The language code internal readonly string LanguageCode; /// The language codes on which to fall-back if a string is not found in this language(In order from best to worst) /// en-US should always be present in this list internal readonly List FallbackCodes; private class XliffFile { internal class Unit { internal readonly string Id; internal readonly string Value; internal Unit(XNamespace xmlns, XElement unit, string languageCode) { XElement source = unit.Element(xmlns + "source"); XElement target = unit.Element(xmlns + "target"); Id = (string)unit.Attribute("id"); if (target == null && languageCode != "en-US") { Value = ((string)source).Replace("\\r\\n", Environment.NewLine).Replace("\\x20", " "); return; } Value = (languageCode != "en-US" ? (string)target : (string)source).Replace("\\r\\n", Environment.NewLine).Replace("\\x20", " "); if (string.IsNullOrEmpty(Value)) { //if target is empty / null, let's use the untranslated value https://github.com/leezer3/OpenBVE/issues/663 Value = ((string)source).Replace("\\r\\n", Environment.NewLine).Replace("\\x20", " "); } } } internal class Group { internal readonly string Id; internal readonly Group[] Groups; internal readonly Unit[] Units; internal Group(XNamespace xmlns, XElement group, string languageCode) { Id = (string)group.Attribute("id"); Groups = group.Elements(xmlns + "group").Select(g => new Group(xmlns, g, languageCode)).ToArray(); Units = group.Elements(xmlns + "trans-unit").Select(t => new Unit(xmlns, t, languageCode)).Where(t => !string.IsNullOrEmpty(t.Value)).ToArray(); } } internal readonly Group[] Groups; internal readonly Unit[] Units; internal XliffFile(TextReader reader, string languageCode) { XDocument xml = XDocument.Load(reader); XNamespace xmlns = xml.Root.Name.Namespace; XElement body = xml.Root.Element(xmlns + "file").Element(xmlns + "body"); Groups = body.Elements(xmlns + "group").Select(g => new Group(xmlns, g, languageCode)).ToArray(); Units = body.Elements(xmlns + "trans-unit").Select(t => new Unit(xmlns, t, languageCode)).Where(t => !string.IsNullOrEmpty(t.Value)).ToArray(); } } /// Creates a new language from a file stream /// The file stream /// The language code internal Language(TextReader languageReader, string languageCode) { Name = "Unknown"; LanguageCode = languageCode; FallbackCodes = new List { "en-US" }; myCommandInfos = new CommandInfo[CommandInfos.Length]; KeyInfos = new KeyInfo[TranslatedKeys.Length]; myQuickReferences = new InterfaceQuickReference(); Array.Copy(CommandInfos, myCommandInfos, myCommandInfos.Length); Array.Copy(TranslatedKeys, KeyInfos, TranslatedKeys.Length); string prefix = string.Empty; XliffFile file = new XliffFile(languageReader, languageCode); List strings = new List(); ExportUnits(prefix, file.Units, strings); foreach (XliffFile.Group group in file.Groups) { ExportGroup(prefix, group, strings); } InterfaceString[] groupLanguage = strings.Where(s => s.Name.StartsWith("language_")).ToArray(); foreach (var interfaceString in groupLanguage) { string key = interfaceString.Name.Split('_')[1]; switch (key) { case "name": Name = interfaceString.Text; strings.Remove(interfaceString); break; case "flag": Flag = interfaceString.Text; strings.Remove(interfaceString); break; } } InterfaceString[] groupOpenBve = strings.Where(s => s.Name.StartsWith("openbve_")).ToArray(); foreach (var interfaceString in groupOpenBve) { string section = interfaceString.Name.Split('_')[1]; string key = string.Join("_", interfaceString.Name.Split('_').Skip(2)); switch (section) { case "handles": switch (key) { case "forward": myQuickReferences.HandleForward = interfaceString.Text; strings.Remove(interfaceString); break; case "neutral": myQuickReferences.HandleNeutral = interfaceString.Text; strings.Remove(interfaceString); break; case "backward": myQuickReferences.HandleBackward = interfaceString.Text; strings.Remove(interfaceString); break; case "power": myQuickReferences.HandlePower = interfaceString.Text; strings.Remove(interfaceString); break; case "powernull": myQuickReferences.HandlePowerNull = interfaceString.Text; strings.Remove(interfaceString); break; case "brake": myQuickReferences.HandleBrake = interfaceString.Text; strings.Remove(interfaceString); break; case "locobrake": myQuickReferences.HandleLocoBrake = interfaceString.Text; strings.Remove(interfaceString); break; case "brakenull": myQuickReferences.HandleBrakeNull = interfaceString.Text; strings.Remove(interfaceString); break; case "release": myQuickReferences.HandleRelease = interfaceString.Text; strings.Remove(interfaceString); break; case "lap": myQuickReferences.HandleLap = interfaceString.Text; strings.Remove(interfaceString); break; case "service": myQuickReferences.HandleService = interfaceString.Text; strings.Remove(interfaceString); break; case "emergency": myQuickReferences.HandleEmergency = interfaceString.Text; strings.Remove(interfaceString); break; case "holdbrake": myQuickReferences.HandleHoldBrake = interfaceString.Text; strings.Remove(interfaceString); break; } break; case "doors": switch (key) { case "left": myQuickReferences.DoorsLeft = interfaceString.Text; strings.Remove(interfaceString); break; case "right": myQuickReferences.DoorsRight = interfaceString.Text; strings.Remove(interfaceString); break; } break; case "misc": switch (key) { case "score": myQuickReferences.Score = interfaceString.Text; strings.Remove(interfaceString); break; } break; case "commands": for (int k = 0; k < myCommandInfos.Length; k++) { if (string.Compare(myCommandInfos[k].Name, key, StringComparison.OrdinalIgnoreCase) == 0) { myCommandInfos[k].Description = interfaceString.Text; strings.Remove(interfaceString); break; } } break; case "keys": for (int k = 0; k < KeyInfos.Length; k++) { if (string.Compare(KeyInfos[k].Name, key, StringComparison.OrdinalIgnoreCase) == 0) { KeyInfos[k].Description = interfaceString.Text; strings.Remove(interfaceString); break; } } break; case "fallback": switch (key) { case "language": FallbackCodes.Add(interfaceString.Text); strings.Remove(interfaceString); break; } break; } } InterfaceStrings = strings.ToArray(); for (int i = 0; i < InterfaceStrings.Length; i++) { if (InterfaceStrings[i].Name.StartsWith("openbve_")) { InterfaceStrings[i].Name = InterfaceStrings[i].Name.Replace("openbve_", string.Empty); } } } private void ExportUnits(string prefix, XliffFile.Unit[] units, List strings) { strings.AddRange(units.Select(u => new InterfaceString { Name = prefix + u.Id, Text = u.Value })); } private void ExportGroup(string prefix, XliffFile.Group group, List strings) { prefix += group.Id + "_"; ExportUnits(prefix, group.Units, strings); foreach (XliffFile.Group childGroup in group.Groups) { ExportGroup(prefix, childGroup, strings); } } /// Always returns the textual name of the language public override string ToString() { return Name; } } } }