Der beste Weg, um Mehrsprachigkeit / Globalisierung in großen .NET-Projekten zu implementieren

85

Ich werde bald an einem großen c # -Projekt arbeiten und möchte von Anfang an mehrsprachige Unterstützung einbauen. Ich habe herumgespielt und kann es mithilfe einer separaten Ressourcendatei für jede Sprache zum Laufen bringen. Verwenden Sie dann einen Ressourcenmanager, um die Zeichenfolgen zu laden.

Gibt es andere gute Ansätze, die ich untersuchen könnte?

tjjjohnson
quelle

Antworten:

88

Verwenden Sie ein separates Projekt mit Ressourcen

Ich kann dies aus unserer Erfahrung mit einer aktuellen Lösung mit 12 24 Projekten, die API, MVC, Projektbibliotheken (Kernfunktionen), WPF, UWP und Xamarin umfasst, beurteilen. Es lohnt sich, diesen langen Beitrag zu lesen, da ich denke, dass dies der beste Weg ist, dies zu tun. Mit Hilfe von VS-Tools leicht exportierbar und importierbar, um an Übersetzungsbüros gesendet oder von anderen Personen überprüft zu werden.

EDIT 02/2018: Die Konvertierung in eine .NET Standard-Bibliothek ist immer noch stark und ermöglicht die Verwendung sogar in .NET Framework und NET Core. Ich habe einen zusätzlichen Abschnitt zum Konvertieren in JSON hinzugefügt, damit beispielsweise Angular ihn verwenden kann.

EDIT 2019: Mit Xamarin funktioniert dies weiterhin auf allen Plattformen. ZB empfiehlt Xamarin.Forms, auch Resx-Dateien zu verwenden. (Ich habe noch keine App in Xamarin.Forms entwickelt, aber die Dokumentation, die für den Einstieg viel zu detailliert ist, behandelt sie: Xamarin.Forms-Dokumentation ). Genau wie beim Konvertieren in JSON können wir es auch in eine XML-Datei für Xamarin.Android konvertieren.

EDIT 2019 (2): Beim Upgrade von WPF auf UWP habe ich festgestellt .resw, dass sie in UWP lieber einen anderen Dateityp verwenden , der inhaltlich identisch ist, aber unterschiedlich verwendet wird. Ich habe einen anderen Weg gefunden, der meiner Meinung nach besser funktioniert als die Standardlösung .

EDIT 2020: Einige Vorschläge für größere (Modulair-) Projekte wurden aktualisiert, für die möglicherweise mehrsprachige Projekte erforderlich sind.

Also, lasst uns loslegen.

Profis

  • Fast überall stark getippt.
  • In WPF müssen Sie sich nicht damit befassen ResourceDirectories.
  • Unterstützt für ASP.NET, Klassenbibliotheken, WPF, Xamarin, .NET Core, .NET Standard, soweit ich getestet habe.
  • Es werden keine zusätzlichen Bibliotheken von Drittanbietern benötigt.
  • Unterstützt Kultur-Fallback: en-US -> en.
  • Nicht nur Back-End, funktioniert auch in XAML für WPF und Xamarin.Forms, in .cshtml für MVC.
  • Manipulieren Sie die Sprache einfach, indem Sie die ändern Thread.CurrentThread.CurrentCulture
  • Suchmaschinen können in verschiedenen Sprachen crawlen und Benutzer können sprachspezifische URLs senden oder speichern.

Con's

  • WPF XAML ist manchmal fehlerhaft, neu hinzugefügte Zeichenfolgen werden nicht direkt angezeigt. Rebuild ist das temporäre Update (vs2015).
  • UWP XAML zeigt keine Intellisense-Vorschläge an und zeigt den Text beim Entwerfen nicht an.
  • Sag mir.

Installieren

