На тему локализации уже было несколько статей на Хабре, например Локализация ASP.NET MVC приложения с помощью БД или MVC 2: Полное руководство по локализации, но все-таки проблема до сих пор актуальна.
Совсем недавно у нас возникла задача локализации (перевода) интерфейса сайта на другой язык. Наш проект разрабатывается на ASP.NET MVC и в проекте достаточно много клиентского кода на JavaScript и шаблонах jQuery. Еще одним важным моментом для нас является возможность интерактивной работы переводчиков, фактически перевода сайта на лету.
В основном все статьи, касающиеся этой темы, предписывают использовать файлы ресурсов, при этом решение получается не гибким, громоздким, и к тому же, никак не учитывается проблема перевода скриптов и шаблонов, которые в любом современном веб-проекте составляют значительную часть. В конечном итоге мы решили написать свою библиотеку Knoema.Localization, которую недавно выложили в открытый доступ. Наш код базируется на замечательной разработке Griffin, но был существенно усовершенствован, включает интерактивный инструмент для перевода, а также средства естественной локализации JS кода и шаблонов.
Возможности
Библиотека позволяет локализовать:
- Представления (views)
- Строковые константы в коде
- Модели и текст в атрибутах валидации данных
- Текст в коде на JavaScript
- Шаблоны jQuery
Основная идея заключается в том, что все локализуемые строки определяются именем файла или классом, а затем представляются в таком виде, в котором они находятся в проекте.
Подключение и конфигурация
Для подключения библиотеки к проекту достаточно установить Nuget пакет Knoema.Localization.Mvc. Для хранения локализованных данных вам также потребуется реализация репозитория. В простейшем случае достаточно подключить готовую реализацию для EF установив пакет Knoema.Localization.EFProvider. Альтернативным вариантом является разработка своего провайдера путем реализации достаточно простого интерфейса ILocalizationRepository.
В Global.asax в Application_Start() инициализировать созданный репозиторий:
Knoema.Localization.LocalizationManager.Repository = new Knoema.Localization.EFProvider.LocalizationRepository();
и в методе RegisterRoutes прописать:
routes.IgnoreRoute("_localization/{*route}");
Начальная конфигурация готова, теперь подробнее напишу о деталях использования.
Локализация представлений
Для локализации представлений в web.config нужно указать, что в качестве базового класса для страниц будет использоваться LocalizedWebViewPage:
<system.web.webPages.razor>
...
<pages pageBaseType="Knoema.Localization.Mvc.LocalizedWebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
...
</namespaces>
</pages>
</system.web.webPages.razor>
После этого, в коде представления станет доступен Html helper:
public string R(string text, params object[] formatterArguments);
Пример
<p>@R("Hello world!")</p>
Текст можно параметризовать:
<p>@R("Hello {0}!", username)</p>
Локализация строковых констант в коде
Строки в коде локализуются с помощью расширения:
public static string Resource(this string value, object obj)
или, если метод статический, где нет объекта this, можно использовать:
public static string Resource(this string value, Type type)
Пример
"The given input does not refer to a valid Search.".Resource(this)
Локализация модели
Для локализации моделей необходимо переопределить стандартный провайдер валидации и провайдер метаданных. Для этого в Application_Start() в Global.asax добавить:
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new ValidationLocalizer());
ModelMetadataProviders.Current = new MetadataLocalizer();
Все модели, которые могут быть локализованы, должны быть помечены атрибутом Localized.
Пример
[Localized]
public class SignInViewModel
{
[Required(ErrorMessage = "Please provide your e-mail")]
[Display(Name = "E-mail")]
public string EMail { get; set; }
[Required(ErrorMessage = "Please type your password")]
public string Password { get; set; }
}
Свойства и атрибуты отдельно локализовывать не нужно. Все метаданные добавятся автоматически, останется только добавить для них соответсвующий перевод.
Локазиция javascript и html шаблонов
Строковые константы в javascript коде локализуются с помощью расширения JQuery:
$.localize(text, scriptSource);
Расширение можно подключить в любом месте сраницы с помощью хелпера, лучше для всего приложения сразу:
@RenderLocalizationIncludes(User.IsInRole("Admin"))
Кроме того, RenderLocalizationIncludes встроит на сайт виджет (при условии, что вы администратор), где вы сможете перевести все, что локализовали.
Пример
$.localize("Layout options", "~/js/shared/site.js");
Примечание
Нам показалось неудобным, что каждый раз необходимо указывать имя файла. В нашем проекте мы используем минификатор ресурсов Cassette, где есть возможность добавить обработчик ресурсов, перед тем как они скомпилируются. Простой пример конфигурации:
public class CassetteConfiguration : ICassetteConfiguration
{
private void CustomizeScript(ScriptBundle bundle)
{
bundle.Processor = new ScriptPipeline().Prepend(new LocalizationResourceProcessor());
}
public void Configure(BundleCollection bundles, CassetteSettings settings)
{
bundles.AddPerIndividualFile<ScriptBundle>("js/shared/site.js", customizeBundle: CustomizeScript);
}
}
Теперь длинный вызов $.localize(«Layout options», "~/js/shared/site.js") можно заменить на:
__R("Layout options");
LocalizationResourceProcessor заменит __R на $.localize и добавит вторым параметром имя файла.
Добавление перевода
Виджет для редактирования перевода:
Серым цветом выделены те представления или модели, которые полностью переведены.
Редактирование:
Можно добавлять новый язык, экспортировать / импортировать уже существующий.
В качестве заключения хочу сказать, что библиотека сначала была написана для внутреннего использования, получилось довольно неплохо и поэтому решили опубликовать ее и написать статью.
Ссылки:
Исходный код на GitHub
Knoema.Localization.Core
Knoema.Localization.MVC
Knoema.Localization.Cassette
Автор: polyakova