32 changed files with 1112 additions and 1 deletions
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="4.2.1" /> |
|||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="5.0.*" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Json |
|||
{ |
|||
[DependsOn(typeof(AbpLocalizationModule))] |
|||
public class AbpLocalizationJsonModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
using Microsoft.Extensions.FileProviders; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Primitives; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Internal; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Localization.Json; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Json |
|||
{ |
|||
public class JsonPhysicalFileLocalizationResourceContributor : ILocalizationResourceContributor |
|||
{ |
|||
private readonly string _filePath; |
|||
|
|||
private IFileProvider _fileProvider; |
|||
private Dictionary<string, ILocalizationDictionary> _dictionaries; |
|||
private bool _subscribedForChanges; |
|||
private readonly object _syncObj = new object(); |
|||
|
|||
public JsonPhysicalFileLocalizationResourceContributor(string filePath) |
|||
{ |
|||
_filePath = filePath; |
|||
} |
|||
|
|||
public void Initialize(LocalizationResourceInitializationContext context) |
|||
{ |
|||
_fileProvider = new PhysicalFileProvider(_filePath); |
|||
} |
|||
|
|||
public LocalizedString GetOrNull(string cultureName, string name) |
|||
{ |
|||
return GetDictionaries().GetOrDefault(cultureName)?.GetOrNull(name); |
|||
} |
|||
|
|||
public void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary) |
|||
{ |
|||
GetDictionaries().GetOrDefault(cultureName)?.Fill(dictionary); |
|||
} |
|||
|
|||
protected virtual Dictionary<string, ILocalizationDictionary> GetDictionaries() |
|||
{ |
|||
var dictionaries = _dictionaries; |
|||
if (dictionaries != null) |
|||
{ |
|||
return dictionaries; |
|||
} |
|||
|
|||
lock (_syncObj) |
|||
{ |
|||
dictionaries = _dictionaries; |
|||
if (dictionaries != null) |
|||
{ |
|||
return dictionaries; |
|||
} |
|||
|
|||
if (!_subscribedForChanges) |
|||
{ |
|||
ChangeToken.OnChange(() => _fileProvider.Watch(_filePath.EnsureEndsWith('/') + "*.*"), |
|||
() => |
|||
{ |
|||
_dictionaries = null; |
|||
}); |
|||
|
|||
_subscribedForChanges = true; |
|||
} |
|||
|
|||
dictionaries = _dictionaries = CreateDictionaries(); |
|||
} |
|||
|
|||
return dictionaries; |
|||
} |
|||
|
|||
protected virtual Dictionary<string, ILocalizationDictionary> CreateDictionaries() |
|||
{ |
|||
var dictionaries = new Dictionary<string, ILocalizationDictionary>(); |
|||
|
|||
foreach (var file in _fileProvider.GetDirectoryContents(string.Empty)) |
|||
{ |
|||
if (file.IsDirectory || !CanParseFile(file)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var dictionary = CreateDictionaryFromFile(file); |
|||
if (dictionaries.ContainsKey(dictionary.CultureName)) |
|||
{ |
|||
throw new AbpException($"{file.GetVirtualOrPhysicalPathOrNull()} dictionary has a culture name '{dictionary.CultureName}' which is already defined!"); |
|||
} |
|||
|
|||
dictionaries[dictionary.CultureName] = dictionary; |
|||
} |
|||
|
|||
return dictionaries; |
|||
} |
|||
|
|||
protected virtual bool CanParseFile(IFileInfo file) |
|||
{ |
|||
return file.Name.EndsWith(".json", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
protected virtual ILocalizationDictionary CreateDictionaryFromFile(IFileInfo file) |
|||
{ |
|||
using (var stream = file.CreateReadStream()) |
|||
{ |
|||
return CreateDictionaryFromFileContent(Utf8Helper.ReadStringFromStream(stream)); |
|||
} |
|||
} |
|||
|
|||
protected virtual ILocalizationDictionary CreateDictionaryFromFileContent(string fileContent) |
|||
{ |
|||
return JsonLocalizationDictionaryBuilder.BuildFromJsonString(fileContent); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Json |
|||
{ |
|||
public static class LocalizationResourceExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 添加Json本地磁盘文件支持
|
|||
/// </summary>
|
|||
/// <param name="localizationResource"></param>
|
|||
/// <param name="jsonFilePath"></param>
|
|||
/// <returns></returns>
|
|||
public static LocalizationResource AddPhysicalJson( |
|||
[NotNull] this LocalizationResource localizationResource, |
|||
[NotNull] string jsonFilePath) |
|||
{ |
|||
Check.NotNull(localizationResource, nameof(localizationResource)); |
|||
Check.NotNull(jsonFilePath, nameof(jsonFilePath)); |
|||
|
|||
localizationResource.Contributors.Add(new JsonPhysicalFileLocalizationResourceContributor(jsonFilePath)); |
|||
|
|||
return localizationResource; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
# LINGYUN.Abp.Localization.Json |
|||
|
|||
## 模块说明 |
|||
|
|||
本地化组件的Json本地文件系统集成,Abp内置组件仅集成了基于IVirtualFileProvider的实现 |
|||
|
|||
此组件基于PhysicalFileProvider |
|||
|
|||
### 基础模块 |
|||
|
|||
### 高阶模块 |
|||
|
|||
### 权限定义 |
|||
|
|||
### 功能定义 |
|||
|
|||
### 配置定义 |
|||
|
|||
### 如何使用 |
|||
|
|||
|
|||
```csharp |
|||
|
|||
[DependsOn( |
|||
typeof(AbpLocalizationJsonModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<YouResource>("en") |
|||
// 一般配置在宿主项目中, 写入宿主项目中存储json文件的绝对路径(受PhysicalFileProvider的限制) |
|||
// json文件格式与Abp默认json格式保持一致 |
|||
// 详情见: https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.fileproviders.physicalfileprovider?view=dotnet-plat-ext-5.0 |
|||
.AddPhysicalJson(Path.Combine(Directory.GetCurrentDirectory(), "Resources")); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
```json |
|||
|
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Hello China": "Hello China!" |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
### 更新日志 |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="4.2.1" /> |
|||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="5.0.*"/> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
[DependsOn(typeof(AbpLocalizationModule))] |
|||
public class AbpLocalizationXmlModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using JetBrains.Annotations; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public static class LocalizationResourceExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// 添加Xml虚拟文件系统支持
|
|||
/// </summary>
|
|||
/// <param name="localizationResource"></param>
|
|||
/// <param name="virtualPath"></param>
|
|||
/// <returns></returns>
|
|||
public static LocalizationResource AddVirtualXml( |
|||
[NotNull] this LocalizationResource localizationResource, |
|||
[NotNull] string virtualPath) |
|||
{ |
|||
Check.NotNull(localizationResource, nameof(localizationResource)); |
|||
Check.NotNull(virtualPath, nameof(virtualPath)); |
|||
|
|||
localizationResource.Contributors.Add(new XmlVirtualFileLocalizationResourceContributor( |
|||
virtualPath.EnsureStartsWith('/') |
|||
)); |
|||
|
|||
return localizationResource; |
|||
} |
|||
/// <summary>
|
|||
/// 添加Xml本地磁盘文件支持
|
|||
/// </summary>
|
|||
/// <param name="localizationResource"></param>
|
|||
/// <param name="xmlFilePath"></param>
|
|||
/// <returns></returns>
|
|||
public static LocalizationResource AddPhysicalXml( |
|||
[NotNull] this LocalizationResource localizationResource, |
|||
[NotNull] string xmlFilePath) |
|||
{ |
|||
Check.NotNull(localizationResource, nameof(localizationResource)); |
|||
Check.NotNull(xmlFilePath, nameof(xmlFilePath)); |
|||
|
|||
localizationResource.Contributors.Add(new XmlPhysicalFileLocalizationResourceContributor(xmlFilePath)); |
|||
|
|||
return localizationResource; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
using Microsoft.Extensions.FileProviders; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Primitives; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Internal; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public abstract class XmlFileLocalizationResourceContributorBase : ILocalizationResourceContributor |
|||
{ |
|||
private readonly string _filePath; |
|||
|
|||
private IFileProvider _fileProvider; |
|||
private Dictionary<string, ILocalizationDictionary> _dictionaries; |
|||
private bool _subscribedForChanges; |
|||
private readonly object _syncObj = new object(); |
|||
|
|||
protected XmlFileLocalizationResourceContributorBase(string filePath) |
|||
{ |
|||
_filePath = filePath; |
|||
} |
|||
|
|||
public virtual void Initialize(LocalizationResourceInitializationContext context) |
|||
{ |
|||
_fileProvider = BuildFileProvider(context); |
|||
|
|||
Check.NotNull(_fileProvider, nameof(_fileProvider)); |
|||
} |
|||
|
|||
public virtual LocalizedString GetOrNull(string cultureName, string name) |
|||
{ |
|||
return GetDictionaries().GetOrDefault(cultureName)?.GetOrNull(name); |
|||
} |
|||
|
|||
public virtual void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary) |
|||
{ |
|||
GetDictionaries().GetOrDefault(cultureName)?.Fill(dictionary); |
|||
} |
|||
|
|||
protected virtual Dictionary<string, ILocalizationDictionary> GetDictionaries() |
|||
{ |
|||
var dictionaries = _dictionaries; |
|||
if (dictionaries != null) |
|||
{ |
|||
return dictionaries; |
|||
} |
|||
|
|||
lock (_syncObj) |
|||
{ |
|||
dictionaries = _dictionaries; |
|||
if (dictionaries != null) |
|||
{ |
|||
return dictionaries; |
|||
} |
|||
|
|||
if (!_subscribedForChanges) |
|||
{ |
|||
ChangeToken.OnChange(() => _fileProvider.Watch(_filePath.EnsureEndsWith('/') + "*.*"), |
|||
() => |
|||
{ |
|||
_dictionaries = null; |
|||
}); |
|||
|
|||
_subscribedForChanges = true; |
|||
} |
|||
|
|||
dictionaries = _dictionaries = CreateDictionaries(_fileProvider, _filePath); |
|||
} |
|||
|
|||
return dictionaries; |
|||
} |
|||
|
|||
protected virtual Dictionary<string, ILocalizationDictionary> CreateDictionaries(IFileProvider fileProvider, string filePath) |
|||
{ |
|||
var dictionaries = new Dictionary<string, ILocalizationDictionary>(); |
|||
|
|||
foreach (var file in fileProvider.GetDirectoryContents(filePath)) |
|||
{ |
|||
if (file.IsDirectory || !CanParseFile(file)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var dictionary = CreateDictionaryFromFile(file); |
|||
if (dictionaries.ContainsKey(dictionary.CultureName)) |
|||
{ |
|||
throw new AbpException($"{file.GetVirtualOrPhysicalPathOrNull()} dictionary has a culture name '{dictionary.CultureName}' which is already defined!"); |
|||
} |
|||
|
|||
dictionaries[dictionary.CultureName] = dictionary; |
|||
} |
|||
|
|||
return dictionaries; |
|||
} |
|||
|
|||
protected virtual bool CanParseFile(IFileInfo file) |
|||
{ |
|||
return file.Name.EndsWith(".xml", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
protected virtual ILocalizationDictionary CreateDictionaryFromFile(IFileInfo file) |
|||
{ |
|||
using (var stream = file.CreateReadStream()) |
|||
{ |
|||
return CreateDictionaryFromFileContent(Utf8Helper.ReadStringFromStream(stream)); |
|||
} |
|||
} |
|||
|
|||
protected virtual ILocalizationDictionary CreateDictionaryFromFileContent(string fileContent) |
|||
{ |
|||
return XmlLocalizationDictionaryBuilder.BuildFromXmlString(fileContent); |
|||
} |
|||
|
|||
protected abstract IFileProvider BuildFileProvider(LocalizationResourceInitializationContext context); |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using Microsoft.Extensions.Localization; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Xml.Serialization; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public static class XmlLocalizationDictionaryBuilder |
|||
{ |
|||
public static ILocalizationDictionary BuildFromFile(string filePath) |
|||
{ |
|||
try |
|||
{ |
|||
return BuildFromXmlString(File.ReadAllText(filePath)); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw new AbpException("Invalid localization file format: " + filePath, ex); |
|||
} |
|||
} |
|||
|
|||
public static ILocalizationDictionary BuildFromXmlString(string xmlString) |
|||
{ |
|||
XmlLocalizationFile xmlFile; |
|||
try |
|||
{ |
|||
XmlSerializer serializer = new XmlSerializer(typeof(XmlLocalizationFile)); |
|||
using (StringReader reader = new StringReader(xmlString)) |
|||
{ |
|||
xmlFile = (XmlLocalizationFile)serializer.Deserialize(reader); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw new AbpException("Can not parse xml string. " + ex.Message); |
|||
} |
|||
|
|||
var cultureCode = xmlFile.Culture.Name; |
|||
if (string.IsNullOrEmpty(cultureCode)) |
|||
{ |
|||
throw new AbpException("Culture is empty in language json file."); |
|||
} |
|||
|
|||
var dictionary = new Dictionary<string, LocalizedString>(); |
|||
var dublicateNames = new List<string>(); |
|||
foreach (var item in xmlFile.Texts) |
|||
{ |
|||
if (string.IsNullOrEmpty(item.Key)) |
|||
{ |
|||
throw new AbpException("The key is empty in given json string."); |
|||
} |
|||
|
|||
if (dictionary.GetOrDefault(item.Key) != null) |
|||
{ |
|||
dublicateNames.Add(item.Key); |
|||
} |
|||
|
|||
dictionary[item.Key] = new LocalizedString(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 new StaticLocalizationDictionary(cultureCode, dictionary); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Xml; |
|||
using System.Xml.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
[Serializable] |
|||
[XmlRoot(Namespace = "lingyun.abp", ElementName = "localization")] |
|||
public class XmlLocalizationFile |
|||
{ |
|||
[XmlElement("culture")] |
|||
public CultureInfo Culture { get; set; } = new CultureInfo(); |
|||
|
|||
[XmlArray("texts")] |
|||
[XmlArrayItem("text")] |
|||
public LocalizationText[] Texts { get; set; } = new LocalizationText[0]; |
|||
|
|||
public XmlLocalizationFile() { } |
|||
|
|||
public XmlLocalizationFile(string culture) |
|||
{ |
|||
Culture = new CultureInfo(culture); |
|||
} |
|||
|
|||
public XmlLocalizationFile(string culture, LocalizationText[] texts) |
|||
{ |
|||
Culture = new CultureInfo(culture); |
|||
Texts = texts; |
|||
} |
|||
|
|||
public void WriteToPath(string filePath) |
|||
{ |
|||
var fileName = Path.Combine(filePath, Culture.Name + ".xml"); |
|||
using FileStream fileStream = new(fileName, FileMode.Create, FileAccess.Write); |
|||
InternalWrite(fileStream, Encoding.UTF8); |
|||
} |
|||
|
|||
private void InternalWrite(Stream stream, Encoding encoding) |
|||
{ |
|||
if (encoding == null) |
|||
{ |
|||
throw new ArgumentNullException("encoding"); |
|||
} |
|||
|
|||
XmlSerializer serializer = new(GetType()); |
|||
XmlWriterSettings settings = new() |
|||
{ |
|||
Indent = true, |
|||
NewLineChars = "\r\n", |
|||
Encoding = encoding, |
|||
IndentChars = " " |
|||
}; |
|||
|
|||
using XmlWriter writer = XmlWriter.Create(stream, settings); |
|||
serializer.Serialize(writer, this); |
|||
writer.Close(); |
|||
} |
|||
} |
|||
|
|||
[Serializable] |
|||
public class CultureInfo |
|||
{ |
|||
[XmlAttribute("name")] |
|||
public string Name { get; set; } |
|||
|
|||
public CultureInfo() |
|||
{ |
|||
} |
|||
|
|||
public CultureInfo(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
} |
|||
|
|||
[Serializable] |
|||
public class LocalizationText |
|||
{ |
|||
[XmlAttribute("key")] |
|||
public string Key { get; set; } |
|||
|
|||
[XmlAttribute("value")] |
|||
public string Value { get; set; } |
|||
|
|||
public LocalizationText() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public LocalizationText(string key, string value) |
|||
{ |
|||
Key = key; |
|||
Value = value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using Microsoft.Extensions.FileProviders; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public class XmlPhysicalFileLocalizationResourceContributor : XmlFileLocalizationResourceContributorBase |
|||
{ |
|||
private readonly string _filePath; |
|||
|
|||
public XmlPhysicalFileLocalizationResourceContributor(string filePath) |
|||
: base(filePath) |
|||
{ |
|||
_filePath = filePath; |
|||
} |
|||
|
|||
protected override IFileProvider BuildFileProvider(LocalizationResourceInitializationContext context) |
|||
{ |
|||
return new PhysicalFileProvider(_filePath); |
|||
} |
|||
|
|||
protected override Dictionary<string, ILocalizationDictionary> CreateDictionaries(IFileProvider fileProvider, string filePath) |
|||
{ |
|||
var dictionaries = new Dictionary<string, ILocalizationDictionary>(); |
|||
|
|||
foreach (var file in fileProvider.GetDirectoryContents(string.Empty)) |
|||
{ |
|||
if (file.IsDirectory || !CanParseFile(file)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var dictionary = CreateDictionaryFromFile(file); |
|||
if (dictionaries.ContainsKey(dictionary.CultureName)) |
|||
{ |
|||
throw new AbpException($"{file.GetVirtualOrPhysicalPathOrNull()} dictionary has a culture name '{dictionary.CultureName}' which is already defined!"); |
|||
} |
|||
|
|||
dictionaries[dictionary.CultureName] = dictionary; |
|||
} |
|||
|
|||
return dictionaries; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.FileProviders; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public class XmlVirtualFileLocalizationResourceContributor : XmlFileLocalizationResourceContributorBase |
|||
{ |
|||
public XmlVirtualFileLocalizationResourceContributor(string filePath) |
|||
: base(filePath) |
|||
{ |
|||
} |
|||
|
|||
protected override IFileProvider BuildFileProvider(LocalizationResourceInitializationContext context) |
|||
{ |
|||
return context.ServiceProvider.GetRequiredService<IVirtualFileProvider>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
# LINGYUN.Abp.Localization.Xml |
|||
|
|||
## 模块说明 |
|||
|
|||
本地化组件的Xml文档集成,内置PhysicalFileProvider与VirtualFileProvider实现 |
|||
|
|||
### 基础模块 |
|||
|
|||
### 高阶模块 |
|||
|
|||
### 权限定义 |
|||
|
|||
### 功能定义 |
|||
|
|||
### 配置定义 |
|||
|
|||
### 如何使用 |
|||
|
|||
|
|||
```csharp |
|||
|
|||
[DependsOn( |
|||
typeof(AbpLocalizationXmlModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<YouProjectModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<YouResource>("en") |
|||
// 当前项目中的虚拟文件系统目录,详情见: https://docs.abp.io/en/abp/latest/Virtual-File-System |
|||
.AddVirtualXml("/LINGYUN/Abp/Localization/Xml/Resources") |
|||
// 一般配置在宿主项目中, 写入宿主项目中存储xml文件的绝对路径(受PhysicalFileProvider的限制) |
|||
// 详情见: https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.fileproviders.physicalfileprovider?view=dotnet-plat-ext-5.0 |
|||
.AddPhysicalXml(Path.Combine(Directory.GetCurrentDirectory(), "Resources")); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
``` |
|||
Xml文件格式如下 |
|||
|
|||
序列化: [XmlLocalizationFile](./LINGYUN/Abp/Localization/Xml/XmlLocalizationFile.cs) 类型实现 |
|||
|
|||
```xml |
|||
|
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<localization xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="lingyun.abp"> |
|||
<culture name="en" /> |
|||
<texts> |
|||
<text key="Hello China" value="Hello China!" /> |
|||
<text key="Welcome" value="Welcome!" /> |
|||
</texts> |
|||
</localization> |
|||
|
|||
``` |
|||
|
|||
### 更新日志 |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="coverlet.collector" Version="1.3.0"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\localization\LINGYUN.Abp.Localization.Json\LINGYUN.Abp.Localization.Json.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Json |
|||
{ |
|||
public abstract class AbpLocalizationJsonTestBase : AbpTestsBase<AbpLocalizationJsonTestModule> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using System.IO; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Json |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpTestsBaseModule), |
|||
typeof(AbpLocalizationJsonModule))] |
|||
public class AbpLocalizationJsonTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<LocalizationTestResource>("en") |
|||
.AddPhysicalJson(Path.Combine(Directory.GetCurrentDirectory(), "TestResources")); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using Microsoft.Extensions.Localization; |
|||
using Shouldly; |
|||
using Volo.Abp.Localization; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Json |
|||
{ |
|||
public class JsonLocalizationTest : AbpLocalizationJsonTestBase |
|||
{ |
|||
private readonly IStringLocalizer<LocalizationTestResource> _localizer; |
|||
|
|||
public JsonLocalizationTest() |
|||
{ |
|||
_localizer = GetRequiredService<IStringLocalizer<LocalizationTestResource>>(); |
|||
} |
|||
|
|||
|
|||
[Fact] |
|||
public void Should_Get_Physical_Localized_Text_If_Defined_In_Current_Culture() |
|||
{ |
|||
using (CultureHelper.Use("zh-Hans")) |
|||
{ |
|||
_localizer["Hello China"].Value.ShouldBe("China 你好!"); |
|||
} |
|||
|
|||
using (CultureHelper.Use("en")) |
|||
{ |
|||
_localizer["Hello China"].Value.ShouldBe("Hello China!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Abp.Localization.Json |
|||
{ |
|||
public class LocalizationTestResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Hello China": "Hello China!" |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Hello China": "China 你好!" |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\Localization\Xml\Resources\en.xml" /> |
|||
<None Remove="LINGYUN\Abp\Localization\Xml\Resources\zh-Hans.xml" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Localization\Xml\Resources\en.xml" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Localization\Xml\Resources\zh-Hans.xml" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> |
|||
<PackageReference Include="xunit" Version="2.4.1" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="coverlet.collector" Version="1.3.0"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\localization\LINGYUN.Abp.Localization.Xml\LINGYUN.Abp.Localization.Xml.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public abstract class AbpLocalizationXmlTestBase : AbpTestsBase<AbpLocalizationXmlTestModule> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using System.IO; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpTestsBaseModule), |
|||
typeof(AbpLocalizationXmlModule))] |
|||
public class AbpLocalizationXmlTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpLocalizationXmlTestModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<LocalizationTestResource>("en") |
|||
.AddVirtualXml("/LINGYUN/Abp/Localization/Xml/Resources") |
|||
.AddPhysicalXml(Path.Combine(Directory.GetCurrentDirectory(), "TestResources")); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public class LocalizationTestResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<localization xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="lingyun.abp"> |
|||
<culture name="en" /> |
|||
<texts> |
|||
<text key="Hello China" value="Hello China!" /> |
|||
</texts> |
|||
</localization> |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<localization xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="lingyun.abp"> |
|||
<culture name="zh-Hans" /> |
|||
<texts> |
|||
<text key="Hello China" value="China 你好!" /> |
|||
</texts> |
|||
</localization> |
|||
@ -0,0 +1,94 @@ |
|||
using Microsoft.Extensions.Localization; |
|||
using Shouldly; |
|||
using System.IO; |
|||
using Volo.Abp.Localization; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Localization.Xml |
|||
{ |
|||
public class XmlLocalizationTest : AbpLocalizationXmlTestBase |
|||
{ |
|||
private readonly static string[] _localizerFiles = new string[] |
|||
{ |
|||
Path.Combine("./TestResources", "zh-Hans.xml"), |
|||
Path.Combine("./TestResources", "en.xml") |
|||
}; |
|||
|
|||
private readonly IStringLocalizer<LocalizationTestResource> _localizer; |
|||
|
|||
public XmlLocalizationTest() |
|||
{ |
|||
_localizer = GetRequiredService<IStringLocalizer<LocalizationTestResource>>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Get_Physical_Localized_Text_If_Defined_In_Current_Culture() |
|||
{ |
|||
Init(); |
|||
|
|||
using (CultureHelper.Use("zh-Hans")) |
|||
{ |
|||
_localizer["Hello World"].Value.ShouldBe("世界你好!"); |
|||
_localizer["C# Test"].Value.ShouldBe("C#测试"); |
|||
} |
|||
|
|||
using (CultureHelper.Use("en")) |
|||
{ |
|||
_localizer["Hello World"].Value.ShouldBe("Hello World!"); |
|||
_localizer["C# Test"].Value.ShouldBe("C# Test!"); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Get_Virtual_Localized_Text_If_Defined_In_Current_Culture() |
|||
{ |
|||
using (CultureHelper.Use("zh-Hans")) |
|||
{ |
|||
_localizer["Hello China"].Value.ShouldBe("China 你好!"); |
|||
} |
|||
|
|||
using (CultureHelper.Use("en")) |
|||
{ |
|||
_localizer["Hello China"].Value.ShouldBe("Hello China!"); |
|||
} |
|||
} |
|||
|
|||
private static void Init() |
|||
{ |
|||
var zhHansfile = new XmlLocalizationFile("zh-Hans") |
|||
{ |
|||
Texts = new LocalizationText[] |
|||
{ |
|||
new LocalizationText("Hello World", "世界你好!"), |
|||
new LocalizationText("C# Test", "C#测试") |
|||
} |
|||
}; |
|||
|
|||
zhHansfile.WriteToPath("./TestResources"); |
|||
|
|||
var enFile = new XmlLocalizationFile("en") |
|||
{ |
|||
Texts = new LocalizationText[] |
|||
{ |
|||
new LocalizationText("Hello World", "Hello World!"), |
|||
new LocalizationText("C# Test", "C# Test!") |
|||
} |
|||
}; |
|||
|
|||
enFile.WriteToPath("./TestResources"); |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
base.Dispose(); |
|||
|
|||
foreach (var file in _localizerFiles) |
|||
{ |
|||
if (File.Exists(file)) |
|||
{ |
|||
File.Delete(file); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<localization xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="lingyun.abp"> |
|||
<culture name="zh-Hans" /> |
|||
<texts> |
|||
<text key="Hello World" value="世界你好!" /> |
|||
<text key="C# Test" value="C#测试" /> |
|||
</texts> |
|||
</localization> |
|||
Loading…
Reference in new issue