Cách tốt nhất để triển khai đa ngôn ngữ / toàn cầu hóa trong dự án .NET lớn


86

Tôi sẽ sớm thực hiện một dự án c # lớn và muốn xây dựng bằng hỗ trợ đa ngôn ngữ ngay từ đầu. Tôi đã thử và có thể làm cho nó hoạt động bằng cách sử dụng tệp tài nguyên riêng biệt cho từng ngôn ngữ, sau đó sử dụng trình quản lý tài nguyên để tải các chuỗi.

Có bất kỳ cách tiếp cận tốt nào khác mà tôi có thể xem xét không?

Câu trả lời:


88

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:

dự án thiết lập ngôn ngữ

Các thuộc tính cho Resources.resx phải là: tính chất

Đả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 Resourcesbê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:

công cụ sửa đổi truy cập

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})" },   // en or en-US
    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 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 chỉ đặt thông tin Văn hóa.

//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)
    {

    }
}

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 langnhư 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://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;
        }
       
    }
}

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:Uidquyề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 TextBlockvă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.cstrong 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";//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);
}


#>

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;
                    //if(!name.ToString().Contains("WEBSHOP_") && !name.ToString().Contains("DASHBOARD_"))//only include keys with these prefixes, or the country ones.
                    //{
                    //  if(name.ToString().Length != 2)
                    //  {
                    //      continue;
                    //  }
                    //}
                    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("&", "&amp;");
                     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-xxcác thư mục, vì vậy ở trên là tiếng Anh cho trong values-enthư mục. Nhưng bạn cũng phải tạo một mặc định đi vào valuesthư 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 resxcá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).


1. làm thế nào để bạn thêm nhiều ngôn ngữ hơn sau khi triển khai ?, bằng cách thêm nhiều tài nguyên hơn vào Mylanguage và biên dịch lại rồi triển khai lại? (2) Có IDE hoặc Tool nào bạn dùng để dịch không (mình có hơn 40 form trong winform), bạn có thể chia sẻ được không? (3) Tôi đã thử và nó không nhận được bản dịch khi tôi thay đổi văn hóa sang tiếng Ả Rập, tôi sẽ tạo một câu hỏi và cho bạn biết
Smith

5
1: Chỉ cần dublicate Resources.xx.resx và thay đổi xx và ở mọi nơi mà tôi có các câu lệnh switch sẽ thêm ngôn ngữ. 2 Tôi sử dụng tiện ích mở rộng ResxManager (của TomEnglert), có thể được cài đặt thông qua Công cụ -> Tiện ích mở rộng, một cách hay để có các ngôn ngữ cạnh nhau nhưng bản dịch thực tế do người khác thực hiện (dễ dàng xuất và nhập vào / từ excel với ResxManager). 3. Gặp phải lần cuối này bằng cách thêm một tệp nữa, hãy kiểm tra tất cả các tệp được liệt kê ở trên, trừ một số điểm ngắt. Tôi đã không sử dụng điều này trong WinForms.
CularBytes

13
Tôi có thể ủng hộ câu trả lời của riêng mình không? Tôi chỉ phải kiểm tra ngớ ngẩn câu trả lời của riêng tôi để sửa chữa một lỗi để chuyển đổi giữa các ngôn ngữ trong WPF -.-
CularBytes

1
@TiagoBrenck Xin chào, chỉ trong trường hợp cụ thể này, nếu tiếng Anh là ngôn ngữ mặc định của bạn, bạn có thể tạo tệp resource.en-gb.resx và thêm 1% thay đổi vào đó. Theo mặc định, nếu ngôn ngữ en-gb được chọn và các từ không được dịch, nó sẽ sử dụng ngôn ngữ mặc định (resources.resx).
CularBytes

1
Bạn đã cấu trúc các khóa của tệp Tài nguyên như thế nào? Vì tôi thấy bạn chỉ sử dụng một tệp Tài nguyên và theo bạn, bạn có một ứng dụng khổng lồ, bạn đã tách các khóa như thế nào? Một cái gì đó như "Feature.MyMessage" hoặc "Area.Feature.MyMessage"
Francesco Venturini

21

Tôi đã thấy các dự án được thực hiện bằng một số cách tiếp cận khác nhau, mỗi cách đều có ưu điểm và nhược điểm.

  • Một người đã làm điều đó trong tệp cấu hình (không phải tệp yêu thích của tôi)
  • Một người đã làm điều đó bằng cách sử dụng cơ sở dữ liệu - điều này hoạt động khá tốt, nhưng bạn không biết phải duy trì những gì.
  • Một tệp tài nguyên đã sử dụng theo cách bạn đang đề xuất và tôi phải nói rằng đó là cách tiếp cận yêu thích của tôi.
  • Điều cơ bản nhất đã làm điều đó bằng cách sử dụng một tệp bao gồm đầy các chuỗi - xấu xí.

