220 lines
6.9 KiB
C#
220 lines
6.9 KiB
C#
|
|
#define UDONSHARP_LOC_DEBUG
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Xml.Linq;
|
|
using UdonSharp.Updater;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace UdonSharp.Localization
|
|
{
|
|
/// <summary>
|
|
/// Locale string ID list used to get a specified localized string
|
|
///
|
|
/// Used for UI, compiler messages, warnings, and errors
|
|
/// The int ID of a given LocStr is not reliable and is liable to change so do not depend on the integer value of it.
|
|
///
|
|
/// Enums are grouped and prefixed by the type of string they represent:
|
|
/// `UI_`: UI related strings for UI elements, for instance the Compile All UdonSharp Programs button
|
|
/// `CI_`: Compiler info messages, ex. Compile of X scripts finished in Y
|
|
/// `CW_`: Compiler warning messages, ex. Multiple UdonSharpProgramAssets referencing the same script
|
|
/// `CE_`: Compiler error messages, ex. Most compile errors
|
|
/// </summary>
|
|
public enum LocStr
|
|
{
|
|
UI_TestLocStr,
|
|
UI_CompileProgram,
|
|
UI_CompileAllPrograms,
|
|
UI_SendCustomEvent,
|
|
UI_TriggerInteract,
|
|
UI_ProgramSource,
|
|
UI_ProgramScript,
|
|
UI_SourceScript,
|
|
UI_SerializedUdonProgramAsset,
|
|
CE_UdonSharpBehaviourConstructorsNotSupported,
|
|
CE_PartialMethodsNotSupported,
|
|
CE_UdonSharpBehaviourGenericMethodsNotSupported,
|
|
CE_LocalMethodsNotSupported,
|
|
CE_NodeNotSupported,
|
|
CE_UdonMethodNotExposed,
|
|
CE_InitializerListsNotSupported,
|
|
CE_UdonFieldNotExposed,
|
|
|
|
Length,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instance of the currently selected locale, with lookups for appropriate messages and errors, falls back to en-US for missing strings
|
|
/// </summary>
|
|
internal class LocaleInstance
|
|
{
|
|
Dictionary<LocStr, string> localizedStringLookup = new Dictionary<LocStr, string>();
|
|
|
|
public LocaleInstance(string locale)
|
|
{
|
|
string localeDir = UdonSharpLocator.LocalizationPath;
|
|
string fileContents = "";
|
|
|
|
try
|
|
{
|
|
fileContents = LoadLocale(Path.Combine(localeDir, locale.ToLowerInvariant() + ".resx"));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"Failed to load locale {locale}\nException: {e}");
|
|
}
|
|
|
|
XName nameAttributeID = XName.Get("name");
|
|
XName valueAttributeID = XName.Get("value");
|
|
|
|
using (StringReader strReader = new StringReader(fileContents))
|
|
{
|
|
XElement xml = XElement.Load(strReader, LoadOptions.PreserveWhitespace);
|
|
|
|
foreach (var element in xml.Elements())
|
|
{
|
|
if (element.Name != "data")
|
|
continue;
|
|
|
|
string elementName = element.Attribute(nameAttributeID).Value;
|
|
string elementValue = element.Element(valueAttributeID).Value;
|
|
|
|
if (Enum.TryParse(elementName, out LocStr elementResult))
|
|
{
|
|
localizedStringLookup.Add(elementResult, elementValue);
|
|
}
|
|
#if UDONSHARP_LOC_DEBUG
|
|
else
|
|
{
|
|
Debug.LogWarning($"Could not find corresponding enum for key '{elementName}'");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if UDONSHARP_LOC_DEBUG
|
|
for (int i = 0; i < (int)LocStr.Length; ++i)
|
|
{
|
|
if (!localizedStringLookup.ContainsKey((LocStr)i))
|
|
Debug.LogWarning($"Did not find string for key '{(LocStr)i}'");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static string LoadLocale(string path)
|
|
{
|
|
if (!File.Exists(path))
|
|
throw new System.IO.FileNotFoundException($"Could not find locale file at {path}, make sure you have installed UdonSharp following the installation instructions.");
|
|
|
|
return ReadFileTextSync(path, 1f);
|
|
}
|
|
|
|
// Stolen from UdonSharpUtils because it's in a different assembly, todo: move it somewhere more accessible
|
|
static string ReadFileTextSync(string filePath, float timeoutSeconds)
|
|
{
|
|
bool sourceLoaded = false;
|
|
|
|
string fileText = "";
|
|
|
|
System.DateTime startTime = System.DateTime.Now;
|
|
|
|
while (true)
|
|
{
|
|
System.IO.IOException exception = null;
|
|
|
|
try
|
|
{
|
|
fileText = System.IO.File.ReadAllText(filePath);
|
|
sourceLoaded = true;
|
|
}
|
|
catch (System.IO.IOException e)
|
|
{
|
|
exception = e;
|
|
|
|
if (e is System.IO.FileNotFoundException ||
|
|
e is System.IO.DirectoryNotFoundException)
|
|
throw e;
|
|
}
|
|
|
|
if (sourceLoaded)
|
|
break;
|
|
else
|
|
System.Threading.Thread.Sleep(20);
|
|
|
|
System.TimeSpan timeFromStart = System.DateTime.Now - startTime;
|
|
|
|
if (timeFromStart.TotalSeconds > timeoutSeconds)
|
|
{
|
|
UnityEngine.Debug.LogError($"Timeout when attempting to read file {filePath}");
|
|
if (exception != null)
|
|
throw exception;
|
|
}
|
|
}
|
|
|
|
return fileText;
|
|
}
|
|
|
|
public string GetString(LocStr locStr)
|
|
{
|
|
if (localizedStringLookup.TryGetValue(locStr, out string foundStr))
|
|
{
|
|
return foundStr;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Localization manager
|
|
/// </summary>
|
|
public static class Loc
|
|
{
|
|
private static LocaleInstance _instance;
|
|
|
|
public static void InitLocalization()
|
|
{
|
|
if (_instance == null)
|
|
ReloadLocalization();
|
|
}
|
|
|
|
internal static void ReloadLocalization()
|
|
{
|
|
_instance = new LocaleInstance("en-us");
|
|
}
|
|
|
|
public static string Get(LocStr locKey)
|
|
{
|
|
InitLocalization();
|
|
return _instance.GetString(locKey);
|
|
}
|
|
|
|
public static string Format(LocStr locKey, params object[] parameters)
|
|
{
|
|
InitLocalization();
|
|
return string.Format(_instance.GetString(locKey), parameters);
|
|
}
|
|
}
|
|
|
|
#if UDONSHARP_LOC_DEBUG && UNITY_EDITOR
|
|
internal class LocalePostProcessor : AssetPostprocessor
|
|
{
|
|
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
|
{
|
|
foreach (string str in importedAssets)
|
|
{
|
|
if (Path.GetExtension(str) == ".resx")
|
|
{
|
|
Loc.ReloadLocalization();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|