我的数据库中有状态表,而“本地化”表包含这些状态的特定于语言的版本。主状态表的要点是定义状态ID值以及有关状态的其他元数据。 “本地化”表是根据用户的首选语言在UI中显示文本表示。这是一个示例模式:
create table [Language]
(
ID smallint primary key,
ISOName varchar(12)
)
create table EmployeeStatus
(
ID smallint primary key,
Code varchar(50)
)
create table EmployeeStatusLocalised
(
EmployeeStatusID smallint,
LanguageID smallint,
Description varchar(50),
constraint PK_EmployeeStatusLocalised primary key
(EmployeeStatusID, LanguageID),
constraint FK_EmployeeStatusLocalised_EmployeeStatus foreign key
(EmployeeStatusID) references EmployeeStatus (ID),
constraint FK_EmployeeStatusLocalised_Language foreign key
(LanguageID) references [Language] (ID)
)
create table Employee
(
ID int identity(1,1) primary key,
EmployeeName varchar(50) not null,
EmployeeStatusID smallint not null,
constraint FK_Employee_EmployeeStatus foreign key
(EmployeeStatusID) references EmployeeStatus (ID)
)
这就是我通常访问该数据的方式:
select e.EmployeeName, esl.Description as EmployeeStatus
from Employee e
inner join EmployeeStatusLocalised esl on
e.EmployeeStatusID = esl.EmployeeStatusID and esl.LanguageID = 1
我对LINQ to SQL以最有效的方式做事情并不高兴。这是一个例子:
using (var context = new MyDbDataContext())
{
var item = (from record in context.Employees
select record).Take(1).SingleOrDefault();
Console.WriteLine("{0}: {1}", item.EmployeeName,
item.EmployeeStatus.EmployeeStatusLocaliseds.
Where(esl => esl.LanguageID == 1).Single().Description);
}
答案 0 :(得分:2)
就个人而言,我可能会将EmployeeStatus代码留在数据库中,并将所有本地化逻辑移到客户端。如果这是一个Web应用程序(ASP.NET或ASP.NET MVC),那么您将使用EmployeeStatus代码作为资源文件的密钥,然后使用UICulture =“Auto”和Culture =“Auto”来告诉ASP .NET根据“Accept-Language”HTTP标头获取正确的资源。
您需要在应用中嵌入默认(不区分文化)资源,并允许satalite程序集覆盖所需的默认值。
对于我来说,在数据库中添加本地化的问题是,您首先会遇到更复杂的查询,您必须不断地将语言环境转移到每个查询中,并且您无法缓存输出查询如此广泛。其次,你有一个表混合,包含持有本地化的实体和表。最后,DBA需要进行本地化。
理想情况下,您希望有人了解如何翻译文本以进行本地化,并让他们使用他们熟悉的工具。有很多.resx工具,以及允许语言专家“做他们的事情”的应用程序。
如果您坚持使用数据库表进行本地化,因为“就是这样”,那么也许您应该单独查询查找到真实数据,并在UI上加入两者。这将至少为您提供将来.RESX的“升级路径”。
如果你对这个领域感兴趣的话,你应该查看Guy Smith-Ferrier关于i18n的书:
答案 1 :(得分:1)
一个选项可能是使用缓存应用程序块或ASP.NET缓存来维护本地化数据的缓存,然后在视图中引用该缓存。
这将限制数据库调用的数量,因为LINQ可能不需要加载状态记录以获取本地化描述。
答案 2 :(得分:0)
您可以在DataContext中使用LoadOptions,因此它会在初始查询中加载数据。线路上的东西:
var options = new DataLoadOptions();
options.AssociateWith<Employee>(e=>
e.EmployeeStatus.EmployeeStatusLocaliseds
.Where(esl => esl.LanguageID == 1)
);
options.LoadWith<Employee>(e=>e.EmployeeStatus.EmployeeStatusLocaliseds);
using (var context = new MyDbDataContext())
{
context.LoadOptions = options;
var item = (from record in context.Employees
select record).Take(1).SingleOrDefault();
Console.WriteLine("{0}: {1}", item.EmployeeName,
item.EmployeeStatus.EmployeeStatusLocaliseds
.Single().Description
);
}
另一方面,状态可能是非常静态的数据,因此缓存它们会非常有效。如果您坚持生成的实体,则可以在使用缓存的Employee的部分类上定义属性。
答案 3 :(得分:0)
除此之外:在这种情况下,我会考虑将状态代码(而不是id)视为主键,并在Code
中对Employee
进行非规范化处理。这保留了外键,但减少了所需的连接和导航数量。它还允许您将代码映射到.NET代码中的enum
。
我可能会使用i18n文本值的延迟(按需)缓存;这样:
我猜测i18n数据变化缓慢,因此缓存方法非常理想。
我会一次加载一个语言的所有相关字符串 - 所以第一次在威尔士语中需要一个状态(例如),我会加载所有威尔士语的状态字符串,并根据语言的标准代码(cy)进行缓存。
要使视图代码更简单,请考虑在员工上使用扩展方法(在UI级别):
public static class EmployeeExtensions {
public static string GetStatusText(this Employee emp) {
/* do your funky thing, presumably using the HttpContext or
some other thread-static value to resolve the current culture */
}
}
然后在您的视图中,您可以使用:
<%=emp.GetStatusText()%>
等。遗憾的是,没有扩展属性,但是使用方法,您还可以选择将语言代码传递给方法(添加参数后):
<%=emp.GetStatusText(lang)%>
答案 4 :(得分:0)
我找到了很好的解决方案。
'设计'如下:
在GlobalizedString上,添加一个名为'Content'的属性(注意这不能通过LINQ2SQL查询,但在LINQ2Objects中可以使用),如下所示:
public string Content
{
get { return LocalizedStrings.Single(
x => x.Culture.CultureCode == 'my current CultureInfo's code'); }
set { /* exercise for reader */ }
}
因此,不是使用nvarchar列,而是指向GlobalizedString表。
现在改为普通的'逻辑'(例如绑定),你只需要引用GlobalizedString的Content属性来获取当前语言的内容:)
如前所述,这个Content属性在Linq2SQL上不起作用,我仍在寻找一种简单的方法来实现这一点(建议欢迎)。
否则,系统正在为我们服务:)