Erstellen Sie ein Sprachprojekt in Ihrer Lösung und geben Sie ihm einen Namen wie MyProject.Language . Fügen Sie einen Ordner mit dem Namen Ressourcen hinzu, und erstellen Sie in diesem Ordner zwei Ressourcendateien (.resx). Eine heißt Resources.resx und eine andere heißt Resources.en.resx (oder .en-GB.resx für bestimmte). In meiner Implementierung habe ich die NL-Sprache (Niederländisch) als Standardsprache, sodass diese in meiner ersten Datei und Englisch in meiner zweiten Datei enthalten ist.

Das Setup sollte folgendermaßen aussehen:

Sprach-Setup-Projekt

Die Eigenschaften für Resources.resx müssen sein: Eigenschaften

Stellen Sie sicher, dass der benutzerdefinierte Tool-Namespace auf Ihren Projekt-Namespace festgelegt ist. Grund dafür ist, dass Sie in WPF nicht auf ResourcesXAML verweisen können .

Setzen Sie in der Ressourcendatei den Zugriffsmodifikator auf Öffentlich:

Zugriffsmodifikator

Wenn Sie eine so große Anwendung haben (sagen wir verschiedene Module), können Sie mehrere Projekte wie oben erstellen. In diesem Fall können Sie Ihren Schlüsseln und Ressourcenklassen das jeweilige Modul voranstellen. Verwenden Sie den besten Spracheditor für Visual Studio, um alle Dateien in einer einzigen Übersicht zu kombinieren.

Verwendung in einem anderen Projekt

Verweis auf Ihr Projekt: Klicken Sie mit der rechten Maustaste auf Verweise -> Verweis hinzufügen -> Projekte \ Lösungen.

Verwenden Sie den Namespace in einer Datei: using MyProject.Language;

Verwenden Sie es wie string someText = Resources.orderGeneralError; folgt im Backend : Wenn es etwas anderes gibt, das als Ressourcen bezeichnet wird, fügen Sie einfach den gesamten Namespace ein.

Verwendung in MVC

In MVC können Sie die Sprache beliebig einstellen, aber ich habe parametrisierte URLs verwendet, die wie folgt eingerichtet werden können:

RouteConfig.cs Unter den anderen Zuordnungen

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 (muss möglicherweise hinzugefügt werden, wenn ja, fügen Sie FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);der Application_start()Methode in hinzuGlobal.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 legt nur die Kulturinformationen fest.

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

    }
}

Verwendung in .cshtml

@using MyProject.Language;
<h3>@Resources.w_home_header</h3>

oder wenn Sie keine Verwendungen definieren möchten, füllen Sie einfach den gesamten Namespace aus ODER Sie können den Namespace unter /Views/web.config definieren:

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

Dieses Tutorial zur Quell-Implementierung von mvc: Awesome tutorial blog

Verwendung in Klassenbibliotheken für Modelle

Die Verwendung im Back-End ist dieselbe, jedoch nur ein Beispiel für die Verwendung in Attributen

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

Wenn Sie eine Umgestaltung vorgenommen haben, wird automatisch überprüft, ob der angegebene Ressourcenname vorhanden ist. Wenn Sie die Typensicherheit bevorzugen, können Sie T4-Vorlagen verwenden, um eine Aufzählung zu generieren

Verwendung in WPF.

Natürlich fügen Sie einen Verweis auf Ihren MyProject.Language- Namespace hinzu. Wir wissen, wie man ihn im Back-End verwendet.

Fügen Sie in XAML in der Kopfzeile eines Fensters oder UserControls eine Namespace-Referenz hinzu, die langwie folgt heißt :

<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">

Dann in einem Etikett:

    <Label x:Name="lblHeader" Content="{x:Static lang:Resources.w_home_header}" TextBlock.FontSize="20" HorizontalAlignment="Center"/>

Da es stark typisiert ist, sind Sie sicher, dass die Ressourcenzeichenfolge vorhanden ist. Möglicherweise müssen Sie das Projekt manchmal während des Setups neu kompilieren. WPF ist manchmal fehlerhaft mit neuen Namespaces.

