boost::locale::translate und umlaute



  • @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.



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

    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.

    Schon klar. Habe nur gerade erstmals davon gehört, da ich nicht alle Vorschläge so zeitnah verfolge, da konnte ich mir den Kommentar nicht verkneifen. Ist ja auch eine logische Folge aus u16string/u32string, macht schon Sinn, sonst wäre es wieder an einer anderen Stelle inkonsistent. Bin jedenfalls froh dass es mit char8_t einen neuen Typen geben wird, der z.B. in Overloads eindeutig von einem char zu unterscheiden ist, obwohl mir diese Unterscheidung bei char und int8_t natürlich lieber gewesen wäre (mache öfter mal kompaktes Binärdaten-Gefuddel mit kleinen ints)... aber ich weiss schon, alter Code und "Zug abgefahren".

    Frage mich gerade ob wir irgendwann mal std::string als schlechten Stil ansehen werden, wegen der unspezifizierten Codierung 🙂



  • Habe mal versucht den anderen Weg zu gehen. Da nun meine ganze Applikation auf Ansi aufgebaut ist und es viel Arbeit wäre das an allen Stellen umzubauen. Wäre ja noch die andere Möglichkeit mein .po File auch auf Ansi umzustellen.

    Das habe ich versucht. Aber erst mal gescheitert.

    Was muss ich den dazu einstellen?
    Mögliche Einstellmöglichkeiten

    im .po File

    "Content-Type: text/plain; charset=UTF-8\n"
    "Content-Transfer-Encoding: 8bit\n"
    "X-Poedit-SourceCharset: UTF-8\n"

    Encoding der Datei an sich ändern

    Oder kann man noch was bei gettetxt bzw bei boost translate einstellen?



  • Boost stellt eine Methode bereit um mir den gelieferten string in utf8 in ansi "Codepage 1252" zu konvertieren. Damit get es.

    boost::locale::conv::from_utf(boost::locale::translate("Umlaute"), "windows-1252")
    

    Danke für eure Hilfe. Hat mir einiges geholfen um die ganze encoding Sache besser zu verstehen.



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

    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.

    In C und C++ war die ANSI Code Page niemals der Normalfall! Wie könnte das auch der Fall sein, wenn zwei ganz wichtige Plattformen (darunter die älteste OS Plattform überhaupt) EBCDIC nutzen und eben kein ANSI. Dazu sind die OS Schnittstellen deutlich älter als Unicode, so dass auf allen relevanten Plattformen ANSI bzw. EBCDIC noch immer die Schnittstelle für die LowLever Funktionen im OS ist.

    Das Thema Unicode ist leider auch nicht trivial, weil es neben dem kurzen UCS-2 Intermezzo reichlich Varianten anbietet: UCS-4 (nun in UTF-32 unbenannt), UTF-16 und UTF-8. Eine korrekte Transkodierung von UTF-32 auf UTF-8 muss eine Prüfung umfassen, ob die Werte die 21Bit Grenze überschreiten, alter Code tut das nicht, weil früher die kompletten 32Bit für Unicode reserviert waren und dann bis zu 6 Bytes für ein Zeichen verwendet wurden.

    Achja, wchar_t ist unter Linux 32Bit groß, damit man UTF-32 Zeichen darin ablegen kann.



  • @john-0 sagte in boost::locale::translate und umlaute:

    Eine korrekte Transkodierung von UTF-32 auf UTF-8 muss eine Prüfung umfassen, ob die Werte die 21Bit Grenze überschreiten, alter Code tut das nicht, weil früher die kompletten 32Bit für Unicode reserviert waren und dann bis zu 6 Bytes für ein Zeichen verwendet wurden.

    Hast du dafür belege? Soweit ich das weis ist eine Konvertierung von UTF-8 <->UTF-32 einfach möglich.
    Aus der Unicode FAQ (http://unicode.org/faq/utf_bom.html#gen5)

    Which of the UTFs do I need to support?

    A: UTF-8 is most common on the web. UTF-16 is used by Java and Windows. UTF-8 and UTF-32 are used by Linux and various Unix systems. The conversions between all of them are algorithmically based, fast and lossless. This makes it easy to support data input or output in multiple formats, while using a particular UTF for internal storage or processing.



  • @john-0 sagte in boost::locale::translate und umlaute:

    Eine korrekte Transkodierung von UTF-32 auf UTF-8 muss eine Prüfung umfassen, ob die Werte die 21Bit Grenze überschreiten

    Für eine korrekte Transkodierung von UTF-32 auf UTF-8 musst man gar nix prüfen. Wenn man es so implementiert dass zu hohe UTF-32 Werte in zu hohe (und zu lange) UTF-8 Werte übersetzt werden kann man es sogar verlustfrei hinbekommen, so dass der Bullshit 1:1 round-trippen kann.

    Was oft die bessere Wahl ist, weil es bedeutet dass man Fehler nicht in dieser Schicht behandeln muss -- die auch meist kein praktisches Handling dafür machen kann. Statt dessen reicht man die Fehlerhaften Daten verlustfrei durch.


    Und wenn man meint unbedingt eine Validierung bei der Konvertierung machen zu müssen, dann muss man nicht nur prüfen ob die Werte nicht zu hoch sind, sondern auch ob es nicht verbotenerweise UTF-16 Surrogates sind die als UTF-32 abgelegt sind.



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

    Hast du dafür belege? Soweit ich das weis ist eine Konvertierung von UTF-8 <->UTF-32 einfach möglich.

    Das setzt immer voraus, dass man korrekte UTF-8 bzw. UTF-32 Strings hat. Nur wer garantiert Ihnen das? Solange man Daten verarbeitet die von außen angeliefert werden, sollte man darauf achten, dass sie auch korrekt sind. D.h. die Werte im Bereich des erlaubten liegen.

    A: UTF-8 is most common on the web. UTF-16 is used by Java and Windows. UTF-8 and UTF-32 are used by Linux and various Unix systems. The conversions between all of them are algorithmically based, fast and lossless. This makes it easy to support data input or output in multiple formats, while using a particular UTF for internal storage or processing.

    In der von Ihnen verlinkten Tabelle steht doch eindeutig, dass legale Unicdoes Strings (UTF-32) nur Zeichencodes zwischen 0x00000000 und 0x0010FFFF haben dürfen. Das wurde aber erst 2001 eingeführt. Wenn man sich mal zum historischen Vergleich RFC2044 anschaut, dann sieht man, dass UCS-4 auf UTF-8 bis zu 6 chars belegen durften. Das kann ein Problem sein, wenn alter Code mit neuer Puffergrößen benutzt wird (Pufferüberlauf ist möglich!). Ja, alter Code funktioniert immer noch korrekt, wenn man da nur Zeichen im heute erlaubten Bereich übergibt und die Puffer ausreichend groß sind.



  • @john-0 sagte in boost::locale::translate und umlaute:

    Solange man Daten verarbeitet die von außen angeliefert werden, sollte man darauf achten, dass sie auch korrekt sind. D.h. die Werte im Bereich des erlaubten liegen.

    Ich sehe eine einfache Transformation einer Representation in eine andere nicht als "Verarbeitung" an, speziell wenn sie verlustfrei und reversibelist. Ja, wenn man wirklich "verarbeitet", dann sollte man prüfen. Speziell wenn man Tabellenzugriffe mit dem Codepoint macht oder dergleichen. Und genau weil man an der Stelle sowieso prüfen muss, muss man beim schlichten Transformieren von UTF-32 nach UTF-8 nicht prüfen.

    Man kann sich die Sache aber natürlich auch unnötig kompliziert machen wenn man möchte.


Anmelden zum Antworten