aspnetcore/src/Microsoft.Extensions.Locali.../Internal/AssemblyResourceStringProvi...

149 lines
5.1 KiB
C#

// 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;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Text;
namespace Microsoft.Extensions.Localization.Internal
{
public class AssemblyResourceStringProvider : IResourceStringProvider
{
private const string AssemblyElementDelimiter = ", ";
private readonly AssemblyWrapper _assembly;
private readonly string _resourceBaseName;
private readonly IResourceNamesCache _resourceNamesCache;
public AssemblyResourceStringProvider(
IResourceNamesCache resourceCache,
AssemblyWrapper resourceAssembly,
string resourceBaseName)
{
_resourceNamesCache = resourceCache;
_assembly = resourceAssembly;
_resourceBaseName = resourceBaseName;
}
private string GetResourceCacheKey(CultureInfo culture)
{
var assemblyName = ApplyCultureToAssembly(culture);
return $"Assembly={assemblyName};resourceName={_resourceBaseName}";
}
private string GetResourceName(CultureInfo culture)
{
var resourceStreamName = _resourceBaseName;
if (!string.IsNullOrEmpty(culture.Name))
{
resourceStreamName += "." + culture.Name;
}
resourceStreamName += ".resources";
return resourceStreamName;
}
private IList<string> ThrowOrNull(CultureInfo culture, bool throwOnMissing)
{
if (throwOnMissing)
{
throw new MissingManifestResourceException(
Resources.FormatLocalization_MissingManifest(GetResourceName(culture)));
}
return null;
}
public IList<string> GetAllResourceStrings(CultureInfo culture, bool throwOnMissing)
{
var cacheKey = GetResourceCacheKey(culture);
return _resourceNamesCache.GetOrAdd(cacheKey, _ =>
{
var assembly = GetAssembly(culture);
if (assembly == null)
{
return ThrowOrNull(culture, throwOnMissing);
}
var resourceStreamName = GetResourceName(culture);
using (var resourceStream = assembly.GetManifestResourceStream(resourceStreamName))
{
if (resourceStream == null)
{
return ThrowOrNull(culture, throwOnMissing);
}
using (var resources = new ResourceReader(resourceStream))
{
var names = new List<string>();
foreach (DictionaryEntry entry in resources)
{
var resourceName = (string)entry.Key;
names.Add(resourceName);
}
return names;
}
}
});
}
protected virtual AssemblyWrapper GetAssembly(CultureInfo culture)
{
var assemblyString = ApplyCultureToAssembly(culture);
Assembly assembly;
try
{
assembly = Assembly.Load(new AssemblyName(assemblyString));
}
catch (FileNotFoundException)
{
return null;
}
return new AssemblyWrapper(assembly);
}
// This is all a workaround for https://github.com/dotnet/coreclr/issues/6123
private string ApplyCultureToAssembly(CultureInfo culture)
{
var builder = new StringBuilder(_assembly.FullName);
var cultureName = string.IsNullOrEmpty(culture.Name) ? "neutral" : culture.Name;
var cultureString = $"Culture={cultureName}";
var cultureStartIndex = _assembly.FullName.IndexOf("Culture", StringComparison.OrdinalIgnoreCase);
if (cultureStartIndex < 0)
{
builder.Append(AssemblyElementDelimiter + cultureString);
}
else
{
var cultureEndIndex = _assembly.FullName.IndexOf(
AssemblyElementDelimiter,
cultureStartIndex,
StringComparison.Ordinal);
var cultureLength = cultureEndIndex - cultureStartIndex;
builder.Remove(cultureStartIndex, cultureLength);
builder.Insert(cultureStartIndex, cultureString);
}
var firstSplit = _assembly.FullName.IndexOf(AssemblyElementDelimiter);
if (firstSplit < 0)
{
//Index of end of Assembly name
firstSplit = _assembly.FullName.Length;
}
builder.Insert(firstSplit, ".resources");
return builder.ToString();
}
}
}