Используйте отдельный проект с ресурсами
Я могу сказать это по собственному опыту, имея текущее решение с 12 24 проектами, которое включает API, MVC, Библиотеки проектов (основные функции), WPF и Xamarin. Стоит прочитать этот длинный пост, так как я думаю, что это лучший способ сделать это. С помощью инструментов VS, легко экспортируемых и импортируемых для отправки в бюро переводов или просмотра другими людьми.
РЕДАКТИРОВАТЬ 02/2018: Преобразование в стандартную библиотеку .NET позволяет по-прежнему успешно использовать ее в .NET Framework и NET Core. Я добавил дополнительный раздел для преобразования его в JSON, чтобы, например, Angular мог его использовать.
РЕДАКТИРОВАНИЕ: 2019: В дальнейшем с Xamarin это все еще работает на всех платформах. Например. Xamarin.Forms также рекомендует использовать файлы resx. (Я еще не разрабатывал приложение в Xamarin.Forms, но документация, которая является подробным, чтобы только начать, охватывает его: Xamarin.Forms Documentation ). Точно так же, как преобразование его в JSON, мы также можем преобразовать его в XML-файл для Xamarin.Android (в настоящее время работает над ним).
Итак, давайте перейдем к нему.
Pro в
- Сильно набран почти везде.
- В WPF вам не нужно иметь дело с
ResourceDirectories
.
- Поддерживается для ASP.NET, библиотек классов, WPF, Xamarin, .NET Core, .NET Standard, насколько я тестировал.
- Никаких дополнительных сторонних библиотек не требуется.
- Поддерживает резерв культуры: en-US -> en.
- Не только бэкэнд, работает также в XAML для WPF и Xamarin.Forms, в .cshtml для MVC.
- Легко манипулируйте языком, изменяя
Thread.CurrentThread.CurrentCulture
- Поисковые системы могут сканировать на разных языках, а пользователь может отправлять или сохранять URL-адреса для определенных языков.
Con в
- WPF XAML иногда глючит, новые добавленные строки не отображаются напрямую. Перестройка - это временное исправление (vs2015).
- Скажи мне.
Настройка
Создайте языковой проект в своем решении, присвойте ему имя, например MyProject.Language . Добавьте в него папку с именем Resources и в этой папке создайте два файла Resources (.resx). Один из них называется Resources.resx , а другой - Resources.en.resx (или .en-GB.resx для конкретных целей). В моей реализации я использую язык NL (голландский) в качестве языка по умолчанию, и это входит в мой первый файл, а английский - во второй.
Настройка должна выглядеть следующим образом:
Свойства для Resources.resx должны быть:
Убедитесь, что для пространства имен настраиваемого инструмента задано пространство имен вашего проекта. Причиной этого является то, что в WPF нельзя ссылаться на Resources
внутри XAML.
А внутри файла ресурсов установите модификатор доступа Public:
Использование в другом проекте
Ссылка на ваш проект: щелкните правой кнопкой мыши на References -> Add Reference -> Prjects \ Solutions.
Использовать пространство имен в файле: using MyProject.Language;
Используйте его так же, как и в бэк-энде:
string someText = Resources.orderGeneralError;
Если есть что-то еще под названием «Ресурсы», просто поместите все пространство имен.
Использование в MVC
В MVC вы можете делать все, что захотите, чтобы установить язык, но я использовал параметризованные URL, которые можно настроить следующим образом:
RouteConfig.cs
Ниже других отображений
routes.MapRoute(
name: "Locolized",
url: "{lang}/{controller}/{action}/{id}",
constraints: new { lang = @"(\w{2})|(\w{2}-\w{2})" }, // en or en-US
defaults: new { controller = "shop", action = "index", id = UrlParameter.Optional }
);
FilterConfig.cs (возможно, потребуется добавить, если так, добавьте FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
к методу Application_start()
в Global.asax
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ErrorHandler.AiHandleErrorAttribute());
//filters.Add(new HandleErrorAttribute());
filters.Add(new LocalizationAttribute("nl-NL"), 0);
}
}
LocalizationAttribute
public class LocalizationAttribute : ActionFilterAttribute
{
private string _DefaultLanguage = "nl-NL";
private string[] allowedLanguages = { "nl", "en" };
public LocalizationAttribute(string defaultLanguage)
{
_DefaultLanguage = defaultLanguage;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string lang = (string) filterContext.RouteData.Values["lang"] ?? _DefaultLanguage;
LanguageHelper.SetLanguage(lang);
}
}
LanguageHelper просто устанавливает информацию о культуре.
//fixed number and date format for now, this can be improved.
public static void SetLanguage(LanguageEnum language)
{
string lang = "";
switch (language)
{
case LanguageEnum.NL:
lang = "nl-NL";
break;
case LanguageEnum.EN:
lang = "en-GB";
break;
case LanguageEnum.DE:
lang = "de-DE";
break;
}
try
{
NumberFormatInfo numberInfo = CultureInfo.CreateSpecificCulture("nl-NL").NumberFormat;
CultureInfo info = new CultureInfo(lang);
info.NumberFormat = numberInfo;
//later, we will if-else the language here
info.DateTimeFormat.DateSeparator = "/";
info.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
Thread.CurrentThread.CurrentUICulture = info;
Thread.CurrentThread.CurrentCulture = info;
}
catch (Exception)
{
}
}
Использование в .cshtml
@using MyProject.Language;
<h3>@Resources.w_home_header</h3>
или если вы не хотите определять использование, просто заполните все пространство имен ИЛИ вы можете определить пространство имен в /Views/web.config:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
...
<add namespace="MyProject.Language" />
</namespaces>
</pages>
</system.web.webPages.razor>
Это учебное руководство по реализации mvc: Потрясающий учебник, блог
Использование в библиотеках классов для моделей
Back-end использование такое же, но только пример использования в атрибутах
using MyProject.Language;
namespace MyProject.Core.Models
{
public class RegisterViewModel
{
[Required(ErrorMessageResourceName = "accountEmailRequired", ErrorMessageResourceType = typeof(Resources))]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
}
}
Если у вас есть Reshaper, он автоматически проверит, существует ли данное имя ресурса. Если вы предпочитаете безопасность типов, вы можете использовать шаблоны T4 для создания перечисления
Использование в WPF.
Конечно, добавьте ссылку на ваше MyProject.Language пространство имен, мы знаем, как использовать его в серверной части.
В XAML, внутри заголовка Window или UserControl, добавьте ссылку на пространство имен с именем lang
примерно так:
<UserControl x:Class="Babywatcher.App.Windows.Views.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProject.App.Windows.Views"
xmlns:lang="clr-namespace:MyProject.Language;assembly=MyProject.Language" <!--this one-->
mc:Ignorable="d"
d:DesignHeight="210" d:DesignWidth="300">
Затем внутри этикетки:
<Label x:Name="lblHeader" Content="{x:Static lang:Resources.w_home_header}" TextBlock.FontSize="20" HorizontalAlignment="Center"/>
Поскольку он строго типизирован, вы уверены, что строка ресурса существует. Возможно, вам придется иногда перекомпилировать проект во время установки, WPF иногда глючит с новыми пространствами имен.
Еще одна вещь для WPF, установите язык внутри App.xaml.cs
. Вы можете сделать свою собственную реализацию (выбрать во время установки) или позволить системе решить.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
SetLanguageDictionary();
}
private void SetLanguageDictionary()
{
switch (Thread.CurrentThread.CurrentCulture.ToString())
{
case "nl-NL":
MyProject.Language.Resources.Culture = new System.Globalization.CultureInfo("nl-NL");
break;
case "en-GB":
MyProject.Language.Resources.Culture = new System.Globalization.CultureInfo("en-GB");
break;
default://default english because there can be so many different system language, we rather fallback on english in this case.
MyProject.Language.Resources.Culture = new System.Globalization.CultureInfo("en-GB");
break;
}
}
}
Использование его в Angular (преобразовать в JSON)
В наши дни более распространено использование фреймворка, такого как Angular, в сочетании с компонентами, то есть без cshtml. Переводы хранятся в файлах json, я не буду рассказывать, как это работает, но если вы хотите преобразовать это в файл JSON, это довольно просто, я использую шаблонный скрипт T4, который преобразует файл ресурсов в файл json. Я рекомендую установить T4 editor , чтобы прочитать синтаксис и правильно его использовать, потому что вам нужно внести некоторые изменения.
Следует отметить только одну вещь: невозможно сгенерировать данные, скопировать их, очистить данные и сгенерировать их для другого языка. Таким образом, вы должны скопировать приведенный ниже код столько раз, сколько у вас языков, и изменить запись перед тем, как «// выберите язык здесь». В настоящее время нет времени, чтобы исправить это, но, вероятно, будет обновляться позже (если интересно).
Путь: MyProject.Language / T4 / CreateWebshopLocalizationEN.tt
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.ComponentModel.Design" #>
<#@ output extension=".json" #>
<#
var fileNameNl = "../Resources/Resources.resx";
var fileNameEn = "../Resources/Resources.en.resx";
var fileNameDe = "../Resources/Resources.de.resx";
var fileNameTr = "../Resources/Resources.tr.resx";
var fileResultName = "../T4/CreateWebshopLocalizationEN.json";//choose language here
var fileResultPath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", fileResultName);
//var fileDestinationPath = "../../MyProject.Web/ClientApp/app/i18n/";
var fileNameDestNl = "nl.json";
var fileNameDestEn = "en.json";
var fileNameDestDe = "de.json";
var fileNameDestTr = "tr.json";
var pathBaseDestination = Directory.GetParent(Directory.GetParent(this.Host.ResolvePath("")).ToString()).ToString();
string[] fileNamesResx = new string[] {fileNameEn }; //choose language here
string[] fileNamesDest = new string[] {fileNameDestEn }; //choose language here
for(int x = 0; x < fileNamesResx.Length; x++)
{
var currentFileNameResx = fileNamesResx[x];
var currentFileNameDest = fileNamesDest[x];
var currentPathResx = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", currentFileNameResx);
var currentPathDest =pathBaseDestination + "/MyProject.Web/ClientApp/app/i18n/" + currentFileNameDest;
using(var reader = new ResXResourceReader(currentPathResx))
{
reader.UseResXDataNodes = true;
#>
{
<#
foreach(DictionaryEntry entry in reader)
{
var name = entry.Key;
var node = (ResXDataNode)entry.Value;
var value = node.GetValue((ITypeResolutionService) null);
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("\n", "");
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("\r", "");
#>
"<#=name#>": "<#=value#>",
<#
}
#>
"WEBSHOP_LASTELEMENT": "just ignore this, for testing purpose"
}
<#
}
File.Copy(fileResultPath, currentPathDest, true);
}
#>
Итак, теперь вы можете использовать один файл ресурсов для всех ваших проектов. Это позволяет очень легко экспортировать все в отдельный документ и позволить кому-то перевести его и импортировать снова.