Tôi muốn nói rằng phương pháp tài nguyên mà bạn đã chọn rất có ý nghĩa. Sẽ rất thú vị khi xem câu trả lời của người khác vì tôi thường tự hỏi liệu có cách nào tốt hơn để làm những việc như thế này không. Tôi đã thấy rất nhiều tài nguyên đều trỏ đến phương thức sử dụng tài nguyên, bao gồm một tài nguyên ngay tại đây trên SO .


5

Tôi không nghĩ rằng có một "cách tốt nhất". Nó thực sự sẽ phụ thuộc vào công nghệ và loại ứng dụng bạn đang xây dựng.

Ứng dụng web có thể lưu trữ thông tin trong cơ sở dữ liệu như các áp phích khác đã đề xuất, nhưng tôi khuyên bạn nên sử dụng các tệp tài nguyên riêng biệt. Đó là các tệp tài nguyên tách biệt khỏi nguồn của bạn . Tệp tài nguyên riêng biệt làm giảm sự tranh chấp cho các tệp giống nhau và khi dự án của bạn phát triển, bạn có thể thấy việc bản địa hóa sẽ được thực hiện tách biệt khỏi logic nghiệp vụ. (Lập trình viên và Biên dịch viên).

Các chuyên gia Microsoft WinForm và WPF khuyên bạn nên sử dụng các cụm tài nguyên riêng biệt được tùy chỉnh theo từng ngôn ngữ.

Khả năng của WPF để kích thước các phần tử giao diện người dùng cho nội dung làm giảm công việc bố cục cần thiết, ví dụ: (từ tiếng Nhật ngắn hơn nhiều so với tiếng Anh).

Nếu bạn đang xem xét WPF: Tôi khuyên bạn nên đọc bài viết này trên msdn msdn Thành thật mà nói, tôi thấy các công cụ bản địa hóa WPF: msbuild, locbaml, (và có thể là một bảng tính excel) tẻ nhạt để sử dụng, nhưng nó hoạt động.

Một cái gì đó chỉ liên quan một chút: Một vấn đề phổ biến mà tôi phải đối mặt là tích hợp các hệ thống cũ gửi thông báo lỗi (thường bằng tiếng Anh), không phải mã lỗi. Điều này buộc phải thay đổi các hệ thống kế thừa hoặc ánh xạ các chuỗi phụ trợ thành mã lỗi của riêng tôi và sau đó đến các chuỗi được bản địa hóa ... yech. Mã lỗi là bạn bè bản địa hóa


4

Cơ sở dữ liệu +1

Các biểu mẫu trong ứng dụng của bạn thậm chí có thể tự dịch lại ngay lập tức nếu cơ sở dữ liệu được sửa chữa.

Chúng tôi đã sử dụng một hệ thống trong đó tất cả các điều khiển được ánh xạ trong một tệp XML (một trên mỗi biểu mẫu) thành các ID tài nguyên ngôn ngữ, nhưng tất cả các ID đều nằm trong cơ sở dữ liệu.

Về cơ bản, thay vì để mỗi điều khiển giữ ID (triển khai một giao diện hoặc sử dụng thuộc tính thẻ trong VB6), chúng tôi sử dụng thực tế là trong .NET, cây điều khiển có thể dễ dàng phát hiện thông qua phản chiếu. Quá trình khi biểu mẫu được tải sẽ xây dựng tệp XML nếu nó bị thiếu. Tệp XML sẽ ánh xạ các điều khiển tới ID tài nguyên của chúng, vì vậy, điều này chỉ cần được điền và ánh xạ tới cơ sở dữ liệu. Điều này có nghĩa là không cần phải thay đổi hệ nhị phân đã biên dịch nếu thứ gì đó không được gắn thẻ hoặc nếu nó cần được chia thành một ID khác (một số từ trong tiếng Anh có thể được sử dụng làm cả danh từ và động từ có thể cần phải dịch sang hai từ khác nhau trong từ điển và không được sử dụng lại, nhưng bạn có thể không phát hiện ra điều này trong lần gán ID ban đầu).

Điều duy nhất mà ứng dụng tham gia nhiều hơn là khi giai đoạn có điểm chèn được sử dụng.

