boost::locale::translate und umlaute



  • @booster sagte in boost::locale::translate und umlaute:

    Es ist allso extrem schwierig sein Programm auf wstring umzustellen.

    Ich würde bei plattform-agnostischen Projekten eher dazu tendieren bei UTF-8 zu bleiben und an den Schnittstellen zu Windows nach UTF-16 zu konvertieren - wo das ist, siehst du ja dann an den Fehlermeldungen, wenn du auf die Windows-Funktionen mit dem W-Suffix umstellst. Besonders wenn du schon solche Bibliotheken wie GNU Gettext verwendest macht das Sinn. Bei den meisten nicht-windows-spezifischen Bibliotheken ist ohnehin eher UTF-8 verbreitet, wenn man mal z.B. von ICU absieht.

    Da du allerdings auch MFC und CString erwähnst, ist nicht ganz klar, ob es sich bei dir nicht doch um ein reines Windows-Projekt handelt. Wenn du mehr WinAPI-Calls als Aufrufe anderer Bibliotheken hast, dann ist der wchar-Weg vielleicht doch nicht verkehrt.

    Konvertierten wirst du so oder so müssen, fragt sich nur an welcher Schnittstelle - WinAPI oder Gettext und andere Libs.
    Choose your poison 😉


  • Administrator

    @booster Weil ausserhalb von Windows verwenden sehr viele (alle?) UTF-8. Viele der englischen Beispiele sind zudem äussert einfach in UTF-8 zu erklären, da keine Sonderzeichen vorkommen. Damit entspricht ein Byte noch einem Zeichen. So kann man die ganze Komplexität von Unicode und Textenkodierung am Anfang auslassen.

    Ob du nun UTF-16 und std::wstring oder UTF-8 und std::string verwendest, kommt, wie Finnegan sagt, sehr auf deine Anwendung an. Wenn du sowieso auf Windows unterwegs bist, die MFC und WinAPI einsetzt, dann geh mit UTF-16 und std::wstring. Ansonsten prüfe, ob du entweder mit UTF-8 oder mit einer sich dynamisch anpassenden Konfiguration gehst, welche automatisch je nach Plattform zwischen UTF-8 und UTF-16 umschaltet.



  • @Dravere

    Also ich passe die Anwendung gerade an und habe auf MFC komplett verzichtet. WinApi wird an einigen Stellen noch verwendet.
    Die Applikation die die Meldungen darstellt ist momentan noch in MFC programmiert wird in Zukunft aber geändert.

    Ich würde am liebsten auf UTF-8 bleiben. Meine Anwendung hat bisher ja auch mit den Umlauten funktioniert.
    Das liegt wohl jetzt nur daran dass gettext bzw boost::locale::translate das falsch codiert.

    wobei ich wieder bei meiner Ausgangsfrage wäre 🙂



  • @booster sagte in boost::locale::translate und umlaute:

    Ich würde am liebsten auf UTF-8 bleiben. Meine Anwendung hat bisher ja auch mit den Umlauten funktioniert.
    Das liegt wohl jetzt nur daran dass gettext bzw boost::locale::translate das falsch codiert.

    An Umlauten würde ich das nicht unbedingt festmachen. Es ist gut möglich, dass deine Quellcode-Dateien wie auch die char*-basierte Textausgabe der WinAPI-Funktionen z.B. die Windows-1252-Codepage verwenden. Das ist eine erweitere ASCII 1-Byte-Zeichenkodierung, die in den höheren Bytes zusätzlich Sonderzeichen westeuropäischer Sprachen wie eben auch Umlaute enthält. Auf Systemen mit einem anderen Default-Zeichensatz würde dein "funktioniert" dann sehr schnell relativiert.

    Wie sind denn deine Quellcode-Dateien in Visual Studio codiert? Für durchgängiges UTF-8 würde ich da die Einstellung Unicode (UTF-8 without signature) - Codepage 65001 empfehlen (siehe auch hier).



  • @booster sagte in boost::locale::translate und umlaute:

    @Dravere

    Also ich passe die Anwendung gerade an und habe auf MFC komplett verzichtet. WinApi wird an einigen Stellen noch verwendet.
    Die Applikation die die Meldungen darstellt ist momentan noch in MFC programmiert wird in Zukunft aber geändert.

    Ich würde am liebsten auf UTF-8 bleiben. Meine Anwendung hat bisher ja auch mit den Umlauten funktioniert.
    Das liegt wohl jetzt nur daran dass gettext bzw boost::locale::translate das falsch codiert.

    wobei ich wieder bei meiner Ausgangsfrage wäre 🙂

    Sicher das gettext/boost::locale::translate das problem ist? Und wie kommst du darauf?
    Wenn die gettext files utf-8 codiert sind, dann lies mal eine Übersetzung mit umlauten als std::string ein. Und dann schau dir mal die "byte" werte der einzelnen chars an (z.b. in hexadezimaler Darstellung).
    Wenn kein kodierungsfehler auftritt müssten an den stellen für die Umlaute folgende 2 byte folgen auftauchen:
    Ä (0xc3 0x84)
    Ö (0xc3 0x96)
    Ü (0xc3 0x9c)
    ä (0xc3 0xa4)
    ö (0xc3 0xb6)
    ü (0xc3 0xbc)

    Aber prüf vor diesem Test ob die gettext files auch wirklich in UTF-8 kodiert sind (z.b. mit notepad++). Ein "charset=UTF-8"" im .po file reicht nicht.
    Mit welchen tool wurden die .po files erstellt?

    Das Problem ist unter winows kann z.b. die "console" initial nichts mit UTF-8 anfangen und dadurch ist eine simple ausgabe zur überprüfung nicht möglich,
    AFAIK hat der debugger von VisualStudio das gleiche Problem



  • @Finnegan sagte in boost::locale::translate und umlaute:

    @booster sagte in boost::locale::translate und umlaute:

    Ich würde am liebsten auf UTF-8 bleiben. Meine Anwendung hat bisher ja auch mit den Umlauten funktioniert.
    Das liegt wohl jetzt nur daran dass gettext bzw boost::locale::translate das falsch codiert.

    An Umlauten würde ich das nicht unbedingt festmachen. Es ist gut möglich, dass deine Quellcode-Dateien wie auch die char*-basierte Textausgabe der WinAPI-Funktionen z.B. die Windows-1252-Codepage verwenden. Das ist eine erweitere ASCII 1-Byte-Zeichenkodierung, die in den höheren Bytes zusätzlich Sonderzeichen westeuropäischer Sprachen wie eben auch Umlaute enthält. Auf Systemen mit einem anderen Default-Zeichensatz würde dein "funktioniert" dann sehr schnell relativiert.

    Wie sind denn deine Quellcode-Dateien in Visual Studio codiert? Für durchgängiges UTF-8 würde ich da die Einstellung Unicode (UTF-8 without signature) - Codepage 65001 empfehlen (siehe auch hier).

    Ist nur relevant, wenn im quellcode selbst non ascii zeichen vorkommen. Da für die Übersetzung gettext (via boost::locale:translate) verwendet wird sollte es in diesem Falle kein Problem sein



  • @firefly sagte in boost::locale::translate und umlaute:

    Sicher das gettext/boost::locale::translate das problem ist? Und wie kommst du darauf?

    Wenn ich std::string = "ÜÄÖ"; schreibe sehe ich im debugger als auch in meiner Ausgabe "ÜÄÖ"
    mein string hat in dem Fall 6 Bytes

    Ü = 0xdc
    Ö = 0xd6
    Ä = 0xc4

    ü = 0xfc
    ö = 0xf6
    ä = 0xe4

    wenn ich std::string = boost::locale::translate("Umlaute"); schreibe erhalte ich im debugger als auch in der Ausgabe irgenwelche Sonderzeichen.
    mein string hat dann 12 bytes und die von dir gezeigten werte.

    @firefly sagte in boost::locale::translate und umlaute:

    Aber prüf vor diesem Test ob die gettext files auch wirklich in UTF-8 kodiert sind (z.b. mit notepad++). Ein "charset=UTF-8"" im .po file reicht nicht.
    Mit welchen tool wurden die .po files erstellt?

    Die .po Files wurden mit PoEdit erstellt. Die Files sind auf UTF-8 eingestellt. Kontrolliert mit Visual Studio Code.



  • @booster sagte in boost::locale::translate und umlaute:

    @firefly sagte in boost::locale::translate und umlaute:

    Sicher das gettext/boost::locale::translate das problem ist? Und wie kommst du darauf?

    Wenn ich std::string = "ÜÄÖ"; schreibe sehe ich im debugger als auch in meiner Ausgabe "ÜÄÖ"
    mein string hat in dem Fall 6 Bytes

    Ü = 0xdc
    Ö = 0xd6
    Ä = 0xc4

    ü = 0xfc
    ö = 0xf6
    ä = 0xe4

    Dann sind die Zeichen nicht in UTF-8 codiert. Sie post von @Finnegan

    wenn ich std::string = boost::locale::translate("Umlaute"); schreibe erhalte ich im debugger als auch in der Ausgabe irgenwelche Sonderzeichen.
    mein string hat dann 12 bytes und die von dir gezeigten werte.

    Ok gut, dann macht boost locale alles richtig und auch die po files sind in UTF-8 kodiert.
    Das du nur komische zeichen im Debugger und in der Ausgabe siehst liegt daran, dass der Debugger und die Ausgabe unter Windows bei default kein UTF-8 kann.
    Hierfür muss dann vorher das ganze nach UTF-16 umkodiert werden damit es unter Windows funktioniert.

    Für ein Test consolen programm siehe folgenden hinweis aus der boost locale dokumentation bezüglich UTF-8 und windows:
    https://www.boost.org/doc/libs/1_50_0/libs/locale/doc/html/running_examples_under_windows.html

    Hier eine gute Beschreibung wenn man intern in C++ UTF-8 für strings verwenden möchte und die Probleme unter windows und mögliche Lösungen:

    http://www.nubaria.com/en/blog/?p=289

    Wenn es ein reines windows projekt ist, dann wäre es günstiger alle strings in UTF-16 zu verwenden, dann gibt es keine Darstellungsprobleme unter windows.



  • @firefly sagte in boost::locale::translate und umlaute:

    Ist nur relevant, wenn im quellcode selbst non ascii zeichen vorkommen. Da für die Übersetzung gettext (via boost::locale:translate) verwendet wird sollte es in diesem Falle kein Problem sein

    Das stimmt zwar, allerdings sprach ich von durchgängigem UTF-8, was in jedem Fall zu empfehlen ist, wenn man sich schon für UTF-8 entscheidet. Man spart sich unter Umständen eine Menge Ärger, wenn man sicherstellt, dass auch die im Quellcode definierten Strings die selbe Codierung verwenden - auch wenn man separate Übersetzungs-Dateien verwendet können durchaus auch mal Sonderzeichen im Code auftauchen - z.B. für sprachunabhängige Symbole ("✓" verwende ich z.B. schonmal ganz gerne um erfolgreich abgeschlossene Aktionen zu markieren).

    @booster sagte in boost::locale::translate und umlaute:

    wenn ich std::string = boost::locale::translate("Umlaute"); schreibe erhalte ich im debugger als auch in der Ausgabe irgenwelche Sonderzeichen.

    Der VS-Debugger scheint das UTF-8 nicht automatisch zu erkennen, auch nicht wenn die Quellcode-Datei selbst als UTF-8 gespeichert wurde. Für Watch-Variablen gibt es aber sog. Format Specifier mit dem man dem Debugger u.a. verklickern kann, dass er den std::string oder auch einen char* interpretieren soll. Zumindest in VS2017 funktioniert das recht gut:

    std::string s = "äöüシ✓";
    

    Wenn man hier eine Watch-Variable mit dem s8-Specifier anlegt, also mit nem Namen s,s8, dann wird dort der String im Debugger exakt so wie im Code angegeben dargestellt (Quellcode-Datei als UTF-8 without signature gespeichert). Leider nur für Watch-Variablen, nicht für Autos, Locals oder im Tooltip des Variablennamens im Quellcode (Vielleicht gibt es ja irgendwo einen projektbezogenen Default-Specifier, das wäre fein, aber dazu habe ich leider nichts finden können).



  • @firefly sagte in boost::locale::translate und umlaute:

    Wenn es ein reines windows projekt ist, dann wäre es günstiger alle strings in UTF-16 zu verwenden, dann gibt es keine Darstellungsprobleme unter windows.

    Und wie mache ich das.

    Mein .po file UTF-16 codieren ??

    Der Quellcode selber ist Windows 1252 codiert. Das sagt mir der Visual Studio code. Das muss ich auch auf UTF-16 umstellen?
    Wo geht das im Visual Studio?



  • @booster sagte in boost::locale::translate und umlaute:

    @firefly sagte in boost::locale::translate und umlaute:

    Wenn es ein reines windows projekt ist, dann wäre es günstiger alle strings in UTF-16 zu verwenden, dann gibt es keine Darstellungsprobleme unter windows.

    Und wie mache ich das.

    Mein .po file UTF-16 codieren ??

    Wäre eine möglichkeit, die andere, die ich mir vorstellen kann ist dass boost::locale::translatestd::wstring() die konvertierung von UTF-8 nach UTF-16 selbst vornimmt.

    Der Quellcode selber ist Windows 1252 codiert. Das sagt mir der Visual Studio code. Das muss ich auch auf UTF-16 umstellen?
    Wo geht das im Visual Studio?

    In dem du einfach einen string literal statt "string" so schreibst L"string", dann wird das string literal automatisch vom compiler als wide char (unter windows ist es dann UTF-16) interpretiert.

    Zusätzlich musst da dabei deine Projekte von MultiByte CharacterSet auf Unicode umgestellt werden, damit auch die ganzen WinAPI funktionen (ohne A/W Zusatz) Unicode verwenden.

    Intern UTF-8 zu verwenden wäre die Portablere variante, da die meisten non Windows system UTF-8 verwenden.
    Nur halt mit den schon beschriebenen Problemen unter Windows/Visual Studio



  • @booster sagte in boost::locale::translate und umlaute:

    Mein .po file UTF-16 codieren ??

    Der Quellcode selber ist Windows 1252 codiert. Das sagt mir der Visual Studio code. Das muss ich auch auf UTF-16 umstellen?
    Wo geht das im Visual Studio?

    Hast du nicht vor eine Weile noch sowas hier geschrieben?

    Also ich passe die Anwendung gerade an und habe auf MFC komplett verzichtet. WinApi wird an einigen Stellen noch verwendet. [...]
    Ich würde am liebsten auf UTF-8 bleiben.

    Ich denke das wichtigste ist erstmal dass du dir klar wirst, welche Codierung du eigentlich verwenden willst und dann bei dieser bleibst und sie konsequent durchziehst. Den halben Code ändern und sich dann wieder umentscheiden wird die Probleme mit den falschen Zeichen wohl eher verstärken 😉



  • 🙂 Du hast recht Finnegan

    Also nochmals zusammengefasst.
    Ich versuche nicht auf Microsoft Erweiterungen zu setzen. Also auf MFC zu verzichten.
    Nichts desto trotz bleibt die Anwendung auf Windows.

    Ich würde am liebsten auf UTF-8 bleiben.

    Nun ja, damit wollte ich eigentlich nur sagen dass ich nicht auf Unicode bzw widestring umstellen möchte.

    Verstehe ich das richtig.

    • Für UTF-8 benötige ich 2 Bytes um meine Zeichen darzustellen. Das ist das was mir gettext liefert.
    • Wenn ich einen string direkt im code anlege wird mir ein zeichen als 1 byte abgelegt. Was ich nicht ganz verstehe. Es ist doch Multibyte Character Set eingestellt. Also wie ich hier jetzt gelernt habe mehrere bytes um 1 Character dar zu stellen.

    Also was ich nicht verstehe.
    Was passiert in dem Fall wenn ich einen std::string direkt im Code setze. Wieso wird dann ein einzelnes Zeichen nur als 1 Byte dargestellt.
    Das ist ja dann nicht mehr UTF-8. UTF-16 kann es ja auch nicht sein. Dazu bräuchte ich ja ein wstring?



  • Wenn du im Code UTF-8 Literale erzeugen wilst, dann schreib u8"literal string". Wenn dann der Compiler das Encoding des Source-Files kennt, dann kann er daraus UTF-8 Literals erzeugen. Egal ob das Encoding des Source-Files jetzt UTF-8 ist oder etwas anderes.

    Aber Achtung: es kann sein dass du beim Switch auf C++20 dann ein paar Sachen anpassen musst. Gibt nämlich ein Proposal dass u8 Literale nicht mehr char sondern char8_t sein sollen. Was dann ein neuer Typ ist, was dann bedeutet dass man entsprechend casten oder kopieren muss, oder halt std::u8string statt std::string verwenden.



  • @hustbaer sagte in boost::locale::translate und umlaute:

    Wenn du im Code UTF-8 Literale erzeugen wilst, dann schreib u8"literal string"

    Ja das kann ich bei den strings machen die ich selber erzeuge. Aber nicht bei denen die ich aus irgendwelchen Funktionen und Membern erhalte.



  • ein string literal wird AFAIK in ASCII oder in der lokalen codepage (ANSI) codiert (Im Falle von Windows ist dass für Deutsch die codepage mit dem Namen "Windows 1252"). Und bei ANSI werden alle Zeichen mit einem byte dargestellt.
    Da in einem byte aber nicht alle Zeichen der Welt dargestellt werden kann, wurde bei ANSI die codepages eingeführt, die definieren welcher wert >127 (Maximalwert von ASCII) für ein bestimmtes Zeichen steht.

    Für UTF-8 benötige ich 2 Bytes um meine Zeichen darzustellen. Das ist das was mir gettext liefert.

    Stimmt nicht ganz. Im Falle der Deutschen Umlaute (äöü) werden diese bei UTF-8 in 2 bytes codiert. Um ein Unicode zeichen in UTF-8 darzustellen, können bis zu 4 bytes verwendet werden.
    Z.B. Alle Zeichen aus dem ASCII Zeichensatz werden in UTF-8 mit einem byte dargestellt (haben sogar die gleichen Werte).
    Chinesische Zeichen werden in UTF-8 mit 2-3 bytes dargestellt.

    Wie schon ein paar mal gesagt, wenn du intern UTF-8 verwendet willst, so musst du nicht das Projekt von MultiByte auf Unicode stellen. Und diese Umstellung bewirkt hauptsächlich auch nur , dass statt der ASCII/ANSI Versionen der WinAPI Funktionen die UNICODE (UTF-16) Versionen verwendet werden, wenn man ein WinAPI Funktion ohne den Zusatz A/W im code verwendet

    Nur wenn du WinAPI funktionen (wie SendMessage) aufrufen musst/willst, so musst du entweder die UNICODE (UTF-16) version (z.b. SendMessageW) aufrufen und vorher den UTF-8 String nach UTF-16 konvertieren. Oder den UTF-8 string in die lokal verwendete ANSI codepage konvertieren um dann die ASCII/ANSI version (z.b. SendMessageA) aurufen zu können.



  • @booster sagte in boost::locale::translate und umlaute:

    @hustbaer sagte in boost::locale::translate und umlaute:

    Wenn du im Code UTF-8 Literale erzeugen wilst, dann schreib u8"literal string"

    Ja das kann ich bei den strings machen die ich selber erzeuge. Aber nicht bei denen die ich aus irgendwelchen Funktionen und Membern erhalte.

    Hast du kontrolle über solche Funktionen? Wenn ja dann kannst du dafür sorgen, dass diese UTF-8 kodierte String zurückliefern. Ansonsten musst du wohl explizit so einen string, welches vermutlich non ASCII zeichen in der lokalen codepage kodiert enthält, nach UTF-8 konvertieren



  • Und hier noch etwas lesestoff bezüglich UTF-8 überall: http://utf8everywhere.org/



  • @hustbaer sagte in boost::locale::translate und umlaute:

    Wenn du im Code UTF-8 Literale erzeugen wilst, dann schreib u8"literal string". Wenn dann der Compiler das Encoding des Source-Files kennt, dann kann er daraus UTF-8 Literals erzeugen. Egal ob das Encoding des Source-Files jetzt UTF-8 ist oder etwas anderes.

    Ich finde wenn man wirklich konsequent mit UTF-8 sein will, dann solle UTF-8 kein Sonderfall sondern der Normalfall sein. Meine persönliche Meinung zu solchen Präfixen ist, dass es diese nur dort geben sollte, wo man von der gewählten Standard-Codierung abweicht. Z.B. beim Aufruf von WinAPI-Funktionen in einem UTF-8-Projekt.

    Aber Achtung: es kann sein dass du beim Switch auf C++20 dann ein paar Sachen anpassen musst. Gibt nämlich ein Proposal dass u8 Literale nicht mehr char sondern char8_t sein sollen. Was dann ein neuer Typ ist, was dann bedeutet dass man entsprechend casten oder kopieren muss, oder halt std::u8string statt std::string verwenden.

    Aus dem gleichen Grund bin ich auch kein Fan von diesem Proposal, da hier auch wieder UTF-8 wie ein Sonderfall behandelt wird. Sowas mag man mit historischem C++-Code begründen können, aber ich fühle mich nicht wirklich wohl dabei 2018 so etwas wie ANSI Codepages als Normalfall zu betrachten. Dann lieber UTF-8, womit klassisches ASCII ebenfalls abgedeckt ist.



  • @Finnegan sagte in boost::locale::translate und umlaute:

    @hustbaer sagte in boost::locale::translate und umlaute:

    Wenn du im Code UTF-8 Literale erzeugen wilst, dann schreib u8"literal string". Wenn dann der Compiler das Encoding des Source-Files kennt, dann kann er daraus UTF-8 Literals erzeugen. Egal ob das Encoding des Source-Files jetzt UTF-8 ist oder etwas anderes.

    Ich finde wenn man wirklich konsequent mit UTF-8 sein will, dann solle UTF-8 kein Sonderfall sondern der Normalfall sein. Meine persönliche Meinung zu solchen Präfixen ist, dass es diese nur dort geben sollte, wo man von der gewählten Standard-Codierung abweicht. Z.B. beim Aufruf von WinAPI-Funktionen in einem UTF-8-Projekt.

    Der Zug ist für C++ halt abgefahren. Klar muss man das nicht gut finden, aber ich wüsste nicht was man da jetzt noch machen könnte.

    Aber Achtung: es kann sein dass du beim Switch auf C++20 dann ein paar Sachen anpassen musst. Gibt nämlich ein Proposal dass u8 Literale nicht mehr char sondern char8_t sein sollen. Was dann ein neuer Typ ist, was dann bedeutet dass man entsprechend casten oder kopieren muss, oder halt std::u8string statt std::string verwenden.

    Aus dem gleichen Grund bin ich auch kein Fan von diesem Proposal, da hier auch wieder UTF-8 wie ein Sonderfall behandelt wird. Sowas mag man mit historischem C++-Code begründen können, aber ich fühle mich nicht wirklich wohl dabei 2018 so etwas wie ANSI Codepages als Normalfall zu betrachten. Dann lieber UTF-8, womit klassisches ASCII ebenfalls abgedeckt ist.

    Die "UTF-8 ist opt-in" Sache ist ja sowieso schon so seit C++11. Das Proposal ändert bloss den Typ von UTF-8 Literals. Was wichtig ist, da man sonst UTF-8 Strings und "irgend ein Encoding wird's schon sein" Strings nicht auseinanderhalten kann. Darüber zu diskutieren dass es schöner/besser gewesen wäre wenn von Anfang an UTF-8 Standard gewesen wäre bringt halt nixe, weil sich dadurch nix ändert. Es bleibt der Fakt dass extrem viel Code existiert bei dem char nicht UTF-8 impliziert. D.h. wenn man etwas haben möchte was UTF-8 impliziert, dann muss es etwas neues sein. Und damit sind wir dann bei char8_t. Und ob du dich dabei wohlfühlst oder nicht, es ist halt einfach so.


Anmelden zum Antworten