From 6a0ebfa343057f3c28f1b8fa6d6d977eb404b540 Mon Sep 17 00:00:00 2001 From: JohanLarsson Date: Sat, 6 Aug 2016 16:14:22 +0200 Subject: [PATCH] Respect NeutralResourcesLanguageAttribute. Fix #19 --- Gu.Localization.sln | 9 +- Gu.Localization/Internals/Culture.cs | 12 ++ Gu.Localization/Internals/ResourceCultures.cs | 35 ++++- Gu.Wpf.Localization.Demo/App.xaml.cs | 3 - .../Gu.Wpf.Localization.UiTests.csproj | 7 +- ...pf.Localization.UiTests.csproj.DotSettings | 2 + .../Helpers/StartInfo.cs | 37 ++++++ .../Helpers/UiItemExt.cs | 19 +++ Gu.Wpf.Localization.UiTests/StartInfo.cs | 27 ---- ...Translates.cs => TranslatesDemoProject.cs} | 2 +- .../TranslatesWithNeutralLanguage.cs | 48 +++++++ .../App.config | 6 + .../App.xaml | 9 ++ .../App.xaml.cs | 19 +++ ...pf.Localization.WithNeutralLanguage.csproj | 110 ++++++++++++++++ .../MainWindow.xaml | 53 ++++++++ .../MainWindow.xaml.cs | 12 ++ .../Properties/AssemblyInfo.cs | 49 +++++++ .../Properties/Resources.Designer.cs | 72 ++++++++++ .../Properties/Resources.pt.resx | 123 ++++++++++++++++++ .../Properties/Resources.resx | 120 +++++++++++++++++ .../Properties/Settings.Designer.cs | 30 +++++ .../Properties/Settings.settings | 7 + .../LanguageSelector/LanguageSelector.cs | 23 ++-- 24 files changed, 785 insertions(+), 49 deletions(-) create mode 100644 Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj.DotSettings create mode 100644 Gu.Wpf.Localization.UiTests/Helpers/StartInfo.cs create mode 100644 Gu.Wpf.Localization.UiTests/Helpers/UiItemExt.cs delete mode 100644 Gu.Wpf.Localization.UiTests/StartInfo.cs rename Gu.Wpf.Localization.UiTests/{Translates.cs => TranslatesDemoProject.cs} (99%) create mode 100644 Gu.Wpf.Localization.UiTests/TranslatesWithNeutralLanguage.cs create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/App.config create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/App.xaml create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/App.xaml.cs create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/Gu.Wpf.Localization.WithNeutralLanguage.csproj create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/MainWindow.xaml create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/MainWindow.xaml.cs create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/Properties/AssemblyInfo.cs create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/Properties/Resources.Designer.cs create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/Properties/Resources.pt.resx create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/Properties/Resources.resx create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/Properties/Settings.Designer.cs create mode 100644 Gu.Wpf.Localization.WithNeutralLanguage/Properties/Settings.settings diff --git a/Gu.Localization.sln b/Gu.Localization.sln index 2c61365d..68408537 100644 --- a/Gu.Localization.sln +++ b/Gu.Localization.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{867813BE-358B-4C9E-9FA6-07168882AA9D}" ProjectSection(SolutionItems) = preProject @@ -37,6 +37,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{EFFBB24C-1 .paket\Readme.paket.md = .paket\Readme.paket.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gu.Wpf.Localization.WithNeutralLanguage", "Gu.Wpf.Localization.WithNeutralLanguage\Gu.Wpf.Localization.WithNeutralLanguage.csproj", "{8F238B89-C06A-4935-8EE9-1E84D297DC4E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +81,10 @@ Global {24A5D9D2-60D8-47B7-8750-48E85A2218DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {24A5D9D2-60D8-47B7-8750-48E85A2218DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {24A5D9D2-60D8-47B7-8750-48E85A2218DE}.Release|Any CPU.Build.0 = Release|Any CPU + {8F238B89-C06A-4935-8EE9-1E84D297DC4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F238B89-C06A-4935-8EE9-1E84D297DC4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F238B89-C06A-4935-8EE9-1E84D297DC4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F238B89-C06A-4935-8EE9-1E84D297DC4E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -87,5 +93,6 @@ Global {85009EF0-C922-4EDB-9648-495B56B30CFC} = {7C464917-49E0-40F2-BB85-EB16977B87A5} {A8722CC6-E477-43B6-A1A0-B7A062FBB290} = {7C464917-49E0-40F2-BB85-EB16977B87A5} {D77D4706-D218-4DC6-B117-611AF843FED9} = {7C464917-49E0-40F2-BB85-EB16977B87A5} + {8F238B89-C06A-4935-8EE9-1E84D297DC4E} = {7C464917-49E0-40F2-BB85-EB16977B87A5} EndGlobalSection EndGlobal diff --git a/Gu.Localization/Internals/Culture.cs b/Gu.Localization/Internals/Culture.cs index e1f24135..63492cf8 100644 --- a/Gu.Localization/Internals/Culture.cs +++ b/Gu.Localization/Internals/Culture.cs @@ -18,6 +18,18 @@ internal static bool Exists(string name) return CultureNames.Contains(name); } + internal static bool TryGet(string name, out CultureInfo culture) + { + if (Exists(name)) + { + culture = CultureInfo.GetCultureInfo(name); + return true; + } + + culture = null; + return false; + } + internal static bool NameEquals(CultureInfo first, CultureInfo other) { return CultureInfoComparer.ByName.Equals(first, other); diff --git a/Gu.Localization/Internals/ResourceCultures.cs b/Gu.Localization/Internals/ResourceCultures.cs index dced0d4d..ae516767 100644 --- a/Gu.Localization/Internals/ResourceCultures.cs +++ b/Gu.Localization/Internals/ResourceCultures.cs @@ -5,6 +5,8 @@ using System.Globalization; using System.IO; using System.Linq; + using System.Reflection; + using System.Resources; /// Utility class for finding resources. internal static class ResourceCultures @@ -17,7 +19,7 @@ internal static class ResourceCultures /// 2) Check that the folder contains resource files /// The result is not cached /// - /// The directory to chek + /// The directory to check /// The cultures found. If none an empty list is returned. internal static IReadOnlyList GetAllCultures(DirectoryInfo executingDirectory) { @@ -26,8 +28,8 @@ internal static IReadOnlyList GetAllCultures(DirectoryInfo executin return EmptyCultures; } - List cultures = null; - foreach (var directory in executingDirectory.EnumerateDirectories()) + HashSet cultures = null; + foreach (var directory in executingDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)) { var cultureName = directory.Name; if (!Culture.Exists(directory.Name)) @@ -36,17 +38,38 @@ internal static IReadOnlyList GetAllCultures(DirectoryInfo executin } if (directory.EnumerateFiles("*.resources.dll", SearchOption.TopDirectoryOnly).Any()) + { + CultureInfo culture; + if (Culture.TryGet(cultureName, out culture)) + { + if (cultures == null) + { + cultures = new HashSet(CultureInfoComparer.ByName); + } + + cultures.Add(culture); + } + } + } + + var entryAssembly = Assembly.GetEntryAssembly(); + if (entryAssembly != null) + { + var neutralLanguageAttribute = (NeutralResourcesLanguageAttribute)Attribute.GetCustomAttribute(entryAssembly, typeof(NeutralResourcesLanguageAttribute)); + var name = neutralLanguageAttribute?.CultureName; + CultureInfo neutralCulture; + if (name != null && Culture.TryGet(name, out neutralCulture)) { if (cultures == null) { - cultures = new List(); + cultures = new HashSet(CultureInfoComparer.ByName); } - cultures.Add(CultureInfo.GetCultureInfo(cultureName)); + cultures.Add(neutralCulture); } } - return cultures ?? EmptyCultures; + return cultures?.OrderBy(x => x.Name).ToArray() ?? EmptyCultures; } internal static DirectoryInfo DefaultResourceDirectory() diff --git a/Gu.Wpf.Localization.Demo/App.xaml.cs b/Gu.Wpf.Localization.Demo/App.xaml.cs index fb398c9b..a0e6fe52 100644 --- a/Gu.Wpf.Localization.Demo/App.xaml.cs +++ b/Gu.Wpf.Localization.Demo/App.xaml.cs @@ -4,9 +4,6 @@ using System.Threading; using System.Windows; - /// - /// Interaction logic for App.xaml - /// public partial class App : Application { protected override void OnStartup(StartupEventArgs e) diff --git a/Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj b/Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj index 84486094..c8a770a8 100644 --- a/Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj +++ b/Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj @@ -39,6 +39,7 @@ + @@ -49,10 +50,12 @@ - + + - + + diff --git a/Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj.DotSettings b/Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj.DotSettings new file mode 100644 index 00000000..8b01856a --- /dev/null +++ b/Gu.Wpf.Localization.UiTests/Gu.Wpf.Localization.UiTests.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/Gu.Wpf.Localization.UiTests/Helpers/StartInfo.cs b/Gu.Wpf.Localization.UiTests/Helpers/StartInfo.cs new file mode 100644 index 00000000..bd1c594c --- /dev/null +++ b/Gu.Wpf.Localization.UiTests/Helpers/StartInfo.cs @@ -0,0 +1,37 @@ +namespace Gu.Wpf.Localization.UiTests +{ + using System; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Reflection; + + public static class StartInfo + { + public static ProcessStartInfo DemoProject { get; } = CreateStartUpInfo("Gu.Wpf.Localization.Demo", CultureInfo.GetCultureInfo("en")); + + public static ProcessStartInfo WithNeutralLanguageProject { get; } = CreateStartUpInfo("Gu.Wpf.Localization.WithNeutralLanguage", CultureInfo.GetCultureInfo("en")); + + private static ProcessStartInfo CreateStartUpInfo(string assemblyName, CultureInfo culture) + { + var processStartInfo = new ProcessStartInfo + { + Arguments = culture.Name, + FileName = GetExeFileName(assemblyName), + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + return processStartInfo; + } + + private static string GetExeFileName(string assemblyName) + { + var testAssembnly = Assembly.GetExecutingAssembly(); + var testDirestory = Path.GetDirectoryName(new Uri(testAssembnly.CodeBase).AbsolutePath); + var exeDirectory = testDirestory.Replace(testAssembnly.GetName().Name, assemblyName); + var fileName = Path.Combine(exeDirectory, $"{assemblyName}.exe"); + return fileName; + } + } +} diff --git a/Gu.Wpf.Localization.UiTests/Helpers/UiItemExt.cs b/Gu.Wpf.Localization.UiTests/Helpers/UiItemExt.cs new file mode 100644 index 00000000..bfa5d12e --- /dev/null +++ b/Gu.Wpf.Localization.UiTests/Helpers/UiItemExt.cs @@ -0,0 +1,19 @@ +namespace Gu.Wpf.Localization.UiTests +{ + using TestStack.White.UIItems; + using TestStack.White.UIItems.Finders; + + public static class UiItemExt + { + public static string ItemStatus(this IUIItem item) + { + return (string)item.AutomationElement.Current.ItemStatus; + } + + public static T GetByText(this UIItemContainer container, string text) + where T : UIItem + { + return container.Get(SearchCriteria.ByText(text)); + } + } +} \ No newline at end of file diff --git a/Gu.Wpf.Localization.UiTests/StartInfo.cs b/Gu.Wpf.Localization.UiTests/StartInfo.cs deleted file mode 100644 index 16988a09..00000000 --- a/Gu.Wpf.Localization.UiTests/StartInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Gu.Wpf.Localization.UiTests -{ - using System; - using System.Diagnostics; - - using Gu.Wpf.Localization.Demo; - - public static class StartInfo - { - public static ProcessStartInfo DemoProject - { - get - { - var assembly = typeof(MainWindow).Assembly; - var processStartInfo = new ProcessStartInfo - { - Arguments = "en", - FileName = new Uri(assembly.CodeBase, UriKind.Absolute).LocalPath, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - return processStartInfo; - } - } - } -} diff --git a/Gu.Wpf.Localization.UiTests/Translates.cs b/Gu.Wpf.Localization.UiTests/TranslatesDemoProject.cs similarity index 99% rename from Gu.Wpf.Localization.UiTests/Translates.cs rename to Gu.Wpf.Localization.UiTests/TranslatesDemoProject.cs index 828ab99d..0533e4fe 100644 --- a/Gu.Wpf.Localization.UiTests/Translates.cs +++ b/Gu.Wpf.Localization.UiTests/TranslatesDemoProject.cs @@ -8,7 +8,7 @@ using TestStack.White.UIItems; using TestStack.White.UIItems.WindowItems; - public class Translates + public class TranslatesDemoProject { private Application application; private Window window; diff --git a/Gu.Wpf.Localization.UiTests/TranslatesWithNeutralLanguage.cs b/Gu.Wpf.Localization.UiTests/TranslatesWithNeutralLanguage.cs new file mode 100644 index 00000000..92e0a427 --- /dev/null +++ b/Gu.Wpf.Localization.UiTests/TranslatesWithNeutralLanguage.cs @@ -0,0 +1,48 @@ +namespace Gu.Wpf.Localization.UiTests +{ + using NUnit.Framework; + using TestStack.White; + using TestStack.White.UIItems; + using TestStack.White.UIItems.WindowItems; + + public class TranslatesWithNeutralLanguage + { + private Application application; + private Window window; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + this.application = Application.AttachOrLaunch(StartInfo.WithNeutralLanguageProject); + this.window = this.application.GetWindow("MainWindow"); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + this.application?.Dispose(); + } + + [Test] + public void EffectiveCulture() + { + Assert.AreEqual("en", this.window.Get