SharedResources 的本地化在 .NET Core 2.1 中不起作用
Localization with SharedResources not working in .NET Core 2.1
我已经为这个问题苦苦挣扎了几个小时...但我找不到它是什么...
我只是想本地化 _Layout.cshtml 文件。 IStringLocalizer
和 IHtmlLocalizer
似乎都没有找到资源文件。
我关注并搜索过:
https://github.com/MormonJesus69420/SharedResourcesExample
.Net Core Data Annotations - localization with shared resources
https://whosebug.com/search?q=shared+resources+.net+core
https://andrewlock.net/adding-localisation-to-an-asp-net-core-application/
我可能忽略了一些愚蠢的事情。
这是我的 startup.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using EduPlaTools.Data;
using EduPlaTools.Models;
using EduPlaTools.Services;
using System.Globalization;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Pomelo.EntityFrameworkCore.MySql;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using Microsoft.AspNetCore.HttpOverrides;
namespace EduPlaTools
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// This is for string translation!
// Adds Localization Services (StringLocalizer, HtmlLocalizer, etc.)
// the opts.ResourcesPath = is the path in which the resources are found.
// In our case the folder is named Resources!
// There's specific and neutral resources. (Specific en-US). (Neutral: es)
/**
* If no ResourcesPath is specified, the view's resources will be expected to be next to the views.
* If ResourcesPath were set to "resources", then view resources would be expected to be ina Resource directory,
* in a path speicifc to their veiw (Resources/Views/Home/About.en.resx, for example).
*
* */
services.AddLocalization(opts => opts.ResourcesPath = "Resources");
// services.AddBContext
// There are subtle differences between the original and the modified version.
services.AddDbContextPool<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("MySQLConnection"),
mysqlOptions =>
{
mysqlOptions.ServerVersion(new Version(8, 0, 12), ServerType.MySql); // replace with your Server Version and Type
}
));
//options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, options => options.ResourcesPath = "Resources")
.AddDataAnnotationsLocalization();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// This may be dangerous and is not recommended
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
.Database.Migrate();
}
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
// These must line up with the ending of the .resx files.
// Example: SharedResources.en.resx, SharedResources.es.rex
// If you want to add specific, then do it like:
// new CultureInfo("en-US")
List<CultureInfo> supportedCultures = new List<CultureInfo>
{
new CultureInfo("es"),
new CultureInfo("en"),
new CultureInfo("es-ES"),
new CultureInfo("en-US")
};
// Registers the localization, and changes the localization per request.
app.UseRequestLocalization(new RequestLocalizationOptions
{
// We give the default support of Spanish.
DefaultRequestCulture = new RequestCulture("es"),
// Format numbers, dates, etc.
SupportedCultures = supportedCultures,
// The strings that we have localized
SupportedUICultures = supportedCultures
});
// This will seed the databse:
SeedDatabase.Initialize(app.ApplicationServices);
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
下面是我尝试在 _Layout.cshtml 中调用它的方式:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject IHtmlLocalizer<SharedResources> _localizer;
@SharedLocalizer["Menu_Home"]
目录结构如下:
Here are the contents of SharedResources.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace EduPlaTools
{
/**
* This is a dummy class that is needed so Localization works.
* Now in .NET Core Localization works as a service, and implementsw
* naming conventions (AT the file level). Therefore, if the files do not
* implement the correct name, there's going to be problems.
*
* See an example, here:
* https://github.com/SteinTheRuler/ASP.NET-Core-Localization/blob/master/Resources/SharedResources.cs
*
* This is a workaround to create a Resource File that can be read by the entire
* application. It's left in blank so the convention over configuration
* picks it up.
*
* */
public class SharedResources
{
}
}
resx 文件的内容如下:
我也试过重命名它们但无济于事..(试过Resources.es.rex,Resources.rex)
我尝试设置断点以查看它的行为。它当然没有找到资源文件。然后我通过回忆一个不存在的密钥将它与 Mormon's repo 进行比较。我将它与我的输出进行了比较,但是 Mormon 的 repo 没有显示 "SearchedLocation"(它是在后来的 .NET Core 版本中引入的吗?)
摩门教的回购:
我的回购:
我知道这可能有点傻...但是已经将近 4 个小时了,我不能停下来因为我有很多事情要做!!
有什么想法吗?
如果要使用共享资源实现本地化,则必须创建自己的文化本地化程序class:
public class CultureLocalizer
{
private readonly IStringLocalizer _localizer;
public CultureLocalizer(IStringLocalizerFactory factory)
{
var type = typeof(ViewResource);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
_localizer = factory.Create("ViewResource", assemblyName.Name);
}
// if we have formatted string we can provide arguments
// e.g.: @Localizer.Text("Hello {0}", User.Name)
public LocalizedString Text(string key, params string[] arguments)
{
return arguments == null
? _localizer[key]
: _localizer[key, arguments];
}
}
然后注册就是启动:
services.AddSingleton<CultureLocalizer>();
并修改视图定位设置:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddViewLocalization(o=>o.ResourcesPath = "Resources")
在您看来,您必须在使用之前注入文化定位器 class。
这些是使用共享资源进行视图本地化的初始设置,您还需要为 DataAnnotation、ModelBinding 和 Identity 错误消息配置本地化设置。
这些文章可以帮助开始:
使用 ASP.NET Core 2.1 Razor Pages 开发多文化 Web 应用程序:
http://www.ziyad.info/en/articles/10-Developing_Multicultural_Web_Application
它包括使用共享资源进行本地化的分步教程,此外,本文是关于本地化身份错误消息的:
http://ziyad.info/en/articles/20-Localizing_Identity_Error_Messages
我想添加一个进一步发展 Laz 解决方案的答案。以防万一有人想要拥有单独的本地化视图。
回到 Startup.cs
,你有:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddViewLocalization(o=>o.ResourcesPath = "Resources")
从技术上讲,您是在指示 MVC 在 "Resources" 文件夹中查找作为主路径,然后按照约定查找本地化的资源文件。
因此
如果您想本地化在 Views/Account/Login.chsmtl
中找到的 Login.cshtml
视图,您必须在以下位置创建资源文件:Resources/Views/Account/Login.en.resx
然后您需要在视图中直接添加以下内容 Login.cshtml
或在 _ViewImports.cshtml
中将其引用到所有视图中:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
之后,您可以在您的代码中执行以下操作:
Localizer["My_Resource_file_key"]
你会翻译它。
以下是一些插图:
对之前答案的更新。由于 .NET Core 3 最近的重大变化 (https://github.com/dotnet/docs/issues/16964),接受的答案只有在资源直接位于资源文件夹中时才有效。
我已经创建了一个在视图中使用共享资源的解决方法(同样适用于控制器、数据注释、服务,无论您需要什么...)。
首先您需要为您的资源创建一个空的 class。这个必须位于 YourApp.Resources
命名空间下。然后创建与 class 同名的资源(在我的示例中,我在命名空间 MyApp.Resources.Shared
和 Views.resx
中有 Views.cs
)。
然后这里是加载共享资源的助手class:
public class SharedViewLocalizer
{
private readonly IStringLocalizer _localizer;
public SharedViewLocalizer(IStringLocalizerFactory factory)
{
var assemblyName = new AssemblyName(typeof(Resources.Shared.Views).GetTypeInfo().Assembly.FullName);
localizer = factory.Create("Shared.Views", assemblyName.Name);
}
public string this[string key] => _localizer[key];
public string this[string key, params object[] arguments] => _localizer[key, arguments];
}
您必须在 Startup.Configure
:
中注册
services.AddSingleton<SharedViewLocalizer>();
我想你使用
services.AddLocalization(options => options.ResourcesPath = "Resources");
设置默认资源位置。
然后在您看来,您可以按如下方式使用它:
@inject IViewLocalizer _localizer
@inject SharedViewLocalizer _sharedLocalizer
@_localizer["View spacific resource"] // Resource from Resources/Views/ControllerName/ViewName.resx
@_sharedLocalizer["Shared resource"] // Resource from Resources/Shared/Views.resx
@_sharedLocalizer["Also supports {0} number of arguments", "unlimited"]
相同的原则可以应用于 DataAnnotations,我们可以在 Startup.Configure
中使用 built-in 方法:
services.AddMvc()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
var assemblyName = new AssemblyName(typeof(DataAnnotations).GetTypeInfo().Assembly.FullName);
return factory.Create("Shared.DataAnnotations", assemblyName.Name
};
})
.AddViewLocalization();
同样,我希望我的资源位于命名空间 Resources.Shared
中,并创建一个名为 DataAnnotations
的空 class。
希望这有助于克服当前的重大变更问题。
我已经为这个问题苦苦挣扎了几个小时...但我找不到它是什么...
我只是想本地化 _Layout.cshtml 文件。 IStringLocalizer
和 IHtmlLocalizer
似乎都没有找到资源文件。
我关注并搜索过: https://github.com/MormonJesus69420/SharedResourcesExample .Net Core Data Annotations - localization with shared resources https://whosebug.com/search?q=shared+resources+.net+core https://andrewlock.net/adding-localisation-to-an-asp-net-core-application/
我可能忽略了一些愚蠢的事情。
这是我的 startup.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using EduPlaTools.Data;
using EduPlaTools.Models;
using EduPlaTools.Services;
using System.Globalization;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Pomelo.EntityFrameworkCore.MySql;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using Microsoft.AspNetCore.HttpOverrides;
namespace EduPlaTools
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// This is for string translation!
// Adds Localization Services (StringLocalizer, HtmlLocalizer, etc.)
// the opts.ResourcesPath = is the path in which the resources are found.
// In our case the folder is named Resources!
// There's specific and neutral resources. (Specific en-US). (Neutral: es)
/**
* If no ResourcesPath is specified, the view's resources will be expected to be next to the views.
* If ResourcesPath were set to "resources", then view resources would be expected to be ina Resource directory,
* in a path speicifc to their veiw (Resources/Views/Home/About.en.resx, for example).
*
* */
services.AddLocalization(opts => opts.ResourcesPath = "Resources");
// services.AddBContext
// There are subtle differences between the original and the modified version.
services.AddDbContextPool<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("MySQLConnection"),
mysqlOptions =>
{
mysqlOptions.ServerVersion(new Version(8, 0, 12), ServerType.MySql); // replace with your Server Version and Type
}
));
//options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, options => options.ResourcesPath = "Resources")
.AddDataAnnotationsLocalization();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// This may be dangerous and is not recommended
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
.Database.Migrate();
}
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
// These must line up with the ending of the .resx files.
// Example: SharedResources.en.resx, SharedResources.es.rex
// If you want to add specific, then do it like:
// new CultureInfo("en-US")
List<CultureInfo> supportedCultures = new List<CultureInfo>
{
new CultureInfo("es"),
new CultureInfo("en"),
new CultureInfo("es-ES"),
new CultureInfo("en-US")
};
// Registers the localization, and changes the localization per request.
app.UseRequestLocalization(new RequestLocalizationOptions
{
// We give the default support of Spanish.
DefaultRequestCulture = new RequestCulture("es"),
// Format numbers, dates, etc.
SupportedCultures = supportedCultures,
// The strings that we have localized
SupportedUICultures = supportedCultures
});
// This will seed the databse:
SeedDatabase.Initialize(app.ApplicationServices);
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
下面是我尝试在 _Layout.cshtml 中调用它的方式:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject IHtmlLocalizer<SharedResources> _localizer;
@SharedLocalizer["Menu_Home"]
目录结构如下:
Here are the contents of SharedResources.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace EduPlaTools
{
/**
* This is a dummy class that is needed so Localization works.
* Now in .NET Core Localization works as a service, and implementsw
* naming conventions (AT the file level). Therefore, if the files do not
* implement the correct name, there's going to be problems.
*
* See an example, here:
* https://github.com/SteinTheRuler/ASP.NET-Core-Localization/blob/master/Resources/SharedResources.cs
*
* This is a workaround to create a Resource File that can be read by the entire
* application. It's left in blank so the convention over configuration
* picks it up.
*
* */
public class SharedResources
{
}
}
resx 文件的内容如下:
我也试过重命名它们但无济于事..(试过Resources.es.rex,Resources.rex)
我尝试设置断点以查看它的行为。它当然没有找到资源文件。然后我通过回忆一个不存在的密钥将它与 Mormon's repo 进行比较。我将它与我的输出进行了比较,但是 Mormon 的 repo 没有显示 "SearchedLocation"(它是在后来的 .NET Core 版本中引入的吗?)
摩门教的回购:
我的回购:
我知道这可能有点傻...但是已经将近 4 个小时了,我不能停下来因为我有很多事情要做!!
有什么想法吗?
如果要使用共享资源实现本地化,则必须创建自己的文化本地化程序class:
public class CultureLocalizer
{
private readonly IStringLocalizer _localizer;
public CultureLocalizer(IStringLocalizerFactory factory)
{
var type = typeof(ViewResource);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
_localizer = factory.Create("ViewResource", assemblyName.Name);
}
// if we have formatted string we can provide arguments
// e.g.: @Localizer.Text("Hello {0}", User.Name)
public LocalizedString Text(string key, params string[] arguments)
{
return arguments == null
? _localizer[key]
: _localizer[key, arguments];
}
}
然后注册就是启动:
services.AddSingleton<CultureLocalizer>();
并修改视图定位设置:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddViewLocalization(o=>o.ResourcesPath = "Resources")
在您看来,您必须在使用之前注入文化定位器 class。
这些是使用共享资源进行视图本地化的初始设置,您还需要为 DataAnnotation、ModelBinding 和 Identity 错误消息配置本地化设置。
这些文章可以帮助开始:
使用 ASP.NET Core 2.1 Razor Pages 开发多文化 Web 应用程序:
http://www.ziyad.info/en/articles/10-Developing_Multicultural_Web_Application
它包括使用共享资源进行本地化的分步教程,此外,本文是关于本地化身份错误消息的:
http://ziyad.info/en/articles/20-Localizing_Identity_Error_Messages
我想添加一个进一步发展 Laz 解决方案的答案。以防万一有人想要拥有单独的本地化视图。
回到 Startup.cs
,你有:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddViewLocalization(o=>o.ResourcesPath = "Resources")
从技术上讲,您是在指示 MVC 在 "Resources" 文件夹中查找作为主路径,然后按照约定查找本地化的资源文件。
因此
如果您想本地化在 Views/Account/Login.chsmtl
中找到的 Login.cshtml
视图,您必须在以下位置创建资源文件:Resources/Views/Account/Login.en.resx
然后您需要在视图中直接添加以下内容 Login.cshtml
或在 _ViewImports.cshtml
中将其引用到所有视图中:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
之后,您可以在您的代码中执行以下操作:
Localizer["My_Resource_file_key"]
你会翻译它。
以下是一些插图:
对之前答案的更新。由于 .NET Core 3 最近的重大变化 (https://github.com/dotnet/docs/issues/16964),接受的答案只有在资源直接位于资源文件夹中时才有效。 我已经创建了一个在视图中使用共享资源的解决方法(同样适用于控制器、数据注释、服务,无论您需要什么...)。
首先您需要为您的资源创建一个空的 class。这个必须位于 YourApp.Resources
命名空间下。然后创建与 class 同名的资源(在我的示例中,我在命名空间 MyApp.Resources.Shared
和 Views.resx
中有 Views.cs
)。
然后这里是加载共享资源的助手class:
public class SharedViewLocalizer
{
private readonly IStringLocalizer _localizer;
public SharedViewLocalizer(IStringLocalizerFactory factory)
{
var assemblyName = new AssemblyName(typeof(Resources.Shared.Views).GetTypeInfo().Assembly.FullName);
localizer = factory.Create("Shared.Views", assemblyName.Name);
}
public string this[string key] => _localizer[key];
public string this[string key, params object[] arguments] => _localizer[key, arguments];
}
您必须在 Startup.Configure
:
services.AddSingleton<SharedViewLocalizer>();
我想你使用
services.AddLocalization(options => options.ResourcesPath = "Resources");
设置默认资源位置。
然后在您看来,您可以按如下方式使用它:
@inject IViewLocalizer _localizer
@inject SharedViewLocalizer _sharedLocalizer
@_localizer["View spacific resource"] // Resource from Resources/Views/ControllerName/ViewName.resx
@_sharedLocalizer["Shared resource"] // Resource from Resources/Shared/Views.resx
@_sharedLocalizer["Also supports {0} number of arguments", "unlimited"]
相同的原则可以应用于 DataAnnotations,我们可以在 Startup.Configure
中使用 built-in 方法:
services.AddMvc()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
var assemblyName = new AssemblyName(typeof(DataAnnotations).GetTypeInfo().Assembly.FullName);
return factory.Create("Shared.DataAnnotations", assemblyName.Name
};
})
.AddViewLocalization();
同样,我希望我的资源位于命名空间 Resources.Shared
中,并创建一个名为 DataAnnotations
的空 class。
希望这有助于克服当前的重大变更问题。