From 8ec484fc1738d3f3aa41839a7d1e941a7615bbd8 Mon Sep 17 00:00:00 2001 From: Kay Unkroth Date: Wed, 24 Feb 2021 17:34:03 -0800 Subject: [PATCH] Adding the Metadata Translation project. --- MetadataTranslator/Metadata Translator.sln | 25 + .../Metadata Translator/App.config | 13 + .../Metadata Translator/App.xaml | 13 + .../Metadata Translator/App.xaml.cs | 22 + .../CollectionEmptyTrueFalseConverter.cs | 26 + .../Converters/PercentageConverter.cs | 27 + .../TranslationPropertyToolTipConverter.cs | 36 + .../TrueFalseVisibilityConverter.cs | 37 + .../Metadata Translator/Data/CsvRow.cs | 15 + .../Metadata Translator/Data/DataModel.cs | 420 +++ .../Data/DisplayFolderContainer.cs | 28 + .../Data/MetadataObjectContainer.cs | 35 + .../Helpers/ExpandoObjectExtensions.cs | 60 + .../Metadata Translator/Helpers/Hourglass.cs | 25 + .../Helpers/ListExtensions.cs | 30 + .../Helpers/ObservableCollectionExtensions.cs | 95 + .../Helpers/StringExtensions.cs | 21 + .../Metadata Translator.csproj | 185 ++ .../Properties/AssemblyInfo.cs | 55 + .../Properties/Resources.Designer.cs | 70 + .../Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 29 + .../Properties/Settings.settings | 7 + .../Resources/StringDictionary.xaml | 43 + .../Resources/supportedlanguages.json | 2361 +++++++++++++++++ .../Translations/Language.cs | 42 + .../Translations/Translation.cs | 14 + .../Translations/TranslationGroup.cs | 28 + .../Translations/TranslationResult.cs | 13 + .../Translations/TranslatorService.cs | 163 ++ .../UI/ImportExportPanel.xaml | 90 + .../UI/ImportExportPanel.xaml.cs | 95 + .../Metadata Translator/UI/LanguagePanel.xaml | 89 + .../UI/LanguagePanel.xaml.cs | 86 + .../Metadata Translator/UI/MainWindow.xaml | 298 +++ .../Metadata Translator/UI/MainWindow.xaml.cs | 638 +++++ .../Metadata Translator/UI/SettingsPanel.xaml | 117 + .../UI/SettingsPanel.xaml.cs | 66 + .../UI/TranslationGroupPanel.xaml | 37 + .../UI/TranslationGroupPanel.xaml.cs | 54 + .../Metadata Translator/icon.ico | Bin 0 -> 67646 bytes .../metadata-translator.pbitool.json | 8 + .../Metadata Translator/packages.config | 5 + 43 files changed, 5638 insertions(+) create mode 100644 MetadataTranslator/Metadata Translator.sln create mode 100644 MetadataTranslator/Metadata Translator/App.config create mode 100644 MetadataTranslator/Metadata Translator/App.xaml create mode 100644 MetadataTranslator/Metadata Translator/App.xaml.cs create mode 100644 MetadataTranslator/Metadata Translator/Converters/CollectionEmptyTrueFalseConverter.cs create mode 100644 MetadataTranslator/Metadata Translator/Converters/PercentageConverter.cs create mode 100644 MetadataTranslator/Metadata Translator/Converters/TranslationPropertyToolTipConverter.cs create mode 100644 MetadataTranslator/Metadata Translator/Converters/TrueFalseVisibilityConverter.cs create mode 100644 MetadataTranslator/Metadata Translator/Data/CsvRow.cs create mode 100644 MetadataTranslator/Metadata Translator/Data/DataModel.cs create mode 100644 MetadataTranslator/Metadata Translator/Data/DisplayFolderContainer.cs create mode 100644 MetadataTranslator/Metadata Translator/Data/MetadataObjectContainer.cs create mode 100644 MetadataTranslator/Metadata Translator/Helpers/ExpandoObjectExtensions.cs create mode 100644 MetadataTranslator/Metadata Translator/Helpers/Hourglass.cs create mode 100644 MetadataTranslator/Metadata Translator/Helpers/ListExtensions.cs create mode 100644 MetadataTranslator/Metadata Translator/Helpers/ObservableCollectionExtensions.cs create mode 100644 MetadataTranslator/Metadata Translator/Helpers/StringExtensions.cs create mode 100644 MetadataTranslator/Metadata Translator/Metadata Translator.csproj create mode 100644 MetadataTranslator/Metadata Translator/Properties/AssemblyInfo.cs create mode 100644 MetadataTranslator/Metadata Translator/Properties/Resources.Designer.cs create mode 100644 MetadataTranslator/Metadata Translator/Properties/Resources.resx create mode 100644 MetadataTranslator/Metadata Translator/Properties/Settings.Designer.cs create mode 100644 MetadataTranslator/Metadata Translator/Properties/Settings.settings create mode 100644 MetadataTranslator/Metadata Translator/Resources/StringDictionary.xaml create mode 100644 MetadataTranslator/Metadata Translator/Resources/supportedlanguages.json create mode 100644 MetadataTranslator/Metadata Translator/Translations/Language.cs create mode 100644 MetadataTranslator/Metadata Translator/Translations/Translation.cs create mode 100644 MetadataTranslator/Metadata Translator/Translations/TranslationGroup.cs create mode 100644 MetadataTranslator/Metadata Translator/Translations/TranslationResult.cs create mode 100644 MetadataTranslator/Metadata Translator/Translations/TranslatorService.cs create mode 100644 MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml create mode 100644 MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml.cs create mode 100644 MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml create mode 100644 MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml.cs create mode 100644 MetadataTranslator/Metadata Translator/UI/MainWindow.xaml create mode 100644 MetadataTranslator/Metadata Translator/UI/MainWindow.xaml.cs create mode 100644 MetadataTranslator/Metadata Translator/UI/SettingsPanel.xaml create mode 100644 MetadataTranslator/Metadata Translator/UI/SettingsPanel.xaml.cs create mode 100644 MetadataTranslator/Metadata Translator/UI/TranslationGroupPanel.xaml create mode 100644 MetadataTranslator/Metadata Translator/UI/TranslationGroupPanel.xaml.cs create mode 100644 MetadataTranslator/Metadata Translator/icon.ico create mode 100644 MetadataTranslator/Metadata Translator/metadata-translator.pbitool.json create mode 100644 MetadataTranslator/Metadata Translator/packages.config diff --git a/MetadataTranslator/Metadata Translator.sln b/MetadataTranslator/Metadata Translator.sln new file mode 100644 index 0000000..2c72677 --- /dev/null +++ b/MetadataTranslator/Metadata Translator.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Metadata Translator", "Metadata Translator\Metadata Translator.csproj", "{CB7D493C-B67E-4438-B304-EFE5D418ADDF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB7D493C-B67E-4438-B304-EFE5D418ADDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB7D493C-B67E-4438-B304-EFE5D418ADDF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB7D493C-B67E-4438-B304-EFE5D418ADDF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB7D493C-B67E-4438-B304-EFE5D418ADDF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0733CEFA-6458-4D8F-B498-FC9827973880} + EndGlobalSection +EndGlobal diff --git a/MetadataTranslator/Metadata Translator/App.config b/MetadataTranslator/Metadata Translator/App.config new file mode 100644 index 0000000..1e2eb3b --- /dev/null +++ b/MetadataTranslator/Metadata Translator/App.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/App.xaml b/MetadataTranslator/Metadata Translator/App.xaml new file mode 100644 index 0000000..bd6ce63 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/App.xaml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/MetadataTranslator/Metadata Translator/App.xaml.cs b/MetadataTranslator/Metadata Translator/App.xaml.cs new file mode 100644 index 0000000..268b902 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/App.xaml.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace Metadata_Translator +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + private void Application_Startup(object sender, StartupEventArgs e) + { + MainWindow mainWindow = new MainWindow(e); + mainWindow.Show(); + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Converters/CollectionEmptyTrueFalseConverter.cs b/MetadataTranslator/Metadata Translator/Converters/CollectionEmptyTrueFalseConverter.cs new file mode 100644 index 0000000..343e0ec --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Converters/CollectionEmptyTrueFalseConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace Metadata_Translator +{ + public class CollectionEmptyTrueFalseConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (value is ObservableCollection collection) ? collection.Count > 0 : false; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + +} diff --git a/MetadataTranslator/Metadata Translator/Converters/PercentageConverter.cs b/MetadataTranslator/Metadata Translator/Converters/PercentageConverter.cs new file mode 100644 index 0000000..ea8804f --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Converters/PercentageConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace Metadata_Translator +{ + public class PercentageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Double size) + { + return size * 0.5; + } + return value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Converters/TranslationPropertyToolTipConverter.cs b/MetadataTranslator/Metadata Translator/Converters/TranslationPropertyToolTipConverter.cs new file mode 100644 index 0000000..d324b5c --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Converters/TranslationPropertyToolTipConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace Metadata_Translator +{ + public class TranslationPropertyToolTipConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(parameter is string toolTipString) + { + string[] toolTip = toolTipString.Split('|'); + if (value is ObservableCollection collection && toolTip?.Length == 2) + { + return collection.Count > 0 ? toolTip[0] : toolTip[1]; + } + else + return toolTipString; + } + + return string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Converters/TrueFalseVisibilityConverter.cs b/MetadataTranslator/Metadata Translator/Converters/TrueFalseVisibilityConverter.cs new file mode 100644 index 0000000..55a6015 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Converters/TrueFalseVisibilityConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace Metadata_Translator +{ + class TrueFalseVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (System.Convert.ToBoolean(value))? Visibility.Collapsed : Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + + class FalseTrueVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (System.Convert.ToBoolean(value)) ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Data/CsvRow.cs b/MetadataTranslator/Metadata Translator/Data/CsvRow.cs new file mode 100644 index 0000000..1559136 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Data/CsvRow.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public class CsvRow + { + public string Type { get; set; } + public string Original { get; set; } + public string Translation { get; set; } + } +} diff --git a/MetadataTranslator/Metadata Translator/Data/DataModel.cs b/MetadataTranslator/Metadata Translator/Data/DataModel.cs new file mode 100644 index 0000000..af360dd --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Data/DataModel.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AS = Microsoft.AnalysisServices; +using Microsoft.AnalysisServices.Tabular; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Dynamic; +using System.Windows.Controls; +using System.Windows.Data; +using System.IO; +using System.Web.Script.Serialization; +using Microsoft.VisualBasic.FileIO; + +namespace Metadata_Translator +{ + public class DataModel + { + Model Model { get; set; } + + public string ContainerColumnHeader { get => "Object"; } + public ObservableCollection Captions { get; private set; } + public ObservableCollection Descriptions { get; private set; } + public ObservableCollection DisplayFolders { get; private set; } + + public string DefaultCulture { get; set; } + + public List CultureNames + { + get + { + List cultures = new List { Model.Culture }; + cultures.AddRange(Model?.Cultures.Where(i => !i.Name.Equals(Model.Culture)).Select(x => x.Name).ToList()); + return cultures; + } + } + public List SupportedLanguages { get; private set; } + public List SelectedLanguages { get => SupportedLanguages?.Where(x => x.IsSelected==true).ToList(); } + public bool HasTargetLanguages { get => SelectedLanguages?.Count > 1; } + + public DataModel(string server, string database) + { + LoadLanguages(); + + Server pbiDesktop = new Server(); + pbiDesktop.Connect($"Data Source={server}"); + Database dataset = pbiDesktop.Databases.GetByName(database); + Model = dataset.Model; + + DefaultCulture = Model.Culture; + + LoadNamedObjectCollections(); + } + + /// + /// A static helper to get the DataModel object. + /// + /// + /// + /// + public static DataModel Connect(string server, string database) + { + return new DataModel(server, database); + } + + /// + /// Gets the tables from the dataset and within it all the columns, measures, and hierarchies + /// and adds these tabular objects to the collections for captions, descriptions, and display folders. + /// + private void LoadNamedObjectCollections() + { + Captions = new ObservableCollection(); + Descriptions = new ObservableCollection(); + DisplayFolders = new ObservableCollection(); + + CultureCollection cultures = Model.Cultures; + + Captions.Add(CreateRow(new MetadataObjectContainer(Model, TranslatedProperty.Caption), Model.Name, DefaultCulture, cultures)); + if (!string.IsNullOrEmpty(Model.Description)) + Descriptions.Add(CreateRow(new MetadataObjectContainer(Model, TranslatedProperty.Description), Model.Description, DefaultCulture, cultures)); + + foreach (Table table in Model.Tables) + { + Captions.Add(CreateRow(new MetadataObjectContainer(table, TranslatedProperty.Caption), table.Name, DefaultCulture, cultures)); + if (!string.IsNullOrEmpty(table.Description)) + Descriptions.Add(CreateRow(new MetadataObjectContainer(table, TranslatedProperty.Description), table.Description, DefaultCulture, cultures)); + foreach (Column column in table.Columns) + { + if (column.Type != ColumnType.RowNumber) + { + Captions.Add(CreateRow(new MetadataObjectContainer(column, TranslatedProperty.Caption), column.Name, DefaultCulture, cultures)); + + if (!string.IsNullOrEmpty(column.Description)) + Descriptions.Add(CreateRow(new MetadataObjectContainer(column, TranslatedProperty.Description), column.Description, DefaultCulture, cultures)); + if (!string.IsNullOrEmpty(column.DisplayFolder)) + DisplayFolders.AddDisplayFolder(column, column.DisplayFolder, DefaultCulture, cultures); + } + } + + foreach (Measure measure in table.Measures) + { + Captions.Add(CreateRow(new MetadataObjectContainer(measure, TranslatedProperty.Caption), measure.Name, DefaultCulture, cultures)); + + if (!string.IsNullOrEmpty(measure.Description)) + Descriptions.Add(CreateRow(new MetadataObjectContainer(measure, TranslatedProperty.Description), measure.Description, DefaultCulture, cultures)); + if (!string.IsNullOrEmpty(measure.DisplayFolder)) + DisplayFolders.AddDisplayFolder(measure, measure.DisplayFolder, DefaultCulture, cultures); + } + + foreach (Hierarchy hierarchy in table.Hierarchies) + { + Captions.Add(CreateRow(new MetadataObjectContainer(hierarchy, TranslatedProperty.Caption), hierarchy.Name, DefaultCulture, cultures)); + + if (!string.IsNullOrEmpty(hierarchy.Description)) + Descriptions.Add(CreateRow(new MetadataObjectContainer(hierarchy, TranslatedProperty.Description), hierarchy.Description, DefaultCulture, cultures)); + if (!string.IsNullOrEmpty(hierarchy.DisplayFolder)) + DisplayFolders.AddDisplayFolder(hierarchy, hierarchy.DisplayFolder, DefaultCulture, cultures); + } + } + } + + /// + /// Loads the list of supported languages from the supportedlanguages.json file. + /// + private void LoadLanguages() + { + SupportedLanguages = new List(); + string content = File.ReadAllText($"{System.AppDomain.CurrentDomain.BaseDirectory}Resources\\supportedlanguages.json"); + foreach (Language lang in new JavaScriptSerializer().Deserialize>(content)) + { + SupportedLanguages.Add(lang); + } + } + + /// + /// Creates a new ExpandoObject for a source string (displayString). + /// + /// + /// + /// + /// + /// An ExpandoObject representing a data row. + public ExpandoObject CreateRow(MetadataObjectContainer objectContainer, string displayString, string defaultCulture, CultureCollection cultures) + { + dynamic row = new ExpandoObject(); + + ((IDictionary)row)[ContainerColumnHeader] = objectContainer; + foreach (var culture in cultures) + { + ((IDictionary)row)[culture.Name] = culture.Name.Equals(defaultCulture) ? displayString : + culture.ObjectTranslations[objectContainer.TabularObject, objectContainer.TranslatedProperty]?.Value; + } + + return row; + } + + /// + /// Combine all collections for translation and updating. + /// + /// + public List GetAllDataRows() + { + var allRows = new List(); + foreach (var item in Captions) allRows.Add(item); + foreach (var item in Descriptions) allRows.Add(item); + foreach (var item in DisplayFolders) allRows.Add(item); + return allRows; + } + + /// + /// Adds a translation to a Tabular metadata object. + /// + /// + /// + /// + private void SetTranslation(Culture culture, MetadataObjectContainer metadataObjectContainer, string translation) + { + culture.ObjectTranslations.SetTranslation( + metadataObjectContainer.TabularObject, metadataObjectContainer.TranslatedProperty, + translation); + } + + /// + /// Updates the Power BI dataset with the translations from the ExpandoObject collections and saves the changes. + /// + public void Update() + { + /// Delete any deselected cultures that still exist in the dataset. + /// + List cultureNames = SelectedLanguages?.Select(sl => sl.LanguageTag)?.ToList(); + + /// There must be at least the default culture in the cultureNames. + /// + if (cultureNames == null || cultureNames.Count < 1) return; + + var culturesToRemove = CultureNames.Where(cn1 => !cultureNames.Any(cn2 => cn2.Equals(cn1))).ToList(); + culturesToRemove.Remove(DefaultCulture); + + foreach(string cultureName in culturesToRemove) + { + if (Model.Cultures.Contains(cultureName)) + { + Model.Cultures.Remove(cultureName); + } + } + + /// Add any newly selected cultures. + /// + foreach (string cultureName in cultureNames) + { + if (!Model.Cultures.Contains(cultureName)) + { + Model.Cultures.Add(new Culture { Name = cultureName }); + } + } + + /// Add the translations to all the metadata objects. + /// + foreach (ExpandoObject row in GetAllDataRows()) + { + if (((IDictionary)row)[ContainerColumnHeader] is MetadataObjectContainer metadataObjectContainer) + { + /* + * Include this part when updating the default culture (i.e. updating the actual metadata objects) is supported. + * + switch (metadataObjectContainer.TranslatedProperty) + { + case TranslatedProperty.Caption: + metadataObjectContainer.TabularObject.Name = row.GetValue(DefaultCulture); + break; + case TranslatedProperty.Description: + if (metadataObjectContainer.TabularObject is Table table) + { + table.Description = row.GetValue(DefaultCulture); + } + else if (metadataObjectContainer.TabularObject is Column col) + { + col.Description = row.GetValue(DefaultCulture); + } + else if (metadataObjectContainer.TabularObject is Measure measure) + { + measure.Description = row.GetValue(DefaultCulture); + } + else if (metadataObjectContainer.TabularObject is Hierarchy hierarchy) + { + hierarchy.Description = row.GetValue(DefaultCulture); + } + break; + case TranslatedProperty.DisplayFolder: + if (metadataObjectContainer.TabularObject is Column column) + { + column.DisplayFolder = row.GetValue(DefaultCulture); + } + else if (metadataObjectContainer.TabularObject is Measure measure) + { + measure.DisplayFolder = row.GetValue(DefaultCulture); + } + else if (metadataObjectContainer.TabularObject is Hierarchy hierarchy) + { + hierarchy.DisplayFolder = row.GetValue(DefaultCulture); + } + break; + } + */ + foreach (string cultureName in cultureNames) + { + SetTranslation(Model.Cultures[cultureName], + metadataObjectContainer, + row.GetValue(cultureName)); + } + } + } + + /// Save the changes in the dataset. + /// + Model.Database.Update(AS.UpdateOptions.ExpandFull); + } + + /// + /// Exports the translations to individual language files. + /// The files are placed into the specified export folder. + /// + /// + public void ExportToCsv(string exportFolderPath) + { + string separator = ","; + List dataRows = GetAllDataRows(); + if (dataRows != null && dataRows.Count > 0) + { + List languages = SelectedLanguages.Where(l => l.IsModelDefault != true).Select(l => l.LanguageTag).ToList(); + + if (languages != null && languages.Count > 0) + { + foreach (string lcid in languages) + { + StringBuilder csvContent = new StringBuilder(); + csvContent.AppendLine("Type,Original,Translation"); + + foreach (var stringValues in dataRows.GetValues(ContainerColumnHeader, DefaultCulture, lcid)) + { + csvContent.AppendLine( + string.Join( + separator, + new string[] { + stringValues.Type.ToCsvString(), + stringValues.Original.ToCsvString(), + stringValues.Translation.ToCsvString() + }) + ); + } + + using (var sw = File.Create(System.IO.Path.Combine(exportFolderPath, $"{lcid}.csv"))) + { + var preamble = Encoding.UTF8.GetPreamble(); + sw.Write(preamble, 0, preamble.Length); + var data = Encoding.UTF8.GetBytes(csvContent.ToString()); + sw.Write(data, 0, data.Length); + } + } + } + } + } + + /// + /// Imports translations from a csv file. The file name must match the LCID of the target language. + /// + /// + /// + /// + public void ImportFromCsv(string filePath, string lcid, bool replaceExistingTranslations) + { + try + { + string csvData = File.ReadAllText(filePath); + if (string.IsNullOrEmpty(csvData)) return; + + List parsedRows = new List(); + + using (TextFieldParser parser = new TextFieldParser(new StringReader(csvData))) + { + parser.CommentTokens = new string[] { "#" }; + parser.SetDelimiters(new string[] { "," }); + parser.HasFieldsEnclosedInQuotes = true; + + /// Skip the header row. + /// + parser.ReadFields(); + while (!parser.EndOfData) + { + var textFields = parser.ReadFields(); + if (textFields != null && textFields.Count() == 3) + { + parsedRows.Add(new CsvRow + { + Type = textFields[0], + Original = textFields[1], + Translation = textFields[2] + }); + } + } + } + + ApplyTranslation(lcid, parsedRows, replaceExistingTranslations); + } + catch { } + } + + /// + /// Applies a list of translations to the ExpandoObject collections + /// + /// + /// + /// + private void ApplyTranslation(string lcid, List translatedRows, bool replaceExistingTranslations) + { + var allDataRows = GetAllDataRows(); + if(!MatchAllRows(allDataRows, lcid, translatedRows, replaceExistingTranslations)) + { + /// Not all rows matched, so let's do this the slow way + /// matching strings. + /// + foreach(ExpandoObject row in allDataRows) + { + var metaContainer = (MetadataObjectContainer)row.GetObject(ContainerColumnHeader); + var original = row.GetValue(DefaultCulture); + var csvRow = translatedRows.Where(x => x.Type == metaContainer.TranslatedProperty.ToString() && x.Original.Equals(original)).FirstOrDefault(); + if(csvRow != null) + { + row.SetValue(lcid, csvRow.Translation, replaceExistingTranslations); + } + } + } + } + + /// + /// Iterates over the dataRows and applies the translated strings with the assumption that + /// translatedRows matches the dataRows in number and order. + /// + private bool MatchAllRows(List dataRows, string lcid, List translatedRows, bool replaceExistingTranslations) + { + if(dataRows == null || dataRows.Count != translatedRows?.Count) + return false; + + for(int i = 0; i < translatedRows.Count; i++) + { + ExpandoObject row = dataRows[i]; + CsvRow csvRow = translatedRows[i]; + + if (row.GetValue(DefaultCulture) != csvRow.Original) + return false; + + row.SetValue(lcid, csvRow.Translation, replaceExistingTranslations); + } + + return true; + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Data/DisplayFolderContainer.cs b/MetadataTranslator/Metadata Translator/Data/DisplayFolderContainer.cs new file mode 100644 index 0000000..ce03d0a --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Data/DisplayFolderContainer.cs @@ -0,0 +1,28 @@ +using Microsoft.AnalysisServices.Tabular; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public class DisplayFolderContainer : MetadataObjectContainer + { + public override NamedMetadataObject TabularObject { get => TabularObjects.FirstOrDefault(); protected set { } } + public List TabularObjects { get; private set; } + + public DisplayFolderContainer(NamedMetadataObject metadataObject, TranslatedProperty translatedProperty) : base(metadataObject, translatedProperty) + { + TabularObjects = new List(); + TabularObjects.Add(metadataObject); + } + + public override string ToString() + { + return (TabularObjects.Count > 1)? + $"DisplayFolder - {TabularObjects.Count} Objects" : + $"DisplayFolder - 1 {TabularObject.ObjectType}"; + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Data/MetadataObjectContainer.cs b/MetadataTranslator/Metadata Translator/Data/MetadataObjectContainer.cs new file mode 100644 index 0000000..316029d --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Data/MetadataObjectContainer.cs @@ -0,0 +1,35 @@ +using Microsoft.AnalysisServices.Tabular; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public class MetadataObjectContainer + { + public virtual NamedMetadataObject TabularObject { get; protected set; } + public TranslatedProperty TranslatedProperty { get; protected set; } + public MetadataObjectContainer(NamedMetadataObject metadataObject, TranslatedProperty translatedProperty) + { + TabularObject = metadataObject; + TranslatedProperty = translatedProperty; + } + + public override string ToString() + { + switch(TranslatedProperty) + { + case TranslatedProperty.Caption: + return $"{TabularObject.ObjectType} - Caption"; + case TranslatedProperty.Description: + return $"{TabularObject.ObjectType} - Description"; + case TranslatedProperty.DisplayFolder: + return $"{TabularObject.ObjectType} - DisplayFolder"; + default: + return TabularObject.ObjectType.ToString(); + } + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Helpers/ExpandoObjectExtensions.cs b/MetadataTranslator/Metadata Translator/Helpers/ExpandoObjectExtensions.cs new file mode 100644 index 0000000..5761539 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Helpers/ExpandoObjectExtensions.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public static class ExpandoObjectExtensions + { + public static string SeparateCamelCase(this ExpandoObject expando, string columnName) + { + if (expando != null && ((IDictionary)expando)[columnName] is string text) + { + char separator = ' '; + char lastChar = separator; + + var sb = new StringBuilder(); + foreach (var currentChar in text.Replace("_", "")) + { + if (char.IsUpper(currentChar) && lastChar != separator) + sb.Append(separator); + + sb.Append(currentChar); + + lastChar = currentChar; + } + + return sb.ToString(); + } + return string.Empty; + } + + public static string GetValue(this ExpandoObject expando, string columnName) + { + return expando.GetObject(columnName)?.ToString(); + } + + public static object GetObject(this ExpandoObject expando, string columnName) + { + if (expando != null && ((IDictionary)expando).ContainsKey(columnName)) + { + return ((IDictionary)expando)[columnName]; + } + return null; + } + + public static void SetValue(this ExpandoObject expando, string columnName, string value, bool overwrite) + { + if (expando != null) + { + if (overwrite || string.IsNullOrEmpty(expando.GetValue(columnName))) + { + ((IDictionary)expando)[columnName] = value; + } + } + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Helpers/Hourglass.cs b/MetadataTranslator/Metadata Translator/Helpers/Hourglass.cs new file mode 100644 index 0000000..7874d25 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Helpers/Hourglass.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace Metadata_Translator +{ + public class Hourglass : IDisposable + { + private Cursor previousCursor; + + public Hourglass() + { + previousCursor = Mouse.OverrideCursor; + Mouse.OverrideCursor = Cursors.Wait; + } + + public void Dispose() + { + Mouse.OverrideCursor = previousCursor; + } + } +} \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Helpers/ListExtensions.cs b/MetadataTranslator/Metadata Translator/Helpers/ListExtensions.cs new file mode 100644 index 0000000..7190aeb --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Helpers/ListExtensions.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public static class ListExtensions + { + public static List GetValues(this List collection, string containerColumnName, string referenceColumnName, string columnName) + { + if (collection == null) return new List(); + + var values = new List(); + foreach (ExpandoObject row in collection) + { + var metaContainer = (MetadataObjectContainer)row.GetObject(containerColumnName); + string refValue = row.GetValue(referenceColumnName); + + if (!string.IsNullOrEmpty(refValue)) + values.Add(new CsvRow { Type = metaContainer.TranslatedProperty.ToString(), Original = refValue, Translation = row.GetValue(columnName) }); + } + return values; + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Helpers/ObservableCollectionExtensions.cs b/MetadataTranslator/Metadata Translator/Helpers/ObservableCollectionExtensions.cs new file mode 100644 index 0000000..88ca394 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Helpers/ObservableCollectionExtensions.cs @@ -0,0 +1,95 @@ +using Microsoft.AnalysisServices.Tabular; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public static class ObservableCollectionExtensions + { + public static string GetValueAt(this ObservableCollection collection, int index, string columnName) + { + if (collection == null) return string.Empty; + + ExpandoObject row = collection[index]; + return ((IDictionary)row)[columnName]?.ToString(); + } + + public static void SetValueAt(this ObservableCollection collection, int index, string columnName, string value) + { + if (collection == null) return; + + ExpandoObject row = collection[index]; + ((IDictionary)row)[columnName] = value; + } + + public static void UpdateDataValues(this ObservableCollection collection, List sourcePhrases, string sourceLanguage, List targetPhrases, string targetLanguage) + { + if (collection == null) return; + int rowOffset = 0; + for (int i = 0; i < collection.Count; i++) + { + string columnValue = collection.GetValueAt(i, sourceLanguage); + if (!string.IsNullOrEmpty(columnValue) && columnValue.Equals(sourcePhrases[i - rowOffset])) + { + collection.SetValueAt(i, targetLanguage, targetPhrases[i - rowOffset]); + } + else + { + rowOffset++; + } + } + } + + public static List GetValues(this ObservableCollection collection, string columnName) + { + if (collection == null) return new List(); + + List values = new List(); + foreach (ExpandoObject row in collection) + { + string value = ((IDictionary)row)[columnName]?.ToString(); + + if (!string.IsNullOrEmpty(value)) + values.Add(value); + } + return values; + } + + public static void AddDisplayFolder(this ObservableCollection collection, NamedMetadataObject metadataObject, string displayString, string defaultCulture, CultureCollection cultures) + { + if (collection == null) return; + + if (!string.IsNullOrEmpty(displayString)) + { + foreach (ExpandoObject item in collection) + { + if (((IDictionary)item)[defaultCulture] is string displayName && displayName.Equals(displayString)) + { + var existingDisplayFolderContainer = ((IDictionary)item)["Object"] as DisplayFolderContainer; + existingDisplayFolderContainer.TabularObjects.Add(metadataObject); + return; + } + } + } + + dynamic row = new ExpandoObject(); + var displayFolderContainer = new DisplayFolderContainer(metadataObject, TranslatedProperty.DisplayFolder); + + ((IDictionary)row)["Object"] = displayFolderContainer; + foreach (var culture in cultures) + { + ((IDictionary)row)[culture.Name] = culture.Name.Equals(defaultCulture) ? displayString : + culture.ObjectTranslations[displayFolderContainer.TabularObject, displayFolderContainer.TranslatedProperty]?.Value; + } + + collection.Add(row); + } + + } +} diff --git a/MetadataTranslator/Metadata Translator/Helpers/StringExtensions.cs b/MetadataTranslator/Metadata Translator/Helpers/StringExtensions.cs new file mode 100644 index 0000000..794b895 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Helpers/StringExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.AnalysisServices.Tabular; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public static class StringExtensions + { + public static string ToCsvString(this string value) + { + if (value == null) return string.Empty; + return value.Contains("\"") ? $"\"{value.Replace("\"", "\"\"")}\"" : value; + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Metadata Translator.csproj b/MetadataTranslator/Metadata Translator/Metadata Translator.csproj new file mode 100644 index 0000000..29a02c3 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Metadata Translator.csproj @@ -0,0 +1,185 @@ + + + + + Debug + AnyCPU + {CB7D493C-B67E-4438-B304-EFE5D418ADDF} + WinExe + Metadata_Translator + Metadata Translator + v4.7.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + icon.ico + + + + ..\packages\Microsoft.AnalysisServices.retail.amd64.19.16.3\lib\net45\Microsoft.AnalysisServices.dll + + + ..\packages\Microsoft.AnalysisServices.AdomdClient.retail.amd64.19.16.3\lib\net45\Microsoft.AnalysisServices.AdomdClient.dll + + + ..\packages\Microsoft.AnalysisServices.retail.amd64.19.16.3\lib\net45\Microsoft.AnalysisServices.Core.dll + + + ..\packages\Microsoft.AnalysisServices.retail.amd64.19.16.3\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll + + + ..\packages\Microsoft.AnalysisServices.retail.amd64.19.16.3\lib\net45\Microsoft.AnalysisServices.Tabular.dll + + + ..\packages\Microsoft.AnalysisServices.retail.amd64.19.16.3\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll + + + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + + + + ImportExportPanel.xaml + + + + LanguagePanel.xaml + + + + SettingsPanel.xaml + + + + TranslationGroupPanel.xaml + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + PreserveNewest + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Properties/AssemblyInfo.cs b/MetadataTranslator/Metadata Translator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b6c04fd --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Metadata Translator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Metadata Translator")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MetadataTranslator/Metadata Translator/Properties/Resources.Designer.cs b/MetadataTranslator/Metadata Translator/Properties/Resources.Designer.cs new file mode 100644 index 0000000..8fb55e3 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Properties/Resources.Designer.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +namespace Metadata_Translator.Properties +{ + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Metadata_Translator.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Properties/Resources.resx b/MetadataTranslator/Metadata Translator/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Properties/Settings.Designer.cs b/MetadataTranslator/Metadata Translator/Properties/Settings.Designer.cs new file mode 100644 index 0000000..1818ecf --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Properties/Settings.Designer.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +namespace Metadata_Translator.Properties +{ + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Properties/Settings.settings b/MetadataTranslator/Metadata Translator/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Resources/StringDictionary.xaml b/MetadataTranslator/Metadata Translator/Resources/StringDictionary.xaml new file mode 100644 index 0000000..a3acf46 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Resources/StringDictionary.xaml @@ -0,0 +1,43 @@ + + Languages + Opens the Languages pane to choose the target languages. + Settings + Opens the Settings pane to configure Machine Translator. + Translated Property + Caption + Displays the captions of the metadata objects and their translations. + Description + Displays the descriptions of the metadata objects and their translations.|The metadata objects in the current dataset have no descriptions. Use Power BI Desktop to add descriptions. + DisplayFolder + Displays the display folder names of the metadata objects and their translations.|The current dataset does not have display folders. Use Power BI Desktop to group columns and measures in display folders. + Prepare + Translate + Translates the captions, descriptions, and display folder names using Microsoft Translator. + Apply + Applies the translated captions, descriptions, and display folder names to the Power BI dataset. + Import/Export + Opens the Import/Export pane to export and import the captions, descriptions, and display folder names via .csv files. + Metadata Translator cannot start without server and database arguments. Double-check the 'arguments' setting in the metadata-translator.pbitool.json file in your Microsoft Shared\Power BI Desktop\External Tools folder. The value must be: "\"%server%\" \"%database%\"". + Dataset Connection String + Translator Subscription Key + Translator Endpoint + Translator Location + Overwrite Translation + Enter your Microsoft Translator service subscription key. + Enter the Microsoft Translator URL, specifically: https://api.cognitive.microsofttranslator.com/ + This is typically the Global region, but could also be a single Azure region. + Please select at least one language before you translate the metadata. + Microsoft Translator was unable to translate the metadata strings into {0}. Please try again later or remove this language from the translation. + Export .csv files + You can export the metadata captions, descriptions, and display folder strings into .csv files based on the locale identifiers (LCIDs) of the selected languages. Metadata Translator creates a separate .csv file for each LCID. + Select the folder where you want to store your .csv files. Note that Metadata Translator might overwrite any existing files in this folder. + Export... + Import .csv files + You can import translated captions, descriptions, and display folder strings from .csv files. Each file name must match a locale identifier (LCID) of a supported language. You can import multiple .csv files in a single step. + Import... + Object - Type + The default culture is read-only. Use Power BI Desktop to change the names, descriptions, and display folder names for the default culture. + \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Resources/supportedlanguages.json b/MetadataTranslator/Metadata Translator/Resources/supportedlanguages.json new file mode 100644 index 0000000..9724a7f --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Resources/supportedlanguages.json @@ -0,0 +1,2361 @@ +[ + { + "LanguageTag": "af-NA", + "TranslationId": "af", + "TranslationGroup": "Afrikaans", + "DisplayName": "Afrikaans (Namibia)", + "NativeName": "Afrikaans (Namibië)" + }, + { + "LanguageTag": "af-ZA", + "TranslationId": "af", + "TranslationGroup": "Afrikaans", + "DisplayName": "Afrikaans (South Africa)", + "NativeName": "Afrikaans (Suid-Afrika)" + }, + { + "LanguageTag": "bs-Latn-BA", + "TranslationId": "bs", + "TranslationGroup": "Bosnian (Latin)", + "DisplayName": "Bosnian (Latin, Bosnia & Herzegovina)", + "NativeName": "bosanski (Bosna i Hercegovina)" + }, + { + "LanguageTag": "ar-001", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (World)", + "NativeName": "العربية (العالم)" + }, + { + "LanguageTag": "ar-AE", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (United Arab Emirates)", + "NativeName": "العربية (الإمارات العربية المتحدة)" + }, + { + "LanguageTag": "ar-BH", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Bahrain)", + "NativeName": "العربية (البحرين)" + }, + { + "LanguageTag": "ar-DJ", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Djibouti)", + "NativeName": "العربية (جيبوتي)" + }, + { + "LanguageTag": "ar-DZ", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Algeria)", + "NativeName": "العربية (الجزائر)" + }, + { + "LanguageTag": "ar-EG", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Egypt)", + "NativeName": "العربية (مصر)" + }, + { + "LanguageTag": "ar-ER", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Eritrea)", + "NativeName": "العربية (إريتريا)" + }, + { + "LanguageTag": "ar-IL", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Israel)", + "NativeName": "العربية (إسرائيل)" + }, + { + "LanguageTag": "ar-IQ", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Iraq)", + "NativeName": "العربية (العراق)" + }, + { + "LanguageTag": "ar-JO", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Jordan)", + "NativeName": "العربية (الأردن)" + }, + { + "LanguageTag": "ar-KM", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Comoros)", + "NativeName": "العربية (جزر القمر)" + }, + { + "LanguageTag": "ar-KW", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Kuwait)", + "NativeName": "العربية (الكويت)" + }, + { + "LanguageTag": "ar-LB", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Lebanon)", + "NativeName": "العربية (لبنان)" + }, + { + "LanguageTag": "ar-LY", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Libya)", + "NativeName": "العربية (ليبيا)" + }, + { + "LanguageTag": "ar-MA", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Morocco)", + "NativeName": "العربية (المملكة المغربية)" + }, + { + "LanguageTag": "ar-MR", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Mauritania)", + "NativeName": "العربية (موريتانيا)" + }, + { + "LanguageTag": "ar-OM", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Oman)", + "NativeName": "العربية (عمان)" + }, + { + "LanguageTag": "ar-PS", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Palestinian Authority)", + "NativeName": "العربية (السلطة الفلسطينية)" + }, + { + "LanguageTag": "ar-QA", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Qatar)", + "NativeName": "العربية (قطر)" + }, + { + "LanguageTag": "ar-SA", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Saudi Arabia)", + "NativeName": "العربية (المملكة العربية السعودية)" + }, + { + "LanguageTag": "ar-SD", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Sudan)", + "NativeName": "العربية (السودان)" + }, + { + "LanguageTag": "ar-SO", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Somalia)", + "NativeName": "العربية (الصومال)" + }, + { + "LanguageTag": "ar-SS", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (South Sudan)", + "NativeName": "العربية (جنوب السودان)" + }, + { + "LanguageTag": "ar-SY", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Syria)", + "NativeName": "العربية (سوريا)" + }, + { + "LanguageTag": "ar-TD", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Chad)", + "NativeName": "العربية (تشاد)" + }, + { + "LanguageTag": "ar-TN", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Tunisia)", + "NativeName": "العربية (تونس)" + }, + { + "LanguageTag": "ar-YE", + "TranslationId": "ar", + "TranslationGroup": "Arabic", + "DisplayName": "Arabic (Yemen)", + "NativeName": "العربية (اليمن)" + }, + { + "LanguageTag": "as-IN", + "TranslationId": "as", + "TranslationGroup": "Assamese", + "DisplayName": "Assamese (India)", + "NativeName": "অসমীয়া (ভাৰত)" + }, + { + "LanguageTag": "bg-BG", + "TranslationId": "bg", + "TranslationGroup": "Bulgarian", + "DisplayName": "Bulgarian (Bulgaria)", + "NativeName": "български (България)" + }, + { + "LanguageTag": "bn-BD", + "TranslationId": "bn", + "TranslationGroup": "Bangla", + "DisplayName": "Bangla (Bangladesh)", + "NativeName": "বাংলা (বাংলাদেশ)" + }, + { + "LanguageTag": "bn-IN", + "TranslationId": "bn", + "TranslationGroup": "Bangla", + "DisplayName": "Bengali (India)", + "NativeName": "বাংলা (ভারত)" + }, + { + "LanguageTag": "ca-AD", + "TranslationId": "ca", + "TranslationGroup": "Catalan", + "DisplayName": "Catalan (Andorra)", + "NativeName": "català (Andorra)" + }, + { + "LanguageTag": "ca-ES", + "TranslationId": "ca", + "TranslationGroup": "Catalan", + "DisplayName": "Catalan (Catalan)", + "NativeName": "català (català)" + }, + { + "LanguageTag": "ca-FR", + "TranslationId": "ca", + "TranslationGroup": "Catalan", + "DisplayName": "Catalan (France)", + "NativeName": "català (França)" + }, + { + "LanguageTag": "ca-IT", + "TranslationId": "ca", + "TranslationGroup": "Catalan", + "DisplayName": "Catalan (Italy)", + "NativeName": "català (Itàlia)" + }, + { + "LanguageTag": "cs-CZ", + "TranslationId": "cs", + "TranslationGroup": "Czech", + "DisplayName": "Czech (Czechia)", + "NativeName": "čeština (Česko)" + }, + { + "LanguageTag": "da-DK", + "TranslationId": "da", + "TranslationGroup": "Danish", + "DisplayName": "Danish (Denmark)", + "NativeName": "dansk (Danmark)" + }, + { + "LanguageTag": "da-GL", + "TranslationId": "da", + "TranslationGroup": "Danish", + "DisplayName": "Danish (Greenland)", + "NativeName": "dansk (Grønland)" + }, + { + "LanguageTag": "en-001", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (World)", + "NativeName": "English (World)" + }, + { + "LanguageTag": "en-029", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Caribbean)", + "NativeName": "English (Caribbean)" + }, + { + "LanguageTag": "en-150", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Europe)", + "NativeName": "English (Europe)" + }, + { + "LanguageTag": "en-AE", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (United Arab Emirates)", + "NativeName": "English (United Arab Emirates)" + }, + { + "LanguageTag": "en-AG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Antigua & Barbuda)", + "NativeName": "English (Antigua & Barbuda)" + }, + { + "LanguageTag": "en-AI", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Anguilla)", + "NativeName": "English (Anguilla)" + }, + { + "LanguageTag": "en-AS", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (American Samoa)", + "NativeName": "English (American Samoa)" + }, + { + "LanguageTag": "en-AT", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Austria)", + "NativeName": "English (Austria)" + }, + { + "LanguageTag": "en-AU", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Australia)", + "NativeName": "English (Australia)" + }, + { + "LanguageTag": "en-BB", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Barbados)", + "NativeName": "English (Barbados)" + }, + { + "LanguageTag": "en-BE", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Belgium)", + "NativeName": "English (Belgium)" + }, + { + "LanguageTag": "en-BI", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Burundi)", + "NativeName": "English (Burundi)" + }, + { + "LanguageTag": "en-BM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Bermuda)", + "NativeName": "English (Bermuda)" + }, + { + "LanguageTag": "en-BS", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Bahamas)", + "NativeName": "English (Bahamas)" + }, + { + "LanguageTag": "en-BW", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Botswana)", + "NativeName": "English (Botswana)" + }, + { + "LanguageTag": "en-BZ", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Belize)", + "NativeName": "English (Belize)" + }, + { + "LanguageTag": "en-CA", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Canada)", + "NativeName": "English (Canada)" + }, + { + "LanguageTag": "en-CC", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Cocos (Keeling) Islands)", + "NativeName": "English (Cocos (Keeling) Islands)" + }, + { + "LanguageTag": "en-CH", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Switzerland)", + "NativeName": "English (Switzerland)" + }, + { + "LanguageTag": "en-CK", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Cook Islands)", + "NativeName": "English (Cook Islands)" + }, + { + "LanguageTag": "en-CM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Cameroon)", + "NativeName": "English (Cameroon)" + }, + { + "LanguageTag": "en-CX", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Christmas Island)", + "NativeName": "English (Christmas Island)" + }, + { + "LanguageTag": "en-CY", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Cyprus)", + "NativeName": "English (Cyprus)" + }, + { + "LanguageTag": "en-DE", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Germany)", + "NativeName": "English (Germany)" + }, + { + "LanguageTag": "en-DK", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Denmark)", + "NativeName": "English (Denmark)" + }, + { + "LanguageTag": "en-DM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Dominica)", + "NativeName": "English (Dominica)" + }, + { + "LanguageTag": "en-ER", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Eritrea)", + "NativeName": "English (Eritrea)" + }, + { + "LanguageTag": "en-FI", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Finland)", + "NativeName": "English (Finland)" + }, + { + "LanguageTag": "en-FJ", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Fiji)", + "NativeName": "English (Fiji)" + }, + { + "LanguageTag": "en-FK", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Falkland Islands)", + "NativeName": "English (Falkland Islands)" + }, + { + "LanguageTag": "de-AT", + "TranslationId": "de", + "TranslationGroup": "German", + "DisplayName": "German (Austria)", + "NativeName": "Deutsch (Österreich)" + }, + { + "LanguageTag": "de-BE", + "TranslationId": "de", + "TranslationGroup": "German", + "DisplayName": "German (Belgium)", + "NativeName": "Deutsch (Belgien)" + }, + { + "LanguageTag": "de-CH", + "TranslationId": "de", + "TranslationGroup": "German", + "DisplayName": "German (Switzerland)", + "NativeName": "Deutsch (Schweiz)" + }, + { + "LanguageTag": "de-DE", + "TranslationId": "de", + "TranslationGroup": "German", + "DisplayName": "German (Germany)", + "NativeName": "Deutsch (Deutschland)" + }, + { + "LanguageTag": "de-IT", + "TranslationId": "de", + "TranslationGroup": "German", + "DisplayName": "German (Italy)", + "NativeName": "Deutsch (Italien)" + }, + { + "LanguageTag": "de-LI", + "TranslationId": "de", + "TranslationGroup": "German", + "DisplayName": "German (Liechtenstein)", + "NativeName": "Deutsch (Liechtenstein)" + }, + { + "LanguageTag": "de-LU", + "TranslationId": "de", + "TranslationGroup": "German", + "DisplayName": "German (Luxembourg)", + "NativeName": "Deutsch (Luxemburg)" + }, + { + "LanguageTag": "el-CY", + "TranslationId": "el", + "TranslationGroup": "Greek", + "DisplayName": "Greek (Cyprus)", + "NativeName": "Ελληνικά (Κύπρος)" + }, + { + "LanguageTag": "el-GR", + "TranslationId": "el", + "TranslationGroup": "Greek", + "DisplayName": "Greek (Greece)", + "NativeName": "Ελληνικά (Ελλάδα)" + }, + { + "LanguageTag": "cy-GB", + "TranslationId": "cy", + "TranslationGroup": "Welsh", + "DisplayName": "Welsh (United Kingdom)", + "NativeName": "Cymraeg (Y Deyrnas Unedig)" + }, + { + "LanguageTag": "en-FM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Micronesia)", + "NativeName": "English (Micronesia)" + }, + { + "LanguageTag": "en-GB", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (United Kingdom)", + "NativeName": "English (United Kingdom)" + }, + { + "LanguageTag": "en-GD", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Grenada)", + "NativeName": "English (Grenada)" + }, + { + "LanguageTag": "en-GG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Guernsey)", + "NativeName": "English (Guernsey)" + }, + { + "LanguageTag": "en-GH", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Ghana)", + "NativeName": "English (Ghana)" + }, + { + "LanguageTag": "en-GI", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Gibraltar)", + "NativeName": "English (Gibraltar)" + }, + { + "LanguageTag": "en-GM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Gambia)", + "NativeName": "English (Gambia)" + }, + { + "LanguageTag": "en-GU", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Guam)", + "NativeName": "English (Guam)" + }, + { + "LanguageTag": "en-GY", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Guyana)", + "NativeName": "English (Guyana)" + }, + { + "LanguageTag": "en-HK", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Hong Kong SAR)", + "NativeName": "English (Hong Kong SAR)" + }, + { + "LanguageTag": "en-ID", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Indonesia)", + "NativeName": "English (Indonesia)" + }, + { + "LanguageTag": "en-IE", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Ireland)", + "NativeName": "English (Ireland)" + }, + { + "LanguageTag": "en-IL", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Israel)", + "NativeName": "English (Israel)" + }, + { + "LanguageTag": "en-IM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Isle of Man)", + "NativeName": "English (Isle of Man)" + }, + { + "LanguageTag": "en-IN", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (India)", + "NativeName": "English (India)" + }, + { + "LanguageTag": "en-IO", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (British Indian Ocean Territory)", + "NativeName": "English (British Indian Ocean Territory)" + }, + { + "LanguageTag": "en-JE", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Jersey)", + "NativeName": "English (Jersey)" + }, + { + "LanguageTag": "en-JM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Jamaica)", + "NativeName": "English (Jamaica)" + }, + { + "LanguageTag": "en-KE", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Kenya)", + "NativeName": "English (Kenya)" + }, + { + "LanguageTag": "en-KI", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Kiribati)", + "NativeName": "English (Kiribati)" + }, + { + "LanguageTag": "en-KN", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (St. Kitts & Nevis)", + "NativeName": "English (St. Kitts & Nevis)" + }, + { + "LanguageTag": "en-KY", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Cayman Islands)", + "NativeName": "English (Cayman Islands)" + }, + { + "LanguageTag": "en-LC", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (St. Lucia)", + "NativeName": "English (St. Lucia)" + }, + { + "LanguageTag": "en-LR", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Liberia)", + "NativeName": "English (Liberia)" + }, + { + "LanguageTag": "en-LS", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Lesotho)", + "NativeName": "English (Lesotho)" + }, + { + "LanguageTag": "en-MG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Madagascar)", + "NativeName": "English (Madagascar)" + }, + { + "LanguageTag": "en-MH", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Marshall Islands)", + "NativeName": "English (Marshall Islands)" + }, + { + "LanguageTag": "en-MO", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Macao SAR)", + "NativeName": "English (Macao SAR)" + }, + { + "LanguageTag": "en-MP", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Northern Mariana Islands)", + "NativeName": "English (Northern Mariana Islands)" + }, + { + "LanguageTag": "en-MS", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Montserrat)", + "NativeName": "English (Montserrat)" + }, + { + "LanguageTag": "en-MT", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Malta)", + "NativeName": "English (Malta)" + }, + { + "LanguageTag": "en-MU", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Mauritius)", + "NativeName": "English (Mauritius)" + }, + { + "LanguageTag": "en-MW", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Malawi)", + "NativeName": "English (Malawi)" + }, + { + "LanguageTag": "en-MY", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Malaysia)", + "NativeName": "English (Malaysia)" + }, + { + "LanguageTag": "en-NA", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Namibia)", + "NativeName": "English (Namibia)" + }, + { + "LanguageTag": "en-NF", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Norfolk Island)", + "NativeName": "English (Norfolk Island)" + }, + { + "LanguageTag": "en-NG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Nigeria)", + "NativeName": "English (Nigeria)" + }, + { + "LanguageTag": "en-NL", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Netherlands)", + "NativeName": "English (Netherlands)" + }, + { + "LanguageTag": "en-NR", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Nauru)", + "NativeName": "English (Nauru)" + }, + { + "LanguageTag": "en-NU", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Niue)", + "NativeName": "English (Niue)" + }, + { + "LanguageTag": "en-NZ", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (New Zealand)", + "NativeName": "English (New Zealand)" + }, + { + "LanguageTag": "en-PG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Papua New Guinea)", + "NativeName": "English (Papua New Guinea)" + }, + { + "LanguageTag": "en-PH", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Philippines)", + "NativeName": "English (Philippines)" + }, + { + "LanguageTag": "en-PK", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Pakistan)", + "NativeName": "English (Pakistan)" + }, + { + "LanguageTag": "en-PN", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Pitcairn Islands)", + "NativeName": "English (Pitcairn Islands)" + }, + { + "LanguageTag": "en-PR", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Puerto Rico)", + "NativeName": "English (Puerto Rico)" + }, + { + "LanguageTag": "en-PW", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Palau)", + "NativeName": "English (Palau)" + }, + { + "LanguageTag": "en-RW", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Rwanda)", + "NativeName": "English (Rwanda)" + }, + { + "LanguageTag": "en-SB", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Solomon Islands)", + "NativeName": "English (Solomon Islands)" + }, + { + "LanguageTag": "en-SC", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Seychelles)", + "NativeName": "English (Seychelles)" + }, + { + "LanguageTag": "en-SD", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Sudan)", + "NativeName": "English (Sudan)" + }, + { + "LanguageTag": "en-SE", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Sweden)", + "NativeName": "English (Sweden)" + }, + { + "LanguageTag": "en-SG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Singapore)", + "NativeName": "English (Singapore)" + }, + { + "LanguageTag": "en-SH", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (St Helena, Ascension, Tristan da Cunha)", + "NativeName": "English (St Helena, Ascension, Tristan da Cunha)" + }, + { + "LanguageTag": "en-SI", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Slovenia)", + "NativeName": "English (Slovenia)" + }, + { + "LanguageTag": "en-SL", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Sierra Leone)", + "NativeName": "English (Sierra Leone)" + }, + { + "LanguageTag": "en-SS", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (South Sudan)", + "NativeName": "English (South Sudan)" + }, + { + "LanguageTag": "en-SX", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Sint Maarten)", + "NativeName": "English (Sint Maarten)" + }, + { + "LanguageTag": "en-SZ", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Eswatini)", + "NativeName": "English (Eswatini)" + }, + { + "LanguageTag": "en-TC", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Turks & Caicos Islands)", + "NativeName": "English (Turks & Caicos Islands)" + }, + { + "LanguageTag": "en-TK", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Tokelau)", + "NativeName": "English (Tokelau)" + }, + { + "LanguageTag": "en-TO", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Tonga)", + "NativeName": "English (Tonga)" + }, + { + "LanguageTag": "en-TT", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Trinidad & Tobago)", + "NativeName": "English (Trinidad & Tobago)" + }, + { + "LanguageTag": "en-TV", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Tuvalu)", + "NativeName": "English (Tuvalu)" + }, + { + "LanguageTag": "en-TZ", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Tanzania)", + "NativeName": "English (Tanzania)" + }, + { + "LanguageTag": "en-UG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Uganda)", + "NativeName": "English (Uganda)" + }, + { + "LanguageTag": "en-UM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (U.S. Outlying Islands)", + "NativeName": "English (U.S. Outlying Islands)" + }, + { + "LanguageTag": "en-US", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (United States)", + "NativeName": "English (United States)" + }, + { + "LanguageTag": "en-VC", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (St. Vincent & Grenadines)", + "NativeName": "English (St. Vincent & Grenadines)" + }, + { + "LanguageTag": "en-VG", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (British Virgin Islands)", + "NativeName": "English (British Virgin Islands)" + }, + { + "LanguageTag": "en-VI", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (U.S. Virgin Islands)", + "NativeName": "English (U.S. Virgin Islands)" + }, + { + "LanguageTag": "en-VU", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Vanuatu)", + "NativeName": "English (Vanuatu)" + }, + { + "LanguageTag": "en-WS", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Samoa)", + "NativeName": "English (Samoa)" + }, + { + "LanguageTag": "en-ZA", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (South Africa)", + "NativeName": "English (South Africa)" + }, + { + "LanguageTag": "en-ZM", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Zambia)", + "NativeName": "English (Zambia)" + }, + { + "LanguageTag": "en-ZW", + "TranslationId": "en", + "TranslationGroup": "English", + "DisplayName": "English (Zimbabwe)", + "NativeName": "English (Zimbabwe)" + }, + { + "LanguageTag": "es-419", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Latin America)", + "NativeName": "español (Latinoamérica)" + }, + { + "LanguageTag": "es-AR", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Argentina)", + "NativeName": "español (Argentina)" + }, + { + "LanguageTag": "es-BO", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Bolivia)", + "NativeName": "español (Bolivia)" + }, + { + "LanguageTag": "es-BR", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Brazil)", + "NativeName": "español (Brasil)" + }, + { + "LanguageTag": "es-BZ", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Belize)", + "NativeName": "español (Belice)" + }, + { + "LanguageTag": "es-CL", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Chile)", + "NativeName": "español (Chile)" + }, + { + "LanguageTag": "es-CO", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Colombia)", + "NativeName": "español (Colombia)" + }, + { + "LanguageTag": "es-CR", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Costa Rica)", + "NativeName": "español (Costa Rica)" + }, + { + "LanguageTag": "es-CU", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Cuba)", + "NativeName": "español (Cuba)" + }, + { + "LanguageTag": "es-DO", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Dominican Republic)", + "NativeName": "español (República Dominicana)" + }, + { + "LanguageTag": "es-EC", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Ecuador)", + "NativeName": "español (Ecuador)" + }, + { + "LanguageTag": "es-ES", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Spain, International Sort)", + "NativeName": "español (España, alfabetización internacional)" + }, + { + "LanguageTag": "es-GQ", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Equatorial Guinea)", + "NativeName": "español (Guinea Ecuatorial)" + }, + { + "LanguageTag": "es-GT", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Guatemala)", + "NativeName": "español (Guatemala)" + }, + { + "LanguageTag": "es-HN", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Honduras)", + "NativeName": "español (Honduras)" + }, + { + "LanguageTag": "es-MX", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Mexico)", + "NativeName": "español (México)" + }, + { + "LanguageTag": "es-NI", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Nicaragua)", + "NativeName": "español (Nicaragua)" + }, + { + "LanguageTag": "es-PA", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Panama)", + "NativeName": "español (Panamá)" + }, + { + "LanguageTag": "es-PE", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Peru)", + "NativeName": "español (Perú)" + }, + { + "LanguageTag": "es-PH", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Philippines)", + "NativeName": "español (Filipinas)" + }, + { + "LanguageTag": "es-PR", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Puerto Rico)", + "NativeName": "español (Puerto Rico)" + }, + { + "LanguageTag": "es-PY", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Paraguay)", + "NativeName": "español (Paraguay)" + }, + { + "LanguageTag": "es-SV", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (El Salvador)", + "NativeName": "español (El Salvador)" + }, + { + "LanguageTag": "es-US", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (United States)", + "NativeName": "español (Estados Unidos)" + }, + { + "LanguageTag": "es-UY", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Uruguay)", + "NativeName": "español (Uruguay)" + }, + { + "LanguageTag": "es-VE", + "TranslationId": "es", + "TranslationGroup": "Spanish", + "DisplayName": "Spanish (Venezuela)", + "NativeName": "español (Venezuela)" + }, + { + "LanguageTag": "et-EE", + "TranslationId": "et", + "TranslationGroup": "Estonian", + "DisplayName": "Estonian (Estonia)", + "NativeName": "eesti (Eesti)" + }, + { + "LanguageTag": "fa-IR", + "TranslationId": "fa", + "TranslationGroup": "Persian", + "DisplayName": "Persian (Iran)", + "NativeName": "فارسی (ایران)" + }, + { + "LanguageTag": "fi-FI", + "TranslationId": "fi", + "TranslationGroup": "Finnish", + "DisplayName": "Finnish (Finland)", + "NativeName": "suomi (Suomi)" + }, + { + "LanguageTag": "fil-PH", + "TranslationId": "fil", + "TranslationGroup": "Filipino", + "DisplayName": "Filipino (Philippines)", + "NativeName": "Filipino (Pilipinas)" + }, + { + "LanguageTag": "fr-029", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Caribbean)", + "NativeName": "français (caraïbes)" + }, + { + "LanguageTag": "fr-BE", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Belgium)", + "NativeName": "français (Belgique)" + }, + { + "LanguageTag": "fr-BF", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Burkina Faso)", + "NativeName": "français (Burkina Faso)" + }, + { + "LanguageTag": "fr-BI", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Burundi)", + "NativeName": "français (Burundi)" + }, + { + "LanguageTag": "fr-BJ", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Benin)", + "NativeName": "français (Bénin)" + }, + { + "LanguageTag": "fr-BL", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (St. Barthélemy)", + "NativeName": "français (Saint-Barthélemy)" + }, + { + "LanguageTag": "fr-CA", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Canada)", + "NativeName": "français (Canada)" + }, + { + "LanguageTag": "fr-CD", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French Congo (DRC)", + "NativeName": "français (Congo, République démocratique du)" + }, + { + "LanguageTag": "fr-CF", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Central African Republic)", + "NativeName": "français (République centrafricaine)" + }, + { + "LanguageTag": "fr-CG", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Congo)", + "NativeName": "français (Congo)" + }, + { + "LanguageTag": "fr-CH", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Switzerland)", + "NativeName": "français (Suisse)" + }, + { + "LanguageTag": "fr-CI", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Côte d’Ivoire)", + "NativeName": "français (Côte d’Ivoire)" + }, + { + "LanguageTag": "fr-CM", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Cameroon)", + "NativeName": "français (Cameroun)" + }, + { + "LanguageTag": "fr-DJ", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Djibouti)", + "NativeName": "français (Djibouti)" + }, + { + "LanguageTag": "fr-DZ", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Algeria)", + "NativeName": "français (Algérie)" + }, + { + "LanguageTag": "fr-FR", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (France)", + "NativeName": "français (France)" + }, + { + "LanguageTag": "fr-GA", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Gabon)", + "NativeName": "français (Gabon)" + }, + { + "LanguageTag": "fr-GF", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (French Guiana)", + "NativeName": "français (Guyane française)" + }, + { + "LanguageTag": "fr-GN", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Guinea)", + "NativeName": "français (Guinée)" + }, + { + "LanguageTag": "fr-GP", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Guadeloupe)", + "NativeName": "français (Guadeloupe)" + }, + { + "LanguageTag": "fr-GQ", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Equatorial Guinea)", + "NativeName": "français (Guinée équatoriale)" + }, + { + "LanguageTag": "fr-HT", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Haiti)", + "NativeName": "français (Haïti)" + }, + { + "LanguageTag": "fr-KM", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Comoros)", + "NativeName": "français (Comores)" + }, + { + "LanguageTag": "fr-LU", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Luxembourg)", + "NativeName": "français (Luxembourg)" + }, + { + "LanguageTag": "fr-MA", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Morocco)", + "NativeName": "français (Maroc)" + }, + { + "LanguageTag": "fr-MC", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Monaco)", + "NativeName": "français (Monaco)" + }, + { + "LanguageTag": "fr-MF", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (St. Martin)", + "NativeName": "français (Saint-Martin)" + }, + { + "LanguageTag": "fr-MG", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Madagascar)", + "NativeName": "français (Madagascar)" + }, + { + "LanguageTag": "fr-ML", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Mali)", + "NativeName": "français (Mali)" + }, + { + "LanguageTag": "fr-MQ", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Martinique)", + "NativeName": "français (Martinique)" + }, + { + "LanguageTag": "fr-MR", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Mauritania)", + "NativeName": "français (Mauritanie)" + }, + { + "LanguageTag": "fr-MU", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Mauritius)", + "NativeName": "français (Maurice)" + }, + { + "LanguageTag": "fr-NC", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (New Caledonia)", + "NativeName": "français (Nouvelle-Calédonie)" + }, + { + "LanguageTag": "fr-NE", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Niger)", + "NativeName": "français (Niger)" + }, + { + "LanguageTag": "fr-PF", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (French Polynesia)", + "NativeName": "français (Polynésie française)" + }, + { + "LanguageTag": "fr-PM", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (St. Pierre & Miquelon)", + "NativeName": "français (Saint-Pierre-et-Miquelon)" + }, + { + "LanguageTag": "fr-RE", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Réunion)", + "NativeName": "français (La Réunion)" + }, + { + "LanguageTag": "fr-RW", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Rwanda)", + "NativeName": "français (Rwanda)" + }, + { + "LanguageTag": "fr-SC", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Seychelles)", + "NativeName": "français (Seychelles)" + }, + { + "LanguageTag": "fr-SN", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Senegal)", + "NativeName": "français (Sénégal)" + }, + { + "LanguageTag": "fr-SY", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Syria)", + "NativeName": "français (Syrie)" + }, + { + "LanguageTag": "fr-TD", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Chad)", + "NativeName": "français (Tchad)" + }, + { + "LanguageTag": "fr-TG", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Togo)", + "NativeName": "français (Togo)" + }, + { + "LanguageTag": "fr-TN", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Tunisia)", + "NativeName": "français (Tunisie)" + }, + { + "LanguageTag": "fr-VU", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Vanuatu)", + "NativeName": "français (Vanuatu)" + }, + { + "LanguageTag": "fr-WF", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Wallis & Futuna)", + "NativeName": "français (Wallis-et-Futuna)" + }, + { + "LanguageTag": "fr-YT", + "TranslationId": "fr", + "TranslationGroup": "French", + "DisplayName": "French (Mayotte)", + "NativeName": "français (Mayotte)" + }, + { + "LanguageTag": "ga-IE", + "TranslationId": "ga", + "TranslationGroup": "Irish", + "DisplayName": "Irish (Ireland)", + "NativeName": "Gaeilge (Éire)" + }, + { + "LanguageTag": "gu-IN", + "TranslationId": "gu", + "TranslationGroup": "Gujarati", + "DisplayName": "Gujarati (India)", + "NativeName": "ગુજરાતી (ભારત)" + }, + { + "LanguageTag": "he-IL", + "TranslationId": "he", + "TranslationGroup": "Hebrew", + "DisplayName": "Hebrew (Israel)", + "NativeName": "עברית (ישראל)" + }, + { + "LanguageTag": "hi-IN", + "TranslationId": "hi", + "TranslationGroup": "Hindi", + "DisplayName": "Hindi (India)", + "NativeName": "हिन्दी (भारत)" + }, + { + "LanguageTag": "hr-BA", + "TranslationId": "hr", + "TranslationGroup": "Croatian", + "DisplayName": "Croatian (Bosnia & Herzegovina)", + "NativeName": "hrvatski (Bosna i Hercegovina)" + }, + { + "LanguageTag": "hr-HR", + "TranslationId": "hr", + "TranslationGroup": "Croatian", + "DisplayName": "Croatian (Croatia)", + "NativeName": "hrvatski (Hrvatska)" + }, + { + "LanguageTag": "hu-HU", + "TranslationId": "hu", + "TranslationGroup": "Hungarian", + "DisplayName": "Hungarian (Hungary)", + "NativeName": "magyar (Magyarország)" + }, + { + "LanguageTag": "id-ID", + "TranslationId": "id", + "TranslationGroup": "Indonesian", + "DisplayName": "Indonesian (Indonesia)", + "NativeName": "Indonesia (Indonesia)" + }, + { + "LanguageTag": "is-IS", + "TranslationId": "is", + "TranslationGroup": "Icelandic", + "DisplayName": "Icelandic (Iceland)", + "NativeName": "íslenska (Ísland)" + }, + { + "LanguageTag": "it-CH", + "TranslationId": "it", + "TranslationGroup": "Italian", + "DisplayName": "Italian (Switzerland)", + "NativeName": "italiano (Svizzera)" + }, + { + "LanguageTag": "it-IT", + "TranslationId": "it", + "TranslationGroup": "Italian", + "DisplayName": "Italian (Italy)", + "NativeName": "italiano (Italia)" + }, + { + "LanguageTag": "it-SM", + "TranslationId": "it", + "TranslationGroup": "Italian", + "DisplayName": "Italian (San Marino)", + "NativeName": "italiano (San Marino)" + }, + { + "LanguageTag": "it-VA", + "TranslationId": "it", + "TranslationGroup": "Italian", + "DisplayName": "Italian (Vatican City)", + "NativeName": "italiano (Città del Vaticano)" + }, + { + "LanguageTag": "iu-Cans-CA", + "TranslationId": "iu", + "TranslationGroup": "Inuktitut", + "DisplayName": "Inuktitut (Syllabics, Canada)", + "NativeName": "ᐃᓄᒃᑎᑐᑦ (ᑲᓇᑕᒥ)" + }, + { + "LanguageTag": "ja-JP", + "TranslationId": "ja", + "TranslationGroup": "Japanese", + "DisplayName": "Japanese (Japan)", + "NativeName": "日本語 (日本)" + }, + { + "LanguageTag": "kk-KZ", + "TranslationId": "kk", + "TranslationGroup": "Kazakh", + "DisplayName": "Kazakh (Kazakhstan)", + "NativeName": "қазақ тілі (Қазақстан)" + }, + { + "LanguageTag": "kn-IN", + "TranslationId": "kn", + "TranslationGroup": "Kannada", + "DisplayName": "Kannada (India)", + "NativeName": "ಕನ್ನಡ (ಭಾರತ)" + }, + { + "LanguageTag": "ko-KP", + "TranslationId": "ko", + "TranslationGroup": "Korean", + "DisplayName": "Korean (North Korea)", + "NativeName": "한국어 (조선민주주의인민공화국)" + }, + { + "LanguageTag": "ko-KR", + "TranslationId": "ko", + "TranslationGroup": "Korean", + "DisplayName": "Korean (Korea)", + "NativeName": "한국어(대한민국)" + }, + { + "LanguageTag": "ku-Arab-IQ", + "TranslationId": "ku", + "TranslationGroup": "Kurdish (Central)", + "DisplayName": "Central Kurdish (Iraq)", + "NativeName": "کوردیی ناوەڕاست (عێراق)" + }, + { + "LanguageTag": "ku-Arab-IR", + "TranslationId": "ku", + "TranslationGroup": "Kurdish (Central)", + "DisplayName": "Kurdish (Perso-Arabic, Iran)", + "NativeName": "کوردی (ئێران)" + }, + { + "LanguageTag": "lt-LT", + "TranslationId": "lt", + "TranslationGroup": "Lithuanian", + "DisplayName": "Lithuanian (Lithuania)", + "NativeName": "lietuvių (Lietuva)" + }, + { + "LanguageTag": "lv-LV", + "TranslationId": "lv", + "TranslationGroup": "Latvian", + "DisplayName": "Latvian (Latvia)", + "NativeName": "latviešu (Latvija)" + }, + { + "LanguageTag": "mg-MG", + "TranslationId": "mg", + "TranslationGroup": "Malagasy", + "DisplayName": "Malagasy (Madagascar)", + "NativeName": "Malagasy (Madagasikara)" + }, + { + "LanguageTag": "mi-NZ", + "TranslationId": "mi", + "TranslationGroup": "Maori", + "DisplayName": "Maori (New Zealand)", + "NativeName": "te reo Māori (Aotearoa)" + }, + { + "LanguageTag": "ml-IN", + "TranslationId": "ml", + "TranslationGroup": "Malayalam", + "DisplayName": "Malayalam (India)", + "NativeName": "മലയാളം (ഇന്ത്യ)" + }, + { + "LanguageTag": "mr-IN", + "TranslationId": "mr", + "TranslationGroup": "Marathi", + "DisplayName": "Marathi (India)", + "NativeName": "मराठी (भारत)" + }, + { + "LanguageTag": "ms-BN", + "TranslationId": "ms", + "TranslationGroup": "Malay", + "DisplayName": "Malay (Brunei)", + "NativeName": "Melayu (Brunei)" + }, + { + "LanguageTag": "ms-MY", + "TranslationId": "ms", + "TranslationGroup": "Malay", + "DisplayName": "Malay (Malaysia)", + "NativeName": "Melayu (Malaysia)" + }, + { + "LanguageTag": "ms-SG", + "TranslationId": "ms", + "TranslationGroup": "Malay", + "DisplayName": "Malay (Singapore)", + "NativeName": "Melayu (Singapura)" + }, + { + "LanguageTag": "mt-MT", + "TranslationId": "mt", + "TranslationGroup": "Maltese", + "DisplayName": "Maltese (Malta)", + "NativeName": "Malti (Malta)" + }, + { + "LanguageTag": "nb-NO", + "TranslationId": "nb", + "TranslationGroup": "Norwegian", + "DisplayName": "Norwegian Bokmål (Norway)", + "NativeName": "norsk bokmål (Norge)" + }, + { + "LanguageTag": "nb-SJ", + "TranslationId": "nb", + "TranslationGroup": "Norwegian", + "DisplayName": "Norwegian Bokmål (Svalbard & Jan Mayen)", + "NativeName": "norsk bokmål (Svalbard og Jan Mayen)" + }, + { + "LanguageTag": "nl-AW", + "TranslationId": "nl", + "TranslationGroup": "Dutch", + "DisplayName": "Dutch (Aruba)", + "NativeName": "Nederlands (Aruba)" + }, + { + "LanguageTag": "nl-BE", + "TranslationId": "nl", + "TranslationGroup": "Dutch", + "DisplayName": "Dutch (Belgium)", + "NativeName": "Nederlands (België)" + }, + { + "LanguageTag": "nl-BQ", + "TranslationId": "nl", + "TranslationGroup": "Dutch", + "DisplayName": "Dutch (Bonaire, Sint Eustatius and Saba)", + "NativeName": "Nederlands (Bonaire, Sint Eustatius en Saba)" + }, + { + "LanguageTag": "nl-CW", + "TranslationId": "nl", + "TranslationGroup": "Dutch", + "DisplayName": "Dutch (Curaçao)", + "NativeName": "Nederlands (Curaçao)" + }, + { + "LanguageTag": "nl-NL", + "TranslationId": "nl", + "TranslationGroup": "Dutch", + "DisplayName": "Dutch (Netherlands)", + "NativeName": "Nederlands (Nederland)" + }, + { + "LanguageTag": "nl-SR", + "TranslationId": "nl", + "TranslationGroup": "Dutch", + "DisplayName": "Dutch (Suriname)", + "NativeName": "Nederlands (Suriname)" + }, + { + "LanguageTag": "nl-SX", + "TranslationId": "nl", + "TranslationGroup": "Dutch", + "DisplayName": "Dutch (Sint Maarten)", + "NativeName": "Nederlands (Sint-Maarten)" + }, + { + "LanguageTag": "or-IN", + "TranslationId": "or", + "TranslationGroup": "Odia", + "DisplayName": "Odia (India)", + "NativeName": "ଓଡ଼ିଆ (ଭାରତ)" + }, + { + "LanguageTag": "pa-IN", + "TranslationId": "pa", + "TranslationGroup": "Punjabi", + "DisplayName": "Punjabi (India)", + "NativeName": "ਪੰਜਾਬੀ (ਭਾਰਤ)" + }, + { + "LanguageTag": "pl-PL", + "TranslationId": "pl", + "TranslationGroup": "Polish", + "DisplayName": "Polish (Poland)", + "NativeName": "polski (Polska)" + }, + { + "LanguageTag": "prs-AF", + "TranslationId": "prs", + "TranslationGroup": "Dari", + "DisplayName": "Dari (Afghanistan)", + "NativeName": "درى (افغانستان)" + }, + { + "LanguageTag": "ps-AF", + "TranslationId": "ps", + "TranslationGroup": "Pashto", + "DisplayName": "Pashto (Afghanistan)", + "NativeName": "پښتو (افغانستان)" + }, + { + "LanguageTag": "ps-PK", + "TranslationId": "ps", + "TranslationGroup": "Pashto", + "DisplayName": "Pashto (Pakistan)", + "NativeName": "پښتو (پاکستان)" + }, + { + "LanguageTag": "pt-AO", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Angola)", + "NativeName": "português (Angola)" + }, + { + "LanguageTag": "pt-BR", + "TranslationId": "pt", + "TranslationGroup": "Portuguese (Brazil)", + "DisplayName": "Portuguese (Brazil)", + "NativeName": "português (Brasil)" + }, + { + "LanguageTag": "pt-CH", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Switzerland)", + "NativeName": "português (Suíça)" + }, + { + "LanguageTag": "pt-CV", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Cabo Verde)", + "NativeName": "português (Cabo Verde)" + }, + { + "LanguageTag": "pt-GQ", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Equatorial Guinea)", + "NativeName": "português (Guiné Equatorial)" + }, + { + "LanguageTag": "pt-GW", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Guinea-Bissau)", + "NativeName": "português (Guiné-Bissau)" + }, + { + "LanguageTag": "pt-LU", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Luxembourg)", + "NativeName": "português (Luxemburgo)" + }, + { + "LanguageTag": "pt-MO", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Macao SAR)", + "NativeName": "português (RAE de Macau)" + }, + { + "LanguageTag": "pt-MZ", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Mozambique)", + "NativeName": "português (Moçambique)" + }, + { + "LanguageTag": "pt-PT", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Portugal)", + "NativeName": "português (Portugal)" + }, + { + "LanguageTag": "pt-ST", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (São Tomé & Príncipe)", + "NativeName": "português (São Tomé e Príncipe)" + }, + { + "LanguageTag": "pt-TL", + "TranslationId": "pt-pt", + "TranslationGroup": "Portuguese", + "DisplayName": "Portuguese (Timor-Leste)", + "NativeName": "português (Timor-Leste)" + }, + { + "LanguageTag": "ro-MD", + "TranslationId": "ro", + "TranslationGroup": "Romanian", + "DisplayName": "Romanian (Moldova)", + "NativeName": "română (Republica Moldova)" + }, + { + "LanguageTag": "ro-RO", + "TranslationId": "ro", + "TranslationGroup": "Romanian", + "DisplayName": "Romanian (Romania)", + "NativeName": "română (România)" + }, + { + "LanguageTag": "ru-BY", + "TranslationId": "ru", + "TranslationGroup": "Russian", + "DisplayName": "Russian (Belarus)", + "NativeName": "русский (Беларусь)" + }, + { + "LanguageTag": "ru-KG", + "TranslationId": "ru", + "TranslationGroup": "Russian", + "DisplayName": "Russian (Kyrgyzstan)", + "NativeName": "русский (Киргизия)" + }, + { + "LanguageTag": "ru-KZ", + "TranslationId": "ru", + "TranslationGroup": "Russian", + "DisplayName": "Russian (Kazakhstan)", + "NativeName": "русский (Казахстан)" + }, + { + "LanguageTag": "ru-MD", + "TranslationId": "ru", + "TranslationGroup": "Russian", + "DisplayName": "Russian (Moldova)", + "NativeName": "русский (Молдова)" + }, + { + "LanguageTag": "ru-RU", + "TranslationId": "ru", + "TranslationGroup": "Russian", + "DisplayName": "Russian (Russia)", + "NativeName": "русский (Россия)" + }, + { + "LanguageTag": "ru-UA", + "TranslationId": "ru", + "TranslationGroup": "Russian", + "DisplayName": "Russian (Ukraine)", + "NativeName": "русский (Украина)" + }, + { + "LanguageTag": "sk-SK", + "TranslationId": "sk", + "TranslationGroup": "Slovak", + "DisplayName": "Slovak (Slovakia)", + "NativeName": "slovenčina (Slovensko)" + }, + { + "LanguageTag": "sl-SI", + "TranslationId": "sl", + "TranslationGroup": "Slovenian", + "DisplayName": "Slovenian (Slovenia)", + "NativeName": "slovenščina (Slovenija)" + }, + { + "LanguageTag": "sr-Cyrl-BA", + "TranslationId": "sr-Cyrl", + "TranslationGroup": "Serbian (Cyrillic)", + "DisplayName": "Serbian (Cyrillic, Bosnia and Herzegovina)", + "NativeName": "српски (Босна и Херцеговина)" + }, + { + "LanguageTag": "sr-Cyrl-ME", + "TranslationId": "sr-Cyrl", + "TranslationGroup": "Serbian (Cyrillic)", + "DisplayName": "Serbian (Cyrillic, Montenegro)", + "NativeName": "српски (Црна Гора)" + }, + { + "LanguageTag": "sr-Cyrl-RS", + "TranslationId": "sr-Cyrl", + "TranslationGroup": "Serbian (Cyrillic)", + "DisplayName": "Serbian (Cyrillic, Serbia)", + "NativeName": "српски (Србија)" + }, + { + "LanguageTag": "sr-Cyrl-XK", + "TranslationId": "sr-Cyrl", + "TranslationGroup": "Serbian (Cyrillic)", + "DisplayName": "Serbian (Cyrillic, Kosovo)", + "NativeName": "српски (Косово)" + }, + { + "LanguageTag": "sr-Latn-BA", + "TranslationId": "sr-Latn", + "TranslationGroup": "Serbian (Latin)", + "DisplayName": "Serbian (Latin, Bosnia & Herzegovina)", + "NativeName": "srpski (Bosna i Hercegovina)" + }, + { + "LanguageTag": "sr-Latn-ME", + "TranslationId": "sr-Latn", + "TranslationGroup": "Serbian (Latin)", + "DisplayName": "Serbian (Latin, Montenegro)", + "NativeName": "srpski (Crna Gora)" + }, + { + "LanguageTag": "sr-Latn-RS", + "TranslationId": "sr-Latn", + "TranslationGroup": "Serbian (Latin)", + "DisplayName": "Serbian (Latin, Serbia)", + "NativeName": "srpski (Srbija)" + }, + { + "LanguageTag": "sr-Latn-XK", + "TranslationId": "sr-Latn", + "TranslationGroup": "Serbian (Latin)", + "DisplayName": "Serbian (Latin, Kosovo)", + "NativeName": "srpski (Kosovo)" + }, + { + "LanguageTag": "sv-AX", + "TranslationId": "sv", + "TranslationGroup": "Swedish", + "DisplayName": "Swedish (Åland Islands)", + "NativeName": "svenska (Åland)" + }, + { + "LanguageTag": "sv-FI", + "TranslationId": "sv", + "TranslationGroup": "Swedish", + "DisplayName": "Swedish (Finland)", + "NativeName": "svenska (Finland)" + }, + { + "LanguageTag": "sv-SE", + "TranslationId": "sv", + "TranslationGroup": "Swedish", + "DisplayName": "Swedish (Sweden)", + "NativeName": "svenska (Sverige)" + }, + { + "LanguageTag": "sw-CD", + "TranslationId": "sw", + "TranslationGroup": "Swahili", + "DisplayName": "Kiswahili (Congo DRC)", + "NativeName": "Kiswahili (Jamhuri ya Kidemokrasia ya Kongo)" + }, + { + "LanguageTag": "sw-KE", + "TranslationId": "sw", + "TranslationGroup": "Swahili", + "DisplayName": "Kiswahili (Kenya)", + "NativeName": "Kiswahili (Kenya)" + }, + { + "LanguageTag": "sw-TZ", + "TranslationId": "sw", + "TranslationGroup": "Swahili", + "DisplayName": "Kiswahili (Tanzania)", + "NativeName": "Kiswahili (Tanzania)" + }, + { + "LanguageTag": "sw-UG", + "TranslationId": "sw", + "TranslationGroup": "Swahili", + "DisplayName": "Kiswahili (Uganda)", + "NativeName": "Kiswahili (Uganda)" + }, + { + "LanguageTag": "ta-IN", + "TranslationId": "ta", + "TranslationGroup": "Tamil", + "DisplayName": "Tamil (India)", + "NativeName": "தமிழ் (இந்தியா)" + }, + { + "LanguageTag": "ta-LK", + "TranslationId": "ta", + "TranslationGroup": "Tamil", + "DisplayName": "Tamil (Sri Lanka)", + "NativeName": "தமிழ் (இலங்கை)" + }, + { + "LanguageTag": "ta-MY", + "TranslationId": "ta", + "TranslationGroup": "Tamil", + "DisplayName": "Tamil (Malaysia)", + "NativeName": "தமிழ் (மலேசியா)" + }, + { + "LanguageTag": "ta-SG", + "TranslationId": "ta", + "TranslationGroup": "Tamil", + "DisplayName": "Tamil (Singapore)", + "NativeName": "தமிழ் (சிங்கப்பூர்)" + }, + { + "LanguageTag": "te-IN", + "TranslationId": "te", + "TranslationGroup": "Telugu", + "DisplayName": "Telugu (India)", + "NativeName": "తెలుగు (భారతదేశం)" + }, + { + "LanguageTag": "th-TH", + "TranslationId": "th", + "TranslationGroup": "Thai", + "DisplayName": "Thai (Thailand)", + "NativeName": "ไทย (ไทย)" + }, + { + "LanguageTag": "to-TO", + "TranslationId": "to", + "TranslationGroup": "Tongan", + "DisplayName": "Tongan (Tonga)", + "NativeName": "lea fakatonga (Tonga)" + }, + { + "LanguageTag": "tr-CY", + "TranslationId": "tr", + "TranslationGroup": "Turkish", + "DisplayName": "Turkish (Cyprus)", + "NativeName": "Türkçe (Kıbrıs)" + }, + { + "LanguageTag": "tr-TR", + "TranslationId": "tr", + "TranslationGroup": "Turkish", + "DisplayName": "Turkish (Turkey)", + "NativeName": "Türkçe (Türkiye)" + }, + { + "LanguageTag": "uk-UA", + "TranslationId": "uk", + "TranslationGroup": "Ukrainian", + "DisplayName": "Ukrainian (Ukraine)", + "NativeName": "українська (Україна)" + }, + { + "LanguageTag": "ur-IN", + "TranslationId": "ur", + "TranslationGroup": "Urdu", + "DisplayName": "Urdu (India)", + "NativeName": "اردو (بھارت)" + }, + { + "LanguageTag": "ur-PK", + "TranslationId": "ur", + "TranslationGroup": "Urdu", + "DisplayName": "Urdu (Pakistan)", + "NativeName": "اُردو (پاکستان)" + }, + { + "LanguageTag": "vi-VN", + "TranslationId": "vi", + "TranslationGroup": "Vietnamese", + "DisplayName": "Vietnamese (Vietnam)", + "NativeName": "Tiếng Việt (Việt Nam)" + }, + { + "LanguageTag": "zh-CN", + "TranslationId": "zh-Hans", + "TranslationGroup": "Chinese (Simplified)", + "DisplayName": "Chinese (Simplified, China)", + "NativeName": "中文(中国)" + }, + { + "LanguageTag": "zh-Hans-HK", + "TranslationId": "zh-Hans", + "TranslationGroup": "Chinese (Simplified)", + "DisplayName": "Chinese (Simplified Han, Hong Kong SAR)", + "NativeName": "中文 (香港特别行政区)" + }, + { + "LanguageTag": "zh-Hans-MO", + "TranslationId": "zh-Hans", + "TranslationGroup": "Chinese (Simplified)", + "DisplayName": "Chinese (Simplified Han, Macao SAR)", + "NativeName": "中文 (澳门特别行政区)" + }, + { + "LanguageTag": "zh-HK", + "TranslationId": "zh-Hant", + "TranslationGroup": "Chinese (Traditional)", + "DisplayName": "Chinese (Traditional, Hong Kong SAR)", + "NativeName": "中文(香港特別行政區)" + }, + { + "LanguageTag": "zh-MO", + "TranslationId": "zh-Hant", + "TranslationGroup": "Chinese (Traditional)", + "DisplayName": "Chinese (Traditional, Macao SAR)", + "NativeName": "中文(澳門特別行政區)" + }, + { + "LanguageTag": "zh-SG", + "TranslationId": "zh-Hans", + "TranslationGroup": "Chinese (Simplified)", + "DisplayName": "Chinese (Simplified, Singapore)", + "NativeName": "中文(新加坡)" + }, + { + "LanguageTag": "zh-TW", + "TranslationId": "zh-Hant", + "TranslationGroup": "Chinese (Traditional)", + "DisplayName": "Chinese (Traditional, Taiwan)", + "NativeName": "中文(台灣)" + } +] \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Translations/Language.cs b/MetadataTranslator/Metadata Translator/Translations/Language.cs new file mode 100644 index 0000000..9cde8a9 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Translations/Language.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public class Language : INotifyPropertyChanged + { + #region INotifyPropertyChanged implementation + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void NotifyPropertyChanged(string propertyName = null) + { + if (PropertyChanged != null) + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + #endregion + + public string LanguageTag { get; set; } + public string TranslationId { get; set; } + public string TranslationGroup { get; set; } + public string DisplayName { get; set; } + public string NativeName { get; set; } + + private bool isSelected = false; + public bool IsSelected + { + get => isSelected; + set + { + isSelected = value; + NotifyPropertyChanged("IsSelected"); + } + } + + public bool IsModelDefault { get; set; } + public bool IsNotModelDefault { get => !IsModelDefault; } + } +} \ No newline at end of file diff --git a/MetadataTranslator/Metadata Translator/Translations/Translation.cs b/MetadataTranslator/Metadata Translator/Translations/Translation.cs new file mode 100644 index 0000000..78adcf0 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Translations/Translation.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public class Translation + { + public string text { get; set; } + public string to { get; set; } + } +} diff --git a/MetadataTranslator/Metadata Translator/Translations/TranslationGroup.cs b/MetadataTranslator/Metadata Translator/Translations/TranslationGroup.cs new file mode 100644 index 0000000..73102b3 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Translations/TranslationGroup.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public class TranslationGroup + { + public string Name { get; set; } + public string Tag { get; set; } + + public override bool Equals(object obj) + { + if (obj is TranslationGroup comparewith) + { + return Name == comparewith.Name && Tag == comparewith.Tag; + } + return false; + } + + public override int GetHashCode() + { + return new { Tag, Name }.GetHashCode(); + } + } +} diff --git a/MetadataTranslator/Metadata Translator/Translations/TranslationResult.cs b/MetadataTranslator/Metadata Translator/Translations/TranslationResult.cs new file mode 100644 index 0000000..0e17b4b --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Translations/TranslationResult.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Metadata_Translator +{ + public class TranslationResult + { + public List translations { get; set; } + } +} diff --git a/MetadataTranslator/Metadata Translator/Translations/TranslatorService.cs b/MetadataTranslator/Metadata Translator/Translations/TranslatorService.cs new file mode 100644 index 0000000..a109c0c --- /dev/null +++ b/MetadataTranslator/Metadata Translator/Translations/TranslatorService.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web.Script.Serialization; + +namespace Metadata_Translator +{ + public class TranslatorService + { + List Languages { get; set; } + string SourceLanguage { get; set; } + string SubscriptionKey { get; set; } + string Endpoint { get; set; } + string Location { get; set; } + + /// + /// + /// + /// + /// + /// + /// + /// + public TranslatorService(List languages, string sourceLanguage, string subscriptionKey, string endpoint, string location) + { + Languages = languages; + SourceLanguage = sourceLanguage; + + SubscriptionKey = subscriptionKey; + Endpoint = endpoint; + Location = location; + } + + public void Translate(List dataRows, bool replaceExistingTranslations) + { + /// No languages, or only the one source language? Nothing to translate! + /// + if (Languages == null || Languages.Count < 2) return; + + /// Get the target languages (i.e. not the SourceLanguage). + /// + List targetLanguages = Languages.Where(l => !l.LanguageTag.Equals(SourceLanguage)).ToList(); + + /// Filter down the data rows to those with values in the source language. + /// + List filteredRows = dataRows.Where(dr => !string.IsNullOrEmpty(dr.GetValue(SourceLanguage)))?.ToList(); + + /// No rows? Nothing to translate! + /// + if (filteredRows == null || filteredRows.Count == 0) return; + + /// Iterate over the TranslationGroups, all languages within the same group + /// share the same translation id. + /// + foreach(string id in targetLanguages.Select(tl => tl.TranslationId).Distinct()) + { + Translate(filteredRows, targetLanguages.Where(tl => tl.TranslationId.Equals(id)), id, replaceExistingTranslations); + } + } + + private void Translate(List dataRows, IEnumerable targetLanguages, string translationId, bool replaceExistingTranslations) + { + List rowsToTranslate = new List(); + + /// Don't replace existing translations? + /// Filter down the data rows to those that are empty for at least one of the target languages. + /// + if (!replaceExistingTranslations) + { + foreach(Language language in targetLanguages) + { + rowsToTranslate.AddRange(dataRows.Where(dr => string.IsNullOrEmpty(dr.GetValue(language.LanguageTag)))); + } + rowsToTranslate = rowsToTranslate.Distinct()?.ToList(); + } + else + { + /// Otherwise translate all data rows. + /// + rowsToTranslate = dataRows; + } + + /// Now translate the source strings recursively. + /// + Translate(rowsToTranslate, targetLanguages, translationId, replaceExistingTranslations, 0); + } + + private void Translate(List dataRows, IEnumerable targetLanguages, string translationId, bool replaceExistingTranslations, int iterationId) + { + int maxBatchSize = 100; + int batchStart = maxBatchSize * iterationId; + + /// Check if all strings have been translated. + /// + if (dataRows.Count <= batchStart) return; + + /// Assemble a translation batch of up to maxBatchSize. + /// + maxBatchSize = (dataRows.Count - batchStart) < maxBatchSize? dataRows.Count - batchStart : maxBatchSize; + List translationBatch = new List(); + for (int i = 0; i < maxBatchSize; i++) + { + translationBatch.Add(new { Text = dataRows[batchStart + i].GetValue(SourceLanguage) }); + } + + /// Translate the batch and assign the translated strings to the target languages. + /// + var translatedStrings = TranslateBatch(translationBatch, translationId); + for (int i = 0; i < maxBatchSize; i++) + { + foreach (Language language in targetLanguages) + { + dataRows[batchStart + i].SetValue(language.LanguageTag, translatedStrings[i], replaceExistingTranslations); + } + } + + Translate(dataRows, targetLanguages, translationId, replaceExistingTranslations, ++iterationId); + } + + private List TranslateBatch(List sourceObjects, string targetLanguage) + { + List translatedPhrases = new List(); + + var requestBody = new JavaScriptSerializer().Serialize(sourceObjects); + using (var client = new HttpClient()) + using (var request = new HttpRequestMessage()) + { + /// Build the Web request. + /// + request.Method = HttpMethod.Post; + request.RequestUri = new Uri($"{Endpoint}/translate?api-version=3.0&from={SourceLanguage}&to={targetLanguage}"); + request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json"); + request.Headers.Add("Ocp-Apim-Subscription-Key", SubscriptionKey); + request.Headers.Add("Ocp-Apim-Subscription-Region", Location); + + /// Send the translation request and get the response. + /// + HttpResponseMessage response = client.SendAsync(request).Result; + string result = response.Content.ReadAsStringAsync().Result; + + /// Parse the results and add the strings to the translated phrases if there was no error, + /// i.e. the target language was returned together with the translated string, which is + /// not the case if the service gives back an error message. + /// + List parsedResults = new JavaScriptSerializer().Deserialize>(result); + if (parsedResults != null) + { + for (int n = 0; n < parsedResults.Count; n++) + { + translatedPhrases.Add((string.IsNullOrEmpty(parsedResults[n].translations[0].to))? "" : parsedResults[n].translations[0].text); + } + } + } + + return translatedPhrases; + } + } +} diff --git a/MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml b/MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml new file mode 100644 index 0000000..8d17d2d --- /dev/null +++ b/MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml.cs b/MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml.cs new file mode 100644 index 0000000..8828182 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/UI/ImportExportPanel.xaml.cs @@ -0,0 +1,95 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using WF = System.Windows.Forms; + +namespace Metadata_Translator +{ + /// + /// Interaction logic for ImportExportPanel.xaml + /// + public partial class ImportExportPanel : UserControl + { + public ImportExportPanel() + { + InitializeComponent(); + } + + private void CancelButton_Click(object sender, RoutedEventArgs e) + { + var mainWnd = MainWindow.GetMainWindow(this); + if (mainWnd != null) + { + mainWnd.ImportExportToggle.IsChecked = false; + } + } + + private void OnImportButton_Click(object sender, RoutedEventArgs e) + { + var mainWnd = MainWindow.GetMainWindow(this); + if (mainWnd != null) + { + Button importButton = sender as Button; + + OpenFileDialog openFileDialog1 = new OpenFileDialog(); + + // Set filter options and filter index. + openFileDialog1.Filter = "CSV Files (.csv)|*.csv|All Files (*.*)|*.*"; + openFileDialog1.FilterIndex = 1; + + openFileDialog1.Multiselect = true; + openFileDialog1.CheckFileExists = true; + + if (openFileDialog1.ShowDialog() == true) + { + using (new Hourglass()) + { + + foreach (string filePath in openFileDialog1.FileNames) + { + string lcid = Path.GetFileNameWithoutExtension(filePath); + mainWnd.AddColumn(lcid); + mainWnd.DataModel?.ImportFromCsv(filePath, lcid, mainWnd.OverwriteTranslation); + } + } + } + + mainWnd.ImportExportToggle.IsChecked = false; + } + } + + private void OnExportButton_Click(object sender, RoutedEventArgs e) + { + var mainWnd = MainWindow.GetMainWindow(this); + if (mainWnd != null) + { + Button exportButton = sender as Button; + WF.FolderBrowserDialog folderDlg = new WF.FolderBrowserDialog + { + SelectedPath = mainWnd.LastUsedExportFolder, + Description = (string)exportButton.Tag, + ShowNewFolderButton = true, + }; + + WF.DialogResult result = folderDlg.ShowDialog(); + if (result == WF.DialogResult.OK) + { + mainWnd.LastUsedExportFolder = folderDlg.SelectedPath; + + using (new Hourglass()) + { + mainWnd.DataModel?.ExportToCsv(folderDlg.SelectedPath); + } + } + + mainWnd.ImportExportToggle.IsChecked = false; + } + } + } +} diff --git a/MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml b/MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml new file mode 100644 index 0000000..6eda33b --- /dev/null +++ b/MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml.cs b/MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml.cs new file mode 100644 index 0000000..893be04 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/UI/LanguagePanel.xaml.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Metadata_Translator +{ + /// + /// Interaction logic for LanguagePanel.xaml + /// + public partial class LanguagePanel : UserControl + { + public LanguagePanel() + { + InitializeComponent(); + } + + private void CancelButton_Click(object sender, RoutedEventArgs e) + { + var mainWnd = MainWindow.GetMainWindow(this); + if (mainWnd != null) + { + mainWnd.LanguageToggle.IsChecked = false; + } + } + + #region Dependency Properties + public static readonly DependencyProperty LanguagesProperty = + DependencyProperty.Register("Languages", typeof(ObservableCollection), typeof(LanguagePanel), + new PropertyMetadata(null, new PropertyChangedCallback(OnLanguagesChanged))); + + public ObservableCollection Languages + { + get { return (ObservableCollection)GetValue(LanguagesProperty); } + set { SetValue(LanguagesProperty, value); } + } + + private static void OnLanguagesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + if (sender is LanguagePanel langPanel) + { + langPanel.OnLanguagesChanged(e); + } + } + + private void OnLanguagesChanged(DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is ObservableCollection newItem) + { + newItem.CollectionChanged += LanguagesCollection_Changed; + } + else if (e.OldValue is ObservableCollection oldItem) + { + oldItem.CollectionChanged -= LanguagesCollection_Changed; + } + } + + private void LanguagesCollection_Changed(object sender, NotifyCollectionChangedEventArgs e) + { + TranslationGroups = Languages.Select(x => new TranslationGroup { Name = x.TranslationGroup, Tag = x.TranslationId }).Distinct().ToList(); + } + + + public static readonly DependencyProperty TranslationGroupsProperty = + DependencyProperty.Register("TranslationGroups", typeof(List), typeof(LanguagePanel)); + + public List TranslationGroups + { + get { return (List)GetValue(TranslationGroupsProperty); } + set { SetValue(TranslationGroupsProperty, value); } + } + #endregion + } +} diff --git a/MetadataTranslator/Metadata Translator/UI/MainWindow.xaml b/MetadataTranslator/Metadata Translator/UI/MainWindow.xaml new file mode 100644 index 0000000..f03f12a --- /dev/null +++ b/MetadataTranslator/Metadata Translator/UI/MainWindow.xaml @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MetadataTranslator/Metadata Translator/UI/MainWindow.xaml.cs b/MetadataTranslator/Metadata Translator/UI/MainWindow.xaml.cs new file mode 100644 index 0000000..c058818 --- /dev/null +++ b/MetadataTranslator/Metadata Translator/UI/MainWindow.xaml.cs @@ -0,0 +1,638 @@ +using Microsoft.AnalysisServices.Tabular; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Configuration; +using System.Dynamic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Web.Script.Serialization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Metadata_Translator +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + Configuration AppConfig { get; set; } + public MainWindow() + { + InitializeComponent(); + + AppConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + + SetDependencyProperty("SubscriptionKey"); + SetDependencyProperty("TranslatorEndpoint"); + SetDependencyProperty("TranslatorLocation"); + SetDependencyProperty("OverwriteTranslation"); + SetDependencyProperty("LastUsedExportFolder"); + + if (string.IsNullOrEmpty(SubscriptionKey)) + { + SettingsToggle.IsChecked = true; + } + } + + public MainWindow(StartupEventArgs e) : this() + { + try + { + if (e.Args.Length != 2) + { + throw new Exception((string)Application.Current.FindResource("InvalidArguments")); + } + else + { + PowerBIEngine = e.Args[0]; + DatabaseName = e.Args[1]; + } + + DataModel = DataModel.Connect(PowerBIEngine, DatabaseName); + + SetDependencyProperty("Languages"); + InitializeDataGrid(); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + Application.Current.Shutdown(); + } + } + + public void AddColumn(string lcid) + { + Language language = GetLanguageByLcid(lcid); + if (language != null) + { + dataGrid.Columns.Add(new DataGridTextColumn + { + Header = language.DisplayName, + Binding = new Binding(lcid) + }); + + language.IsSelected = true; + } + } + + #region Helper functions + /// + /// Set Dependency Properties that wrap apps ettings. + /// + /// + private void SetDependencyProperty(string parameterName) + { + switch (parameterName) + { + case "SubscriptionKey": + SubscriptionKey = AppConfig.AppSettings.Settings[parameterName].Value; + break; + case "TranslatorEndpoint": + TranslatorEndpoint = AppConfig.AppSettings.Settings[parameterName].Value; + break; + case "TranslatorLocation": + TranslatorLocation = AppConfig.AppSettings.Settings[parameterName].Value; + break; + case "OverwriteTranslation": + string configSetting = AppConfig.AppSettings.Settings[parameterName].Value; + OverwriteTranslation = bool.TryParse(configSetting, out bool value) && value; + break; + case "LastUsedExportFolder": + LastUsedExportFolder = AppConfig.AppSettings.Settings[parameterName].Value; + break; + case "Languages": + Languages = new ObservableCollection(); + foreach (var language in DataModel.SupportedLanguages) + { + Languages.Add(language); + } + break; + } + } + + /// + /// Initializes the datagrid columns and sets the ItemsSource to the default collection. + /// + private void InitializeDataGrid() + { + List cultures = DataModel.CultureNames; + + /// Create some setters and triggers for the + /// styles of the read-only columns. + /// + Trigger iIsSelectedTrigger = new Trigger() + { + Property = DataGridTextColumn.IsReadOnlyProperty, + Value = true + }; + + var foregroundSetter = new Setter(DataGridCell.ForegroundProperty, new SolidColorBrush(Colors.Black)); + iIsSelectedTrigger.Setters.Add(foregroundSetter); + + var backgroundSetter = new Setter(DataGridCell.BackgroundProperty, new SolidColorBrush(Colors.LightGray)); + + /// The first column is for the metadata container object. + /// + var objectColumnStyle = new Style(typeof(DataGridCell)); + objectColumnStyle.Setters.Add(backgroundSetter); + objectColumnStyle.Setters.Add(foregroundSetter); + objectColumnStyle.Triggers.Add(iIsSelectedTrigger); + dataGrid.Columns.Add(new DataGridTextColumn + { + Header = FindResource("TabularObjectColumnHeader").ToString(), + Binding = new Binding(DataModel.ContainerColumnHeader), + IsReadOnly = true, + CellStyle = objectColumnStyle + }); + + /// The second column is for the default culture of the data model, + /// which is always the first language in the list of data model cultures. + /// + var defaultLangColumnStyle = new Style(typeof(DataGridCell)); + defaultLangColumnStyle.Setters.Add(foregroundSetter); + defaultLangColumnStyle.Triggers.Add(iIsSelectedTrigger); + + /// Add a tooltip to flag that the default culture is readonly. + /// + var objectHeaderStyle = new Style(typeof(DataGridColumnHeader)); + objectHeaderStyle.Setters.Add(new Setter(ToolTipService.ToolTipProperty, + FindResource("DefaultCultureColumnHeaderToolTip").ToString())); + + Language defaultLang = GetLanguageByLcid(cultures[0]); + dataGrid.Columns.Add(new DataGridTextColumn + { + Header = $"{defaultLang.DisplayName}*", + HeaderStyle = objectHeaderStyle, + Binding = new Binding(cultures[0]), + IsReadOnly = true, + CellStyle = defaultLangColumnStyle + }); + SetLanguageFlags(cultures[0], true, true); + + + /// Add the remaining languages that already exist in the data model + /// and mark them as selected in the list of supported languages. + /// + for (int i = 1; i< cultures.Count; i++) + { + AddColumn(cultures[i]); + } + + /// And set Captions as the default content of the datagrid. + dataGrid.ItemsSource = DataModel.Captions; + } + + /// + /// Marks a language specified by lcid as selected and as model default. + /// + /// + /// + /// + private void SetLanguageFlags(string lcid, bool isSelected, bool isModelDefault = false) + { + Language language = Languages.Where(x => x.LanguageTag.Equals(lcid)).FirstOrDefault(); + if (language != null) + { + language.IsSelected = isSelected; + language.IsModelDefault = isModelDefault; + } + } + + /// + /// Gets the Language object based on the lcid (i.e. LanguageTag). + /// + /// + /// + private Language GetLanguageByLcid(string lcid) + { + return Languages.Where(x => x.LanguageTag.Equals(lcid)).FirstOrDefault(); + } + + /// + /// Get a handle to the main window object so that other user controls can + /// access the public properties of the main window object directly. + /// + /// + /// + public static MainWindow GetMainWindow(DependencyObject child) + { + if (child == null) return null; + + DependencyObject parentObject = VisualTreeHelper.GetParent(child); + if (parentObject is MainWindow parent) + { + return parent; + } + else + { + return GetMainWindow(parentObject); + } + } + + /// + /// Save app settings to the application config file. + /// + /// + /// + private void OnAppSettingChanged(string settingName, string newValue) + { + AppConfig.AppSettings.Settings[settingName].Value = newValue; + AppConfig.Save(ConfigurationSaveMode.Modified); + ConfigurationManager.RefreshSection("appSettings"); + } + #endregion + + #region Dependency Properties + public static readonly DependencyProperty DataModelProperty = + DependencyProperty.Register("DataModel", typeof(DataModel), typeof(MainWindow)); + + public DataModel DataModel + { + get { return (DataModel)GetValue(DataModelProperty); } + set { SetValue(DataModelProperty, value); } + } + + /// + /// Supported languages collection + /// + public static readonly DependencyProperty LanguagesProperty = + DependencyProperty.Register("Languages", typeof(ObservableCollection), typeof(MainWindow), + new PropertyMetadata(null, new PropertyChangedCallback(OnLanguagesChanged))); + + public ObservableCollection Languages + { + get { return (ObservableCollection)GetValue(LanguagesProperty); } + set { SetValue(LanguagesProperty, value); } + } + + private static void OnLanguagesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + (sender as MainWindow).OnLanguagesChanged(e); + } + + private void OnLanguagesChanged(DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is ObservableCollection newCollection) + { + newCollection.CollectionChanged += LanguagesCollection_Changed; + + if (newCollection.Count > 0) + AttachLanguagePropertyChangedEventHandler(newCollection); + } + else if (e.OldValue is ObservableCollection oldCollection) + { + oldCollection.CollectionChanged -= LanguagesCollection_Changed; + + if (oldCollection.Count > 0) + RemoveLanguagePropertyChangedEventHandler(oldCollection); + } + } + + private void LanguagesCollection_Changed(object sender, NotifyCollectionChangedEventArgs e) + { + switch(e.Action) + { + case NotifyCollectionChangedAction.Add: + AttachLanguagePropertyChangedEventHandler(e.NewItems); + break; + case NotifyCollectionChangedAction.Remove: + RemoveLanguagePropertyChangedEventHandler(e.OldItems); + break; + case NotifyCollectionChangedAction.Replace: + RemoveLanguagePropertyChangedEventHandler(e.OldItems); + AttachLanguagePropertyChangedEventHandler(e.NewItems); + break; + default: + throw new NotSupportedException(); + } + } + + private void AttachLanguagePropertyChangedEventHandler(System.Collections.IList items) + { + foreach (Language language in items) + { + language.PropertyChanged += LanguageProperty_Changed; + } + } + + private void RemoveLanguagePropertyChangedEventHandler(System.Collections.IList items) + { + foreach (Language language in items) + { + language.PropertyChanged -= LanguageProperty_Changed; + } + } + + /// + /// Event handler for LanguageProperty_Changed event to add or remove a lanugage + /// from the datagrid headers. + /// + /// + /// + private void LanguageProperty_Changed(object sender, PropertyChangedEventArgs e) + { + + if(sender is Language language + && e.PropertyName == "IsSelected" + && !language.LanguageTag.Equals(DataModel.DefaultCulture)) + { + var existingColumn = dataGrid.Columns.Where(x => language.DisplayName.Equals(x.Header.ToString())).FirstOrDefault(); + if (language.IsSelected) + { + if (existingColumn == null) + { + dataGrid.Columns.Add(new DataGridTextColumn + { + Header = language.DisplayName, + Binding = new Binding(language.LanguageTag) + }); + } + } + else + { + if (existingColumn != null) + { + dataGrid.Columns.Remove(existingColumn); + } + } + } + } + + /// + /// SubscriptionKey Property + /// + public static readonly DependencyProperty SubscriptionKeyProperty = + DependencyProperty.Register("SubscriptionKey", typeof(string), typeof(MainWindow), + new PropertyMetadata(new PropertyChangedCallback(OnSubscriptionKeyChanged))); + + public string SubscriptionKey + { + get { return (string)GetValue(SubscriptionKeyProperty); } + set { SetValue(SubscriptionKeyProperty, value); } + } + + private static void OnSubscriptionKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as MainWindow)?.OnAppSettingChanged("SubscriptionKey", (string)e.NewValue); + + } + + /// + /// TranslatorEndpoint Property + /// + public static readonly DependencyProperty TranslatorEndpointProperty = + DependencyProperty.Register("TranslatorEndpoint", typeof(string), typeof(MainWindow), + new PropertyMetadata(new PropertyChangedCallback(OnTranslatorEndpointChanged))); + + public string TranslatorEndpoint + { + get { return (string)GetValue(TranslatorEndpointProperty); } + set { SetValue(TranslatorEndpointProperty, value); } + } + + private static void OnTranslatorEndpointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as MainWindow)?.OnAppSettingChanged("TranslatorEndpoint", (string)e.NewValue); + + } + + /// + /// TranslatorLocation Property + /// + public static readonly DependencyProperty TranslatorLocationProperty = + DependencyProperty.Register("TranslatorLocation", typeof(string), typeof(MainWindow), + new PropertyMetadata(new PropertyChangedCallback(OnTranslatorLocationChanged))); + + public string TranslatorLocation + { + get { return (string)GetValue(TranslatorLocationProperty); } + set { SetValue(TranslatorLocationProperty, value); } + } + + private static void OnTranslatorLocationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as MainWindow)?.OnAppSettingChanged("TranslatorLocation", (string)e.NewValue); + + } + + /// + /// OverwriteTranslation Property + /// + public static readonly DependencyProperty OverwriteTranslationProperty = + DependencyProperty.Register("OverwriteTranslation", typeof(bool), typeof(MainWindow), + new PropertyMetadata(new PropertyChangedCallback(OnOverwriteTranslationChanged))); + + public bool OverwriteTranslation + { + get { return (bool)GetValue(OverwriteTranslationProperty); } + set { SetValue(OverwriteTranslationProperty, value); } + } + + private static void OnOverwriteTranslationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as MainWindow)?.OnAppSettingChanged("OverwriteTranslation", e.NewValue.ToString()); + + } + + /// + /// LastUsedExportFolder Property + /// + public static readonly DependencyProperty LastUsedExportFolderProperty = + DependencyProperty.Register("LastUsedExportFolder", typeof(string), typeof(MainWindow), + new PropertyMetadata(new PropertyChangedCallback(OnLastUsedExportFolderChanged))); + + public string LastUsedExportFolder + { + get { return (string)GetValue(LastUsedExportFolderProperty); } + set { SetValue(LastUsedExportFolderProperty, value); } + } + + private static void OnLastUsedExportFolderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as MainWindow)?.OnAppSettingChanged("LastUsedExportFolder", (string)e.NewValue); + + } + + /// + /// PowerBIEngine Property + /// + public static readonly DependencyProperty PowerBIEngineProperty = + DependencyProperty.Register("PowerBIEngine", typeof(string), typeof(MainWindow)); + public string PowerBIEngine + { + set { SetValue(PowerBIEngineProperty, value); } + get { return (string)GetValue(PowerBIEngineProperty); } + } + + /// + /// DatabaseName Property + /// + public static readonly DependencyProperty DatabaseNameProperty = + DependencyProperty.Register("DatabaseName", typeof(string), typeof(MainWindow)); + public string DatabaseName + { + set { SetValue(DatabaseNameProperty, value); } + get { return (string)GetValue(DatabaseNameProperty); } + } + #endregion + + #region Event Handlers + /// + /// Event handler for ToolBar_Loaded event to remove some of the + /// unnecessary standard UI elements of the toolbar. + /// + /// + /// + /// + private void ToolBar_Loaded(object sender, RoutedEventArgs e) + { + if(sender is ToolBar toolBar) + { + if (toolBar.Template.FindName("OverflowGrid", toolBar) is FrameworkElement overflowGrid) + { + overflowGrid.Visibility = Visibility.Collapsed; + } + + if (toolBar.Template.FindName("MainPanelBorder", toolBar) is FrameworkElement mainPanelBorder) + { + mainPanelBorder.Margin = new Thickness(); + } + } + } + + /// + /// Enabling the deselect of a selected toggle button on mouseclick. + /// + /// + /// + private void OnToggleButton_Click(object sender, RoutedEventArgs e) + { + if(sender is RadioButton radio) + { + bool tagState = (radio.Tag == null); + radio.Tag = (radio.Tag == null) ? new object() : null; + radio.IsChecked = tagState; + } + } + + /// + /// Unsets the tag that indicates if a toggle button was clicked to be deselected. + /// + /// + /// + private void OnToggleButton_Uncheck(object sender, RoutedEventArgs e) + { + if (sender is RadioButton radio) + { + radio.Tag = null; + } + } + + /// + /// Display the Captions in the datagrid. + /// + /// + /// + private void OnCaptionRadioButton_Click(object sender, RoutedEventArgs e) + { + if (DataModel != null) + { + dataGrid.ItemsSource = DataModel.Captions; + } + } + + /// + /// Display the Descriptions in the datagrid. + /// + /// + /// + private void OnDescriptionRadioButton_Click(object sender, RoutedEventArgs e) + { + if (DataModel != null) + { + dataGrid.ItemsSource = DataModel.Descriptions; + } + } + + /// + /// Display the DisplayFolder strings in the datagrid. + /// + /// + /// + private void OnDisplayFolderRadioButton_Click(object sender, RoutedEventArgs e) + { + if (DataModel != null) + { + dataGrid.ItemsSource = DataModel.DisplayFolders; + } + } + + /// + /// Make the terms in the default culture more translatable by splitting the strings + /// based on camel casing. + /// + /// + /// + private void OnPrepareButton_Click(object sender, RoutedEventArgs e) + { + if (DataModel == null) return; + + string defaultCulture = DataModel.DefaultCulture; + foreach(ExpandoObject row in DataModel.GetAllDataRows()) + { + ((IDictionary)row)[defaultCulture] = row.SeparateCamelCase(defaultCulture); + } + } + + /// + /// Translate the strings tn the default culture into the + /// selected target languages. + /// + /// + /// + private void OnTranslateButton_Click(object sender, RoutedEventArgs e) + { + if (DataModel.HasTargetLanguages) + { + using (new Hourglass()) + { + var ts = new TranslatorService(Languages.Where(x => x.IsSelected == true)?.ToList(), DataModel.DefaultCulture, SubscriptionKey, TranslatorEndpoint, TranslatorLocation); + ts.Translate(DataModel.GetAllDataRows(), OverwriteTranslation); + } + } + else + { + MessageBox.Show(FindResource("NothingToTranslate").ToString()); + LanguageToggle.IsChecked = true; + } + } + + /// + /// Apply the current values in the Captions, Descriptions, and DisplayFolders collections to the data model. + /// + /// + /// + private void OnApplyButton_Click(object sender, RoutedEventArgs e) + { + DataModel.Update(); + } + #endregion + } +} diff --git a/MetadataTranslator/Metadata Translator/UI/SettingsPanel.xaml b/MetadataTranslator/Metadata Translator/UI/SettingsPanel.xaml new file mode 100644 index 0000000..0b5fd7c --- /dev/null +++ b/MetadataTranslator/Metadata Translator/UI/SettingsPanel.xaml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + +