本文为Google翻译的 原文地址
本文介绍如何使用SQL数据库在ASP.NET Core中使用SQL本地化。 演示中的SQL本地化使用Entity Framework Core访问SQLite数据库。 可以将其配置为使用任何EF核心提供程序。
使用SQL本地化
要使用SQL本地化,需要将该库添加到project.json文件中的依赖项。 该库称为Localization.SqlLocalizer。
1 2 3 "dependencies" : { "Localization.SqlLocalizer" : "2.0.0" }
然后可以将其添加到Startup类的ConfigureServices方法中。 AddSqlLocalization方法要求在实体框架服务扩展中配置LocalizationModelContext类。 在此示例中,SQLite用作LocalizationModelContext上下文类的提供程序。 其余的本地化可以根据需要进行配置。 该示例支持en-US,de-CH,fr-CH,it-CH。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public void ConfigureServices (IServiceCollection services ){ var sqlConnectionString = Configuration["DbStringLocalizer:ConnectionString" ]; services.AddDbContext<LocalizationModelContext>(options => options.UseSqlite( sqlConnectionString, b => b.MigrationsAssembly("AspNet5Localization" ) ) ); services.AddSqlLocalization(); services.AddMvc() .AddViewLocalization() .AddDataAnnotationsLocalization(); services.AddScoped<LanguageActionFilter>(); services.Configure<RequestLocalizationOptions>( options => { var supportedCultures = new List<CultureInfo> { new CultureInfo("en-US" ), new CultureInfo("de-CH" ), new CultureInfo("fr-CH" ), new CultureInfo("it-CH" ) }; options.DefaultRequestCulture = new RequestCulture(culture: "en-US" , uiCulture: "en-US" ); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; }); }
还需要在Configure方法中添加本地化。
1 2 3 4 5 6 7 8 9 10 11 12 public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory ){ loggerFactory.AddConsole(); loggerFactory.AddDebug(); var locOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>(); app.UseRequestLocalization(locOptions.Value); app.UseStaticFiles(); app.UseMvc(); }
现在需要创建数据库。 如果使用SQLite,则在 SqliteCreateLocalizationRecord.sql 文件中 提供创建sql脚本 。 如果使用其他数据库,则需要创建该数据库。 我没有提供迁移脚本。 可以使用SQLite Manager在Firefox中执行SQLite脚本。
1 2 3 4 5 6 7 8 CREATE TABLE "LocalizationRecords" ( "Id" INTEGER NOT NULL CONSTRAINT "PK_DataEventRecord" PRIMARY KEY AUTOINCREMENT, "Key" TEXT, "ResourceKey" TEXT, "Text" TEXT, "LocalizationCulture" TEXT, "UpdatedTimestamp" TEXT NOT NULL )
该数据库现在应该存在。 我添加了一些基本的演示行。
SQL Localization的默认配置使用资源名称,然后使用资源密钥(如果遵循Microsoft的建议,则可能是默认语言文本),然后使用区域性。 这些属性中的每个属性在数据库中都有一个单独的字段。 如果找不到本地化,则返回搜索到的关键字。 这是一个未找到并返回到UI的本地化示例。
ASP.NET Core MVC控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Localization; namespace AspNet5Localization.Controllers { [ServiceFilter(typeof(LanguageActionFilter)) ] [Route("api/{culture}/[controller]" ) ] public class AboutWithCultureInRouteController : Controller { private readonly IStringLocalizer<SharedResource> _localizer; public AboutWithCultureInRouteController (IStringLocalizer<SharedResource> localizer ) { _localizer = localizer; } [HttpGet ] public string Get () { return _localizer["Name" ]; } } }
使用的网址:
1 http://localhost:5000/api/fr-CH/AboutWithCultureInRoute
结果:ResoureKey:SharedResource,Key:名称,本地化文化:fr-CH
1 SharedResource.Name.fr-CH
这应该使查找和添加缺失的本地化变得容易。
配置SQL本地化
还可以将SQL Localization配置为使用不同的键在数据库中搜索本地化。 可以使用services.AddSqlLocalization并添加options参数在Startup类中进行配置。
SqlLocalizationOptions具有两个属性,UseTypeFullNames和UseOnlyPropertyNames。 如果UseOnlyPropertyNames为true,则在数据库中仅将属性名称用作带有ResourceKey全局键的键。 您还可以通过设置UseTypeFullNames将其配置为使用FullNames作为键。 如果设置了此名称,则数据库的ResourceKey属性中需要完整的类型名称。
1 2 3 4 5 6 7 8 9 10 11 12 public class SqlLocalizationOptions { public bool UseTypeFullNames { get ; set ; } public bool UseOnlyPropertyNames { get ; set ; } }
在Startup类中使用选项的示例:
1 2 3 4 5 6 7 8 9 var sqlConnectionString = Configuration["DbStringLocalizer:ConnectionString" ]; services.AddEntityFramework() .AddSqlite() .AddDbContext<LocalizationModelContext>( options => options.UseSqlite(sqlConnectionString)); services.AddSqlLocalization(options => options.UseTypeFullNames = true );
用于HTTP请求的Controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System.Globalization;using System.Threading; namespace AspNet5Localization.Controllers { using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.Extensions.Localization; [Route("api/[controller]" ) ] public class AboutController : Controller { private readonly IStringLocalizer<SharedResource> _localizer; private readonly IStringLocalizer<AboutController> _aboutLocalizerizer; public AboutController (IStringLocalizer<SharedResource> localizer, IStringLocalizer<AboutController> aboutLocalizerizer ) { _localizer = localizer; _aboutLocalizerizer = aboutLocalizerizer; } [HttpGet ] public string Get () { return _aboutLocalizerizer["AboutTitle" ]; } } }
网址:
1 http://localhost:5000/api/about?culture=it-CH
结果:从结果中可以看到,SQL本地化使用FullName搜索了本地化。 ResoureKey:AspNet5Localization.Controllers.AboutController,键:AboutTitle,LocalizationCulture:it-CH
1 AspNet5Localization.Controllers.AboutController.AboutTitle.it-CH
SQL本地化详细
SQL本地化库使用扩展方法来提供其服务,该服务可在应用程序的Startup类中使用。 该库取决于Entity Framework Core。 任何数据库提供程序都可以用于此目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 using System;using Microsoft.Extensions.DependencyInjection.Extensions;using Microsoft.Extensions.Localization; namespace Microsoft.Extensions.DependencyInjection { using global ::Localization.SqlLocalizer; using global ::Localization.SqlLocalizer.DbStringLocalizer; public static class SqlLocalizationServiceCollectionExtensions { public static IServiceCollection AddSqlLocalization (this IServiceCollection services ) { if (services == null ) { throw new ArgumentNullException(nameof (services)); } return AddSqlLocalization(services, setupAction: null ); } public static IServiceCollection AddSqlLocalization ( this IServiceCollection services, Action<SqlLocalizationOptions> setupAction ) { if (services == null ) { throw new ArgumentNullException(nameof (services)); } services.TryAdd(new ServiceDescriptor( typeof (IStringLocalizerFactory), typeof (SqlStringLocalizerFactory), ServiceLifetime.Singleton)); services.TryAdd(new ServiceDescriptor( typeof (IStringLocalizer), typeof (SqlStringLocalizer), ServiceLifetime.Singleton)); if (setupAction != null ) { services.Configure(setupAction); } return services; } } }
SqlStringLocalizerFactory类实现IStringLocalizerFactory,该对象负责数据库访问。 该数据库仅用于每个资源的第一个请求。 这意味着此后会有更好的性能,但是新的翻译仅 在 应用程序重新启动后 才能读取 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 namespace Localization.SqlLocalizer.DbStringLocalizer { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using Microsoft.Extensions.PlatformAbstractions; public class SqlStringLocalizerFactory : IStringLocalizerFactory { private readonly LocalizationModelContext _context; private readonly ConcurrentDictionary<string , IStringLocalizer> _resourceLocalizations = new ConcurrentDictionary<string , IStringLocalizer>(); private readonly IOptions<SqlLocalizationOptions> _options; private const string Global = "global" ; public SqlStringLocalizerFactory ( LocalizationModelContext context, IApplicationEnvironment applicationEnvironment, IOptions<SqlLocalizationOptions> localizationOptions ) { if (context == null ) { throw new ArgumentNullException(nameof (LocalizationModelContext)); } if (applicationEnvironment == null ) { throw new ArgumentNullException(nameof (applicationEnvironment)); } if (localizationOptions == null ) { throw new ArgumentNullException(nameof (localizationOptions)); } _options = localizationOptions; _context = context; } public IStringLocalizer Create (Type resourceSource ) { SqlStringLocalizer sqlStringLocalizer; if (_options.Value.UseOnlyPropertyNames) { if (_resourceLocalizations.Keys.Contains(Global)) { return _resourceLocalizations[Global]; } sqlStringLocalizer = new SqlStringLocalizer(GetAllFromDatabaseForResource(Global), Global); return _resourceLocalizations.GetOrAdd(Global, sqlStringLocalizer); } if (_options.Value.UseTypeFullNames) { if (_resourceLocalizations.Keys.Contains(resourceSource.FullName)) { return _resourceLocalizations[resourceSource.FullName]; } sqlStringLocalizer = new SqlStringLocalizer(GetAllFromDatabaseForResource(resourceSource.FullName), resourceSource.FullName); _resourceLocalizations.GetOrAdd(resourceSource.FullName, sqlStringLocalizer); } if (_resourceLocalizations.Keys.Contains(resourceSource.Name)) { return _resourceLocalizations[resourceSource.Name]; } sqlStringLocalizer = new SqlStringLocalizer(GetAllFromDatabaseForResource(resourceSource.Name), resourceSource.Name); return _resourceLocalizations.GetOrAdd(resourceSource.Name, sqlStringLocalizer); } public IStringLocalizer Create (string baseName, string location ) { if (_resourceLocalizations.Keys.Contains(baseName + location)) { return _resourceLocalizations[baseName + location]; } var sqlStringLocalizer = new SqlStringLocalizer(GetAllFromDatabaseForResource(baseName + location), baseName + location); return _resourceLocalizations.GetOrAdd(baseName + location, sqlStringLocalizer); } private Dictionary<string , string > GetAllFromDatabaseForResource (string resourceKey ) { return _context.LocalizationRecords.Where(data => data.ResourceKey == resourceKey).ToDictionary(kvp => (kvp.Key + "." + kvp.LocalizationCulture), kvp => kvp.Text); } } }
SqlStringLocalizer实现IStringLocalizer。 因为它只是GET资源,所以用作应用程序的单例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 namespace Localization.SqlLocalizer.DbStringLocalizer { using System; using System.Collections.Generic; using System.Globalization; using Microsoft.Extensions.Localization; public class SqlStringLocalizer : IStringLocalizer { private readonly Dictionary<string , string > _localizations; private readonly string _resourceKey; public SqlStringLocalizer (Dictionary<string , string > localizations, string resourceKey ) { _localizations = localizations; _resourceKey = resourceKey; } public LocalizedString this [string name] { get { return new LocalizedString(name, GetText(name)); } } public LocalizedString this [string name, params object [] arguments] { get { return new LocalizedString(name, GetText(name)); } } public IEnumerable<LocalizedString> GetAllStrings (bool includeParentCultures ) { throw new NotImplementedException(); } public IStringLocalizer WithCulture (CultureInfo culture ) { throw new NotImplementedException(); } private string GetText (string key ) { #if NET451 var culture = System.Threading.Thread.CurrentThread.CurrentCulture.ToString(); #elif NET46 var culture = System.Threading.Thread.CurrentThread.CurrentCulture.ToString(); #else var culture = CultureInfo.CurrentCulture.ToString(); #endif string computedKey = $"{key} .{culture} " ; string result; if (_localizations.TryGetValue(computedKey, out result)) { return result; } else { return _resourceKey + "." + computedKey; } } } }
LocalizationModelContext类是实体框架核心DbContext实现。 这使用LocalizationRecord模型类进行数据访问。 影子属性用于数据库UpdatedTimestamp,在使用本地化数据库导入进行更新时将使用该属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 namespace Localization.SqlLocalizer.DbStringLocalizer { using System; using System.Linq; using Microsoft.EntityFrameworkCore; public class LocalizationModelContext : DbContext { public LocalizationModelContext (DbContextOptions<LocalizationModelContext> options ) :base (options ) { } public DbSet <LocalizationRecord > LocalizationRecords { get ; set ; } protected override void OnModelCreating (ModelBuilder builder ) { builder.Entity<LocalizationRecord>().HasKey(m => m.Id); builder.Entity<LocalizationRecord>().Property<DateTime>("UpdatedTimestamp" ); base .OnModelCreating(builder); } public override int SaveChanges () { ChangeTracker.DetectChanges(); updateUpdatedProperty<LocalizationRecord>(); return base .SaveChanges(); } private void updateUpdatedProperty <T >() where T : class { var modifiedSourceInfo = ChangeTracker.Entries<T>() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified); foreach (var entry in modifiedSourceInfo) { entry.Property("UpdatedTimestamp" ).CurrentValue = DateTime.UtcNow; } } } }
下一步
该应用程序可以打包为NuGet并添加到NuGet服务器。 该数据库可以优化。 我还可以添加一些额外的配置选项。 我想为SPA json文件以及可以由外部公司翻译的csv文件实现导入,导出功能。
我很乐意提供反馈,非常感谢您提供改进方法的提示。 也许可以将其添加到ASP.NET Core中。