// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Localization.Internal;
namespace Microsoft.Framework.Localization
{
///
/// An that uses the and
/// to provide localized strings.
///
public class ResourceManagerStringLocalizer : IStringLocalizer
{
private readonly ConcurrentDictionary _missingManifestCache =
new ConcurrentDictionary();
private readonly IResourceNamesCache _resourceNamesCache;
private readonly ResourceManager _resourceManager;
private readonly AssemblyWrapper _resourceAssemblyWrapper;
private readonly string _resourceBaseName;
///
/// Creates a new .
///
/// The to read strings from.
/// The that contains the strings as embedded resources.
/// The base name of the embedded resource in the that contains the strings.
/// Cache of the list of strings for a given resource assembly name.
public ResourceManagerStringLocalizer(
[NotNull] ResourceManager resourceManager,
[NotNull] Assembly resourceAssembly,
[NotNull] string baseName,
[NotNull] IResourceNamesCache resourceNamesCache)
: this(resourceManager, new AssemblyWrapper(resourceAssembly), baseName, resourceNamesCache)
{
}
///
/// Intended for testing purposes only.
///
public ResourceManagerStringLocalizer(
[NotNull] ResourceManager resourceManager,
[NotNull] AssemblyWrapper resourceAssemblyWrapper,
[NotNull] string baseName,
[NotNull] IResourceNamesCache resourceNamesCache)
{
_resourceAssemblyWrapper = resourceAssemblyWrapper;
_resourceManager = resourceManager;
_resourceBaseName = baseName;
_resourceNamesCache = resourceNamesCache;
}
///
public virtual LocalizedString this[[NotNull] string name]
{
get
{
var value = GetStringSafely(name, null);
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
///
public virtual LocalizedString this[[NotNull] string name, params object[] arguments]
{
get
{
var format = GetStringSafely(name, null);
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
}
///
/// Creates a new for a specific .
///
/// The to use.
/// A culture-specific .
public IStringLocalizer WithCulture(CultureInfo culture)
{
return culture == null
? new ResourceManagerStringLocalizer(
_resourceManager,
_resourceAssemblyWrapper.Assembly,
_resourceBaseName,
_resourceNamesCache)
: new ResourceManagerWithCultureStringLocalizer(
_resourceManager,
_resourceAssemblyWrapper.Assembly,
_resourceBaseName,
_resourceNamesCache,
culture);
}
///
/// Gets a resource string from the and returns null instead of
/// throwing exceptions if a match isn't found.
///
/// The name of the string resource.
/// The to get the string for.
/// The resource string, or null if none was found.
protected string GetStringSafely([NotNull] string name, CultureInfo culture)
{
var cacheKey = $"name={name}&culture={(culture ?? CultureInfo.CurrentUICulture).Name}";
if (_missingManifestCache.ContainsKey(cacheKey))
{
return null;
}
try
{
return culture == null ? _resourceManager.GetString(name) : _resourceManager.GetString(name, culture);
}
catch (MissingManifestResourceException)
{
_missingManifestCache.TryAdd(cacheKey, null);
return null;
}
}
///
/// Returns an for all strings in the current culture.
///
/// The .
public virtual IEnumerator GetEnumerator() => GetEnumerator(CultureInfo.CurrentUICulture);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
///
/// Returns an for all strings in the specified culture.
///
/// The to get strings for.
/// The .
protected IEnumerator GetEnumerator([NotNull] CultureInfo culture)
{
var resourceNames = GetResourceNamesFromCultureHierarchy(culture);
foreach (var name in resourceNames)
{
var value = GetStringSafely(name, culture);
yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture)
{
var currentCulture = startingCulture;
var resourceNames = new HashSet();
while (true)
{
try
{
var cultureResourceNames = GetResourceNamesForCulture(currentCulture);
foreach (var resourceName in cultureResourceNames)
{
resourceNames.Add(resourceName);
}
}
catch (MissingManifestResourceException) { }
if (currentCulture == currentCulture.Parent)
{
// currentCulture begat currentCulture, probably time to leave
break;
}
currentCulture = currentCulture.Parent;
}
return resourceNames;
}
private IList GetResourceNamesForCulture(CultureInfo culture)
{
var resourceStreamName = _resourceBaseName;
if (!string.IsNullOrEmpty(culture.Name))
{
resourceStreamName += "." + culture.Name;
}
resourceStreamName += ".resources";
var cacheKey = $"assembly={_resourceAssemblyWrapper.FullName};resourceStreamName={resourceStreamName}";
var cultureResourceNames = _resourceNamesCache.GetOrAdd(cacheKey, key =>
{
var names = new List();
using (var cultureResourceStream = _resourceAssemblyWrapper.GetManifestResourceStream(key))
using (var resources = new ResourceReader(cultureResourceStream))
{
foreach (DictionaryEntry entry in resources)
{
var resourceName = (string)entry.Key;
names.Add(resourceName);
}
}
return names;
});
return cultureResourceNames;
}
}
}