我在我的项目中部分实施了全球化/本土化。该项目需要一个用于资源字符串的数据库,我发现了一个很好的NuGet包,名为WestWind.Globalization,它完成了我所需要的功能。
此NuGet包允许您使用几种不同的方法显示资源字符串。它提供了一个选项,用于生成包含所有资源字符串的强类型类,以便您可以如下所示:
@Html.Encode( Resources.lblResourceName )或
object Value = this.GetLocalResourceObject("ResourceName");或
object GlobalValue = this.GetGlobalResourceObject("Resources","ResourceKey");甚至:
dbRes.T(resourceName, resourceSet, culture)我不想手动指定区域性,所以选择了以下方法:
<p class="pageprompt">@AccountRequestAccount.pagePrompt</p>对我来说,Westwind.Globalization很神奇。这解决了我的一个大问题,但我遇到了一个障碍,我不知道如何克服。也就是说,如何设置区域性/文化性use,以便包能够自动使用指定的语言资源。
我创建了一个包含下拉语言列表的PartialView。它包含在~/Views/Shared/文件夹中,并包含在_Layout.cshtml中。我对GET和POST控制器操作进行了编码,这些操作按预期工作,只是无法持久化区域性/CultureUI设置。我怀疑这是因为在选择语言之后立即重定向(下文解释)。
所以,我找到了一个所以问题,它的答案似乎是可行的。我把这个答案整合到我的项目中。有关守则是:
RouteConfig.cs:
routes.MapRoute("DefaultLocalized",
"{language}-{culture}/{controller}/{action}/{id}",
new
{
controller = "Home",
action = "Index",
id = "",
language = "en",
culture = "US"
});~/帮助者/国际化属性。
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc;
namespace GPS_Web_App.Helpers
{
public class InternationalizationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string language =
(string)filterContext.RouteData.Values["language"] ?? "en";
string culture =
(string)filterContext.RouteData.Values["culture"] ?? "US";
Thread.CurrentThread.CurrentCulture =
CultureInfo.GetCultureInfo(string.Format("{0}-{1}",
language, culture));
Thread.CurrentThread.CurrentUICulture =
CultureInfo.GetCultureInfo(string.Format("{0}-{1}",
language, culture));
}
}
}在我的控制员中:
[Authorize]
[Internationalization]
public class AccountController : Controller
{
...
}到目前一切尚好。这是因为我能够访问http://example.com/en-mx/Account/Login/的URL,并看到页面被Westwind.Globalization本地化,以及我创建的资源字符串。
我遇到的问题是:
所有的代码都为下拉列表做好了准备,允许用户进行语言选择。上述#1、#2和#3的逻辑工作正常,但不会生效并触发were .全球化的DbResourceProvider来传递所选的语言资源字符串。
通过调试,我发现我的设置没有坚持:
System.Threading.Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.GetCultureInfo(SelectedLanguage);
System.Threading.Thread.CurrentThread.CurrentUICulture =
System.Globalization.CultureInfo.GetCultureInfo(SelectedLanguage);通过我在这里的问题提供的答复,我了解到,如果在原始视图呈现之前进行重定向,这些设置将不会持续/生效。然而,重定向回到原来的视图似乎是明智的,因为语言正在被改变,需要重新呈现。我认为@jao的解决方案克服了重定向问题,但它强制由URL指定全球化/本地化?有点像-22.
我已经要求@jao回顾这个问题,并提供任何提示。我想我的问题最好概括如下:
如何使用用户的cookie/配置文件设置来一次性设置文化/文化URL,以便Westwind.Globalization能够读取全球化/本地化,而不依赖于在URL中传递的区域性?
发布于 2014-11-11 15:57:45
我将此答案作为使用异步控制器的ASP.NET MVC5进行本地化的另一种定制方式发布。也许您会在我的解决方案中发现一些问题,特别是在路由和设置cookie方面。
这是我为我的异构/自定义方法草草写下的一个简短的教程。所以我更喜欢这样,而不是WordPress。:)
很抱歉没有给出你的问题的精确和离散的答案。希望它能在其他方面帮助您,也能帮助其他人,他们希望做同样的设置。
在他的博客帖子中,Nadeem描述了在解决方案中创建一个单独的项目Resource以使用静态资源文件实现国际化的策略。在博客续集中,他详细介绍了如何通过数据库和XML驱动的方法扩展相同的项目以处理资源。对于前者,他使用了ADO.NET,与实体框架分离。
我们需要在MVC项目中实现静态和动态资源,同时尊重MVC约定的概念。
首先,让我们在项目根目录中添加一个参考资料文件夹,并提供所需的语言变体:~/Resources/Resources.resx (默认资源文件对应en-US区域性)、~/Resources/Resources.fi.resx和~/Resources/Resources.nl.resx。将资源标记为公共资源,以便在视图中可用。
在~/Views/Web.config中,在<namespace>元素:<add namespace="YourMainNamespace.Reousrces" />下添加资源命名空间。在“控制器”下,创建一个基本控制器类:
来了cookies
namespace YourNamespace.Controllers
{
// Don't forget to inherit other controllers with this
public class BaseController : Controller
{
protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
{
string cultureName = null;
// Attempt to read the culture cookie from Request
HttpCookie cultureCookie = Request.Cookies["_culture"];
if (cultureCookie != null)
cultureName = cultureCookie.Value;
else
cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ?
Request.UserLanguages[0] : // obtain it from HTTP header AcceptLanguages
null;
// Validate culture name
cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe
// Modify current thread's cultures
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
return base.BeginExecuteCore(callback, state);
}
}
}接下来,在~/Global.asax.cs中注册一个全局过滤器,以确保每个操作在执行之前都应该使用正确的区域性:
又来了饼干!
public class SetCultureActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
var response = filterContext.RequestContext.HttpContext.Response;
var culture = filterContext.RouteData.Values["culture"].ToString();
// Validate input
culture = CultureHelper.GetImplementedCulture(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
// Save culture in a cookie
HttpCookie cookie = filterContext.RequestContext.HttpContext.Request.Cookies["_culture"];
if (cookie != null)
cookie.Value = culture; // update cookie value
else
{
cookie = new HttpCookie("_culture");
cookie.Value = culture;
cookie.Expires = DateTime.Now.AddYears(1);
}
response.Cookies.Add(cookie);
}
}并在GlobalFilters.Filters.Add(new SetCultureActionFilterAttribute());方法中添加MyApplication.Application_Start()方法。
在~/App_Start/RoutesConfig.cs中,将默认路由更改为:
routes.MapRoute(
name: "Default",
url: "{culture}/{controller}/{action}/{id}",
defaults: new { culture = "en-US", controller = "Home", action = "Index", id = UrlParameter.Optional }
);在这一点上,我们将能够使用资源。例如,@Resources.Headline。
接下来,我们将为模型属性创建一个名为Translatable的自定义属性。
class TranslatableAttribute : Attribute
{ }这就够了。但是,如果您希望能够指定作用域,可以使用这个类来实现它。
现在添加一个名为Resource的模型,其中包含三个属性和一个助手方法:
public class Resource
{
[Key, Column(Order = 0)]
public string Culture { get; set; }
[Key, Column(Order = 1)]
public string Name { get; set; }
public string Value { get; set; }
#region Helpers
// Probably using reflection not the best approach.
public static string GetPropertyValue<T>(string id, string propertyName) where T : class
{
return GetPropertyValue<T>(id, propertyName, Thread.CurrentThread.CurrentUICulture.Name);
}
public static string GetPropertyValue<T>(string id, string propertyName, string culture) where T : class
{
Type entityType = typeof(T);
string[] segments = propertyName.Split('.');
if (segments.Length > 1)
{
entityType = Type.GetType("YourNameSpace.Models." + segments[0]);
propertyName = segments[1];
}
if (entityType == null)
return "?<invalid type>";
var propertyInfo = entityType.GetProperty(propertyName);
var translateableAttribute = propertyInfo.GetCustomAttributes(typeof(TranslatableAttribute), true)
.FirstOrDefault();
/*var requiredAttribute = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true)
.FirstOrDefault();*/
if (translateableAttribute == null)
return "?<this field has no translatable attribute>";
var dbCtx = new YourNamespaceDbContext();
var className = entityType.Name;
Resource resource = dbCtx.Resources.Where(r =>
(r.Culture == culture) &&
r.Name == className + id + propertyName).FirstOrDefault();
if (resource != null)
return resource.Value;
//return requiredAttribute == null ? string.Empty : "?<translation not found>";
return string.Empty;
}
#endregion
}此助手方法将帮助您检索已翻译的内容。例如,考虑到,您可以说:
var name = Resource.GetPropertyValue<Product>(item.Id.ToString(), "Name");注意,在任何时候,可翻译字段列中的数据都是不可靠的;它总是保存最后更新的值。在创建记录时,我们将镜像所有受支持的区域性的资源模型中所有可转换属性的值。
我们使用异步控制器,因此为了插入、修改和删除,我们将在我们的SaveChangesAsync()类中重写DbContext:
public override Task<int> SaveChangesAsync()
{
ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;
List<ObjectStateEntry> objectDeletedStateEntryList =
ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted)
.ToList();
List<ObjectStateEntry> objectCreateOrModifiedStateEntryList =
ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified)
.ToList();
// First handle the delition case,
// before making changes to entry state
bool changed = UpdateResources(objectDeletedStateEntryList);
// Now save the changes
int result = base.SaveChangesAsync().Result;
// Finally handle the remaining cases
changed |= UpdateResources(objectCreateOrModifiedStateEntryList);
if (changed)
return base.SaveChangesAsync();
return Task.FromResult<int>(result);
}
private bool UpdateResources(List<ObjectStateEntry> objectStateEntryList)
{
bool changed = false;
foreach (ObjectStateEntry entry in objectStateEntryList)
{
var typeName = entry.EntitySet.ElementType.Name;
if (entry.IsRelationship || typeName == "Resource")
return false;
var type = Type.GetType("YourNamespace.Models." + typeName);
if (type == null) // When seeds run (db created for the first-time), sometimes types might not be create
return false;
if (entry.State == EntityState.Deleted)
{
changed |= DeleteResources(type, typeName, entry);
continue;
}
foreach (var propertyInfo in type.GetProperties())
{
var attribute = propertyInfo.GetCustomAttributes(typeof(TranslatableAttribute), true).FirstOrDefault();
if (attribute == null)
continue;
CurrentValueRecord current = entry.CurrentValues;
object idField = current.GetValue(current.GetOrdinal("Id"));
if (idField == null)
continue;
var id = idField.ToString();
var propertyName = propertyInfo.Name;
string newValue = current.GetValue(current.GetOrdinal(propertyName)).ToString();
var name = typeName + id + propertyName;
Resource existingResource = this.Resources.Find(Thread.CurrentThread.CurrentUICulture.Name, name);
if (existingResource == null)
{
foreach (var culture in CultureHelper.Cultures)
{
this.Resources.Add(new Resource
{
Culture = culture,
Name = name,
Value = newValue
});
changed |= true;
}
}
else
{
existingResource.Value = newValue;
changed |= true;
}
}
}
return changed;
}
private bool DeleteResources(Type type, string typeName, ObjectStateEntry entry)
{
bool changed = false;
var firstKey = entry.EntityKey.EntityKeyValues.Where(k => k.Key.Equals("Id", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if (firstKey == null)
return false;
var id = firstKey.Value.ToString();
foreach (var propertyInfo in type.GetProperties())
{
var name = typeName + id + propertyInfo.Name;
foreach (var culture in CultureHelper.Cultures)
{
Resource existingResource = this.Resources.Find(culture, name);
if (existingResource == null)
continue;
this.Resources.Remove(existingResource);
changed |= true;
}
}
return changed;
}这将负责更新和删除。
https://stackoverflow.com/questions/24418585
复制相似问题