boost::locale::translate und umlaute



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



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

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

    Genau diese Denke führt zu den vielen Exploits.



  • @john-0
    Wie soll das zu Exploits führen? Da kann nur 'was schief gehen, wenn zwei weitere Dinge dazukommen

    1. Man validiert die Daten dort wo sie reinkommen nicht
    2. Man validiert die Daten dort wo ungültige Werte zu einem Problem führen könnten nicht

    (1) halte ich für völlig OK. (2) halte ich für grob fahrlässig. AUSSER man arbeitet in einem Projekt wo vorgeschrieben ist dass sämtliche Daten dort wo sie ins System reinkommen validiert werden müssen. Wobei ich solche Vorgaben/Richtlinien für einen groben Fehler halte, da es oft nicht mit akzeptablem Aufwand (sowohl bei der Implementierung als auch zur Laufzeit) machbar ist.

    Oder anders gesagt: Was zu Fehlern führt ist sich dort wo sie entstehen könnten darauf zu verlassen, dass andere Codeteile, die kein Problem mit ungültigen Daten bekommen können, diese bereits verifiziert hätten. So eine Annahme kann ich nur als naiv bis schlichtweg dumm einstufen.

    Und da die hier diskutierte UTF-32-to-UTF-8 Konvertierung - WENN man sie passend implementiert - kein Problem mit ungültigen Codepoints bekommen kann, sehe ich auch keinen Grund da eine Validierung einzubauen. Kann man machen, muss man aber nicht. Und ich persönlich würde eher dahin tendieren nicht zu validieren und statt dessen aus allen 32 Bit Werten die reinkommen Output Bytefolgen zu generieren -- mit der "natürlichen" Fortführung der UTF-8 Kodierungsregeln.



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

    Und da die hier diskutierte UTF-32-to-UTF-8 Konvertierung - WENN man sie passend implementiert - kein Problem mit ungültigen Codepoints bekommen kann, sehe ich auch keinen Grund da eine Validierung einzubauen. Kann man machen, muss man aber nicht.

    Es kostet faktisch nichts, und es verhindert Objekte mit ungültigen Zuständen zu erzeugen. Es bringt auch nichts das Problem irgend wie zu verschleppen – ungültige Zustände haben in einem Programm nichts zu suchen. Und genau das meinte ich mit diese Denke führt zu Exploits. Es bringt nichts das Problem zu verschieben, wenn man das sieht muss man es gleich eskalieren nur so ist man in der Lage auf systematische Fehler zu reagieren.



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

    Es kostet faktisch nichts,

    Ist mir klar.

    und es verhindert Objekte mit ungültigen Zuständen zu erzeugen.

    WTF? Das "Objekt mit ungültigem Zuständ" existiert bereits, man konvertiert bloss die Representation.

    Es bringt auch nichts das Problem irgend wie zu verschleppen – ungültige Zustände haben in einem Programm nichts zu suchen.

    Aha. Ist leider nicht mehr als Blablubb.

    Und genau das meinte ich mit diese Denke führt zu Exploits.

    Ebenfalls.

    Es bringt nichts das Problem zu verschieben,

    Doch, das bringt manchmal sehr viel. Weil man nicht unbedingt alle Fehler an allen Stellen gut behandeln kann.

    wenn man das sieht muss man es gleich eskalieren nur so ist man in der Lage auf systematische Fehler zu reagieren.

    Nein, muss man nicht. Und was genau meinst du mit "systematische Fehler"? Systematische Fehler so wie z.B. ..... äh, .... Eingabefehler? Blödsinn.