diff --git a/Mapify/Locale.cs b/Mapify/Locale.cs index 193a2c8c..33736e0f 100644 --- a/Mapify/Locale.cs +++ b/Mapify/Locale.cs @@ -1,74 +1,48 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; -using I2.Loc; -using Mapify.Utils; +using DVLangHelper.Data; +using DVLangHelper.Runtime; +using UnityModManagerNet; namespace Mapify { public static class Locale { - private const string DEFAULT_LANGUAGE = "English"; - private const string MISSING_TRANSLATION = "[ MISSING TRANSLATION ]"; - public const string PREFIX = "mapify/"; - public const string STATION_PREFIX = PREFIX + "station/"; - public const string SESSION__MAP_SELECTOR = PREFIX + "session/map_selector"; - public const string LAUNCHER__SESSION_MAP = PREFIX + "launcher/session_map"; - public const string LAUNCHER__SESSION_MAP_NOT_INSTALLED = PREFIX + "launcher/session_map_not_installed"; - public const string LOADING__PLEASE_WAIT = PREFIX + "loading/please_wait"; - public const string LOADING__LOADING_MAP = PREFIX + "loading/loading_map"; - private static readonly char[] PREFIX_CHARS = PREFIX.ToCharArray(); + private const string LOCALE_FILE = "locale.csv"; + // copied from DV.Localization.LocalizationAPI.Sanitized + public const string MISSING_TRANSLATION = "[ MISSING TRANSLATION ]"; - private static bool initializeAttempted; - private static ReadOnlyDictionary> csv; + public const string SESSION__MAP_SELECTOR = "session/map_selector"; + public const string LAUNCHER__SESSION_MAP = "launcher/session_map"; + public const string LAUNCHER__SESSION_MAP_NOT_INSTALLED = "launcher/session_map_not_installed"; + public const string LOADING__PLEASE_WAIT = "loading/please_wait"; + public const string LOADING__LOADING_MAP = "loading/loading_map"; - public static bool Load(string localeFilePath) + private static TranslationInjector translationsInjector; + + public static bool Setup() { - initializeAttempted = true; - if (!File.Exists(localeFilePath)) - return false; - csv = CSV.Parse(File.ReadAllText(localeFilePath)); - return true; + translationsInjector = new TranslationInjector(Mapify.ModEntry.Info.Id); + return Reset(); } - public static string Get(string key) + public static bool Reset() { - if (!initializeAttempted) - throw new InvalidOperationException("Not initialized"); + translationsInjector.ResetData(); + var localeFilePath = Path.Combine(Mapify.ModEntry.Path, LOCALE_FILE); - string locale = LocalizationManager.CurrentLanguage; - - if (!csv.ContainsKey(locale)) + if (!File.Exists(localeFilePath)) { - if (locale == DEFAULT_LANGUAGE) - { - Mapify.LogError($"Failed to find locale language {locale}! Something is broken, this shouldn't happen. Dumping CSV data:"); - Mapify.LogError($"\n{CSV.Dump(csv)}"); - return MISSING_TRANSLATION; - } - - locale = DEFAULT_LANGUAGE; - Mapify.LogWarning($"Failed to find locale language {locale}"); - } - - Dictionary localeDict = csv[locale]; - - if (localeDict.TryGetValue(key.TrimStart(PREFIX_CHARS), out string value)) { - return value; - } - - // If there is no translation for this station's name, don't translate it. - if (key.StartsWith(STATION_PREFIX)) { - return key.Replace(STATION_PREFIX, "");; + Mapify.LogError($"Failed to find locale file at {localeFilePath}! Please make sure it's there."); + return false; } - return MISSING_TRANSLATION; + translationsInjector.AddTranslationsFromCsv(localeFilePath); + return true; } - public static string Get(string key, params object[] placeholders) + public static void AddTranslation(string key, DVLanguage translationSetLanguage, string translationSetTranslation) { - return string.Format(Get(key), placeholders); + translationsInjector.AddTranslation(key, translationSetLanguage, translationSetTranslation); } } } diff --git a/Mapify/Map/MapLifeCycle.cs b/Mapify/Map/MapLifeCycle.cs index b331c3e7..3354ec5d 100644 --- a/Mapify/Map/MapLifeCycle.cs +++ b/Mapify/Map/MapLifeCycle.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text.RegularExpressions; using DV.CashRegister; +using DV.Localization; using DV.Utils; using Mapify.Editor; using Mapify.Editor.Utils; @@ -44,15 +45,15 @@ public static IEnumerator LoadMap(BasicMapInfo basicMapInfo) WorldStreamingInit wsi = SingletonBehaviour.Instance; DisplayLoadingInfo loadingInfo = Object.FindObjectOfType(); - string loadingMapLogMsg = Locale.Get(Locale.LOADING__LOADING_MAP, basicMapInfo.name); + string loadingMapLogMsg = LocalizationAPI.L(Locale.LOADING__LOADING_MAP, basicMapInfo.name); loadingInfo.UpdateLoadingStatus(loadingMapLogMsg, 0); yield return null; // Load asset bundles loadedAssetBundles = new List(); - string mapDir = Maps.GetDirectory(basicMapInfo); // Register mapinfo + string mapDir = Maps.GetDirectory(basicMapInfo); Mapify.LogDebug(() => $"Loading AssetBundle '{Names.MAP_INFO_ASSET_BUNDLE}'"); AssetBundleCreateRequest mapInfoRequest = AssetBundle.LoadFromFileAsync(Maps.GetMapAsset(Names.MAP_INFO_ASSET_BUNDLE, mapDir)); do @@ -336,6 +337,7 @@ private static void InitializeLists() private static void Cleanup() { + Locale.Reset(); WorldMapSetup.Cleanup(); StreamedObjectInitPatch.ResetStreamers(); Maps.UnregisterLoadedMap(); diff --git a/Mapify/Mapify.cs b/Mapify/Mapify.cs index 068ddac3..adbae3ed 100644 --- a/Mapify/Mapify.cs +++ b/Mapify/Mapify.cs @@ -1,19 +1,14 @@ using System; -using System.IO; -using DV.UI; using HarmonyLib; using Mapify.Map; -using Mapify.Patches; using UnityModManagerNet; -using Object = UnityEngine.Object; namespace Mapify { public static class Mapify { - private static UnityModManager.ModEntry ModEntry { get; set; } + public static UnityModManager.ModEntry ModEntry { get; private set; } private static Settings Settings; - private const string LOCALE_FILE = "locale.csv"; internal static Harmony Harmony { get; private set; } @@ -27,7 +22,10 @@ private static bool Load(UnityModManager.ModEntry modEntry) try { - LoadLocale(); + if (!Locale.Setup()) + { + return false; + } Maps.Init(); Patch(); } @@ -40,13 +38,6 @@ private static bool Load(UnityModManager.ModEntry modEntry) return true; } - private static void LoadLocale() - { - string localePath = Path.Combine(ModEntry.Path, LOCALE_FILE); - if (!Locale.Load(localePath)) - LogError($"Failed to find locale file at {localePath}! Please make sure it's there."); - } - private static void Patch() { Log("Patching..."); @@ -63,12 +54,22 @@ public static void LogDebugExtreme(Func resolver) LogDebug(resolver); } + public static void LogDebugExtreme(object msg) + { + LogDebugExtreme(() => msg); + } + public static void LogDebug(Func resolver) { if (Settings.VerboseLogging) ModEntry.Logger.Log($"[Debug] {resolver.Invoke()}"); } + public static void LogDebug(object msg) + { + LogDebug(() => msg); + } + public static void Log(object msg) { ModEntry.Logger.Log($"[Info] {msg}"); diff --git a/Mapify/Mapify.csproj b/Mapify/Mapify.csproj index 2cc1751a..6acd2cad 100644 --- a/Mapify/Mapify.csproj +++ b/Mapify/Mapify.csproj @@ -16,6 +16,7 @@ + @@ -49,6 +50,12 @@ + + + + + + diff --git a/Mapify/Patches/DisplayLoadingInfoPatch.cs b/Mapify/Patches/DisplayLoadingInfoPatch.cs index 03c05b95..aafff53c 100644 --- a/Mapify/Patches/DisplayLoadingInfoPatch.cs +++ b/Mapify/Patches/DisplayLoadingInfoPatch.cs @@ -1,3 +1,4 @@ +using DV.Localization; using HarmonyLib; namespace Mapify.Patches @@ -44,7 +45,7 @@ private static bool Prefix(DisplayLoadingInfo __instance, string message, bool i } string formattedWhat = string.IsNullOrWhiteSpace(what) ? "" : $" {what}"; - __instance.percentageLoadedTMP.text = Locale.Get(Locale.LOADING__PLEASE_WAIT, formattedWhat, percentageLoaded.ToString("F0")); + __instance.percentageLoadedTMP.text = LocalizationAPI.L(Locale.LOADING__PLEASE_WAIT, formattedWhat, percentageLoaded.ToString("F0")); if (!Bootstrap.bootstrapped) return false; diff --git a/Mapify/Patches/LauncherControllerPatch.cs b/Mapify/Patches/LauncherControllerPatch.cs index a843d518..89b0d52f 100644 --- a/Mapify/Patches/LauncherControllerPatch.cs +++ b/Mapify/Patches/LauncherControllerPatch.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using DV.Common; +using DV.Localization; using DV.UI; using DV.UI.PresetEditors; using DV.UIFramework; @@ -71,7 +72,7 @@ private static bool Prefix(LauncherController __instance, ISaveGame ___saveGame) Popup okPopupPrefab = __instance.GetComponentInParent().continueLoadNewController.career.okPopupPrefab; Popup popup = popupManager.ShowPopup(okPopupPrefab); - popup.labelTMPro.text = Locale.Get(Locale.LAUNCHER__SESSION_MAP_NOT_INSTALLED, basicMapInfo.name); + popup.labelTMPro.text = LocalizationAPI.L(Locale.LAUNCHER__SESSION_MAP_NOT_INSTALLED, basicMapInfo.name); return false; } } diff --git a/Mapify/Patches/LocalizationManagerPatch.cs b/Mapify/Patches/LocalizationManagerPatch.cs deleted file mode 100644 index b8ea7c52..00000000 --- a/Mapify/Patches/LocalizationManagerPatch.cs +++ /dev/null @@ -1,18 +0,0 @@ -using HarmonyLib; -using I2.Loc; - -namespace Mapify.Patches -{ - [HarmonyPatch(typeof(LocalizationManager), nameof(LocalizationManager.GetTranslation))] - public static class LocalizationManager_GetTranslation_Patch - { - private static bool Prefix(ref string __result, string Term) - { - if (!Term.StartsWith(Locale.PREFIX)) - return true; - - __result = Locale.Get(Term); - return false; - } - } -} diff --git a/Mapify/SceneInitializers/GameContent/StationSetup.cs b/Mapify/SceneInitializers/GameContent/StationSetup.cs index 5a6ede14..44f2e384 100644 --- a/Mapify/SceneInitializers/GameContent/StationSetup.cs +++ b/Mapify/SceneInitializers/GameContent/StationSetup.cs @@ -4,6 +4,7 @@ using DV.Teleporters; using DV.ThingTypes; using DV.Utils; +using DVLangHelper.Data; using HarmonyLib; using Mapify.Editor; using Mapify.Map; @@ -31,8 +32,7 @@ public override void Run() GameObject stationObject = station.gameObject; StationController stationController = stationObject.GetComponent(); - // Station info - stationController.stationInfo = new StationInfo(station.stationName, " ", station.stationID, station.color, Locale.STATION_PREFIX+station.stationName); + SetupStationInfo(station, stationController); // Station tracks stationController.storageRailtracksGONames = station.storageTrackNames; @@ -51,6 +51,20 @@ public override void Run() SingletonBehaviour.Instance.gameObject.SetActive(true); } + private void SetupStationInfo(Station station, StationController stationController) + { + var locaKey = station.GetStationLocalizationKey(); + stationController.stationInfo = new StationInfo(station.stationName, " ", station.stationID, station.color, locaKey); + + var mbs = station.GetComponents(); + station.stationNameTranslations = mbs.Take(station.stationNameTranslationsCount).Select(mb => mb.ToOriginal()).ToList(); + + foreach (var translationSet in station.stationNameTranslations) + { + Locale.AddTranslation(locaKey, (DVLanguage)translationSet.language, translationSet.translation); + } + } + private static void SetupJobBookletSpawnSurface(Station station, StationController stationController) { PointOnPlane jobBookletSpawnSurface = station.transform.parent.GetComponentInChildren(); diff --git a/Mapify/SceneInitializers/GameContent/WorldMapSetup.cs b/Mapify/SceneInitializers/GameContent/WorldMapSetup.cs index a2775f0c..683ee66e 100644 --- a/Mapify/SceneInitializers/GameContent/WorldMapSetup.cs +++ b/Mapify/SceneInitializers/GameContent/WorldMapSetup.cs @@ -62,11 +62,12 @@ private static void UpdateMapOverview(Transform transform) name.FindChildByName("Color").GetComponent().color = station.color; name.FindChildByName("IndustryCode").GetComponent().text = station.stationID; name.FindChildByName("OriginalName").GetComponent().text = station.stationName; - GameObject localizedName = name.FindChildByName("LocalizedName"); - localizedName.GetComponent().text = station.stationName; - foreach (Localize i2Localize in localizedName.GetComponents()) + GameObject localizedNameObject = name.FindChildByName("LocalizedName"); + localizedNameObject.GetComponent().text = station.GetLocalizedStationName(); + + foreach (Localize i2Localize in localizedNameObject.GetComponents()) Object.DestroyImmediate(i2Localize); - Object.DestroyImmediate(localizedName.GetComponent()); + Object.DestroyImmediate(localizedNameObject.GetComponent()); } Object.Destroy(listItemPrefab.gameObject); @@ -128,7 +129,7 @@ private static void ShowNamesOnMap(Transform mapTransform) TMP_Text tmp = name.GetComponent(); tmp.rectTransform.localPosition = station.YardCenter.position.ToXZ().Scale(0, Maps.LoadedMap.worldSize, -0.175f, 0.175f); - tmp.text = ShowStationNamesOnMap ? station.stationName : station.stationID; + tmp.text = ShowStationNamesOnMap ? station.GetLocalizedStationName() : station.stationID; } Object.Destroy(namePrefab); diff --git a/Mapify/Utils/CSVParser.cs b/Mapify/Utils/CSVParser.cs deleted file mode 100644 index 8df6da2e..00000000 --- a/Mapify/Utils/CSVParser.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Linq; -using System.Text; - -namespace Mapify.Utils -{ - public static class CSV - { - /// - /// Parses a CSV string into a dictionary of columns, each of which is a dictionary of rows, keyed by the first column. - /// - public static ReadOnlyDictionary> Parse(string data) - { - string[] lines = data.Split('\n'); - - // Dictionary> - OrderedDictionary columns = new OrderedDictionary(lines.Length - 1); - - List keys = ParseLine(lines[0]); - foreach (string key in keys) - columns.Add(key, new Dictionary()); - - for (int i = 1; i < lines.Length; i++) - { - string line = lines[i]; - List values = ParseLine(line); - if (values.Count == 0) - continue; - string key = values[0]; - for (int j = 0; j < values.Count; j++) - ((Dictionary)columns[j]).Add(key, values[j]); - } - - return new ReadOnlyDictionary>(columns.Cast() - .ToDictionary(entry => (string)entry.Key, entry => (Dictionary)entry.Value)); - } - - private static List ParseLine(string line) - { - bool inQuotes = false; - List values = new List(); - StringBuilder builder = new StringBuilder(); - - void FinishLine() - { - values.Add(builder.ToString()); - builder.Clear(); - } - - foreach (char c in line) - { - if (c == '\r') - { - Mapify.LogWarning("Encountered carriage return in CSV! Please use Unix-style line endings (LF)."); - continue; - } - - if (c == '\n' || (!inQuotes && c == ',')) - { - FinishLine(); - continue; - } - - if (c == '"') - { - inQuotes = !inQuotes; - continue; - } - - builder.Append(c); - } - - if (builder.Length > 0) - FinishLine(); - - return values; - } - - public static string Dump(ReadOnlyDictionary> data) - { - StringBuilder result = new StringBuilder(); - - foreach (KeyValuePair> column in data) - result.Append($"{column.Key},"); - - result.Remove(result.Length - 1, 1); - result.Append('\n'); - - int rowCount = data.Values.FirstOrDefault()?.Count ?? 0; - - for (int i = 0; i < rowCount; i++) - { - foreach (KeyValuePair> column in data) - if (column.Value.Count > i) - { - string value = column.Value.ElementAt(i).Value; - result.Append(value.Contains(',') ? $"\"{value}\"," : $"{value},"); - } - else - { - result.Append(','); - } - - result.Remove(result.Length - 1, 1); - result.Append('\n'); - } - - return result.ToString(); - } - } -} diff --git a/Mapify/Utils/Extensions.cs b/Mapify/Utils/Extensions.cs index 11102b98..8faa771b 100644 --- a/Mapify/Utils/Extensions.cs +++ b/Mapify/Utils/Extensions.cs @@ -5,8 +5,10 @@ using CommandTerminal; using DV; using DV.JObjectExtstensions; +using DV.Localization; using DV.PointSet; using DV.ThingTypes; +using DVLangHelper.Data; using HarmonyLib; using Mapify.Editor; using Mapify.Editor.Utils; @@ -291,6 +293,18 @@ public static GameObject Replace(this VanillaObject vanillaObject, bool active = return vanillaObject.gameObject.Replace(AssetCopier.Instantiate(vanillaObject.asset, active, originShift), preserveTypes, vanillaObject.keepChildren, vanillaObject.rotationOffset); } + public static string GetLocalizedStationName(this Station station) + { + var localized = LocalizationAPI.L(station.GetStationLocalizationKey()); + //if there is no translation, don't translate + return localized == Locale.MISSING_TRANSLATION ? station.stationName : localized; + } + + public static string GetStationLocalizationKey(this Station station) + { + return "station/" + station.stationID; + } + #endregion } } diff --git a/MapifyEditor/Cargo/CargoSetMonoBehaviour.cs b/MapifyEditor/Cargo/CargoSetMonoBehaviour.cs index 80dbbca6..8af619ad 100644 --- a/MapifyEditor/Cargo/CargoSetMonoBehaviour.cs +++ b/MapifyEditor/Cargo/CargoSetMonoBehaviour.cs @@ -4,6 +4,7 @@ namespace Mapify.Editor { // I can't with Unity man... + [ExecuteInEditMode] public class CargoSetMonoBehaviour : MonoBehaviour { public List cargoTypes; @@ -16,5 +17,11 @@ public CargoSet ToOriginal() stations = stations }; } + + private void OnEnable() + { + //hide this script in the editor + hideFlags = HideFlags.HideInInspector; + } } } diff --git a/MapifyEditor/Export/BuildUpdaters/GameContent/StationUpdater.cs b/MapifyEditor/Export/BuildUpdaters/GameContent/StationUpdater.cs index b36ca6aa..dff9d4d0 100644 --- a/MapifyEditor/Export/BuildUpdaters/GameContent/StationUpdater.cs +++ b/MapifyEditor/Export/BuildUpdaters/GameContent/StationUpdater.cs @@ -46,6 +46,13 @@ protected override void Update(Scenes scenes) station.outputCargoGroups.ForEach(set => set.ToMonoBehaviour(station.gameObject)); #endregion + + #region Translations + + station.stationNameTranslationsCount = station.stationNameTranslations.Count; + station.stationNameTranslations.ForEach(set => set.ToMonoBehaviour(station.gameObject)); + + #endregion } } diff --git a/MapifyEditor/Export/Validators/GameContent/StationValidator.cs b/MapifyEditor/Export/Validators/GameContent/StationValidator.cs index 6256669e..fa2a47fc 100644 --- a/MapifyEditor/Export/Validators/GameContent/StationValidator.cs +++ b/MapifyEditor/Export/Validators/GameContent/StationValidator.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text.RegularExpressions; using Mapify.Editor.Utils; +using UnityEngine; namespace Mapify.Editor.Validators { @@ -28,6 +29,11 @@ protected override IEnumerator Validate(Scenes scenes) if (string.IsNullOrWhiteSpace(station.stationName)) yield return Result.Error($"Station '{station.name}' must have a name", station); + foreach (var translationSet in station.stationNameTranslations.Where(translationSet => string.IsNullOrWhiteSpace(translationSet.translation))) + { + yield return Result.Error($"Station name translation in {translationSet.language} on '{station.stationName}' must not be empty", station); + } + if (string.IsNullOrWhiteSpace(station.stationID)) yield return Result.Error($"Station '{station.name}' must have an ID", station); diff --git a/MapifyEditor/Station/Station.cs b/MapifyEditor/Station/Station.cs index 7b527744..76bdcff6 100644 --- a/MapifyEditor/Station/Station.cs +++ b/MapifyEditor/Station/Station.cs @@ -11,6 +11,13 @@ public class Station : MonoBehaviour [Header("Station Info")] [Tooltip("The display name of the station")] public string stationName; + + [Tooltip("Station name translations")] + [SerializeField] + public List stationNameTranslations; + [HideInNormalInspector] + public int stationNameTranslationsCount; + [Tooltip("The 2-3 character ID of the station (e.g. HB for Harbor, SM for Steel Mill, etc)")] public string stationID; [Tooltip("The color of the station shown on job booklets")] diff --git a/MapifyEditor/Translations/LanguageEnum.cs b/MapifyEditor/Translations/LanguageEnum.cs new file mode 100644 index 00000000..d6c8c42d --- /dev/null +++ b/MapifyEditor/Translations/LanguageEnum.cs @@ -0,0 +1,32 @@ +namespace Mapify.Editor +{ + //must match DVLangHelper.Data.DVLanguage + public enum LanguageEnum + { + English, + Czech, + Danish, + German, + Spanish, + Finnish, + French, + Hindi, + Hungarian, + Italian, + Japanese, + Korean, + Norwegian, + Dutch, + Polish, + Portuguese, + Portuguese_BR, + Romanian, + Russian, + Slovak, + Swedish, + Turkish, + Ukrainian, + Chinese_Simple, + Chinese_Trad + } +} diff --git a/MapifyEditor/Translations/TranslationSet.cs b/MapifyEditor/Translations/TranslationSet.cs new file mode 100644 index 00000000..2b0c2332 --- /dev/null +++ b/MapifyEditor/Translations/TranslationSet.cs @@ -0,0 +1,20 @@ +using System; +using UnityEngine; + +namespace Mapify.Editor +{ + [Serializable] + public class TranslationSet + { + public LanguageEnum language; + public string translation; + + public TranslationSetBehaviour ToMonoBehaviour(GameObject gameObject) + { + var mb = gameObject.AddComponent(); + mb.language = language; + mb.translation = translation; + return mb; + } + } +} diff --git a/MapifyEditor/Translations/TranslationSetBehaviour.cs b/MapifyEditor/Translations/TranslationSetBehaviour.cs new file mode 100644 index 00000000..274b96b5 --- /dev/null +++ b/MapifyEditor/Translations/TranslationSetBehaviour.cs @@ -0,0 +1,27 @@ +using System; +using UnityEngine; + +namespace Mapify.Editor +{ + //this is a hack. we are using this because serializing Lists of serializable classes just doesn't work i guess + [ExecuteInEditMode] + public class TranslationSetBehaviour : MonoBehaviour + { + public LanguageEnum language; + public string translation; + + public TranslationSet ToOriginal() + { + return new TranslationSet { + language = language, + translation = translation + }; + } + + private void OnEnable() + { + //hide this script in the editor + hideFlags = HideFlags.HideInInspector; + } + } +} diff --git a/info.json b/info.json index 2543bede..e61eb405 100644 --- a/info.json +++ b/info.json @@ -6,5 +6,6 @@ "EntryMethod": "Mapify.Mapify.Load", "ManagerVersion": "0.27.13", "HomePage": "https://www.nexusmods.com/derailvalley/mods/593", - "Repository": "https://raw.githubusercontent.com/Insprill/dv-mapify/master/repository.json" + "Repository": "https://raw.githubusercontent.com/Insprill/dv-mapify/master/repository.json", + "Requirements": ["DVLangHelper-1.2.1"] } diff --git a/locale.csv b/locale.csv index c080492a..05fce76b 100644 --- a/locale.csv +++ b/locale.csv @@ -1,8 +1,13 @@ -Keys,Description,English -launcher/session_map,,Map: -session/map_selector,,Map -session/map_selector__tooltip,,Select a Map -launcher/map_selector__tooltip_disabled,, -loading/please_wait,,"loading{0}, please wait... {1}%" -loading/loading_map,,Loading map {0} -launcher/session_map_not_installed,,The map {0} isn't installed! Please install it before loading this save. +Keys,Description,English,Dutch,Chinese_Simplified,Chinese_Traditional,German,Italian,French,Russian,Hungarian,Spanish +launcher/session_map,,Map:,Kaart:,地图:,地圖:,Karte:,Mappa:,Carte:,карта,Térkép,Mapa: +session/map_selector,,Map,Kaart,地图,地圖,Karte,Mappa,Carte,выбор карты,Térkép,Mapa +session/map_selector__tooltip,,Select a Map,Kies een kaart,选择一张地图,選擇壹張地圖:,Wähle eine Karte,Seleziona una mappa,Selectionner une carte,,Válasszon ki egy térkép,Selecciona un mapa +launcher/map_selector__tooltip_disabled,,,,,,,,,,, +loading/please_wait,,"loading{[0]}, please wait... {[1]}%","{[0]} aan het laden, even wachten aub... {[1]}%","加载中{[0]}, 请稍等...{[1]}%","加載中{[0]}, 請稍等...{[1]}%",Lädt {[0]} bitte warten ....{[1]}%,"caricamento{[0]}, attendere... {[1]}%","chargement{[0]}, veuillez patienter..{[1]}%","Загрузка{[0]}, пожалуйста подождите... {[1]}%","Betöltés{[0]}, kérem várjon... {[1]}%","cargando{[0]}, por favor espere... {[1]}%" +loading/loading_map,,Loading map {[0]},Kaart aan het laden: {[0]},"加载地图""{[0]}""中","加載地圖""{[0]}""中",Karte wird geladen... {[0]}%,Caricamento mappa {[0]},Chargement de la carte {[0]},Загрузка карты {[0]},Térkép betöltés{[0]},Cargando mapa {[0]} +launcher/session_map_not_installed,,The map {[0]} isn't installed! Please install it before loading this save.,De kaart {[0]} is niet geïnstalleerd! Installeer het a.u.b. voordat u dit opgeslagen spel laadt.,"""{[0]}""这张地图还没下载呢!请你在加载这个存档之前先下载它","""{[0]}""這張地圖還沒下載呢!請妳在加載這個存檔之前先下載它",Die Karte {[0]} ist nicht installiert! Bitte installieren Sie die Karte bevor Sie diesen Spielstand laden.,La mappa {[0]} non è installata! Per favore installala prima di caricare questo salvataggio.,La carte {[0]} n'est pas installée sur le système! Veuillez l'installer avant de charger cette sauvegarde. ,Карта не установлена. Установите карту прежде чем загружать сохранение.,A {[0]} térkép nincs telepítve!! Kérem telepítése mielőtt betőlti a játékot,"¡El mapa {[0]} no está instalado! Por favor, instálalo antes de cargar esta partida." +,,,,,,,,,,, +,,,,,,,,,,, +,,,,,,,,,,, +,,,,,,,,,,, +mapify/translator:,,Insprill,Tostiman,我叫无聊 My Name Is BorING,我叫无聊 My Name Is BorING,kev & Coookie,tablesidecat02,"Corpet, Louvet & Cie",RaY,Dank_memey,The72