Eine weitere Sache für WPF, stellen Sie die Sprache innerhalb der App.xaml.cs. Sie können Ihre eigene Implementierung durchführen (während der Installation auswählen) oder das System entscheiden lassen.

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

Verwendung in UWP

In UWP verwendet Microsoft diese Lösung , dh Sie müssen neue Ressourcendateien erstellen. Außerdem können Sie den Text auch nicht wiederverwenden, da Sie x:Uiddas Steuerelement in XAML auf einen Schlüssel in Ihren Ressourcen setzen sollen. Und in Ihren Ressourcen müssen Sie tun Example.Text, um TextBlockden Text von a zu füllen . Diese Lösung hat mir überhaupt nicht gefallen, weil ich meine Ressourcendateien wiederverwenden möchte. Schließlich fand ich die folgende Lösung. Ich habe das heute (26.09.2019) herausgefunden, sodass ich möglicherweise mit etwas anderem zurückkomme, wenn sich herausstellt, dass dies nicht wie gewünscht funktioniert.

Fügen Sie dies Ihrem Projekt hinzu:

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

Fügen Sie dies App.xaml.csim Konstruktor hinzu:

CustomXamlResourceLoader.Current = new MyXamlResourceLoader();

Wo immer Sie möchten, ändern Sie die Sprache in Ihrer App:

ApplicationLanguages.PrimaryLanguageOverride = "nl";
Frame.Navigate(this.GetType());

Die letzte Zeile wird benötigt, um die Benutzeroberfläche zu aktualisieren. Während ich noch an diesem Projekt arbeite, habe ich festgestellt, dass ich dies zweimal tun muss. Möglicherweise wird beim ersten Start des Benutzers eine Sprachauswahl angezeigt. Da dies jedoch über den Windows Store verteilt wird, entspricht die Sprache normalerweise der Systemsprache.

Dann verwenden Sie in XAML:

<TextBlock Text="{CustomResource ExampleResourceKey}"></TextBlock>

Verwendung in Angular (Konvertierung in JSON)

Heutzutage ist es üblicher, ein Framework wie Angular in Kombination mit Komponenten zu haben, also ohne cshtml. Übersetzungen werden in JSON-Dateien gespeichert. Ich werde nicht darauf eingehen, wie das funktioniert. Ich würde ngx-translate anstelle der eckigen Mehrfachübersetzung nur empfehlen . Wenn Sie also Übersetzungen in eine JSON-Datei konvertieren möchten, ist dies ziemlich einfach. Ich verwende ein T4-Vorlagenskript, das die Ressourcendatei in eine JSON-Datei konvertiert. Ich empfehle, den T4-Editor zu installieren , um die Syntax zu lesen und korrekt zu verwenden, da Sie einige Änderungen vornehmen müssen.

Nur eines ist zu beachten: Es ist nicht möglich, die Daten zu generieren, zu kopieren, zu bereinigen und für eine andere Sprache zu generieren. Sie müssen also den folgenden Code so oft kopieren, wie Sie Sprachen haben, und den Eintrag vor '// Sprache hier auswählen' ändern. Derzeit ist keine Zeit, dies zu beheben, wird aber wahrscheinlich später aktualisiert (falls interessiert).

Pfad: 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);
}


#>

Wenn Sie eine Modulair-Anwendung haben und meinem Vorschlag gefolgt sind, mehrsprachige Projekte zu erstellen, müssen Sie für jedes eine T4-Datei erstellen. Stellen Sie sicher, dass die JSON-Dateien logisch definiert sind, es muss nicht sein en.json, es kann auch sein example-en.json. Befolgen Sie die Anweisungen hier, um mehrere JSON- Dateien für die Verwendung mit ngx-translate zu kombinieren

Verwendung in Xamarin.Android

