mirror of https://github.com/abpframework/abp.git
32 changed files with 990 additions and 6 deletions
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.TestBase |
|||
{ |
|||
public class AbpTestBaseModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
namespace Volo.Abp.Internal |
|||
{ |
|||
internal static class Utf8Helper |
|||
{ |
|||
public static string ReadStringFromStream(Stream stream) |
|||
{ |
|||
var bytes = stream.GetAllBytes(); |
|||
var skipCount = HasBom(bytes) ? 3 : 0; |
|||
return Encoding.UTF8.GetString(bytes, skipCount, bytes.Length - skipCount); |
|||
} |
|||
|
|||
private static bool HasBom(byte[] bytes) |
|||
{ |
|||
if (bytes.Length < 3) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (!(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,173 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Globalization; |
|||
using Microsoft.Extensions.Localization; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
//TODO: Remove old/unused methods!
|
|||
|
|||
public class AbpDictionaryBasedStringLocalizer : IStringLocalizer |
|||
{ |
|||
public LocalizationResource Resource { get; } |
|||
|
|||
public AbpDictionaryBasedStringLocalizer(LocalizationResource resource) |
|||
{ |
|||
Resource = resource; |
|||
} |
|||
|
|||
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) |
|||
{ |
|||
return GetAllStrings(CultureInfo.CurrentUICulture.Name, includeParentCultures); |
|||
} |
|||
|
|||
public IStringLocalizer WithCulture(CultureInfo culture) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
LocalizedString IStringLocalizer.this[string name] |
|||
{ |
|||
get { return GetString(name); } |
|||
} |
|||
|
|||
LocalizedString IStringLocalizer.this[string name, params object[] arguments] |
|||
{ |
|||
get |
|||
{ |
|||
var localizedString = GetString(name); |
|||
return new LocalizedString(name, string.Format(localizedString.Value, arguments, localizedString.ResourceNotFound, localizedString.SearchedLocation)); |
|||
} |
|||
} |
|||
|
|||
public LocalizedString GetString(string name) |
|||
{ |
|||
return GetString(name, CultureInfo.CurrentUICulture.Name); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public LocalizedString GetString(string name, string cultureName) |
|||
{ |
|||
var value = GetStringOrNull(name, cultureName); |
|||
|
|||
if (value == null) |
|||
{ |
|||
return new LocalizedString(name, name, true); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
public LocalizedString GetStringOrNull(string name, bool tryDefaults = true) |
|||
{ |
|||
return GetStringOrNull(name, CultureInfo.CurrentUICulture.Name, tryDefaults); |
|||
} |
|||
|
|||
public LocalizedString GetStringOrNull(string name, string cultureName, bool tryDefaults = true) |
|||
{ |
|||
var dictionaries = Resource.DictionaryProvider.Dictionaries; |
|||
|
|||
//Try to get from original dictionary (with country code)
|
|||
ILocalizationDictionary originalDictionary; |
|||
if (dictionaries.TryGetValue(cultureName, out originalDictionary)) |
|||
{ |
|||
var strOriginal = originalDictionary.GetOrNull(name); |
|||
if (strOriginal != null) |
|||
{ |
|||
return new LocalizedString(name, strOriginal.Value); |
|||
} |
|||
} |
|||
|
|||
if (!tryDefaults) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
//Try to get from same language dictionary (without country code)
|
|||
if (cultureName.Contains("-")) //Example: "tr-TR" (length=5)
|
|||
{ |
|||
ILocalizationDictionary langDictionary; |
|||
if (dictionaries.TryGetValue(GetBaseCultureName(cultureName), out langDictionary)) |
|||
{ |
|||
var strLang = langDictionary.GetOrNull(name); |
|||
if (strLang != null) |
|||
{ |
|||
return new LocalizedString(name, strLang.Value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Try to get from default language
|
|||
var defaultDictionary = Resource.DictionaryProvider.Dictionaries[Resource.DefaultCultureName]; //TODO: What if not contains a default dictionary?
|
|||
if (defaultDictionary == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var strDefault = defaultDictionary.GetOrNull(name); |
|||
if (strDefault == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return new LocalizedString(name, strDefault.Value); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public IReadOnlyList<LocalizedString> GetAllStrings(string cultureName, bool includeDefaults = true) |
|||
{ |
|||
//TODO: Can be optimized (example: if it's already default dictionary, skip overriding)
|
|||
|
|||
var dictionaries = Resource.DictionaryProvider.Dictionaries; |
|||
|
|||
//Create a temp dictionary to build
|
|||
var allStrings = new Dictionary<string, LocalizedString>(); |
|||
|
|||
if (includeDefaults) |
|||
{ |
|||
//Fill all strings from default dictionary
|
|||
var defaultDictionary = Resource.DictionaryProvider.Dictionaries[Resource.DefaultCultureName]; //TODO: What if not contains a default dictionary?
|
|||
if (defaultDictionary != null) |
|||
{ |
|||
foreach (var defaultDictString in defaultDictionary.GetAllStrings()) |
|||
{ |
|||
allStrings[defaultDictString.Name] = new LocalizedString(defaultDictString.Name, defaultDictString.Value); |
|||
} |
|||
} |
|||
|
|||
//Overwrite all strings from the language based on country culture
|
|||
if (cultureName.Contains("-")) |
|||
{ |
|||
ILocalizationDictionary langDictionary; |
|||
if (dictionaries.TryGetValue(GetBaseCultureName(cultureName), out langDictionary)) |
|||
{ |
|||
foreach (var langString in langDictionary.GetAllStrings()) |
|||
{ |
|||
allStrings[langString.Name] = new LocalizedString(langString.Name, langString.Value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Overwrite all strings from the original dictionary
|
|||
ILocalizationDictionary originalDictionary; |
|||
if (dictionaries.TryGetValue(cultureName, out originalDictionary)) |
|||
{ |
|||
foreach (var originalLangString in originalDictionary.GetAllStrings()) |
|||
{ |
|||
allStrings[originalLangString.Name] = new LocalizedString(originalLangString.Name, originalLangString.Value); |
|||
} |
|||
} |
|||
|
|||
return allStrings.Values.ToImmutableList(); |
|||
} |
|||
|
|||
private static string GetBaseCultureName(string cultureName) |
|||
{ |
|||
return cultureName.Contains("-") |
|||
? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal)) |
|||
: cultureName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class AbpLocalizationOptions |
|||
{ |
|||
public LocalizationResourceList Resources { get; } |
|||
public AbpStringLocalizerList Resolvers { get; } |
|||
|
|||
public AbpLocalizationOptions() |
|||
{ |
|||
Resources = new LocalizationResourceList(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Linq; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Localization.Json; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class AbpStringLocalizerFactory : IStringLocalizerFactory |
|||
{ |
|||
private readonly ResourceManagerStringLocalizerFactory _innerFactory; |
|||
private readonly AbpLocalizationOptions _abpLocalizationOptions; |
|||
private readonly IServiceProvider _serviceProvider; |
|||
|
|||
private readonly ConcurrentDictionary<Type, AbpDictionaryBasedStringLocalizer> _localizerCache; |
|||
|
|||
//TODO: It's better to use decorator pattern for IStringLocalizerFactory instead of getting ResourceManagerStringLocalizerFactory as a dependency.
|
|||
public AbpStringLocalizerFactory( |
|||
ResourceManagerStringLocalizerFactory innerFactory, |
|||
IOptions<AbpLocalizationOptions> abpLocalizationOptions, IServiceProvider serviceProvider) |
|||
{ |
|||
_innerFactory = innerFactory; |
|||
_serviceProvider = serviceProvider; |
|||
_abpLocalizationOptions = abpLocalizationOptions.Value; |
|||
|
|||
_localizerCache = new ConcurrentDictionary<Type, AbpDictionaryBasedStringLocalizer>();; |
|||
} |
|||
|
|||
public virtual IStringLocalizer Create(Type resourceSource) |
|||
{ |
|||
//TODO: Optimize!
|
|||
|
|||
var localizationResource = _abpLocalizationOptions.Resources.FirstOrDefault(l => l.ResourceType == resourceSource); |
|||
if (localizationResource == null) |
|||
{ |
|||
return _innerFactory.Create(resourceSource); |
|||
} |
|||
|
|||
return _localizerCache.GetOrAdd(resourceSource, _ => CreateAbpStringLocalizer(localizationResource)); |
|||
} |
|||
|
|||
private AbpDictionaryBasedStringLocalizer CreateAbpStringLocalizer(LocalizationResource resource) |
|||
{ |
|||
resource.Initialize(_serviceProvider); |
|||
|
|||
//Use JSON/XML/...etc based provider that reads resource from source and creates a dictionary
|
|||
//Extend dictionary with extensions
|
|||
//Wrap reader by wrappers (like db wrapper which implement multitenancy/regions and so on...)
|
|||
|
|||
|
|||
//Notes: Localizer will be cached, so wrappers are responsible to cache/invalidate themselves!
|
|||
|
|||
var localizer = new AbpDictionaryBasedStringLocalizer(resource); //TODO: !!!
|
|||
|
|||
//TODO: Wrap with DB provider or other premium sources
|
|||
|
|||
return localizer; |
|||
} |
|||
|
|||
public virtual IStringLocalizer Create(string baseName, string location) |
|||
{ |
|||
//TODO: Investigate when this is called?
|
|||
|
|||
return _innerFactory.Create(baseName, location); |
|||
} |
|||
|
|||
internal static void Replace(IServiceCollection services) |
|||
{ |
|||
services.Replace(ServiceDescriptor.Singleton<IStringLocalizerFactory, AbpStringLocalizerFactory>()); |
|||
services.AddSingleton<ResourceManagerStringLocalizerFactory>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class AbpStringLocalizerList : List<IAbpStringLocalizer> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public interface IAbpStringLocalizer |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a dictionary that is used to find a localized string.
|
|||
/// </summary>
|
|||
public interface ILocalizationDictionary |
|||
{ |
|||
string CultureName { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets/sets a string for this dictionary with given name (key).
|
|||
/// </summary>
|
|||
/// <param name="name">Name to get/set</param>
|
|||
LocalString this[string name] { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="LocalString"/> for given <paramref name="name"/>.
|
|||
/// </summary>
|
|||
/// <param name="name">Name (key) to get localized string</param>
|
|||
/// <returns>The localized string or null if not found in this dictionary</returns>
|
|||
LocalString GetOrNull(string name); |
|||
|
|||
/// <summary>
|
|||
/// Gets a list of all strings in this dictionary.
|
|||
/// </summary>
|
|||
/// <returns>List of all <see cref="LocalString"/> object</returns>
|
|||
IReadOnlyList<LocalString> GetAllStrings(); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public interface ILocalizationDictionaryProvider |
|||
{ |
|||
IDictionary<string, ILocalizationDictionary> Dictionaries { get; } |
|||
|
|||
void Initialize(LocalizationResource resource); |
|||
|
|||
void Extend(ILocalizationDictionary dictionary); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using System.Reflection; |
|||
using Volo.Abp.Internal; |
|||
|
|||
namespace Volo.Abp.Localization.Json |
|||
{ |
|||
/// <summary>
|
|||
/// Provides localization dictionaries from JSON files embedded into an <see cref="Assembly"/>.
|
|||
/// </summary>
|
|||
public class JsonEmbeddedFileLocalizationDictionaryProvider : LocalizationDictionaryProviderBase |
|||
{ |
|||
private readonly Assembly _assembly; |
|||
private readonly string _rootNamespace; |
|||
|
|||
public JsonEmbeddedFileLocalizationDictionaryProvider(Assembly assembly, string rootNamespace) |
|||
{ |
|||
_assembly = assembly; |
|||
_rootNamespace = rootNamespace; |
|||
} |
|||
|
|||
public override void Initialize(LocalizationResource resource) |
|||
{ |
|||
var resourceNames = _assembly.GetManifestResourceNames(); |
|||
foreach (var resourceName in resourceNames) |
|||
{ |
|||
if (resourceName.StartsWith(_rootNamespace)) |
|||
{ |
|||
using (var stream = _assembly.GetManifestResourceStream(resourceName)) |
|||
{ |
|||
var jsonString = Utf8Helper.ReadStringFromStream(stream); |
|||
|
|||
var dictionary = CreateJsonLocalizationDictionary(jsonString); |
|||
if (Dictionaries.ContainsKey(dictionary.CultureName)) |
|||
{ |
|||
throw new AbpException(resource.ResourceType.FullName + " source contains more than one dictionary for the culture: " + dictionary.CultureName); |
|||
} |
|||
|
|||
Dictionaries[dictionary.CultureName] = dictionary; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual JsonLocalizationDictionary CreateJsonLocalizationDictionary(string jsonString) |
|||
{ |
|||
return JsonLocalizationDictionary.BuildFromJsonString(jsonString); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
//using System.IO;
|
|||
//using Abp.Localization.Dictionaries.Xml;
|
|||
//using Abp.Localization.Sources;
|
|||
|
|||
//namespace Abp.Localization.Dictionaries.Json
|
|||
//{
|
|||
// /// <summary>
|
|||
// /// Provides localization dictionaries from json files in a directory.
|
|||
// /// </summary>
|
|||
// public class JsonFileLocalizationDictionaryProvider : LocalizationDictionaryProviderBase
|
|||
// {
|
|||
// private readonly string _directoryPath;
|
|||
|
|||
// /// <summary>
|
|||
// /// Creates a new <see cref="JsonFileLocalizationDictionaryProvider" />.
|
|||
// /// </summary>
|
|||
// /// <param name="directoryPath">Path of the dictionary that contains all related XML files</param>
|
|||
// public JsonFileLocalizationDictionaryProvider(string directoryPath)
|
|||
// {
|
|||
// _directoryPath = directoryPath;
|
|||
// }
|
|||
|
|||
// public override void Initialize(string sourceName)
|
|||
// {
|
|||
// var fileNames = Directory.GetFiles(_directoryPath, "*.json", SearchOption.TopDirectoryOnly);
|
|||
|
|||
// foreach (var fileName in fileNames)
|
|||
// {
|
|||
// var dictionary = CreateJsonLocalizationDictionary(fileName);
|
|||
// if (Dictionaries.ContainsKey(dictionary.CultureInfo.Name))
|
|||
// {
|
|||
// throw new AbpInitializationException(sourceName + " source contains more than one dictionary for the culture: " + dictionary.CultureInfo.Name);
|
|||
// }
|
|||
|
|||
// Dictionaries[dictionary.CultureInfo.Name] = dictionary;
|
|||
|
|||
// if (fileName.EndsWith(sourceName + ".json"))
|
|||
// {
|
|||
// if (DefaultDictionary != null)
|
|||
// {
|
|||
// throw new AbpInitializationException("Only one default localization dictionary can be for source: " + sourceName);
|
|||
// }
|
|||
|
|||
// DefaultDictionary = dictionary;
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
|
|||
// protected virtual JsonLocalizationDictionary CreateJsonLocalizationDictionary(string fileName)
|
|||
// {
|
|||
// return JsonLocalizationDictionary.BuildFromFile(fileName);
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
@ -0,0 +1,97 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
|
|||
namespace Volo.Abp.Localization.Json |
|||
{ |
|||
/// <summary>
|
|||
/// This class is used to build a localization dictionary from json.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Use static Build methods to create instance of this class.
|
|||
/// </remarks>
|
|||
public class JsonLocalizationDictionary : LocalizationDictionary |
|||
{ |
|||
/// <summary>
|
|||
/// Private constructor.
|
|||
/// </summary>
|
|||
/// <param name="cultureName">Culture of the dictionary</param>
|
|||
private JsonLocalizationDictionary(string cultureName) |
|||
: base(cultureName) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Builds an <see cref="JsonLocalizationDictionary" /> from given file.
|
|||
/// </summary>
|
|||
/// <param name="filePath">Path of the file</param>
|
|||
public static JsonLocalizationDictionary BuildFromFile(string filePath) |
|||
{ |
|||
try |
|||
{ |
|||
return BuildFromJsonString(File.ReadAllText(filePath)); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw new AbpException("Invalid localization file format! " + filePath, ex); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Builds an <see cref="JsonLocalizationDictionary" /> from given json string.
|
|||
/// </summary>
|
|||
/// <param name="jsonString">Json string</param>
|
|||
public static JsonLocalizationDictionary BuildFromJsonString(string jsonString) |
|||
{ |
|||
JsonLocalizationFile jsonFile; |
|||
try |
|||
{ |
|||
jsonFile = JsonConvert.DeserializeObject<JsonLocalizationFile>( |
|||
jsonString, |
|||
new JsonSerializerSettings |
|||
{ |
|||
ContractResolver = new CamelCasePropertyNamesContractResolver() |
|||
}); |
|||
} |
|||
catch (JsonException ex) |
|||
{ |
|||
throw new AbpException("Can not parse json string. " + ex.Message); |
|||
} |
|||
|
|||
var cultureCode = jsonFile.Culture; |
|||
if (string.IsNullOrEmpty(cultureCode)) |
|||
{ |
|||
throw new AbpException("Culture is empty in language json file."); |
|||
} |
|||
|
|||
var dictionary = new JsonLocalizationDictionary(cultureCode); |
|||
var dublicateNames = new List<string>(); |
|||
foreach (var item in jsonFile.Texts) |
|||
{ |
|||
if (string.IsNullOrEmpty(item.Key)) |
|||
{ |
|||
throw new AbpException("The key is empty in given json string."); |
|||
} |
|||
|
|||
if (dictionary.Contains(item.Key)) |
|||
{ |
|||
dublicateNames.Add(item.Key); |
|||
} |
|||
|
|||
dictionary[item.Key] = new LocalString(item.Key, item.Value.NormalizeLineEndings()); |
|||
} |
|||
|
|||
if (dublicateNames.Count > 0) |
|||
{ |
|||
throw new AbpException( |
|||
"A dictionary can not contain same key twice. There are some duplicated names: " + |
|||
dublicateNames.JoinAsString(", ")); |
|||
} |
|||
|
|||
return dictionary; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Localization.Json |
|||
{ |
|||
public class JsonLocalizationFile |
|||
{ |
|||
/// <summary>
|
|||
/// Culture name; eg : en , en-us, zh-CN
|
|||
/// </summary>
|
|||
public string Culture { get; set; } |
|||
|
|||
public Dictionary<string, string> Texts { get; } |
|||
|
|||
public JsonLocalizationFile() |
|||
{ |
|||
Texts = new Dictionary<string, string>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class LocalString |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string Value { get; set; } |
|||
|
|||
public LocalString(string name, string value) |
|||
{ |
|||
Name = name; |
|||
Value = value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Globalization; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a simple implementation of <see cref="ILocalizationDictionary"/> interface.
|
|||
/// </summary>
|
|||
public class LocalizationDictionary : ILocalizationDictionary, IEnumerable<LocalString> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public string CultureName { get; private set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual LocalString this[string name] |
|||
{ |
|||
get |
|||
{ |
|||
var localizedString = GetOrNull(name); |
|||
return localizedString == null ? null : localizedString; |
|||
} |
|||
set { _dictionary[name] = value; } |
|||
} |
|||
|
|||
private readonly Dictionary<string, LocalString> _dictionary; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new <see cref="LocalizationDictionary"/> object.
|
|||
/// </summary>
|
|||
/// <param name="cultureName">Culture of the dictionary</param>
|
|||
public LocalizationDictionary(string cultureName) |
|||
{ |
|||
CultureName = cultureName; |
|||
_dictionary = new Dictionary<string, LocalString>(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual LocalString GetOrNull(string name) |
|||
{ |
|||
LocalString localizedString; |
|||
return _dictionary.TryGetValue(name, out localizedString) ? localizedString : null; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual IReadOnlyList<LocalString> GetAllStrings() |
|||
{ |
|||
return _dictionary.Values.ToImmutableList(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual IEnumerator<LocalString> GetEnumerator() |
|||
{ |
|||
return GetAllStrings().GetEnumerator(); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return GetAllStrings().GetEnumerator(); |
|||
} |
|||
|
|||
protected bool Contains(string name) |
|||
{ |
|||
return _dictionary.ContainsKey(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public abstract class LocalizationDictionaryProviderBase : ILocalizationDictionaryProvider |
|||
{ |
|||
public LocalizationResource ResourceType { get; private set; } |
|||
|
|||
public ILocalizationDictionary DefaultDictionary { get; protected set; } |
|||
|
|||
public IDictionary<string, ILocalizationDictionary> Dictionaries { get; private set; } |
|||
|
|||
protected LocalizationDictionaryProviderBase() |
|||
{ |
|||
Dictionaries = new Dictionary<string, ILocalizationDictionary>(); |
|||
} |
|||
|
|||
public virtual void Initialize(LocalizationResource resourceType) |
|||
{ |
|||
ResourceType = resourceType; |
|||
} |
|||
|
|||
public void Extend(ILocalizationDictionary dictionary) |
|||
{ |
|||
//Add
|
|||
ILocalizationDictionary existingDictionary; |
|||
if (!Dictionaries.TryGetValue(dictionary.CultureName, out existingDictionary)) |
|||
{ |
|||
Dictionaries[dictionary.CultureName] = dictionary; |
|||
return; |
|||
} |
|||
|
|||
//Override
|
|||
var localizedStrings = dictionary.GetAllStrings(); |
|||
foreach (var localizedString in localizedStrings) |
|||
{ |
|||
existingDictionary[localizedString.Name] = localizedString; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class LocalizationResource |
|||
{ |
|||
public Type ResourceType { get; } |
|||
|
|||
public string DefaultCultureName { get; set; } |
|||
|
|||
public ILocalizationDictionaryProvider DictionaryProvider |
|||
{ |
|||
get => _dictionaryProvider; |
|||
set |
|||
{ |
|||
Check.NotNull(value, nameof(value)); |
|||
_dictionaryProvider = value; |
|||
} |
|||
} |
|||
private ILocalizationDictionaryProvider _dictionaryProvider; |
|||
|
|||
public LocalizationResource([NotNull] Type resourceType, [NotNull] string defaultCultureName, [NotNull] ILocalizationDictionaryProvider dictionaryProvider) |
|||
{ |
|||
Check.NotNull(resourceType, nameof(resourceType)); |
|||
Check.NotNull(defaultCultureName, nameof(defaultCultureName)); |
|||
Check.NotNull(dictionaryProvider, nameof(dictionaryProvider)); |
|||
|
|||
ResourceType = resourceType; |
|||
DefaultCultureName = defaultCultureName; |
|||
DictionaryProvider = dictionaryProvider; |
|||
} |
|||
|
|||
public virtual void Initialize(IServiceProvider serviceProvider) //TODO: Create a LocalizationResourceInitializationContext!
|
|||
{ |
|||
DictionaryProvider.Initialize(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class LocalizationResourceList : List<LocalizationResource> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Localization.Json; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public static class LocalizationResourceListExtensions |
|||
{ |
|||
public static void AddJson<TResource>(this LocalizationResourceList resourceList, [NotNull] string defaultCultureName) |
|||
{ |
|||
var type = typeof(TResource); |
|||
|
|||
resourceList.Add( |
|||
new LocalizationResource( |
|||
type, |
|||
defaultCultureName, |
|||
new JsonEmbeddedFileLocalizationDictionaryProvider( |
|||
type.Assembly, |
|||
type.Namespace |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.DependencyInjection |
|||
{ |
|||
public class DependencyInjection_Tests |
|||
{ |
|||
[Fact] |
|||
public void Singletons_Should_Resolve_Transients_Independent_From_Current_Scope() |
|||
{ |
|||
//Arrange
|
|||
|
|||
var services = new ServiceCollection(); |
|||
|
|||
services |
|||
.AddSingleton<MySingletonService>() |
|||
.AddTransient<MyTransientServiceUsesSingleton>() |
|||
.AddTransient<MyTransientService>(); |
|||
|
|||
MySingletonService singletonService; |
|||
|
|||
using (var serviceProvider = services.BuildServiceProvider()) |
|||
{ |
|||
//Act
|
|||
|
|||
using (var scope = serviceProvider.CreateScope()) |
|||
{ |
|||
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt(); |
|||
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt(); |
|||
} |
|||
|
|||
using (var scope = serviceProvider.CreateScope()) |
|||
{ |
|||
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt(); |
|||
scope.ServiceProvider.GetRequiredService<MyTransientServiceUsesSingleton>().DoIt(); |
|||
scope.ServiceProvider.GetRequiredService<MySingletonService>().ShouldNotBeDisposed(); |
|||
} |
|||
|
|||
singletonService = serviceProvider.GetRequiredService<MySingletonService>(); |
|||
singletonService.ShouldNotBeDisposed(); |
|||
} |
|||
|
|||
singletonService.ShouldBeDisposed(); |
|||
} |
|||
|
|||
private class MyTransientServiceUsesSingleton |
|||
{ |
|||
private readonly MySingletonService _singletonService; |
|||
|
|||
public MyTransientServiceUsesSingleton(MySingletonService singletonService) |
|||
{ |
|||
_singletonService = singletonService; |
|||
} |
|||
|
|||
public void DoIt() |
|||
{ |
|||
_singletonService.DoIt(); |
|||
} |
|||
} |
|||
|
|||
private class MySingletonService |
|||
{ |
|||
private readonly IServiceProvider _serviceProvider; |
|||
|
|||
private readonly List<MyTransientService> _instances; |
|||
|
|||
public MySingletonService(IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
_instances = new List<MyTransientService>(); |
|||
} |
|||
|
|||
public void DoIt() |
|||
{ |
|||
_instances.Add(_serviceProvider.GetRequiredService<MyTransientService>()); |
|||
} |
|||
|
|||
public void ShouldNotBeDisposed() |
|||
{ |
|||
foreach (var instance in _instances) |
|||
{ |
|||
instance.IsDisposed.ShouldBeFalse(); |
|||
} |
|||
} |
|||
|
|||
public void ShouldBeDisposed() |
|||
{ |
|||
foreach (var instance in _instances) |
|||
{ |
|||
instance.IsDisposed.ShouldBeTrue(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private class MyTransientService : IDisposable |
|||
{ |
|||
public bool IsDisposed { get; private set; } |
|||
|
|||
public void Dispose() |
|||
{ |
|||
IsDisposed = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Shouldly; |
|||
using Volo.Abp.Localization.Source; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.TestBase; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.TestModule> |
|||
{ |
|||
private readonly IStringLocalizer<LocalizationTestResource> _localizer; |
|||
|
|||
public AbpLocalization_Tests() |
|||
{ |
|||
_localizer = GetRequiredService<IStringLocalizer<LocalizationTestResource>>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Get_Same_Text_If_Not_Defined_Anywhere() |
|||
{ |
|||
const string text = "A string that is not defined anywhere!"; |
|||
|
|||
_localizer[text].Value.ShouldBe(text); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Get_Localized_Text_If_Defined_In_Current_Culture() |
|||
{ |
|||
using (AbpCultureHelper.Use("en")) |
|||
{ |
|||
_localizer["Car"].Value.ShouldBe("Car"); |
|||
_localizer["CarPlural"].Value.ShouldBe("Cars"); |
|||
} |
|||
|
|||
using (AbpCultureHelper.Use("tr")) |
|||
{ |
|||
_localizer["Car"].Value.ShouldBe("Araba"); |
|||
_localizer["CarPlural"].Value.ShouldBe("Araba"); |
|||
} |
|||
} |
|||
|
|||
[DependsOn(typeof(AbpTestBaseModule))] |
|||
public class TestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources.AddJson<LocalizationTestResource>("en"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.Localization.Source |
|||
{ |
|||
public sealed class LocalizationTestResource |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Hello <b>{0}</b>.": "Hello <b>{0}</b>", |
|||
"Car": "Car", |
|||
"CarPlural": "Cars" |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"culture": "tr", |
|||
"texts": { |
|||
"Hello <b>{0}</b>.": "Merhaba <b>{0}</b>", |
|||
"Car": "Araba", |
|||
"CarPlural": "Araba" |
|||
} |
|||
} |
|||
Loading…
Reference in new issue