Abp源码分析之Abp本地化

简介: 本文介绍了如何在 ASP.NET Core MVC 项目中实现本地化功能,包括使用资源文件和 JSON 文件两种方式。首先,通过修改 `Program.cs` 配置支持的多语言环境,并创建相应的资源文件目录。接着,展示了如何在视图中使用本地化字符串。此外,还介绍了使用 ABP 框架实现本地化的具体步骤,包括新建模块、配置服务和创建资源文件。最后,通过源码分析详细解释了本地化机制的实现原理。

aspnetcore mvc 实现本地化

新建mvc项目

修改Program.cs

using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using System.Globalization;

var builder = WebApplication.CreateBuilder(args);

var supportedCultures = new[]
{
    new CultureInfo("zh-CN"),
    new CultureInfo("en-US"),
};
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new RequestCulture("zh-CN");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
});

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.AddControllersWithViews()
        .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
        .AddDataAnnotationsLocalization();

var app = builder.Build();

app.UseRequestLocalization();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");


app.Run();

跟本地化有关的代码

var supportedCultures = new[]
{
    new CultureInfo("zh-CN"),
    new CultureInfo("en-US"),
};
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new RequestCulture("zh-CN");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
});

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.AddControllersWithViews()
        .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
        .AddDataAnnotationsLocalization();
app.UseRequestLocalization();

新建Resources目录,内容如下

修改Index.cshtml

@using Microsoft.Extensions.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using WebApplication1.Controllers

@inject IHtmlLocalizer<HomeController> HtmlLocalizer
@inject IStringLocalizer<HomeController> StringLocalizer
@inject IViewLocalizer ViewLocalizer

@{
    ViewData["Title"] = "Home Page";
}

<div>string: @StringLocalizer["HelloWorld"]</div>

<div>html: @HtmlLocalizer["HelloWorld"]</div>

<div>view: @ViewLocalizer["HelloWorld"]</div>

访问首页

使用Json资源文件

新建mvc项目

安装WeihanLi.Extensions.Localization.Json包

我为了研究方便,下载了源码,所以引用了源码项目,我们正式使用时只要安装nuget包就可以了

修改Program.cs

using System.Globalization;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using WeihanLi.Extensions.Localization.Json;


var builder = WebApplication.CreateBuilder(args);

var services = builder.Services;

// Add services to the container.
builder.Services.AddControllersWithViews();

var supportedCultures = new[]
{
    new CultureInfo("zh-CN"),
    new CultureInfo("en-US"),
};
services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new RequestCulture("zh-CN");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
});
var resourcesPath = builder.Configuration.GetAppSetting("ResourcesPath") ?? "Resources";
services.AddJsonLocalization(options =>
{
    options.ResourcesPath = resourcesPath;
    // options.ResourcesPathType = ResourcesPathType.TypeBased;
    options.ResourcesPathType = ResourcesPathType.CultureBased;
});

services.AddControllersWithViews()
    .AddMvcLocalization(options =>
    {
        options.ResourcesPath = resourcesPath;
    }, LanguageViewLocationExpanderFormat.Suffix);

var app = builder.Build();

app.UseRequestLocalization();



// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

与aspnetcore原始的代码仅一下不同

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
改为:
services.AddJsonLocalization(options =>
{
    options.ResourcesPath = resourcesPath;
    // options.ResourcesPathType = ResourcesPathType.TypeBased;
    options.ResourcesPathType = ResourcesPathType.CultureBased;
});

资源文件文件目录

内容:

源码分析

就是写了一个类JsonStringLocalizerFactory实现了IStringLocalizerFactory

写了JsonStringLocalizer实现了IStringLocalizer

在JsonStringLocalizer类的GetResources中加载json文件


    private Dictionary<string, string> GetResources(string culture)
    {
        return _resourcesCache.GetOrAdd(culture, _ =>
        {
            var resourceFile = "json";
            if (_resourcesPathType == ResourcesPathType.TypeBased)
            {
                resourceFile = $"{culture}.json";
                if (_resourceName != null)
                {
                    resourceFile = string.Join(".", _resourceName.Replace('.', Path.DirectorySeparatorChar), resourceFile);
                }
            }
            else
            {
                resourceFile = string.Join(".",
                    Path.Combine(culture, _resourceName.Replace('.', Path.DirectorySeparatorChar)), resourceFile);
            }

            _searchedLocation = Path.Combine(_resourcesPath, resourceFile);
            Dictionary<string, string> value = null;

            if (File.Exists(_searchedLocation))
            {
                try
                {
                    using var stream = File.OpenRead(_searchedLocation);
                    value = JsonSerializer.Deserialize<Dictionary<string, string>>(stream);
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Failed to get json content, path: {path}", _searchedLocation);
                }
            }
            else
            {
                _logger.LogWarning("Resource file {path} not exists", _searchedLocation);
            }

            return value;
        });
    }