Wie oben in den Updates erläutert, verwende ich dieselbe Methode wie bei Angular / JSON. Da Android jedoch XML-Dateien verwendet, habe ich eine T4-Datei geschrieben, die diese XML-Dateien generiert.

Pfad: 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 funktioniert mit values-xxOrdnern, daher ist oben für Englisch im values-enOrdner. Sie müssen aber auch einen Standard generieren, der in den valuesOrdner verschoben wird. Kopieren Sie einfach die obige T4-Vorlage und ändern Sie den Ordner im obigen Code.

Jetzt können Sie eine einzige Ressourcendatei für alle Ihre Projekte verwenden. Dies macht es sehr einfach, alles in ein Ausschlussdokument zu exportieren und es von jemandem übersetzen und erneut importieren zu lassen.

Besonderer Dank geht an diese erstaunliche VS-Erweiterung, die hervorragend mit resxDateien funktioniert . Betrachten Sie spenden ihm für seine großartige Arbeit (Ich habe nichts damit zu tun, ich liebe einfach die Erweiterung).

CularBytes
quelle
1. Wie können Sie nach der Bereitstellung weitere Sprachen hinzufügen, indem Sie der Mylanguage mehr Ressourcen hinzufügen und neu kompilieren und dann erneut bereitstellen? (2) Gibt es eine IDE oder ein Tool, das Sie für die Übersetzung verwendet haben (ich habe über 40 Formulare in Winform), können Sie diese teilen? (3) Ich habe es versucht und es wurde nicht übersetzt, als ich die Kultur auf Arabisch änderte. Ich werde eine Frage erstellen und Sie wissen lassen
Smith
5
1: Veröffentlichen Sie einfach Resources.xx.resx und ändern Sie xx und überall dort, wo ich die switch-Anweisungen habe, fügen Sie die Sprache hinzu. 2 Ich verwende die ResxManager-Erweiterung (von TomEnglert), kann über Tools -> Extensions installiert werden. Dies ist eine gute Möglichkeit, die Sprachen nebeneinander zu haben, aber die eigentliche Übersetzung erfolgt durch eine andere Person (einfacher Export und Import nach / von Excel mit ResxManager). 3. Auch beim letzten Mal, indem Sie eine hinzufügen, überprüfen Sie alle oben aufgeführten Dateien, aber einige Haltepunkte. Ich habe dies jedoch nicht in WinForms verwendet.
CularBytes
12
Kann ich meine eigene Antwort positiv bewerten? Ich musste nur dumm meine eigene Antwort überprüfen, um einen Fehler beim Wechseln zwischen Sprachen in WPF -.-
CularBytes
1
@TiagoBrenck Hallo, nur in dieser speziellen Situation können Sie, wenn Englisch Ihre Standardsprache ist, eine Datei resources.en-gb.resx erstellen und die 1% igen Änderungen dort hinzufügen. Wenn die Sprache en-gb ausgewählt ist und Wörter nicht übersetzt werden, wird standardmäßig die Standardsprache (resources.resx) verwendet.
CularBytes
1
Wie haben Sie die Schlüssel der Ressourcendatei strukturiert? Da ich sehe, dass Sie nur eine Ressourcendatei verwenden und Ihrer Meinung nach eine riesige App haben, wie haben Sie die Schlüssel aufgeteilt? So etwas wie "Feature.MyMessage" oder "Area.Feature.MyMessage"
Francesco Venturini
21

Ich habe Projekte gesehen, die mit verschiedenen Ansätzen umgesetzt wurden, von denen jeder seine Vor- und Nachteile hat.

  • Einer hat es in der Konfigurationsdatei gemacht (nicht mein Favorit)
  • Einer hat es mit einer Datenbank gemacht - das hat ziemlich gut funktioniert, aber es war ein Problem, wenn man weiß, was man warten muss.
  • Eine verwendete Ressourcendateien, wie Sie es vorschlagen, und ich muss sagen, es war mein Lieblingsansatz.
  • Am einfachsten war es, eine Include-Datei voller Strings zu verwenden - hässlich.

