using System; using System.Collections.Generic; using System.Linq; using UnityEngine.Localization.Metadata; using UnityEngine.Localization.Pseudo; using UnityEngine.Localization.Settings; using UnityEngine.Localization.SmartFormat; using UnityEngine.Localization.SmartFormat.Core.Formatting; using UnityEngine.Localization.SmartFormat.GlobalVariables; namespace UnityEngine.Localization.Tables { /// /// A entry. /// public class StringTableEntry : TableEntry { FormatCache m_FormatCache; /// /// Used when is true and is called. /// Contains information about the format including any that were used. /// public FormatCache FormatCache { get => m_FormatCache; set => m_FormatCache = value; } /// /// The raw localized value without any formatting applied. /// public string Value { get => Data.Localized; set { Data.Localized = value; if (m_FormatCache != null) { FormatCachePool.Release(m_FormatCache); m_FormatCache = null; } } } /// /// Is the entry marked with the ? /// Entries that are smart will use to format the localized text. /// public bool IsSmart { get => HasTagMetadata() || Data.Metadata.GetMetadata() != null; set { if (value) { if (m_FormatCache != null) { FormatCachePool.Release(m_FormatCache); m_FormatCache = null; } AddTagMetadata(); } else { RemoveTagMetadata(); } } } internal StringTableEntry() { } /// /// Attempts to remove the entry from the that it belongs to. /// If is null then a warning will be produced. /// public void RemoveFromTable() { var stringTable = Table as StringTable; if (stringTable == null) { Debug.LogWarning($"Failed to remove {nameof(StringTableEntry)} with id {KeyId} and value `{Value}` as it does not belong to a table."); } else { stringTable.Remove(KeyId); } } internal FormatCache GetOrCreateFormatCache() { if (m_FormatCache == null && !string.IsNullOrEmpty(Data.Localized)) { m_FormatCache = FormatCachePool.Get(LocalizationSettings.StringDatabase.SmartFormatter.Parser.ParseFormat(Data.Localized, LocalizationSettings.StringDatabase.SmartFormatter.GetNotEmptyFormatterExtensionNames())); m_FormatCache.Table = Table; } return m_FormatCache; } /// /// Returns the localized text after formatting has been applied. /// This will use SmartFormat if is true else it will return the raw unformatted value. /// /// /// The following process is applied when generating a localized string: /// ![](../manual/images/scripting/ProcessStringEntry.dot.svg) /// /// public string GetLocalizedString() => GetLocalizedString(null, null, LocalizationSettings.SelectedLocaleAsync.Result as PseudoLocale); /// /// Returns the localized text after formatting has been applied. /// Formatting will use SmartFormat if is true else it will default to String.Format. /// /// Arguments that will be applied to Smart Format or String.Format. /// public string GetLocalizedString(params object[] args) => GetLocalizedString(null, args, LocalizationSettings.SelectedLocaleAsync.Result as PseudoLocale); /// /// Returns the localized text after formatting has been applied. /// Formatting will use SmartFormat if is true else it will default to String.Format. /// /// Arguments that will be applied to Smart Format or String.Format. /// public string GetLocalizedString(IList args) => GetLocalizedString(null, args, LocalizationSettings.SelectedLocaleAsync.Result as PseudoLocale); /// /// Returns the localized text after formatting has been applied. /// Formatting will use SmartFormat is is true else it will default to String.Format. /// /// Custom format provider used with String.Format and smart strings. /// If formatProvider is , RemoveFromTable uses the 's . /// Arguments that will be applied to Smart Format or String.Format. /// public string GetLocalizedString(IFormatProvider formatProvider, IList args) => GetLocalizedString(formatProvider, args, LocalizationSettings.SelectedLocaleAsync.Result as PseudoLocale); /// /// Returns the localized text after formatting has been applied. /// Formatting will use SmartFormat is is true else it will default to String.Format. /// /// Custom format provider used with String.Format and smart strings. /// If formatProvider is , RemoveFromTable uses the 's . /// Arguments that are be applied to Smart Format or String.Format. /// Optional that will be applied to the final string. /// public string GetLocalizedString(IFormatProvider formatProvider, IList args, PseudoLocale pseudoLocale) { if (formatProvider == null) formatProvider = LocalizationSettings.AvailableLocales?.GetLocale(Table.LocaleIdentifier)?.Formatter; string translatedText = null; if (IsSmart) { #if UNITY_EDITOR if (!LocalizationSettings.Instance.IsPlayingOrWillChangePlaymode) { var localVariables = m_FormatCache?.LocalVariables; m_FormatCache = null; m_FormatCache = GetOrCreateFormatCache(); if (m_FormatCache != null) m_FormatCache.LocalVariables = localVariables; } #endif if (m_FormatCache == null) m_FormatCache = GetOrCreateFormatCache(); translatedText = LocalizationSettings.StringDatabase.SmartFormatter.FormatWithCache(ref m_FormatCache, Data.Localized, formatProvider, args); } else if (!string.IsNullOrEmpty(Data.Localized)) { if (args != null && args.Count > 0) { try { translatedText = formatProvider == null ? string.Format(Data.Localized, args as object[] ?? args.ToArray()) : string.Format(formatProvider, Data.Localized, args as object[] ?? args.ToArray()); } catch (FormatException fe) { // Supplement with a better error message as its likely that the string was a Smart String. throw new FormatException($"Input string was not in the correct format for String.Format. Ensure that the string is marked as Smart if you intended to use Smart Format.\n`{Data.Localized}`\n{fe}", fe); } } else translatedText = Data.Localized; } if (pseudoLocale != null && !string.IsNullOrEmpty(translatedText)) translatedText = pseudoLocale.GetPseudoString(translatedText); return translatedText; } } /// /// A table that stores localized strings for a specific . /// public class StringTable : DetailedLocalizationTable { /// /// Returns the unique characters used by all entries in this table. /// This will also include Smart String entries but will only consider the values, /// it will not consider values. /// /// public string GenerateCharacterSet() { var literals = CollectLiteralCharacters(); // Sort the output so the results are more deterministic. var sorted = literals.Distinct().OrderBy(c => c); return string.Concat(sorted); } internal IEnumerable CollectLiteralCharacters() { IEnumerable e = ""; var smart = new SmartFormatterLiteralCharacterExtractor(LocalizationSettings.StringDatabase?.SmartFormatter); foreach (var entry in Values) { if (entry.IsSmart) { e = e.Concat(smart.ExtractLiteralsCharacters(entry.LocalizedValue)); } else { e = e.Concat(entry.LocalizedValue.AsEnumerable()); } } return e; } /// /// Creates a new, empty StringTableEntry. /// /// public override StringTableEntry CreateTableEntry() { return new StringTableEntry() { Table = this, Data = new TableEntryData() }; } } }