ABP本地化

新建mvc项目 导入下面四个包

## 新建BookAppWebModule.cs

using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
using Volo.Abp.Autofac;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Hosting.Internal;
using BookApp.Localization;

namespace BookApp
{
    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpLocalizationModule),
        typeof(AbpVirtualFileSystemModule),
        typeof(AbpAspNetCoreMvcModule)
    )]
    public class BookAppWebModule: AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();

            context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
            {
                options.AddAssemblyResource(
                    typeof(BookStoreResource)
                );
            });
        }
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();

            ConfigureVirtualFileSystem(hostingEnvironment);

            Configure<AbpLocalizationOptions>(options =>
            {
                options.Languages.Add(new LanguageInfo("ar", "ar", "العربية"));
                options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština"));
                options.Languages.Add(new LanguageInfo("en", "en", "English"));
                options.Languages.Add(new LanguageInfo("en-GB", "en-GB", "English (UK)"));
                options.Languages.Add(new LanguageInfo("hu", "hu", "Magyar"));
                options.Languages.Add(new LanguageInfo("fi", "fi", "Finnish"));
                options.Languages.Add(new LanguageInfo("fr", "fr", "Français"));
                options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi"));
                options.Languages.Add(new LanguageInfo("it", "it", "Italiano"));
                options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português"));
                options.Languages.Add(new LanguageInfo("ru", "ru", "Русский"));
                options.Languages.Add(new LanguageInfo("sk", "sk", "Slovak"));
                options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
                options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
                options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "繁體中文"));
                options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "Deutsch"));
                options.Languages.Add(new LanguageInfo("es", "es", "Español"));

                options.Resources
                    .Add<BookStoreResource>("en")
                    .AddVirtualJson("/Localization/BookStore");

                options.DefaultResourceType = typeof(BookStoreResource);
            });

            //context.Services.AddControllersWithViews()
            //    .AddMvcLocalization(options =>
            //    {
            //        options.ResourcesPath = "/Localization/BookStore";
            //    }, LanguageViewLocationExpanderFormat.Suffix);

            //Configure<AbpExceptionLocalizationOptions>(options =>
            //{
            //    options.MapCodeNamespace("BookStore", typeof(BookStoreResource));
            //});
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            app.UseAbpRequestLocalization();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();
        }

        private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
        {
            Configure<AbpVirtualFileSystemOptions>(options =>
            {
                options.FileSets.AddEmbedded<BookAppWebModule>();

                if (hostingEnvironment.IsDevelopment())
                {
                    options.FileSets.ReplaceEmbeddedByPhysical<BookAppWebModule>(hostingEnvironment.ContentRootPath);
                }
            });
        }
    }
}

修改Program.cs

using BookApp;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Host
    .AddAppSettingsSecretsJson()
    .UseAutofac();

await builder.AddApplicationAsync<BookAppWebModule>();

var app = builder.Build();

await app.InitializeApplicationAsync();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

await app.RunAsync();

新建资源文件与目录

en.json内容

我们只用到AppName

新建BookStoreResource.cs

using Volo.Abp.Localization;

namespace BookApp.Localization;

[LocalizationResourceName("BookStore")]
public class BookStoreResource
{

}

修改Index.cshtml

@using Microsoft.Extensions.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using BookApp.Controllers
@using BookApp.Localization

@inject IHtmlLocalizer<BookStoreResource> HtmlLocalizer
@inject IStringLocalizer<BookStoreResource> StringLocalizer
@inject IViewLocalizer ViewLocalizer
@{
    ViewData["Title"] = "Home Page";
}

<div>string: @StringLocalizer["AppName"]</div>

<div>html: @HtmlLocalizer["AppName"]</div>

<div>view: @ViewLocalizer["AppName"]</div>

显示效果

源码分析

AbpLocalizationModule.cs中

using Volo.Abp.Localization.Resources.AbpLocalization;
using Volo.Abp.Modularity;
using Volo.Abp.Settings;
using Volo.Abp.Threading;
using Volo.Abp.VirtualFileSystem;

