Dav*_*all 5 language-agnostic dependency-injection localization
我正在开发一个我需要本地化和国际化的Web应用程序.在我看来,我可以使用依赖注入框架来做到这一点.假设我声明了一个接口ILocalResources(在这个例子中使用C#,但这并不重要):
interface ILocalResources {
public string OkString { get; }
public string CancelString { get; }
public string WrongPasswordString { get; }
...
}
Run Code Online (Sandbox Code Playgroud)
并创建此接口的实现,每个语言需要支持一个.然后,我将设置我的DI框架,以静态或动态地实例化正确的实现(例如,基于请求的浏览器首选语言).
有什么理由我不应该使用DI框架来做这种事情吗?我能找到的唯一反对意见是它可能有点矫枉过正,但如果我在我的网络应用程序中使用DI框架,我不妨用它进行国际化?
构建DI框架以进行依赖注入,本地化可能只是您的服务之一,因此在这种情况下没有理由不使用DI框架IMO.也许我们应该开始讨论提供的ILocalResources
接口.虽然我支持编译时支持,但我不确定所提供的接口是否会对您有所帮助,因为该接口可能是您系统中将发生最大变化的类型.并使用该接口实现它的类型/类型.也许你应该采用不同的设计.
当我们查看大多数本地化框架/提供者/工厂(或其他)时,它们都是基于字符串的.因此,请考虑以下设计:
public interface ILocalResources
{
string GetStringResource(string key);
string GetStringResource(string key, CultureInfo culture);
}
Run Code Online (Sandbox Code Playgroud)
这将允许您将键和文化添加到基础消息数据存储,而无需更改接口.下行当然是你永远不应该换钥匙,因为这可能是一个地狱.
另一种方法可以是抽象基类型:
public abstract class LocalResources
{
public string OkMessage { get { return this.GetString("OK"); } }
public string CancelMessage { get { return this.GetString("Cancel"); } }
...
protected abstract string GetStringResource(string key,
CultureInfo culture);
private string GetString(string key)
{
Culture culture = CultureInfo.CurrentCulture;
string resource = GetStringResource(key, culture);
// When the resource is not found, fall back to the neutral culture.
while (resource == null && culture != CultureInfo.InvariantCulture)
{
culture = culture.Parent;
resource = this.GetStringResource(key, culture);
}
if (resource == null) throw new KeyNotFoundException(key);
return resource;
}
}
Run Code Online (Sandbox Code Playgroud)
此类型的实现可能如下所示:
public sealed class SqlLocalResources : LocalResources
{
protected override string GetStringResource(string key,
CultureInfo culture)
{
using (var db = new LocalResourcesContext())
{
return (
from resource in db.StringResources
where resource.Culture == culture.Name
where resource.Key == key
select resource.Value).FirstOrDefault();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法在两个方面都是最好的,因为密钥不会分散在应用程序中,添加新属性只需要在一个地方完成.使用您喜欢的 DI库,您可以注册这样的实现:
container.RegisterSingleton<LocalResources>(new SqlLocalResources());
Run Code Online (Sandbox Code Playgroud)
由于LocalResources
类型只有一个抽象方法可以完成所有工作,因此很容易创建一个添加缓存的装饰器,以防止从数据库中请求相同的数据:
public sealed class CachedLocalResources : LocalResources
{
private readonly Dictionary<CultureInfo, Dictionary<string, string>> cache =
new Dictionary<CultureInfo, Dictionary<string, string>>();
private readonly LocalResources decoratee;
public CachedLocalResources(LocalResources decoratee) { this.decoratee = decoratee; }
protected override string GetStringResource(string key, CultureInfo culture) {
lock (this.cache) {
string res;
var cultureCache = this.GetCultureCache(culture);
if (!cultureCache.TryGetValue(key, out res)) {
cultureCache[key] = res= this.decoratee.GetStringResource(key, culture);
}
return res;
}
}
private Dictionary<string, string> GetCultureCache(CultureInfo culture) {
Dictionary<string, string> cultureCache;
if (!this.cache.TryGetValue(culture, out cultureCache)) {
this.cache[culture] = cultureCache = new Dictionary<string, string>();
}
return cultureCache;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以按如下方式应用装饰器:
container.RegisterSingleton<LocalResources>(
new CachedLocalResources(new SqlLocalResources()));
Run Code Online (Sandbox Code Playgroud)
请注意,此装饰器无限期地缓存字符串资源,这可能会导致内存泄漏,因此您希望将字符串包装在WeakReference
实例中或对其进行某种过期超时.但这个想法是你可以在不改变任何现有实现的情况下应用缓存.
我希望这有帮助.
如果您无法使用现有的资源框架(例如 ASP.Net 中内置的资源框架)并且必须构建自己的资源框架,我将假设您在某些时候需要公开提供本地化资源的服务。
DI 框架用于处理服务实例化。您的本地化框架将公开提供本地化的服务。为什么该服务不应该由框架提供?
此处不使用 DI 就像说:“我正在构建 CRM 应用程序,但不能使用 DI,因为 DI 不是为客户关系管理而构建的”。
所以,是的,如果您已经在应用程序的其余部分中使用 DI,那么在我看来,不将其用于处理本地化的服务是错误的。
归档时间: |
|
查看次数: |
1186 次 |
最近记录: |