Ich würde sagen, die von Ihnen gewählte Ressourcenmethode ist sehr sinnvoll. Es wäre interessant, auch die Antworten anderer Leute zu sehen, da ich mich oft frage, ob es einen besseren Weg gibt, solche Dinge zu tun. Ich habe zahlreiche Ressourcen gesehen, die alle auf die Methode der Verwendung von Ressourcen verweisen, einschließlich einer hier auf SO .

BenAlabaster
quelle
5

Ich glaube nicht, dass es einen "besten Weg" gibt. Dies hängt wirklich von den Technologien und der Art der Anwendung ab, die Sie erstellen.

Webapps können die Informationen in der Datenbank speichern, wie andere Poster vorgeschlagen haben. Ich empfehle jedoch, separate Ressourcendateien zu verwenden. Das sind Ressourcendateien, die von Ihrer Quelle getrennt sind . Separate Ressourcendateien reduzieren die Konkurrenz für dieselben Dateien, und wenn Ihr Projekt wächst, wird die Lokalisierung möglicherweise getrennt von der Geschäftslogik durchgeführt. (Programmierer und Übersetzer).

Microsoft WinForm- und WPF-Gurus empfehlen die Verwendung separater Ressourcenassemblys, die an jedes Gebietsschema angepasst sind.

Die Fähigkeit von WPF, UI-Elemente an den Inhalt anzupassen, verringert die erforderliche Layoutarbeit, z. B.: (Japanische Wörter sind viel kürzer als englische).

Wenn Sie über WPF nachdenken: Ich schlage vor, diesen MSDN-Artikel zu lesen. Um ehrlich zu sein, fand ich die WPF-Lokalisierungstools: msbuild, locbaml (und möglicherweise eine Excel-Tabelle) mühsam zu verwenden, aber es funktioniert.

Etwas nur geringfügig Verwandtes: Ein häufiges Problem ist die Integration von Legacy-Systemen, die Fehlermeldungen (normalerweise in Englisch) und keine Fehlercodes senden. Dies erzwingt entweder Änderungen an Legacy-Systemen oder die Zuordnung von Backend-Zeichenfolgen zu meinen eigenen Fehlercodes und dann zu lokalisierten Zeichenfolgen ... yech. Fehlercodes sind Freunde der Lokalisierung

MW_dev
quelle
4

+1 Datenbank

Formulare in Ihrer App können sich sogar im laufenden Betrieb neu übersetzen, wenn Korrekturen an der Datenbank vorgenommen werden.

Wir haben ein System verwendet, bei dem alle Steuerelemente in einer XML-Datei (eines pro Formular) Sprachressourcen-IDs zugeordnet wurden, sich jedoch alle IDs in der Datenbank befanden.

Anstatt dass jedes Steuerelement die ID enthält (Implementierung einer Schnittstelle oder Verwendung der Tag-Eigenschaft in VB6), haben wir im Grunde die Tatsache verwendet, dass in .NET der Steuerelementbaum durch Reflexion leicht erkennbar war. Ein Prozess beim Laden des Formulars erstellt die XML-Datei, wenn sie fehlt. Die XML-Datei ordnete die Steuerelemente ihren Ressourcen-IDs zu, sodass diese lediglich ausgefüllt und der Datenbank zugeordnet werden mussten. Dies bedeutete, dass die kompilierte Binärdatei nicht geändert werden musste, wenn etwas nicht markiert war oder wenn es in eine andere ID aufgeteilt werden musste (einige englische Wörter, die sowohl als Substantive als auch als Verben verwendet werden können, müssen möglicherweise in zwei verschiedene Wörter übersetzt werden im Wörterbuch und nicht wiederverwendet werden, aber Sie werden dies möglicherweise bei der ersten Zuweisung von IDs nicht feststellen).

Die App wird nur dann stärker einbezogen, wenn eine Phase mit Einfügemarken verwendet wird.

