using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using UnityEngine.Localization; using UnityEngine.Localization.Metadata; using UnityEngine.Localization.Tables; using static UnityEngine.Localization.Tables.SharedTableData; namespace UnityEditor.Localization { /// /// Provides methods for managing multiple in the Editor. /// public class StringTableCollection : LocalizationTableCollection { /// protected internal override Type TableType => typeof(StringTable); /// protected internal override Type RequiredExtensionAttribute => typeof(StringTableCollectionExtensionAttribute); /// protected internal override string DefaultGroupName => "String Table"; /// /// A helper property which is the contents of loaded and cast to . /// public virtual ReadOnlyCollection StringTables => new ReadOnlyCollection(Tables.Select(t => t.asset as StringTable).ToList().AsReadOnly()); /// /// Returns a string that contains all the unique characters that are used for all localized values in the tables that belong to the supplied 's. /// This will also include Smart String entries but will only consider the values, /// it will not consider values. /// /// The tables to be included. /// All distinct characters or an empty string if no tables or entries.. public string GenerateCharacterSet(params LocaleIdentifier[] localeIdentifiers) { if (localeIdentifiers == null || localeIdentifiers.Length == 0) throw new ArgumentException(nameof(localeIdentifiers), "Must provide at least 1 LocaleIdentifier"); var characters = ExtractLiteralCharacters(localeIdentifiers); var distinct = characters.Distinct().OrderBy(c => c); return string.Concat(distinct); } internal IEnumerable ExtractLiteralCharacters(params LocaleIdentifier[] localeIdentifiers) { IEnumerable e = ""; foreach (var id in localeIdentifiers) { // Create an enumerator for all the tables and entries. var table = GetTable(id) as StringTable; if (table != null) e = e.Concat(table.CollectLiteralCharacters()); } return e; } /// /// Updates the collection entries with a and optionally removes entries that are missing from the update. /// Used by various importers such as and . /// /// The entries that should not be removed if is . /// The new sorted entries. /// Optional log for reporting what entries were removed. /// Should missing entries be removed? If they will be placed at the end after the sorted entries. internal void MergeUpdatedEntries(HashSet entriesToKeep, List sortedEntries, StringBuilder removedEntriesLog, bool removeMissingEntries) { // We either remove missing entries or add them to the end. var stringTables = StringTables; removedEntriesLog.AppendLine("Removed missing entries:"); for (int i = 0; i < SharedData.Entries.Count; ++i) { var entry = SharedData.Entries[i]; if (entriesToKeep.Contains(entry.Id)) continue; if (!removeMissingEntries) { // Missing entries that we want to keep go to the bottom of the list. sortedEntries.Add(entry); } else if (entry.Metadata.HasMetadata()) { // Add back the entry which has ExcludeEntryFromExport Metadata. sortedEntries.Insert(i, entry); } else { removedEntriesLog.AppendLine($" {entry}"); // Remove from tables foreach (var table in stringTables) { table.Remove(entry.Id); } } } // Now replace the old list with our new one that is in the correct order. SharedData.Entries = sortedEntries; } /// /// Returns an enumerator that can be used to step through each key and its localized values, such as in a foreach loop. /// Internally and 's are separate assets with their own internal list of values. /// This means that when iterating through each key a lookup must be made in each table in order to retrieve the localized value, /// this can become slow when dealing with a large number of tables and entries. /// GetRowEnumerator improves this process by first sorting the multiple internal lists and then stepping through each conceptual row at a time. /// It handles missing keys and table entries and provides a more efficient and faster way to iterate through the tables. /// /// /// This example shows how a StringTableCollection could be exported as CSV. /// /// /// public IEnumerable> GetRowEnumerator() => GetRowEnumerator(StringTables); /// /// Returns an enumerator that can be used to step through each key and its localized values, such as in a foreach loop. /// This version does not sort the items by the Key Id but instead returns them in the order of the . /// If the order of the rows is not important then using will provide better performance. /// /// public IEnumerable> GetRowEnumeratorUnsorted() => GetRowEnumeratorUnsorted(StringTables); /// /// /// /// /// public static IEnumerable> GetRowEnumerator(params StringTable[] tables) => GetRowEnumerator(tables); /// public override void RemoveEntry(TableEntryReference entryReference) { var entry = SharedData.GetEntryFromReference(entryReference); if (entry == null) return; foreach (var table in StringTables) table.RemoveEntry(entry.Id); SharedData.RemoveKey(entry.Key); LocalizationEditorSettings.EditorEvents.RaiseTableEntryRemoved(this, entry); } /// public override void ClearAllEntries() { foreach (var table in StringTables) { if (table == null) continue; table.Clear(); EditorUtility.SetDirty(table); } base.ClearAllEntries(); } } }