From f3855b9cf257fcca6dce39927d88c96429cdd377 Mon Sep 17 00:00:00 2001 From: ryanbrandenburg Date: Tue, 15 Dec 2015 16:36:17 -0800 Subject: [PATCH] * Throw proper exception when missing resource --- .../Properties/Resources.Designer.cs | 62 +++++++++ .../ResourceManagerStringLocalizer.cs | 50 +++++-- .../Resources.resx | 126 ++++++++++++++++++ .../ResourceManagerStringLocalizerTest.cs | 52 +++++++- 4 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 src/Microsoft.Extensions.Localization/Properties/Resources.Designer.cs create mode 100644 src/Microsoft.Extensions.Localization/Resources.resx diff --git a/src/Microsoft.Extensions.Localization/Properties/Resources.Designer.cs b/src/Microsoft.Extensions.Localization/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..1123d648ad --- /dev/null +++ b/src/Microsoft.Extensions.Localization/Properties/Resources.Designer.cs @@ -0,0 +1,62 @@ +// +namespace Microsoft.Extensions.Localization +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.Extensions.Localization.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// The manifest '{0}' was not found. + /// + internal static string Localization_MissingManifest + { + get { return GetString("Localization_MissingManifest"); } + } + + /// + /// The manifest '{0}' was not found. + /// + internal static string FormatLocalization_MissingManifest(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("Localization_MissingManifest"), p0); + } + + /// + /// No manifests exist for the current culture. + /// + internal static string Localization_MissingManifest_Parent + { + get { return GetString("Localization_MissingManifest_Parent"); } + } + + /// + /// No manifests exist for the current culture. + /// + internal static string FormatLocalization_MissingManifest_Parent() + { + return GetString("Localization_MissingManifest_Parent"); + } + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Microsoft.Extensions.Localization/ResourceManagerStringLocalizer.cs b/src/Microsoft.Extensions.Localization/ResourceManagerStringLocalizer.cs index 47d4884bd6..80a6af7d6d 100644 --- a/src/Microsoft.Extensions.Localization/ResourceManagerStringLocalizer.cs +++ b/src/Microsoft.Extensions.Localization/ResourceManagerStringLocalizer.cs @@ -153,6 +153,13 @@ namespace Microsoft.Extensions.Localization ? GetResourceNamesFromCultureHierarchy(culture) : GetResourceNamesForCulture(culture); + if (resourceNames == null && !includeParentCultures) + { + var resourceStreamName = GetResourceStreamName(culture); + throw new MissingManifestResourceException( + Resources.FormatLocalization_MissingManifest(resourceStreamName)); + } + foreach (var name in resourceNames) { var value = GetStringSafely(name, culture); @@ -197,17 +204,21 @@ namespace Microsoft.Extensions.Localization var currentCulture = startingCulture; var resourceNames = new HashSet(); + var hasAnyCultures = false; + while (true) { - try + + var cultureResourceNames = GetResourceNamesForCulture(currentCulture); + + if (cultureResourceNames != null) { - var cultureResourceNames = GetResourceNamesForCulture(currentCulture); foreach (var resourceName in cultureResourceNames) { resourceNames.Add(resourceName); } + hasAnyCultures = true; } - catch (MissingManifestResourceException) { } if (currentCulture == currentCulture.Parent) { @@ -218,10 +229,15 @@ namespace Microsoft.Extensions.Localization currentCulture = currentCulture.Parent; } + if (!hasAnyCultures) + { + throw new MissingManifestResourceException(Resources.Localization_MissingManifest_Parent); + } + return resourceNames; } - private IList GetResourceNamesForCulture(CultureInfo culture) + private string GetResourceStreamName(CultureInfo culture) { var resourceStreamName = _resourceBaseName; if (!string.IsNullOrEmpty(culture.Name)) @@ -230,22 +246,36 @@ namespace Microsoft.Extensions.Localization } resourceStreamName += ".resources"; + return resourceStreamName; + } + + private IList GetResourceNamesForCulture(CultureInfo culture) + { + var resourceStreamName = GetResourceStreamName(culture); + var cacheKey = $"assembly={_resourceAssemblyWrapper.FullName};resourceStreamName={resourceStreamName}"; var cultureResourceNames = _resourceNamesCache.GetOrAdd(cacheKey, _ => { - var names = new List(); using (var cultureResourceStream = _resourceAssemblyWrapper.GetManifestResourceStream(resourceStreamName)) - using (var resources = new ResourceReader(cultureResourceStream)) { - foreach (DictionaryEntry entry in resources) + if (cultureResourceStream == null) { - var resourceName = (string)entry.Key; - names.Add(resourceName); + return null; + } + + using (var resources = new ResourceReader(cultureResourceStream)) + { + var names = new List(); + foreach (DictionaryEntry entry in resources) + { + var resourceName = (string)entry.Key; + names.Add(resourceName); + } + return names; } } - return names; }); return cultureResourceNames; diff --git a/src/Microsoft.Extensions.Localization/Resources.resx b/src/Microsoft.Extensions.Localization/Resources.resx new file mode 100644 index 0000000000..b679f04664 --- /dev/null +++ b/src/Microsoft.Extensions.Localization/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The manifest '{0}' was not found. + + + No manifests exist for the current culture. + + \ No newline at end of file diff --git a/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerTest.cs b/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerTest.cs index 0ec5a69dfa..ef8a6f074c 100644 --- a/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerTest.cs +++ b/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerTest.cs @@ -59,10 +59,60 @@ namespace Microsoft.Extensions.Localization.Tests Assert.Equal(expectedCallCount, resourceAssembly2.GetManifestResourceStreamCallCount); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ResourceManagerStringLocalizer_GetAllStrings_ReturnsExpectedValue(bool includeParentCultures) + { + // Arrange + var baseName = "test"; + var resourceNamesCache = new ResourceNamesCache(); + var resourceAssembly = new TestAssemblyWrapper(); + var resourceManager = new TestResourceManager(baseName, resourceAssembly.Assembly); + var localizer = new ResourceManagerStringLocalizer(resourceManager, resourceAssembly, baseName, resourceNamesCache); + + // Act + // We have to access the result so it evaluates. + var strings = localizer.GetAllStrings(includeParentCultures).ToList(); + + // Assert + var value = Assert.Single(strings); + Assert.Equal("TestName", value.Value); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ResourceManagerStringLocalizer_GetAllStrings_MissingResourceThrows(bool includeParentCultures) + { + // Arrange + var resourceNamesCache = new ResourceNamesCache(); + var baseName = "testington"; + var resourceAssembly = new TestAssemblyWrapper("Assembly1"); + var resourceManager = new TestResourceManager(baseName, resourceAssembly.Assembly); + var localizer = new ResourceManagerWithCultureStringLocalizer( + resourceManager, + resourceAssembly.Assembly, + baseName, + resourceNamesCache, + CultureInfo.CurrentCulture); + + // Act & Assert + var exception = Assert.Throws(() => + { + // We have to access the result so it evaluates. + localizer.GetAllStrings(includeParentCultures).ToArray(); + }); + var expected = includeParentCultures + ? "No manifests exist for the current culture." + : "The manifest 'testington.en-US.resources' was not found."; + Assert.Equal(expected, exception.Message); + } + private static Stream MakeResourceStream() { var stream = new MemoryStream(); - var resourceWriter = new ResourceWriter(stream); + var resourceWriter = new ResourceWriter(stream); resourceWriter.AddResource("TestName", "value"); resourceWriter.Generate(); stream.Position = 0;