Die Datenbankübersetzungssoftware war Ihr grundlegender CRUD-Wartungsbildschirm mit verschiedenen Workflow-Optionen, um das Durchsuchen der fehlenden Übersetzungen usw. zu erleichtern.

Cade Roux
quelle
Ich mag Ihre Methode. Können Sie mir sagen, wie die XML-Datei generiert und dann der Datenbank zugeordnet wird?
Smith
In Form_Load oder was auch immer wurde die Übersetzungsfunktion für die Formularinstanz aufgerufen. Die XML-Datei für dieses Formular wurde geladen. Wenn es nicht existierte, wurde es erstellt. Ich habe das Schema nicht zur Hand, aber im Grunde hat es den Kontrollnamen in einem Formular einer Übersetzungs-ID zugeordnet. Jedes Steuerelement für das Formular, das sich nicht in der XML-Datei befindet, erhält einen Eintrag ohne Übersetzungs-ID. Da sie bei Bedarf erstellt werden, können Sie einfach Ihr Formular erstellen und die App ausführen, mit der die XML-Datei für fehlende Steuerelemente erstellt oder aktualisiert wird. Geben Sie dann die Übersetzungs-IDs für die Artikel ein.
Cade Roux
4

Ich habe gesucht und Folgendes gefunden:

Wenn Sie WPF oder Silverlight verwenden, kann Ihr Ansatz aus vielen Gründen WPF LocalizationExtension verwenden .

Es ist Open Source Es ist KOSTENLOS (und wird kostenlos bleiben) und befindet sich in einem echten stabilen Zustand

In einer Windows-Anwendung können Sie Folgendes tun:

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

Und ich denke auf einer Wep-Seite könnte der Ansatz der gleiche sein.

Viel Glück!

JxXx
quelle
2

Ich würde mit den mehreren Ressourcendateien gehen. Es sollte nicht so schwer zu konfigurieren sein. Tatsächlich habe ich kürzlich eine ähnliche Frage zum Festlegen globaler sprachbasierter Ressourcendateien in Verbindung mit Ressourcendateien in Formsprache beantwortet.

Lokalisierung in Visual Studio 2008

Ich würde das zumindest für die WinForm-Entwicklung als den besten Ansatz betrachten.

KMessenger
quelle
Ein Nachteil der Ressourcen ist, dass Sie neu starten müssen, um die Sprache zu wechseln. Es ist wahrscheinlich für die meisten akzeptabel, aber es ist mein Haustier ärgern ...
Roman
2

Sie können kommerzielle Tools wie Sisulizer verwenden . Es wird eine Satellitenassemblierung für jede Sprache erstellt. Sie sollten nur darauf achten, die Namen der Formularklassen nicht zu verschleiern (wenn Sie den Verschleierer verwenden).

Davorin
quelle
0

Die meisten OpenSource-Projekte verwenden zu diesem Zweck GetText . Ich weiß nicht, wie und ob es jemals zuvor in einem .NET-Projekt verwendet wurde.

Vasil
quelle
0

Wir verwenden einen benutzerdefinierten Anbieter für die Unterstützung mehrerer Sprachen und fügen alle Texte in eine Datenbanktabelle ein. Es funktioniert gut, außer dass wir manchmal Probleme beim Zwischenspeichern haben, wenn wir Texte in der Datenbank aktualisieren, ohne die Webanwendung zu aktualisieren.

Cossintan
quelle
0

Standardressourcendateien sind einfacher. Wenn Sie jedoch sprachabhängige Daten wie Nachschlagetabellen haben, müssen Sie zwei Ressourcensätze verwalten.

Ich habe es nicht getan, aber in meinem nächsten Projekt würde ich einen Datenbankressourcenanbieter implementieren. Ich habe auf MSDN herausgefunden, wie es geht:

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

Ich habe auch diese Implementierung gefunden:

DBResource-Anbieter

Ben Dempsey
quelle