Phần mềm dịch cơ sở dữ liệu là màn hình bảo trì CRUD cơ bản của bạn với các tùy chọn quy trình làm việc khác nhau để tạo thuận lợi cho việc xem xét các bản dịch còn thiếu, v.v.


Tôi thích phương pháp của bạn, bạn có thể cho tôi biết cách tệp xml được tạo và sau đó được ánh xạ vào cơ sở dữ liệu không?
Smith

Trong Form_Load hoặc bất cứ thứ gì, hàm dịch đã được gọi trên mẫu biểu mẫu. Tệp XML cho biểu mẫu đó đã được tải. Nếu nó không tồn tại, nó đã được tạo. Tôi không có giản đồ, nhưng về cơ bản, nó đã ánh xạ tên điều khiển trên biểu mẫu thành ID dịch. Vì vậy, bất kỳ điều khiển nào trên biểu mẫu không có trong tệp XML sẽ nhận được mục nhập không có ID dịch. Bởi vì chúng sẽ được tạo theo yêu cầu, bạn có thể chỉ cần tạo biểu mẫu của mình, chạy ứng dụng sẽ tạo hoặc cập nhật tệp XML cho bất kỳ điều khiển nào bị thiếu. Sau đó điền ID dịch cho các mục.
Cade Roux,

4

Tôi đã tìm kiếm và tôi đã tìm thấy điều này:

Nếu bạn sử dụng WPF hoặc Silverlight, aproach của bạn có thể sử dụng WPF LocalizationExtension vì nhiều lý do.

Nguồn mở của NÓ MIỄN PHÍ (và sẽ miễn phí) ở trạng thái ổn định thực sự

Trong Ứng dụng Windows, bạn có thể thực hiện một số thao tác như sau:

public partial class App : Application  
{  
     public App()  
     {             
     }  
 
     protected override void OnStartup(StartupEventArgs e)  
     {  
         Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("de-DE"); ;  
         Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("de-DE"); ;  
 
          FrameworkElement.LanguageProperty.OverrideMetadata(  
              typeof(FrameworkElement),  
              new FrameworkPropertyMetadata(  
                  XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));  
          base.OnStartup(e);  
    }  
} 

Và tôi nghĩ rằng trên một Trang Wep, aproach có thể giống nhau.

Chúc may mắn!


2

Tôi sẽ đi với nhiều tệp tài nguyên. Nó không khó để cấu hình. Trên thực tế, gần đây tôi đã trả lời một câu hỏi tương tự về cách đặt tệp tài nguyên dựa trên ngôn ngữ chung kết hợp với tệp tài nguyên ngôn ngữ biểu mẫu.

Bản địa hóa trong Visual Studio 2008

Tôi sẽ coi đó là cách tiếp cận tốt nhất ít nhất để phát triển WinForm.


Một hạn chế của tài nguyên là bạn phải khởi động lại để chuyển đổi ngôn ngữ. Đây có thể là chấp nhận được đối với hầu hết, nhưng nó peeve vật cưng của tôi ...
La Mã Starkov

2

Bạn có thể sử dụng các công cụ thương mại như Sisulizer . Nó sẽ tạo lắp ráp vệ tinh cho mỗi ngôn ngữ. Chỉ có điều bạn nên chú ý là không làm xáo trộn tên lớp của biểu mẫu (nếu bạn sử dụng obfuscator).


0

Hầu hết các dự án nguồn mở sử dụng GetText cho mục đích này. Tôi không biết làm thế nào và nếu nó đã từng được sử dụng trên một dự án .Net trước đây.


0

Chúng tôi sử dụng nhà cung cấp tùy chỉnh để hỗ trợ đa ngôn ngữ và đặt tất cả văn bản vào bảng cơ sở dữ liệu. Nó hoạt động tốt ngoại trừ đôi khi chúng tôi gặp phải sự cố bộ nhớ đệm khi cập nhật văn bản trong cơ sở dữ liệu mà không cập nhật ứng dụng web.


0

Các tệp tài nguyên tiêu chuẩn dễ dàng hơn. Tuy nhiên, nếu bạn có bất kỳ dữ liệu phụ thuộc vào ngôn ngữ như bảng tra cứu thì bạn sẽ phải quản lý hai tập tài nguyên.

Tôi chưa làm điều đó, nhưng trong dự án tiếp theo của tôi, tôi sẽ triển khai một nhà cung cấp tài nguyên cơ sở dữ liệu. Tôi đã tìm thấy cách thực hiện trên MSDN:

http://msdn.microsoft.com/en-us/library/aa905797.aspx

Tôi cũng tìm thấy cách triển khai này:

Nhà cung cấp DBResource

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.