我目前正在参与编写现有(Delphi)基于桌面的软件产品的 ASP.NET MVC 4 Web 版本(使用 Razor 视图引擎),目前允许客户(企业)完全自定义其实例中的所有文本应用程序,既可以对其进行本地化,也可以根据其特定环境对其进行自定义。
例如条款-
- 我的任务
- 产品
- 工作流程
- 设计
可能全部更改为业务中使用的个别术语。
目前,这种定制只是在存储在应用程序数据库中的文本字符串中完成,并在 Delphi 数据库中的每个表单加载时进行比较和加载。即,表单上的每个字符串都与数据库英语字符串进行比较,如果可用,则在表单上呈现基于所选语言环境的替换。我觉得这既不是可扩展的,也不是特别高效的。
我个人也不喜欢在本地化方法中进行自定义的想法,即最终客户可以更改应用程序中的每个字符串——这可能会导致文本一致性方面的支持问题,以及指令不正确的混乱更改或未更新。应用程序中有很多字符串,除了将它们本地化到用户的语言环境(本地语言和/或格式约定)之外,可能不应该更改这些字符串。
我个人宁愿坚持使用 ASP.NET API 和约定来本地化应用程序的 Web 版本,使用 RESX 资源文件和资源键而不是字符串匹配。这比字符串匹配灵活得多,其中字符串可能具有不同的上下文或大小写,并且不能简单地整体更改(有许多英语单词在不同的上下文中可能具有不同的含义,并且在其他情况下可能不会映射到相同的含义集语言),关键是避免往返数据库以获取获取页面所需的字符串,并且还允许使用围绕标准 RESX 文件的大量工具轻松翻译。这也意味着不需要为未来的开发人员维护或记录自定义实现。
然而,这确实给我们如何处理这些自定义条款带来了问题。
我目前认为我们应该为这些术语创建一个单独的 RESX 文件,其中列出了给定语言环境的默认值。然后我会创建一个新的数据库表,类似于
CREATE TABLE [dbo].[WEB_CUSTOM_TERMS]
(
[TERM_ID] int identity primary key,
[COMPANY_ID] int NOT NULL, -- Present for legacy reasons
[LOCALE] varchar(8) NOT NULL,
[TERM_KEY] varchar(40) NOT NULL,
[TERM] nvarchar(50) -- Intentionally short, this is to be used for single words or short phrases
);
这可能会在需要时读入 Dictionary<string, string> 并由 IIS 缓存以提供查找,而不会延迟连接到 SQL 服务器和执行查询。
public static class DatabaseTerms
{
private static string DictionaryKey
{
get { return string.Format("CustomTermsDictionary-{0}", UserCulture); }
}
private static string UserCulture
{
get { return System.Threading.Thread.CurrentThread.CurrentCulture.Name; }
}
public static Dictionary<string, string> TermsDictionary
{
get
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
var databaseTerms = HttpContext.Current.Cache[DictionaryKey] as Dictionary<string, string>;
if (databaseTerms != null)
{
return databaseTerms;
}
}
var membershipProvider = Membership.Provider as CustomMembershipProvider;
int? companyId = null;
if (membershipProvider != null)
{
companyId = CustomMembershipProvider.CompanyId;
}
using (var context = new VisionEntities())
{
var databaseTerms = (from term in context.CustomTerms
where (companyId == null || term.CompanyId == companyId) &&
(term.Locale == UserCulture)
orderby term.Key
select term).ToDictionary(t => t.Key, t => t.Text);
HttpContext.Current.Cache.Insert(DictionaryKey, databaseTerms, null, DateTime.MaxValue,
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
return databaseTerms;
}
}
set
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
HttpContext.Current.Cache.Remove(DictionaryKey);
}
HttpContext.Current.Cache.Insert(DictionaryKey, value, null, DateTime.Now.AddHours(8),
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
}
}
}
然后我可以有一个公开公共属性的类,根据这个字典值或RESX文件中的值返回一个字符串——以不为空的为准。就像是-
public static class CustomTerm
{
public static string Product
{
get
{
return (DatabaseTerms.TermsDictionary.ContainsKey("Product") ?
DatabaseTerms.TermsDictionary["Product"] : CustomTermsResources.Product);
}
}
}
如果需要,可以使用字符串格式将它们添加到更大的本地化字符串中,或者单独用作菜单的标签等。
这种方法的主要缺点是需要提前预测最终客户可能希望定制哪些条款,但我确实认为这可能是两全其美的。
这看起来是一种可行的方法吗?其他开发人员是如何解决这个问题的?
提前致谢。