Wie lassen sich Zeichenketten dynamisch mehrsprachig verwenden, ohne im
Quelltext mit Indizes (kryptisch) oder Symbolen (aufwendig) arbeiten zu
müssen?
1
lang = "de";
2
print("Hallo Welt!"); // Hallo Welt!
3
lang = "en";
4
print("Hallo Welt!"); // Hello world!
Als alternative Syntax evtl. direkt mit Übersetzung(en)
1
print("Hallo Welt!|Hello world!");
In jedem Fall ist aber ein code-modifizierender Präprozessor
erforderlich
1. Strings aus print() extrahieren
2. Tabelle mit Zeichenketten erstellen bzw. den String einer
bestehenden Übersetzungstabelle zuordnen
3. Daten & Indizes erstellen (source-Datei)
4. Strings in print() mit Symbolen ersetzen
Welche (besseren) Methoden gibt es außerdem?
Laufzeit. Auf den (generierten) Index wird dann noch der zur
Compile-Zeit ermittelte Offset addiert. Oder es wird eine andere
Datenquelle ausgewählt und indizes gemappt oder so.
Als Zusatzaufgabe könnten substrings und formatstrings berücksichtigt
werden:
Simpel schrieb:> Wie lassen sich Zeichenketten dynamisch mehrsprachig verwenden, ohne im> Quelltext mit Indizes (kryptisch) oder Symbolen (aufwendig) arbeiten zu> müssen?
man nimmt dem Text als Index.
https://de.wikipedia.org/wiki/GNU_gettext
Vielleicht etwas gebastelt - aber mal so als Idee und ins Unreine:
#define __LANGUAGE_DE 0
#define __LANGUAGE_EN 1
#define __txt_HALLO_WELT 0
#define __txt_HALLO_TAXI 1
char *en[]
{
"hello world",
"hello taxi"
};
char *de[]
{
"Hallo Welt",
"Hallo Taxi"
}
char **currentLanguage = de;
void SetLanguage(int language)
{
switch (language)
{
case __LANGUAGE_DE:
currentLanguage = de;
break;
case __LANGUAGE_EN:
currentLanguage = en;
break;
}
}
char* Translate(int x)
{
return currentLanguage[x];
}
Anwendung wäre dann:
printf(Translate(__txt_HALLO_WELT));
Sprachumschaltung:
SetLanguage(__LANGUAGE_EN);
Neue Sprachen könnte man mit einem kleinen Script erzeugen,
die das Textarray der Referenzsprache einliest, Übersetzungen dafür
erfragt und daraus ein neues Array generiert.
Danke dir, aber es ist sehr aufwändig, die Daten in drei Abschnitten
synchron zu halten. Ausserdem ist "__txt_HALLO_WELT" praktisch redundant
zu "Hallo Welt!" (man muss sich das fast identische Symbol ausdenken)
und fehleranfällig bzw. es ist dann nochmal nachzusehen, welches Symbol
denn jetzt exakt für den gewünschten String steht.
Für wenige Zeichenketten aber eine Möglichkeit.
Simpel schrieb:> und fehleranfällig bzw. es ist dann nochmal nachzusehen, welches Symbol> denn jetzt exakt für den gewünschten String steht.> Für wenige Zeichenketten aber eine Möglichkeit.
Du bist lustig.
Simpel schrieb:> Danke dir, aber es ist sehr aufwändig, die Daten in drei Abschnitten> synchron zu halten.
Ja da gebe ich dir recht. Ich habe das mal so gemacht und die .c und .h
Dateien dann aus einer Excel-Tabelle heraus generiert, weil die
Konsistenz irgendwann problematisch wurde. Mit Code-Generierung gings
dann aber!
Peter II schrieb:> man nimmt dem Text als Index.
Oder (bei größeren Mengen verschiedener Texte) deutlich effizienter:
einen Hash des Textes und zusätzlich einen gewichteten Binärbaum zur
schnellen Suche in den Hashes.
Cyblord -. schrieb:> Du bist lustig.
Verstehe ich nicht. Wo ist mein Widerspruch?
c-hater schrieb:>> man nimmt dem Text als Index.
Wenn ich die Wikipedia-Seite richtig verstehe, werden die Quelltexte
nicht modifiziert, sondern die Funktion _(s) sucht dann in der
jeweiligen Sprachdatei nach dem Eintrag zu s. Wenn das nicht klappt,
gibt sie s zurück.
Sind dann nicht alle Originalstrings doppelt vorhanden (einmal als
Argument, einmal als key), oder wird das optimiert (key ist Pointer auf
Argument oder andersherum)?
Simpel schrieb:> c-hater schrieb:>>> man nimmt dem Text als Index.
Zitatfälscher, dammichter! Das schrieb nicht ich, sondern Peter II. Ich
hab's meinerseits auch nur zitiert.
Tatsächlich geht aber auch der Vorschlag von Peter II. Bei kleinen
Zahlen verschiedener Strings (bis etwa einige hundert) ist er auch
locker gut genug, jedenfalls wenn die Dinger in der Primärsprache (also
dem, was als Index dienen soll) sortiert vorliegen.
Simpel schrieb:> Wie lassen sich Zeichenketten dynamisch mehrsprachig verwenden, ohne im> Quelltext mit Indizes (kryptisch) oder Symbolen (aufwendig) arbeiten zu> müssen?
Gar nicht.
Bedenke mal, daß Texte in unterschiedlichen Sprachen eben
unterschiedlich sind (eben, eben!) und deshalb in jeder vorgesehenen
Sprache eben vorgehalten werden müssen. Da kommst du nicht drumherum.
Du kannst nur eines machen: dir einen Textlieferer schreiben, der mit
Tokens gefüttert wird und dir einen der Texte als Pointer zurückliefert.
Ein bissel feilen kann man, indem man in selbigem dort, wo ein
fremdsprachiger Text nicht vorliegt, eben auf eine Standardsprache
zurückfällt.
Also sinngemäß sowas:
extern void SetzeSprache (int Sprache);
extern const char* GetText (dword token);
...
SetzeSprache(lngEnglisch);
DruckeDieses(GetText(idKlappeZu), wohin);
...
W.S.
Also die Frameworks die ich kenne (Qts tr(), KDEs i18n*()) benutzen
keine Tokens (wozu auch) sondern einfach den englischen Text. Gibt es
keine Übersetzung, wird der ausgegeben.
Ich würde nicht dazu raten das selber nachzubauen. Es gibt Lösungen
dafür, bei denen haben sich Leute was gedacht (vor allem Leute, die sich
mit Sprachen auskennen, die ganz anders sind als deutsch oder
englisch!), warum nicht einfach das verwenden was da ist?
Bei Menüs verwendet man üblicherweise mehrdimensionale Arrays.
Vielleicht hier das auch hier weiter.
#define LANGUAGE 3
uint8_t HELLO[LANGUAGE][16]
{
{" Hallo Welt },
{" Hello World },
{" was auch immer}
}
So wird die Ausgabe immer gleich vorgenommen und nur eine Variable
(ActualLanguage) geändert.
Print_LCD (&HELLO[ActualLanguage][0]);
Mein Weg:
Ich verwende eine Textdatei, jeweils eine Zeile für das Schlüsselwort,
dann den Inhalt in der jeweiligen Sprache. Abhängig vom Browser bzw der
Wahl des Benutzers wird die gewünschte Sprache ausgewählt.
Da es einfache Textdateien sind kann ich sie jemandem zum Übersetzen
geben - idealerweise dem Kunden.
Die übersetzten Strings werden einfach über printf() ausgegeben.
Man kann die Sprachdateien auch in XML vorhalten,
einlesen -> (ggf.) hashen -> Liste aufbauen.
<XML>
<String Value="Deutsch" Name="lang_de"/>
<String Value="English" Name="lang_en"/>
</XML>
printf(Translate("lang_de"));
Vorteil hier wäre, dass man die XML-Dateien direkt
einem professionellen Übersetzer geben kann.
W.S. schrieb:> Simpel schrieb:>> Wie lassen sich Zeichenketten dynamisch mehrsprachig verwenden, ohne im>> Quelltext mit Indizes (kryptisch) oder Symbolen (aufwendig) arbeiten zu>> müssen?>> Gar nicht.
++
Das ist mal wieder so eine typische "Wasch mir den Pelz, aber mach mich
nicht naß" Frage. I18N ist eine komplizierte Materie. Einfacher als die
Verwendung fertiger Frameworks a'la gettext() wird es nicht, zumindest
nicht solange man nicht massive Abstriche bei der Funktionalität
hinnimmt. Andererseits schließt der TE gerade diese Hilfsmittel ja schon
von vornherein aus.
Die einzig korrekte Antwort ist also "geht nicht". Mit dem längeren
Nachsatz "weil deine Anforderungen widersprüchlich sind".
Axel S. schrieb:> Andererseits schließt der TE gerade diese Hilfsmittel ja schon von> vornherein aus.
Das würde ich aus seinem Posting nicht so herauslesen.
Ich würde mich denjenigen anschließen, die zu einer fertigen Lösung
raten, denn die haben alles dabei, was man braucht. Insbesondere
gibt's dort dann Tools, mit denen man die Übersetzungen unabhängig
vom eigentlichen Quelltext pflegen kann. Damit kann man bei Bedarf
die Übersetzung auch komplett „outsourcen“.
Allerdings setzen solche Frameworks voraus, dass es in der Applikation
zumindest so viel Kontext eines (wenigstens rudimentären) OSes gibt,
dass man zur Laufzeit die Texte für die jeweilige Sprache aus einer
Datei nachladen kann. Falls das nicht gegeben ist (weil das bspw. auf
einem eher kleinen Controller wie AVR laufen soll), dann fände ich
einen Ansatz wie diesen:
Beitrag "Re: Mehrsprachige Strings ohne Hilfsprogramme?"
gar nicht so verkehrt.
Nur, um's noch gesagt zu haben:
Beitrag "Re: Mehrsprachige Strings ohne Hilfsprogramme?"
1
7.1.3 Reserved identifiers
2
[…]
3
— All identifiers that begin with an underscore and either an uppercase letter or another
4
underscore are always reserved for any use.
5
[…]
6
2 No other identifiers are reserved. If the program declares or defines an identifier in a
7
context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved
8
identifier as a macro name, the behavior is undefined.
Jörg W. schrieb:> Axel S. schrieb:>> Andererseits schließt der TE gerade diese Hilfsmittel ja schon von>> vornherein aus.>> Das würde ich aus seinem Posting nicht so herauslesen.
Wenn man den Betreff als zum Posting zugehörig betrachtet, dann schon.
Axel S. schrieb:>> Das würde ich aus seinem Posting nicht so herauslesen.>> Wenn man den Betreff als zum Posting zugehörig betrachtet, dann schon.
OK, stimmt auch wieder. Nun, genügend Vorschläge hat er, jetzt wär's
ohnehin an der Zeit, dass er sich selbst mal wieder äußert.
dl8dtl hat es schon gut zusammengefasst.
Kein gettext für die AVR-Klasse.
Daniels Makro-Magie halte ich für eine sehr gute Lösung für kleine
Projekte. Sie beantwortet meine Frage mit "Ja".
Hier mal die Ausgabe:
1
Hallo Welt!
2
Hello World!
3
???
Kennt jemand eine Implementierung von
> einen Hash des Textes und zusätzlich einen gewichteten Binärbaum zur> schnellen Suche in den Hashes.
?
Simpel schrieb:> Kein gettext für die AVR-Klasse.
Ahso, Du brauchst es also für AVR. Die Info fehlte bisher.
> Daniels Makro-Magie halte ich für eine sehr gute Lösung für kleine> Projekte.
Du wirst Dich noch wundern, wenn aus den zwei Hallo-Welt-Texten mal
dreißig oder vierzig auf einem AVR werden. Wenn Du da nicht mit PROGMEM
oder __flash arbeitest, wird Dir ruckzuck das RAM (ja, RAM!) ausgehen -
besonders, wenn Du beide Sprachvarianten (DE und EN) zur Laufzeit
gleichzeitig anbieten willst.
Etwas Optimierung ist also noch angesagt.
Simpel schrieb:> Aber ich habe heute Jeans an.
Du kapierst aber, dass der Wille der Leute, sich mit deinem Problem
zu befassen, nach derartig pampigen Antworten schnell gegen Null geht,
ja?
Stefan U. schrieb:> Der to hat doch ausdrücklich nach einer dynamischen Auswahl zur> Laufzeit gefragt!
Ok, überlesen, ist sogar einfacher:
#define L(a,b) (lang==de?(a):(b))
@MaWin
Und jetzt kommt spanisch dazu... (Mann stelle sich das bei vielen
übersetzungen vor).
Mein 2tes Makro hat einen Englisch fallback und toleriert vergessene
Übersetzungen. Um bei deinem Makro eine Sprache hinzuzufügen müsste man
bei jeder Übersetzung einen Eintrag ergänzen damit es wieder kompiliert,
und das kann bei vielen Einträgen schnell lästig werden. Bei mir kann
man nach und nach Übersetzungen ergänzen, plus man sieht für welche
Sprache ein Eintrag ist.
> Und jetzt kommt spanisch dazu
Ja, und die korrekte Formatierung von Zahlen, Datumswerten udn die
richtige Schreibrichtung für Asien.
Also wenn du ausgereifte i18n Frameworks verwenden willst, dann gehört
das doch eher auf einen PC statt auf einen Mikrocontroller. Oder
wenigstens etwas in der Leistungsklasse eines Raspberry Pi.
Beim Qt Framework haben die das elegant gelöst. Man schreibt seinen
Quelltext auf englisch und dann benutzt man ein Hilfsprogramm, das alle
relevanten Strings findet und dich dazu auffordert, die fehlenden
Übersetzungen einzugeben.
Ist vielleicht nicht perfekt Performant, aber wen interessiert das schon
bei einer Benutzeroberfläche?