public void YarnImporterUtility_CanCreateLocalizationInLocalizationDatabase() { // Arrange: Import a yarn script and create a localization // database for it string fileName = Path.GetRandomFileName(); string path = Path.Combine("Assets", fileName + ".yarn"); createdFilePaths.Add(path); File.WriteAllText(path, TestYarnScriptSource); AssetDatabase.Refresh(); var importer = AssetImporter.GetAtPath(path) as YarnImporter; var importerSerializedObject = new SerializedObject(importer); YarnImporterUtility.CreateNewLocalizationDatabase(importerSerializedObject); createdFilePaths.Add(AssetDatabase.GetAssetPath(importer.localizationDatabase)); var databaseSerializedObject = new SerializedObject(importer.localizationDatabase); // Act: Create a new localization CSV file for some new language LocalizationDatabaseUtility.CreateLocalizationWithLanguage(databaseSerializedObject, AlternateLocaleCode); YarnImporterUtility.CreateLocalizationForLanguageInProgram(importerSerializedObject, AlternateLocaleCode); foreach (var loc in importer.localizationDatabase.Localizations) { createdFilePaths.Add(AssetDatabase.GetAssetPath(loc)); } foreach (var loc in importer.localizations) { createdFilePaths.Add(AssetDatabase.GetAssetPath(loc.text)); } importer.SaveAndReimport(); // Assert: Verify that it exists, contains the string table // entries we expect, and has the language we expect. var expectedLanguages = new HashSet <string> { importer.baseLanguageID, AlternateLocaleCode }.OrderBy(n => n); var foundLanguages = importer.programContainer.localizations.Select(l => l.languageName).OrderBy(n => n); CollectionAssert.AreEquivalent(expectedLanguages, foundLanguages, $"The locales should be what we expect to see"); }
public void YarnImporterUtility_CanUpdateLocalizedCSVs_WhenBaseScriptChanges() { // Arrange: Import a yarn script and create a localization // database for it, create an alternate localization for it string fileName = Path.GetRandomFileName(); string path = Path.Combine("Assets", fileName + ".yarn"); createdFilePaths.Add(path); File.WriteAllText(path, TestYarnScriptSource); AssetDatabase.Refresh(); var importer = AssetImporter.GetAtPath(path) as YarnImporter; var importerSerializedObject = new SerializedObject(importer); YarnImporterUtility.CreateNewLocalizationDatabase(importerSerializedObject); var localizationDatabaseSerializedObject = new SerializedObject(importer.localizationDatabase); LocalizationDatabaseUtility.CreateLocalizationWithLanguage(localizationDatabaseSerializedObject, AlternateLocaleCode); YarnImporterUtility.CreateLocalizationForLanguageInProgram(importerSerializedObject, AlternateLocaleCode); var unmodifiedBaseStringsTable = StringTableEntry.ParseFromCSV((importerSerializedObject.targetObject as YarnImporter).baseLanguage.text); var unmodifiedLocalizedStringsTable = StringTableEntry.ParseFromCSV((importerSerializedObject.targetObject as YarnImporter).localizations.First(l => l.languageName == AlternateLocaleCode).text.text); // Act: modify the imported script so that lines are added, // changed and deleted, and then update the localized CSV // programmatically File.WriteAllText(path, TestYarnScriptSourceModified); AssetDatabase.Refresh(); YarnImporterUtility.UpdateLocalizationCSVs(importerSerializedObject); var modifiedBaseStringsTable = StringTableEntry.ParseFromCSV((importerSerializedObject.targetObject as YarnImporter).baseLanguage.text); var modifiedLocalizedStringsTable = StringTableEntry.ParseFromCSV((importerSerializedObject.targetObject as YarnImporter).localizations.First(l => l.languageName == AlternateLocaleCode).text.text); // Assert: verify the base language string table contains the // string table entries we expect, verify the localized string // table contains the string table entries we expect System.Func <StringTableEntry, string> CompareIDs = t => t.ID; System.Func <StringTableEntry, string> CompareLocks = t => t.Lock; var tests = new[] {
public void YarnImporterUtility_CanCreateNewLocalizationDatabase() { // Arrange: Import a yarn script string fileName = Path.GetRandomFileName(); string path = Path.Combine("Assets", fileName + ".yarn"); createdFilePaths.Add(path); File.WriteAllText(path, TestYarnScriptSource); AssetDatabase.ImportAsset(path); AssetDatabase.Refresh(); var importer = AssetImporter.GetAtPath(path) as YarnImporter; var serializedObject = new SerializedObject(importer); var localizationDatabaseAfterImport = importer.localizationDatabase; // Act: create a new localization database. YarnImporterUtility.CreateNewLocalizationDatabase(serializedObject); importer.SaveAndReimport(); // Assert: Verify that the new localization database exists, // and contains a single localization, and that localization // contains the string table entries we expect. Assert.Null(localizationDatabaseAfterImport, "The script should not have a localization database after initial creation"); Assert.NotNull(importer.localizationDatabase, "Importer should have a localization database"); createdFilePaths.Add(AssetDatabase.GetAssetPath(importer.localizationDatabase)); var db = importer.localizationDatabase; Assert.AreEqual(1, db.Localizations.Count(), "Localization database should have a single localization"); createdFilePaths.Add(AssetDatabase.GetAssetPath(importer.localizationDatabase.Localizations.First())); var localization = db.Localizations.First(); Assert.AreEqual(localization.LocaleCode, importer.baseLanguageID, "Localization locale should match script's language"); Assert.Contains(importer.baseLanguageID, ProjectSettings.TextProjectLanguages, "Script language should be present in the project language settings"); }
private void DrawLocalizationGUI() { using (var changed = new EditorGUI.ChangeCheckScope()) { var previousLocalizationDatabase = localizationDatabaseProperty.objectReferenceValue as LocalizationDatabase; // Show the 'localization database' property EditorGUILayout.PropertyField(localizationDatabaseProperty); // If this changed to a valid value, update that database so // that it tracks all selected programs if (changed.changed) { var newObjectReference = localizationDatabaseProperty.objectReferenceValue; if (previousLocalizationDatabase != null && previousLocalizationDatabase != newObjectReference) { // The property used to refer to a localization // database, but that's changed. Tell the previous // value to stop tracking this program. foreach (YarnImporter importer in serializedObject.targetObjects) { if (importer.programContainer == null) { continue; } previousLocalizationDatabase.RemoveTrackedProgram(importer.programContainer); // Mark that the localization database has changed, // so needs to be saved EditorUtility.SetDirty(previousLocalizationDatabase); } } // Tell the new database that it should track us if (newObjectReference is LocalizationDatabase database) { foreach (YarnImporter importer in serializedObject.targetObjects) { // If we don't actually have a program (because of // a compile error), there's nothing to do here if (importer.programContainer == null) { continue; } database.AddTrackedProgram(importer.programContainer); // Mark that the localization database should save // changes EditorUtility.SetDirty(previousLocalizationDatabase); } } } } // If no localization database is provided, offer a button that // will create a new one that 1. tracks this script 2. has a // localization set to this script's base language 3. and also we // make sure that this project's language list includes this // program's base language. if (localizationDatabaseProperty.objectReferenceValue == null) { if (GUILayout.Button("Create New Localization Database")) { YarnImporterUtility.CreateNewLocalizationDatabase(serializedObject); } } // For every localization in the localization database: // - If we have a TextAsset for it, show it here // - If we don't, create a button that creates one // // We only do this if we're editing a single object, because each // separate script will have its own translations. if (serializedObject.isEditingMultipleObjects == false && localizationDatabaseProperty.objectReferenceValue != null) { EditorGUI.indentLevel += 1; var importer = serializedObject.targetObject as YarnImporter; var localizationDatabase = localizationDatabaseProperty.objectReferenceValue as LocalizationDatabase; var languagesList = new List <string>(); languagesList.Add(importer.baseLanguageID); // Expose the base language asset in the inspector, but disable // it because it's always a derived sub-asset using (new EditorGUI.DisabledScope(true)) using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.PropertyField(baseLanguageProperty, new GUIContent(importer.baseLanguageID)); // Not actually used, but makes this base language item // visually consistent with the additional ones below GUILayout.Button("-", EditorStyles.miniButton, GUILayout.ExpandWidth(false)); } foreach (SerializedProperty localization in localizationsProperty) { var nameProperty = localization.FindPropertyRelative("languageName"); var assetReferenceProperty = localization.FindPropertyRelative("text"); var languageName = nameProperty.stringValue; var languageDisplayName = Cultures.GetCulture(languageName).DisplayName; using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.PropertyField(assetReferenceProperty, new GUIContent(languageDisplayName)); if (GUILayout.Button("-", EditorStyles.miniButton, GUILayout.ExpandWidth(false))) { // We delete this property twice: // - once to clear the value from the array entry // - again to remove the cleared entry from the // array // // (If the entry is already empty, the first delete // will remove it; the second delete appears to be // a no-op, so it's safe.) localization.DeleteCommand(); localization.DeleteCommand(); } } // Mark that we've seen this language name languagesList.Add(languageName); } // For each language that's present in the localization // database but not present in this script, offer buttons that // create a CSV for that language var languagesMissing = localizationDatabase.GetLocalizationLanguages().Except(languagesList); foreach (var language in languagesMissing) { if (GUILayout.Button($"Create {language} Localization")) { YarnImporterUtility.CreateLocalizationForLanguageInProgram(serializedObject, language); } } // Show a warning for any languages that the script has a // localization for, but that the database doesn't call for var languagesExtraneous = languagesList.Except(localizationDatabase.GetLocalizationLanguages()); if (languagesExtraneous.Count() > 0) { EditorGUILayout.HelpBox($"This script has localizations for the following languages, but the localization database isn't set up to use them: {string.Join(", ", languagesExtraneous)}", MessageType.Warning); } // TODO: is it possible to interleave the property fields for // existing localisations with buttons, in alphabetical order // of language code? EditorGUI.indentLevel -= 1; if (GUILayout.Button("Update Localizations")) { YarnImporterUtility.UpdateLocalizationCSVs(serializedObject); } EditorGUILayout.HelpBox("To add a new localization, select the Localization Database, and click Create New Localization.", MessageType.Info); } }