2011-01-10 2 views
5

Я работаю над веб-приложением, которое мне нужно локализовать и интернационализировать. Мне пришло в голову, что я могу сделать это, используя инфраструктуру инъекций зависимостей. Скажем, я объявить интерфейс ILocalResources (с помощью C# для этого примера, но это не очень важно):Локализация с использованием рамки DI - хорошая идея?

interface ILocalResources { 
    public string OkString { get; } 
    public string CancelString { get; } 
    public string WrongPasswordString { get; } 
    ... 
} 

и создавать реализации этого интерфейса, по одному для каждого языка, мне нужно поддерживать. Затем я установил бы свою инфраструктуру DI, чтобы создать правильную реализацию, статически или динамически (например, на основе предпочитаемого браузера предпочтительного языка).

Есть ли какая-то причина, по которой я не должен использовать рамки DI для такого рода вещей? Единственное возражение, которое я мог найти, это то, что это может быть немного переборщием, но если я все равно использую инфраструктуру DI в своем веб-приложении, я бы тоже использовал его для интернационализации?

ответ

2

Если вы не можете использовать существующую инфраструктуру ресурсов (например, встроенную в ASP.Net) и вам придется создавать свои собственные, я предполагаю, что вам в какой-то момент нужно будет предоставить службы, предоставляющие локализованные ресурсы.

Структуры DI используются для обработки экземпляра службы. В вашей инфраструктуре локализации будут представлены службы, предоставляющие локализацию. Почему эта служба не должна обслуживаться рамками?

Не использовать DI для его использования здесь, как сказать: «Я создаю CRM-приложение, но не могу использовать DI, потому что DI не создан для управления отношениями с клиентами».

Так что да, если вы уже используете DI в остальной части вашего приложения, IMO было бы неправильно использовать его для локализации служб, которые используют его.

2

Единственный недостаток, который я вижу, заключается в том, что для любого обновления «ресурсов» вам придется перекомпилировать сборку, содержащую ресурсы. И в зависимости от вашего проекта этот недостаток может быть хорошим советом только для использования инфраструктуры DI для решения какой-либо ResourceService, а не для самих значений.

6

Рамка DI построена так, чтобы сделать инъекцию зависимостей, и локализация могла бы быть только одной из ваших услуг, поэтому в этом случае нет причин не для использования каркаса DI IMO. Возможно, нам стоит начать обсуждать предоставленный интерфейс ILocalResources. Хотя я сторонник поддержки времени компиляции, я не уверен, что предоставленный интерфейс вам поможет, потому что этот интерфейс будет, вероятно, типом вашей системы, который изменит больше всего. И с этим интерфейсом тип/типы, которые его реализуют. Возможно, вы должны пойти с другим дизайном.

Когда мы смотрим на большинство локализационных фреймворков/поставщиков/фабрик (или что-то еще), все они основаны на строках. Из-за этого, подумайте о следующем дизайне:

public interface ILocalResources 
{ 
    string GetStringResource(string key); 
    string GetStringResource(string key, CultureInfo culture); 
} 

Это позволит вам добавить ключи и культуры в базовом хранилище данных сообщения, без изменения интерфейса. Недостаток - это, конечно, что вы никогда не должны менять ключ, потому что это, вероятно, будет чертовски.

Другой подход может быть абстрактным базовым типом:

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; 
    } 
} 

И реализация этого типа может выглядеть следующим образом:

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(); 
     } 
    } 
} 

Этот подход имеет лучшее из обоих миров, потому что выиграл ключи» t разбросано через приложение, и добавление новых свойств должно быть сделано только в одном месте. Использование favorite библиотеки DI, вы можете зарегистрировать реализацию так:

container.RegisterSingleton<LocalResources>(new SqlLocalResources()); 

А поскольку 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; 
    } 
} 

Вы можете применить декоратор следующим образом:

container.RegisterSingleton<LocalResources>(
    new CachedLocalResources(new SqlLocalResources())); 

не e, что этот декоратор бесконечно кэширует строковые ресурсы, что может вызвать утечку памяти, поэтому вы хотите обернуть строки в WeakReference экземплярах или иметь какой-то тайм-аут истечения срока действия на нем. Но идея состоит в том, что вы можете применять кеширование без необходимости изменять существующую реализацию.

Надеюсь, это поможет.

+0

В качестве альтернативы вы также можете реализовать методы расширения для интерфейса «ILocalResources», но этот недостаток заключается в том, что у вас будут методы вместо свойств. Но это, наверное, не так уж плохо. – Steven

Смежные вопросы