namespace Volo.Abp.Localization;

[DependsOn(
    typeof(AbpVirtualFileSystemModule),
    typeof(AbpSettingsModule),
    typeof(AbpLocalizationAbstractionsModule),
    typeof(AbpThreadingModule)
    )]
public class AbpLocalizationModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        AbpStringLocalizerFactory.Replace(context.Services);

        Configure<AbpVirtualFileSystemOptions>(options =>
        {
            options.FileSets.AddEmbedded<AbpLocalizationModule>("Volo.Abp", "Volo/Abp");
        });

        Configure<AbpLocalizationOptions>(options =>
        {
            options
                .Resources
                .Add<DefaultResource>("en");

            options
                .Resources
                .Add<AbpLocalizationResource>("en")
                .AddVirtualJson("/Localization/Resources/AbpLocalization");
        });
    }
}

我们查看AbpStringLocalizerFactory.Replace(context.Services);的内容

    internal static void Replace(IServiceCollection services)
    {
        services.Replace(ServiceDescriptor.Singleton<IStringLocalizerFactory, AbpStringLocalizerFactory>());
        services.AddSingleton<ResourceManagerStringLocalizerFactory>();
    }

我们发现自定义的AbpStringLocalizerFactory实现了IStringLocalizerFactory

AbpDictionaryBasedStringLocalizer实现了IStringLocalizer

跟踪GetLocalizedString()

在这里读取json文件

相关文章

理解ASP.NET Core - 全球化&本地化&多语言(Globalization and Localization) - xiaoxiaotank - 博客园](https://www.cnblogs.com/xiaoxiaotank/p/17466952.html))

作者

吴晓阳(手机:13736969112微信同号)

目录
相关文章
|
3月前
|
存储 开发框架 前端开发
基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用
基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用
|
1天前
|
存储 缓存 JSON
Abp源码分析之Abp本地化
【11月更文挑战第5天】Abp 框架的本地化功能允许应用程序根据用户的语言和地区设置展示不同语言的文本,提升用户体验。其核心组件包括资源文件、本地化管理器和语言提供程序。资源文件存储本地化文本,本地化管理器负责加载和缓存文本,语言提供程序确定用户语言。通过依赖注入和视图层的支持,本地化功能可以无缝集成到应用中。
|
12天前
|
API 数据库
Abp源码分析之Abp最小系统
本文详细介绍了如何构建一个基于ABP框架的最小系统,包括创建API项目、配置模块、访问数据库等步骤。通过创建API项目、修改`Program.cs`和`BookAbpModule.cs`文件,以及添加模块和数据库访问功能,最终实现了基本的CRUD操作。文章还展示了如何使用Swagger生成API文档,并通过控制台输出验证模块的加载顺序。适合初学者快速上手ABP框架。
31 3
Abp源码分析之Abp最小系统
|
22天前
|
缓存 测试技术 C#
使用Radzen Blazor组件库开发的基于ABP框架炫酷UI主题
【10月更文挑战第20天】本文介绍了使用 Radzen Blazor 组件库开发基于 ABP 框架的炫酷 UI 主题的步骤。从准备工作、引入组件库、设计主题、集成到 ABP 框架,再到优化和调试,详细讲解了每个环节的关键点和注意事项。通过这些步骤,你可以打造出高性能、高颜值的应用程序界面。
|
4月前
|
开发框架 前端开发 JavaScript
ABP开发框架前后端开发系列---(16)ABP框架升级最新版本的经验总结
ABP开发框架前后端开发系列---(16)ABP框架升级最新版本的经验总结
|
3月前
|
存储 开发框架 前端开发
利用代码生成工具快速生成基于SqlSugar框架的Winform界面项目
利用代码生成工具快速生成基于SqlSugar框架的Winform界面项目
|
4月前
|
开发框架 前端开发 JavaScript
基于SqlSugar的开发框架循序渐进介绍(1)--框架基础类的设计和使用
基于SqlSugar的开发框架循序渐进介绍(1)--框架基础类的设计和使用
|
4月前
|
存储 开发框架 前端开发
ABP VNext框架基础知识介绍(1)--框架基础类继承关系
ABP VNext框架基础知识介绍(1)--框架基础类继承关系
|
SQL 开发框架 缓存
C# Abp框架入门系列文章(一)(上)
C# Abp框架入门系列文章(一)
371 0