public TableCreator() { var asset = Resources.GetTemplateAsset(nameof(TableCreator)); asset.CloneTree(this); var locales = LocalizationEditorSettings.GetLocales(); m_LocalesList = this.Q <ScrollView>("locales-list"); foreach (var locale in locales) { AddLocaleElement(locale); } m_CreateStringTablesButton = this.Q <Button>("create-string-tables-button"); m_CreateStringTablesButton.clickable.clicked += () => CreateCollection(LocalizationEditorSettings.CreateStringTableCollection); m_CreateAssetTablesButton = this.Q <Button>("create-asset-tables-button"); m_CreateAssetTablesButton.clickable.clicked += () => CreateCollection(LocalizationEditorSettings.CreateAssetTableCollection); UpdateCreateButtonState(); this.Q <Button>("select-all-button").clickable.clicked += () => SelectAllLocales(true); this.Q <Button>("select-none-button").clickable.clicked += () => SelectAllLocales(false); this.Q <Button>("locale-generator-button").clickable.clicked += () => LocaleGeneratorWindow.ShowWindow(); m_TableCollectionName = this.Q <TextField>("new-table-name-field"); LocalizationEditorSettings.EditorEvents.LocaleAdded += OnLocaleAdded; LocalizationEditorSettings.EditorEvents.LocaleRemoved += OnLocaleRemoved; }
void RefreshTables() { // Find loose tables m_LooseTables.Clear(); if (m_Collection.SharedData == null) { return; } LocalizationEditorSettings.FindLooseStringTablesUsingSharedTableData(m_Collection.SharedData, m_LooseTables); // Find missing tables by project locales var projectLocales = LocalizationEditorSettings.GetLocales(); m_MissingTables.Clear(); foreach (var locale in projectLocales) { if (!m_Collection.ContainsTable(locale.Identifier)) { m_MissingTables.Add(locale); } } Repaint(); }
/// <summary> /// Updates the Android Gradle project file with localized values using <see cref="AppInfo"/>. /// </summary> /// <param name="projectDirectory">The root project directory to be updated. This is where the Android player was built to.</param> /// <param name="appInfo">Contains the localized values for the App.</param> /// <param name="roundIconsInfo">Contains the localized values for Android Round Icon. Refer Android documentation for more details : https://developer.android.com/about/versions/nougat/android-7.1.html#circular-icons</param> /// <param name="legacyIconsInfo">Contains the localized values for Android Legacy Icon.</param> /// <param name="adaptiveIconsInfo">Contains the localized values for Android Adaptive Icon. . Refer Android documentation for more details : https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive</param> public static void AddLocalizationToAndroidGradleProject(string projectDirectory, AppInfo appInfo, RoundIconsInfo roundIconsInfo = null, LegacyIconsInfo legacyIconsInfo = null, AdaptiveIconsInfo adaptiveIconsInfo = null) { if (appInfo == null) { throw new ArgumentNullException(nameof(appInfo)); } var project = new GradleProjectSettings(); foreach (var locale in LocalizationEditorSettings.GetLocales()) { var localeIdentifier = locale.Identifier.Code.Replace("-", "-r"); GenerateLocalizedXmlFile("App Name", Path.Combine(Directory.CreateDirectory(Path.Combine(project.GetResFolderPath(projectDirectory), "values-b+" + localeIdentifier)).FullName, k_InfoFile), locale, appInfo); //Generate icons var folderNames = new List <string> { $"mipmap-{localeIdentifier}-hdpi", $"mipmap-{localeIdentifier}-ldpi", $"mipmap-{localeIdentifier}-mdpi", $"mipmap-{localeIdentifier}-xhdpi", $"mipmap-{localeIdentifier}-xxhdpi", $"mipmap-{localeIdentifier}-xxxhdpi", $"mipmap-{localeIdentifier}-anydpi-v26" }; if (roundIconsInfo != null || legacyIconsInfo != null || adaptiveIconsInfo != null) { GenerateIconDirectory(folderNames, project.GetResFolderPath(projectDirectory), locale); } if (roundIconsInfo != null) { GenerateRoundIcons(folderNames, project.GetResFolderPath(projectDirectory), locale, roundIconsInfo); } if (legacyIconsInfo != null) { GenerateLegacyIcons(folderNames, project.GetResFolderPath(projectDirectory), locale, legacyIconsInfo); } if (adaptiveIconsInfo != null) { GenerateAdaptiveIcons(folderNames, project.GetResFolderPath(projectDirectory), locale, adaptiveIconsInfo); } } var androidManifest = new AndroidManifest(project.GetManifestPath(projectDirectory)); androidManifest.SetAtrribute("label", project.LabelName); if (adaptiveIconsInfo != null || legacyIconsInfo != null || roundIconsInfo != null) { androidManifest.SetAtrribute("icon", project.IconLabelName); androidManifest.SetAtrribute("roundIcon", project.RoundIconLabelName); } androidManifest.SaveIfModified(); }
void CheckListContainsProjectLocales(WrapperWindow wnd) { var listScrollView = wnd.rootVisualElement.Q <ReorderableList>().Q <ScrollView>(); var locales = LocalizationEditorSettings.GetLocales().ToList(); Assert.AreEqual(locales.Count, listScrollView.childCount, "Expected list size to match the number of project locales."); int localeCount = locales.Count; for (int i = 0; i < localeCount; ++i) { var item = listScrollView[i]; var name = item.Q <TextField>("name"); var code = item.Q <TextField>("code"); Assert.NotNull(name, "Could not find name field."); Assert.NotNull(code, "Could not find code field."); var matchingLocale = locales.FirstOrDefault(l => l.name == name.value); Assert.NotNull(matchingLocale, $"Could not find a matching locale for {name.value}({code.value})"); locales.Remove(matchingLocale); } Assert.That(locales, Is.Empty, "Expected all project locales to be in the ListView but they were not."); }
static List <Locale> GetChoices() { s_Locales.Clear(); s_Locales.Add(null); s_Locales.AddRange(LocalizationEditorSettings.GetLocales()); s_Locales.AddRange(LocalizationEditorSettings.GetPsuedoLocales()); return(s_Locales); }
static void UpdateList(ReorderableList list) { list.List.Clear(); foreach (var locale in LocalizationEditorSettings.GetLocales()) { list.List.Add(locale); } }
public override float GetPropertyHeight(Data data, SerializedProperty property, GUIContent label) { float height = base.GetPropertyHeight(data, property, label); if (property.isExpanded && data.SelectedTableEntry != null) { height += LocalizationEditorSettings.GetLocales().Count *(EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing); } return(height); }
/// <summary> /// (Editor only) /// Gets a locale to use in edit mode in the editor. /// </summary> /// <param name="tableCollection">Optional table collection with which to filter the available locales.</param> /// <returns>The locale, null if none usable found.</returns> static Locale Editor_GetValidLocaleInEditMode(LocalizationTableCollection tableCollection) { foreach (var locale in LocalizationEditorSettings.GetLocales()) { if (locale != null && (tableCollection == null || tableCollection.GetTable(locale.Identifier) != null)) { return(locale); } } return(null); }
/// <summary> /// Creates columns by attempting to match to expected column names(case insensitive).<br/> /// The following names are checked:<br/> /// "key" => <see cref="KeyColumn"/><br/> /// "key id" => <see cref="KeyIdColumn"/><br/> /// Project <see cref="Locale"/>'s name, <see cref="LocaleIdentifier.ToString"/> or <see cref="LocaleIdentifier.Code"/> => <see cref="LocaleColumn"/> /// </summary> /// <param name="columnNames">The column names to create mappings for.</param> /// <param name="unusedNames">Optional list that can be populated with the names that a match could not be found for.</param> /// <returns></returns> public static List <SheetColumn> CreateMappingsFromColumnNames(IList <string> columnNames, IList <string> unusedNames = null) { var columns = new List <SheetColumn>(); // We map all potential name variations into the dictionary and then check each name against it. // We could cache this however we would have to keep it in sync with the Locale's so for simplicity's sake, we don't. var nameMap = new Dictionary <string, Func <SheetColumn> >(StringComparer.OrdinalIgnoreCase) { ["Key"] = () => new JsonField() }; var projectLocales = LocalizationEditorSettings.GetLocales(); foreach (var locale in projectLocales) { Func <SheetColumn> createLocaleFunc = () => new LocaleColumn { LocaleIdentifier = locale.Identifier }; Func <SheetColumn> createCommentFunc = () => new LocaleCommentColumn { LocaleIdentifier = locale.Identifier }; nameMap[locale.name] = createLocaleFunc; nameMap[locale.Identifier.ToString()] = createLocaleFunc; nameMap[locale.Identifier.Code] = createLocaleFunc; // Comment nameMap[locale.name + " Comments"] = createCommentFunc; nameMap[locale.Identifier + " Comments"] = createCommentFunc; nameMap[locale.Identifier.Code + " Comments"] = createCommentFunc; } // Now map the columns for (int i = 0; i < columnNames.Count; ++i) { if (nameMap.TryGetValue(columnNames[i], out var createFunc)) { var column = createFunc(); column.ColumnIndex = i; columns.Add(column); } else if (unusedNames != null) { unusedNames.Add(columnNames[i]); } } return(columns); }
List <Locale> GetSelectedLocales() { var locales = LocalizationEditorSettings.GetLocales(); var selectedLocales = new List <Locale>(); for (int i = 0; i < m_LocalesList.contentContainer.childCount; ++i) { if (m_LocalesList.contentContainer.ElementAt(i) is Toggle toggle && toggle.value) { Debug.Assert(locales[i].name == toggle.text, $"Expected locale to match toggle. Expected {locales[i].name} but got {toggle.name}"); selectedLocales.Add(locales[i]); } } return(selectedLocales); }
public void ProjectLocalesIsUpdatedWhenRemoveLocaleIsUndone() { const string localeAssetPath = "Assets/HebrewRemove.asset"; var locale = Locale.CreateLocale(SystemLanguage.Hebrew); AssetDatabase.CreateAsset(locale, localeAssetPath); LocalizationEditorSettings.AddLocale(locale, false); Assert.That(LocalizationEditorSettings.GetLocales(), Does.Contain(locale), "Expected new locale asset to be added to Project Locales."); LocalizationEditorSettings.RemoveLocale(locale, true); Assert.That(LocalizationEditorSettings.GetLocales(), Does.Not.Contains(locale), "Expected locale to not be in project locales after calling RemoveLocale."); Undo.PerformUndo(); Assert.That(LocalizationEditorSettings.GetLocales(), Does.Contain(locale), "Expected locale asset to be in project locale after calling Undo."); AssetDatabase.DeleteAsset(localeAssetPath); }
public static void AddLocaleMappings(IList <CsvColumns> cells, bool includeComments = true) { var projectLocales = LocalizationEditorSettings.GetLocales(); foreach (var locale in projectLocales) { // The locale is already mapped so we can ignore it if (cells.Any(c => c is LocaleColumns lc && lc.LocaleIdentifier == locale.Identifier)) { continue; } cells.Add(new LocaleColumns { LocaleIdentifier = locale.Identifier, IncludeComments = includeComments }); } }
/// <summary> /// Creates a <see cref="LocaleColumn"/> for any project <see cref="Locale"/> that is missing from the columns. /// </summary> /// <param name="columns">The existing column that will also be appeneded with any missing <see cref="Locale"/>'s</param> public static void AddLocaleMappings(IList <SheetColumn> columns) { var projectLocales = LocalizationEditorSettings.GetLocales(); foreach (var locale in projectLocales) { // The locale is already mapped so we can ignore it if (columns.Any(c => c is LocaleColumn lc && lc.LocaleIdentifier == locale.Identifier)) { continue; } columns.Add(new LocaleColumn { LocaleIdentifier = locale.Identifier, Column = locale.Identifier.Code }); } }
protected override void DrawTableEntryDetails(ref Rect rowPosition, Data data, Rect position) { base.DrawTableEntryDetails(ref rowPosition, data, position); var projectLocales = LocalizationEditorSettings.GetLocales(); foreach (var locale in projectLocales) { var table = data.SelectedTableCollection.Tables.FirstOrDefault(tbl => tbl.asset?.LocaleIdentifier == locale.Identifier).asset as AssetTable; if (table != null) { var tableEntry = table.GetEntry(data.SelectedTableEntry.Id); Object asset = null; if (tableEntry != null && !tableEntry.IsEmpty) { asset = AssetDatabase.LoadAssetAtPath <Object>(AssetDatabase.GUIDToAssetPath(tableEntry.Guid)); } EditorGUI.BeginChangeCheck(); var newAsset = EditorGUI.ObjectField(rowPosition, locale.Identifier.ToString(), asset, data.assetType, false); if (EditorGUI.EndChangeCheck()) { if (newAsset != null) { data.SelectedTableCollection.AddAssetToTable(table, data.SelectedTableEntry.Id, newAsset, true); } else { data.SelectedTableCollection.RemoveAssetFromTable(table, data.SelectedTableEntry.Id, true); } } } else { var buttonPosition = EditorGUI.PrefixLabel(rowPosition, new GUIContent(locale.Identifier.ToString())); if (GUI.Button(buttonPosition, "Create Table")) { data.SelectedTableCollection.AddNewTable(locale.Identifier); GUIUtility.ExitGUI(); } } rowPosition.y += rowPosition.height + EditorGUIUtility.standardVerticalSpacing; } }
/// <summary> /// Creates a <see cref="LocaleColumn"/> for any project <see cref="Locale"/> that is missing from the columns. /// </summary> /// <param name="columns">The existing column that will also be appeneded with any missing <see cref="Locale"/>'s</param> public static void AddLocaleMappings(IList <SheetColumn> columns) { var usedColumnIds = new HashSet <int>(columns.Select(c => c.ColumnIndex)); var projectLocales = LocalizationEditorSettings.GetLocales(); foreach (var locale in projectLocales) { // The locale is already mapped so we can ignore it if (columns.Any(c => c is LocaleColumn lc && lc.LocaleIdentifier == locale.Identifier)) { continue; } columns.Add(new LocaleColumn { LocaleIdentifier = locale.Identifier, Column = GetNextAvailableColumn(usedColumnIds) }); } }
public TableCreator() { var asset = Resources.GetTemplateAsset(nameof(TableCreator)); asset.CloneTree(this); var locales = LocalizationEditorSettings.GetLocales(); m_LocalesList = this.Q <ScrollView>("locales-list"); foreach (var locale in locales) { AddLocaleElement(locale); } m_LocaleHelpBoxContainer = this.Q("locale-help-box-container"); var items = new List <Type> { typeof(StringTableCollection), typeof(AssetTableCollection) }; m_CollectionTypeContainer = this.Q <VisualElement>("table-collection-type-container"); m_CollectionTypePopup = new PopupField <Type>("Type", items, 0) { formatListItemCallback = type => ObjectNames.NicifyVariableName(type.Name), formatSelectedValueCallback = type => ObjectNames.NicifyVariableName(type.Name) }; m_CollectionTypePopup.RegisterValueChangedCallback(it => UpdateCreateButtonState()); m_CollectionTypeContainer.Add(m_CollectionTypePopup); m_CreateTableCollectionButton = this.Q <Button>("create-table-collection-button"); m_CreateTableCollectionButton.clickable.clicked += CreateCollection; this.Q <Button>("select-all-button").clickable.clicked += () => SelectAllLocales(true); this.Q <Button>("select-none-button").clickable.clicked += () => SelectAllLocales(false); this.Q <Button>("locale-generator-button").clickable.clicked += () => LocaleGeneratorWindow.ShowWindow(); m_TableCollectionName = this.Q <TextField>("new-table-name-field"); m_TableCollectionName.RegisterValueChangedCallback(it => UpdateCreateButtonState()); InitializeTableName(); m_TableNameHelpBoxContainer = this.Q("table-name-help-box-container"); LocalizationEditorSettings.EditorEvents.LocaleAdded += OnLocaleAdded; LocalizationEditorSettings.EditorEvents.LocaleRemoved += OnLocaleRemoved; }
void CheckListViewContainsProjectLocales() { var rows = m_PropertyDrawer.ListView.GetRows(); var locales = LocalizationEditorSettings.GetLocales().ToList(); foreach (var item in rows) { var localeItem = item as SerializedLocaleItem; Assert.That(localeItem, Is.Not.Null); // Check the locale exists in the project Assert.That(locales, Does.Contain(localeItem.Reference), "Expected the Locale in the ListView to be in the project but it was not."); // Remove the locale, it should only be in the list once and we want to know whats leftover. locales.Remove(localeItem.Reference); } Assert.That(locales, Is.Empty, "Expected all project locales to be in the ListView but they were not."); }
protected override TreeViewItem BuildRoot() { var root = new TreeViewItem(-1, -1, "root"); var items = new List <TreeViewItem>(); foreach (var l in LocalizationEditorSettings.GetLocales()) { items.Add(new SerializedLocaleItem(l)); } ApplySorting(items); for (int i = 0; i < items.Count; ++i) { items[i].id = i; } SetupParentsAndChildrenFromDepths(root, items); return(root); }
/// <summary> /// Updates the Xcode project file with localized values using <see cref="AppInfo"/>. /// </summary> /// <param name="projectDirectory">The root project directory to be updated. This is where the iOS player was built to.</param> /// <param name="appInfo">Contains the localized values for the App.</param> public static void AddLocalizationToXcodeProject(string projectDirectory, AppInfo appInfo) { if (appInfo == null) { throw new ArgumentNullException(nameof(appInfo)); } var pbxPath = PBXProject.GetPBXProjectPath(projectDirectory); var project = new PBXProject(); project.ReadFromFile(pbxPath); project.ClearKnownRegions(); // Remove the deprecated regions that get added automatically. var plistDocument = new PlistDocument(); var plistPath = Path.Combine(projectDirectory, "Info.plist"); plistDocument.ReadFromFile(plistPath); var bundleLanguages = plistDocument.root.CreateArray("CFBundleLocalizations"); foreach (var locale in LocalizationEditorSettings.GetLocales()) { var code = locale.Identifier.Code.Replace("-", "_"); project.AddKnownRegion(code); bundleLanguages.AddString(code); var localeDir = code + ".lproj"; var dir = Path.Combine(projectDirectory, localeDir); Directory.CreateDirectory(dir); var filePath = Path.Combine(dir, kInfoFile); var relativePath = Path.Combine(localeDir, kInfoFile); GenerateLocalizedInfoPlistFile(locale, appInfo, plistDocument, filePath); project.AddLocaleVariantFile(kInfoFile, code, relativePath); } plistDocument.WriteToFile(plistPath); project.WriteToFile(pbxPath); }
static void ShowAddItemMenu(ReorderableList list, int index) { var menu = new GenericMenu(); var hashSet = new HashSet <string>(); for (int i = 0; i < list.ListProperty.arraySize; ++i) { var element = list.ListProperty.GetArrayElementAtIndex(i); var codeProperty = element.FindPropertyRelative("localeIdentifier.m_Code"); hashSet.Add(codeProperty.stringValue); } var locales = LocalizationEditorSettings.GetLocales(); foreach (var locale in locales) { if (!hashSet.Contains(locale.Identifier.Code)) { menu.AddItem(new GUIContent(locale.ToString()), false, () => { var element = list.ListProperty.InsertArrayElement(index); var codeProperty = element.FindPropertyRelative("localeIdentifier.m_Code"); codeProperty.stringValue = locale.Identifier.Code; list.ListProperty.serializedObject.ApplyModifiedProperties(); list.RefreshList(); }); } else { menu.AddDisabledItem(new GUIContent(locale.ToString()), true); } } menu.ShowAsContext(); }
internal void ExportSelectedLocales(string path) { try { // Generate the locale assets EditorUtility.DisplayProgressBar(Texts.progressTitle, "Creating Locale Objects", 0); var localeDict = new Dictionary <LocaleIdentifier, Locale>(); // Used for quick look up of parents var locales = new List <Locale>(); var selectedIdentifiers = m_ListView.GetSelectedLocales(); foreach (var selectedIdentifier in selectedIdentifiers) { var locale = CreateInstance <Locale>(); locale.Identifier = selectedIdentifier; locale.name = selectedIdentifier.CultureInfo.EnglishName; locales.Add(locale); localeDict[selectedIdentifier] = locale; } // When checking for fallbacks we also need to take into account the existing locales var allLocales = new List <Locale>(locales); allLocales.AddRange(LocalizationEditorSettings.GetLocales()); // Set up fallbacks foreach (var locale in allLocales) { // A custom locale may not have a CultureInfo. var cultureInfo = locale.Identifier.CultureInfo; if (cultureInfo == null) { continue; } var localeParentCultureInfo = locale.Identifier.CultureInfo.Parent; Locale foundParent = null; while (localeParentCultureInfo != CultureInfo.InvariantCulture && foundParent == null) { localeDict.TryGetValue(localeParentCultureInfo.Name, out foundParent); localeParentCultureInfo = localeParentCultureInfo.Parent; } if (foundParent != null) { locale.Metadata.AddMetadata(new FallbackLocale(foundParent)); EditorUtility.SetDirty(locale); } } // Export the assets AssetDatabase.StartAssetEditing(); // Batch the assets into a single asset operation var relativePath = MakePathRelative(path); for (int i = 0; i < locales.Count; ++i) { var locale = locales[i]; EditorUtility.DisplayProgressBar(Texts.progressTitle, "Creating Asset " + locale.name, i / (float)locales.Count); var assetPath = Path.Combine(relativePath, $"{locale.name} ({locale.Identifier.Code}).asset"); assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath); AssetDatabase.CreateAsset(locale, assetPath); } AssetDatabase.StopAssetEditing(); Close(); } finally { EditorUtility.ClearProgressBar(); } }
public void OnEnable() { m_root = rootVisualElement; // Import UXML var asset = Resources.GetTemplateAsset(nameof(CustomLocaleUIWindow)); asset.CloneTree(m_root); var localeName = m_root.Q <TextField>("customLocaleUI_localeName"); var localeIdentifier = m_root.Q <TextField>("customLocaleUI_localeIdentifier"); localeIdentifier.RegisterValueChangedCallback(evt => { var enableCreateBTN = true; var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); var locales = LocalizationEditorSettings.GetLocales(); var localeIDName = localeIdentifier.value; foreach (var culture in cultures) { if (culture.Name == localeIdentifier.value && localeIdentifier.value.Length > 1) { enableCreateBTN = false; localeIDName = culture.ToString(); } } for (int i = 0; i < (int)SystemLanguage.Unknown; ++i) { var localeID = new LocaleIdentifier((SystemLanguage)i); if (localeID.Code == localeIdentifier.value && localeIdentifier.value.Length > 1) { enableCreateBTN = false; localeIDName = localeID.ToString(); } } foreach (var availableLocale in locales) { if (availableLocale.Identifier.Code == localeIdentifier.value && localeIdentifier.value.Length > 1) { enableCreateBTN = false; localeIDName = availableLocale.name; } } m_createButton.SetEnabled(enableCreateBTN); if (!enableCreateBTN) { if (m_helpBox == null) { AddHelpInfo(localeIDName); } } else { if (m_helpBox != null) { m_root.Remove(m_helpBox); m_helpBox = null; } } }); m_createButton = m_root.Q <Button>("customLocaleUI_CreateBTN"); m_createButton.clickable.clicked += () => { CreateCustomLocale(localeName.value, localeIdentifier.value); }; }
internal static void ExportSelectedLocales(string path, List <LocaleIdentifier> selectedIdentifiers) { try { // Generate the locale assets EditorUtility.DisplayProgressBar(Texts.progressTitle, "Creating Locale Objects", 0); var localeDict = new Dictionary <LocaleIdentifier, Locale>(); // Used for quick look up of parents var locales = new List <Locale>(); foreach (var selectedIdentifier in selectedIdentifiers) { var locale = CreateInstance <Locale>(); locale.Identifier = selectedIdentifier; locale.name = selectedIdentifier.CultureInfo.EnglishName; locales.Add(locale); localeDict[selectedIdentifier] = locale; } // When checking for fallbacks we also need to take into account the existing locales var allLocales = new List <Locale>(locales); allLocales.AddRange(LocalizationEditorSettings.GetLocales()); // Set up fallbacks foreach (var locale in allLocales) { // A custom locale may not have a CultureInfo. var cultureInfo = locale.Identifier.CultureInfo; if (cultureInfo == null) { continue; } var localeParentCultureInfo = locale.Identifier.CultureInfo.Parent; Locale foundParent = null; while (localeParentCultureInfo != CultureInfo.InvariantCulture && foundParent == null) { localeDict.TryGetValue(localeParentCultureInfo.Name, out foundParent); localeParentCultureInfo = localeParentCultureInfo.Parent; } if (foundParent != null) { locale.Metadata.AddMetadata(new FallbackLocale(foundParent)); EditorUtility.SetDirty(locale); } } // Export the assets AssetDatabase.StartAssetEditing(); // Batch the assets into a single asset operation var relativePath = PathHelper.MakePathRelative(path); for (int i = 0; i < locales.Count; ++i) { var locale = locales[i]; EditorUtility.DisplayProgressBar(Texts.progressTitle, "Creating Asset " + locale.name, i / (float)locales.Count); var assetPath = Path.Combine(relativePath, $"{locale.name} ({locale.Identifier.Code}).asset"); assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath); AssetDatabase.CreateAsset(locale, assetPath); } AssetDatabase.StopAssetEditing(); // Import the Locales now instead of waiting for them to be imported via the asset post processor. // If we wait for them to be imported during the asset post processor then they will not be available // until all current assets have been imported. // This can cause duplicate Locales to be created when importing multiple tables with missing Locales. locales.ForEach(l => LocalizationEditorSettings.AddLocale(l)); } finally { EditorUtility.ClearProgressBar(); } }