Sử dụng một dự án riêng biệt với Tài nguyên
Tôi có thể nói điều này từ kinh nghiệm, có một giải pháp hiện tại với 12 24 dự án bao gồm API, MVC, Thư viện dự án (Chức năng cốt lõi), WPF, UWP và Xamarin. Thật đáng để đọc bài đăng dài này vì tôi nghĩ đó là cách tốt nhất để làm như vậy. Với sự trợ giúp của các công cụ VS, có thể dễ dàng xuất và nhập để gửi đến các cơ quan dịch thuật hoặc xem xét bởi người khác.
EDIT 02/2018: Vẫn đang hoạt động mạnh mẽ, việc chuyển đổi nó sang thư viện .NET Standard giúp bạn thậm chí có thể sử dụng nó trên .NET Framework và NET Core. Tôi đã thêm một phần bổ sung để chuyển đổi nó thành JSON để ví dụ: angle có thể sử dụng nó.
CHỈNH SỬA 2019: Tiếp tục với Xamarin, tính năng này vẫn hoạt động trên tất cả các nền tảng. Ví dụ: Xamarin.Forms cũng khuyên bạn nên sử dụng các tệp resx. (Tôi chưa phát triển một ứng dụng nào trong Xamarin.Forms, nhưng tài liệu hướng dẫn chi tiết để bắt đầu, bao gồm: Xamarin.Forms Documentation ). Cũng giống như chuyển đổi nó sang JSON, chúng tôi cũng có thể chuyển đổi nó thành tệp .xml cho Xamarin.Android.
CHỈNH SỬA 2019 (2): Trong khi nâng cấp lên UWP từ WPF, tôi thấy rằng trong UWP họ thích sử dụng một loại tệp khác .resw
, về mặt nội dung giống hệt nhau nhưng cách sử dụng thì khác. Tôi đã tìm thấy một cách khác để làm điều này, theo ý kiến của tôi, hoạt động tốt hơn giải pháp mặc định .
EDIT 2020: Đã cập nhật một số đề xuất cho các dự án lớn hơn (mô-đun) có thể yêu cầu nhiều dự án ngôn ngữ.
Vì vậy, hay thực hiện ngay bây giơ.
Chuyên nghiệp
- Được gõ mạnh mẽ ở hầu hết mọi nơi.
- Trong WPF, bạn không phải đối phó với
ResourceDirectories
.
- Được hỗ trợ cho ASP.NET, Thư viện lớp, WPF, Xamarin, .NET Core, .NET Standard theo như tôi đã thử nghiệm.
- Không cần thêm thư viện của bên thứ ba.
- Hỗ trợ dự phòng văn hóa: en-US -> en.
- Không chỉ back-end, còn hoạt động trong XAML cho WPF và Xamarin.Forms, trong .cshtml cho MVC.
- Dễ dàng thao tác ngôn ngữ bằng cách thay đổi
Thread.CurrentThread.CurrentCulture
- Công cụ tìm kiếm có thể Thu thập thông tin bằng các ngôn ngữ khác nhau và người dùng có thể gửi hoặc lưu các url theo ngôn ngữ cụ thể.
Con's
- WPF XAML đôi khi có lỗi, các chuỗi mới được thêm vào không hiển thị trực tiếp. Xây dựng lại là bản sửa lỗi tạm thời (so với 2015).
- UWP XAML không hiển thị các gợi ý intellisense và không hiển thị văn bản trong khi thiết kế.
- Nói với tôi.
Thiết lập
Tạo dự án ngôn ngữ trong giải pháp của bạn, đặt tên như MyProject.Language . Thêm một thư mục có tên là Tài nguyên và trong thư mục đó, tạo hai tệp Tài nguyên (.resx). Một cái gọi là Resources.resx và một cái khác gọi là Resources.en.resx (hoặc .en-GB.resx cụ thể). Trong quá trình triển khai của mình, tôi có ngôn ngữ NL (tiếng Hà Lan) làm ngôn ngữ mặc định, vì vậy ngôn ngữ này được đưa vào tệp đầu tiên của tôi và tiếng Anh sẽ có trong tệp thứ hai của tôi.
Thiết lập sẽ giống như sau:
Các thuộc tính cho Resources.resx phải là:
Đảm bảo rằng không gian tên công cụ tùy chỉnh được đặt thành không gian tên dự án của bạn. Lý do cho điều này là trong WPF, bạn không thể tham chiếu đến Resources
bên trong XAML.
Và bên trong tệp tài nguyên, hãy đặt công cụ sửa đổi quyền truy cập thành Công khai:
Nếu bạn có một ứng dụng lớn như vậy (giả sử các mô-đun khác nhau), bạn có thể cân nhắc tạo nhiều dự án như trên. Trong trường hợp đó, bạn có thể đặt trước Khóa và các lớp tài nguyên của mình bằng Mô-đun cụ thể. Sử dụng trình chỉnh sửa ngôn ngữ tốt nhất dành cho Visual Studio để kết hợp tất cả các tệp thành một tổng quan duy nhất.
Sử dụng trong một dự án khác
Tham chiếu đến dự án của bạn: Nhấp chuột phải vào Tài liệu tham khảo -> Thêm tài liệu tham khảo -> Prjects \ Solutions.
Sử dụng không gian tên trong tệp: using MyProject.Language;
Sử dụng nó như vậy trong back-end:
string someText = Resources.orderGeneralError;
Nếu có thứ gì đó khác được gọi là Tài nguyên, thì chỉ cần đặt toàn bộ không gian tên.
Sử dụng trong MVC
Trong MVC, bạn có thể làm theo cách bạn muốn đặt ngôn ngữ, nhưng tôi đã sử dụng url được tham số hóa, có thể được thiết lập như vậy:
RouteConfig.cs
Bên dưới các ánh xạ khác
routes.MapRoute(
name: "Locolized",
url: "{lang}/{controller}/{action}/{id}",
constraints: new { lang = @"(\w{2})|(\w{2}-\w{2})" },
defaults: new { controller = "shop", action = "index", id = UrlParameter.Optional }
);
FilterConfig.cs (có thể cần được thêm, nếu có, hãy thêm FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
vào Application_start()
phương thức trongGlobal.asax
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ErrorHandler.AiHandleErrorAttribute());
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 chỉ đặt thông tin Văn hóa.
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;
info.DateTimeFormat.DateSeparator = "/";
info.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
Thread.CurrentThread.CurrentUICulture = info;
Thread.CurrentThread.CurrentCulture = info;
}
catch (Exception)
{
}
}
Cách sử dụng trong .cshtml
@using MyProject.Language;
<h3>@Resources.w_home_header</h3>
hoặc nếu bạn không muốn xác định cách sử dụng thì chỉ cần điền vào toàn bộ không gian tên HOẶC bạn có thể xác định không gian tên trong /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>
Hướng dẫn nguồn triển khai mvc này: Blog hướng dẫn tuyệt vời
Sử dụng trong thư viện lớp cho các mô hình
Sử dụng back-end giống nhau, nhưng chỉ là một ví dụ để sử dụng trong các thuộc tính
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; }
}
}
Nếu bạn đã reshaper, nó sẽ tự động kiểm tra xem tên tài nguyên đã cho có tồn tại hay không. Nếu bạn thích an toàn kiểu, bạn có thể sử dụng các mẫu T4 để tạo một enum
Sử dụng trong WPF.
Tất nhiên là thêm một tham chiếu đến không gian tên MyProject.Language của bạn , chúng tôi biết cách sử dụng nó trong back-end.
Trong XAML, bên trong tiêu đề của Window hoặc UserControl, hãy thêm một tham chiếu không gian tên được gọi lang
như vậy:
<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">
Sau đó, bên trong một nhãn:
<Label x:Name="lblHeader" Content="{x:Static lang:Resources.w_home_header}" TextBlock.FontSize="20" HorizontalAlignment="Center"/>
Vì nó được gõ mạnh nên bạn chắc chắn rằng chuỗi tài nguyên tồn tại. Đôi khi bạn có thể cần phải biên dịch lại dự án trong khi thiết lập, đôi khi WPF gặp lỗi với các không gian tên mới.
Một điều nữa đối với WPF, hãy đặt ngôn ngữ bên trong App.xaml.cs
. Bạn có thể tự thực hiện (chọn trong khi cài đặt) hoặc để hệ thống quyết định.
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:
MyProject.Language.Resources.Culture = new System.Globalization.CultureInfo("en-GB");
break;
}
}
}
Sử dụng trong UWP
Trong UWP, Microsoft sử dụng giải pháp này , nghĩa là bạn sẽ cần tạo các tệp tài nguyên mới. Ngoài ra, bạn cũng không thể sử dụng lại văn bản vì họ muốn bạn đặt x:Uid
quyền kiểm soát của bạn trong XAML thành một khóa trong tài nguyên của bạn. Và trong tài nguyên của bạn, bạn phải làm gì Example.Text
để điền vào TextBlock
văn bản. Tôi không thích giải pháp đó chút nào vì tôi muốn sử dụng lại các tệp tài nguyên của mình. Cuối cùng tôi đã đưa ra giải pháp sau. Tôi vừa phát hiện ra điều này hôm nay (29-09-26), vì vậy tôi có thể quay lại với thứ khác nếu hóa ra điều này không hoạt động như mong muốn.
Thêm cái này vào dự án của bạn:
using Windows.UI.Xaml.Resources;
public class MyXamlResourceLoader : CustomXamlResourceLoader
{
protected override object GetResource(string resourceId, string objectType, string propertyName, string propertyType)
{
return MyProject.Language.Resources.ResourceManager.GetString(resourceId);
}
}
Thêm điều này vào App.xaml.cs
trong hàm tạo:
CustomXamlResourceLoader.Current = new MyXamlResourceLoader();
Bất cứ khi nào bạn muốn trong ứng dụng của mình, hãy sử dụng điều này để thay đổi ngôn ngữ:
ApplicationLanguages.PrimaryLanguageOverride = "nl";
Frame.Navigate(this.GetType());
Dòng cuối cùng là cần thiết để làm mới giao diện người dùng. Trong khi tôi vẫn đang làm dự án này, tôi nhận thấy rằng tôi cần phải làm điều này 2 lần. Tôi có thể kết thúc với một lựa chọn ngôn ngữ ở lần đầu tiên người dùng bắt đầu. Nhưng vì điều này sẽ được phân phối thông qua Windows Store, ngôn ngữ thường bằng ngôn ngữ hệ thống.
Sau đó, sử dụng trong XAML:
<TextBlock Text="{CustomResource ExampleResourceKey}"></TextBlock>
Sử dụng nó trong Angular (chuyển đổi sang JSON)
Ngày nay, việc có một khung công tác như Angular kết hợp với các thành phần phổ biến hơn, vì vậy không có cshtml. Các bản dịch được lưu trữ trong các tệp json, tôi sẽ không trình bày cách hoạt động của nó, tôi chỉ thực sự khuyên bạn nên sử dụng ngx-dịch thay vì bản dịch nhiều góc cạnh. Vì vậy, nếu bạn muốn chuyển đổi bản dịch sang tệp JSON, nó khá dễ dàng, tôi sử dụng tập lệnh mẫu T4 chuyển đổi tệp Tài nguyên sang tệp json. Tôi khuyên bạn nên cài đặt trình chỉnh sửa T4 để đọc cú pháp và sử dụng nó một cách chính xác vì bạn cần thực hiện một số sửa đổi.
Chỉ có một điều cần lưu ý: Không thể tạo dữ liệu, sao chép nó, làm sạch dữ liệu và tạo nó cho ngôn ngữ khác. Vì vậy, bạn phải sao chép mã bên dưới nhiều lần tùy theo ngôn ngữ bạn có và thay đổi mục nhập trước khi '// chọn ngôn ngữ ở đây'. Hiện tại chưa có thời gian sửa lỗi này nhưng có lẽ sẽ cập nhật sau (nếu quan tâm).
Đường dẫn: MyProject.Language / T4 / CreateLocalizationEN.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/CreateLocalizationEN.json";
var fileResultPath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", fileResultName);
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 };
string[] fileNamesDest = new string[] {fileNameDestEn };
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);
}
#>
Nếu bạn có một ứng dụng mô-đun và bạn đã làm theo gợi ý của tôi để tạo nhiều dự án ngôn ngữ, thì bạn sẽ phải tạo tệp T4 cho từng dự án đó. Đảm bảo rằng các tệp json được định nghĩa một cách hợp lý, nó không cần phải như vậy en.json
, nó cũng có thể được example-en.json
. Để kết hợp nhiều tệp json để sử dụng với ngx-translate , hãy làm theo hướng dẫn tại đây
Sử dụng trong Xamarin.Android
Như đã giải thích ở trên trong các bản cập nhật, tôi sử dụng phương pháp tương tự như tôi đã làm với Angular / JSON. Nhưng Android sử dụng các tệp XML, vì vậy tôi đã viết tệp T4 để tạo các tệp XML đó.
Đường dẫn: MyProject.Language / T4 / CreateAppLocalizationEN.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=".xml" #>
<#
var fileName = "../Resources/Resources.en.resx";
var fileResultName = "../T4/CreateAppLocalizationEN.xml";
var fileResultRexPath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", fileName);
var fileResultPath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "MyProject.Language", fileResultName);
var fileNameDest = "strings.xml";
var pathBaseDestination = Directory.GetParent(Directory.GetParent(this.Host.ResolvePath("")).ToString()).ToString();
var currentPathDest =pathBaseDestination + "/MyProject.App.AndroidApp/Resources/values-en/" + fileNameDest;
using(var reader = new ResXResourceReader(fileResultRexPath))
{
reader.UseResXDataNodes = true;
#>
<resources>
<#
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", "");
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("&", "&");
if (!String.IsNullOrEmpty(value.ToString())) value = value.ToString().Replace("<<", "");
#>
<string name="<#=name#>">"<#=value#>"</string>
<#
}
#>
<string name="WEBSHOP_LASTELEMENT">just ignore this</string>
<#
#>
</resources>
<#
File.Copy(fileResultPath, currentPathDest, true);
}
#>
Android hoạt động với values-xx
các thư mục, vì vậy ở trên là tiếng Anh cho trong values-en
thư mục. Nhưng bạn cũng phải tạo một mặc định đi vào values
thư mục. Chỉ cần sao chép mẫu T4 ở trên và thay đổi thư mục trong mã trên.
Vậy là xong, bây giờ bạn có thể sử dụng một tệp tài nguyên duy nhất cho tất cả các dự án của mình. Điều này giúp bạn dễ dàng xuất mọi thứ sang một tài liệu ngoại trừ và cho phép ai đó dịch nó và nhập lại.
Đặc biệt cảm ơn phần mở rộng VS tuyệt vời này hoạt động tuyệt vời với resx
các tệp. Cân nhắc quyên góp cho anh ấy vì tác phẩm tuyệt vời của anh ấy (tôi không liên quan gì đến điều đó, tôi chỉ thích phần mở rộng).