Mehrsprachige Strings ohne Hilfsprogramme?

Gast #4710934
Lesenswert?
• ▲
▼
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?
Gast #4710954
Lesenswert?
• ▲
▼
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:
1
print("Hallo!"); // Hello! (aus gleichen Daten)
2
print("Du bist {} schlaue(r) Programmierer!"); // "10 smart programmers."
Gast #4710974
Lesenswert?
• ▲
▼
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.
Gast #4710987
Lesenswert?
• ▲
▼
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.
Gast #4710993
Lesenswert?
• ▲
▼
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!
Gast #4711060
Lesenswert?
• ▲
▼
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)?
#4711070
Lesenswert?
• ▲
▼
Getestet, nur ne Idee. Wie war das noch gleich mit designated 
initialisern bei arrays in c?
1
#ifndef LOCALISATION_H
2
#define LOCALISATION_H
3

4
#define UNPACK(...) __VA_ARGS__
5
#define L(x) (((const char*const [LANG_COUNT]){UNPACK x})[lang])
6

7
enum langs {
8
  DE, EN,
9
  LANG_COUNT
10
};
11

12
extern enum langs lang;
13

14
#endif
1
#include <stdio.h>
2
#include "localisation.h"
3

4
enum langs lang = EN;
5

6
int main(){
7
  puts(L((
8
    [EN] = "Hello World!",
9
    [DE] = "Hallo Welt!"
10
  )));
11
  return 0;
12
}
Gast #4711121
Lesenswert?
• ▲
▼
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.
Gast #4711145
Lesenswert?
• ▲
▼
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.
#4711418
Lesenswert?
• ▲
▼
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?
Gast #4711609
Lesenswert?
• ▲
▼
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]);
(Firma: JDCC) #4711625
Lesenswert?
• ▲
▼
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.
Gast #4711638
Lesenswert?
• ▲
▼
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.
#4711639
Lesenswert?
• ▲
▼
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".
Moderator Persönliche Seite #4711727
Lesenswert?
• ▲
▼
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.
Gast #4711880
Lesenswert?
• ▲
▼
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.
?
Moderator Persönliche Seite #4711889
Lesenswert?
• ▲
▼
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.
#4711914
Lesenswert?
• ▲
▼
Hier eine weitere Möglichkeit mit X-macros:

(ungetestet und hoffentlich ohne Fehler)

texte.h

ENTRY( MSG_1, "Nachricht 1", "Message 1" )
ENTRY( MSG_2, "Nachricht 2", "Message 2" )
ENTRY( MSG_3, "Nachricht 3", "Message 3" )
ENTRY( MSG_4, "Nachricht 4", "Message 4" )


main.c

// enumeration
#define ENTRY(a, b, c) a,
enum MESSAGES {
#include "texte.h"
NUM_MESSAGES
};
#undef ENTRY

// Deutsche texte
#define ENTRY(a, b, c) b,
char * lang_de[] = {
#include "texte.h"
};
#undef ENTRY

// Englische Texte
#define ENTRY(a, b, c) c,
char * lang_en[] = {
#include "texte.h"
};
#undef ENTRY
Gast #4712388
Lesenswert?
• ▲
▼
Daniel A. schrieb:
> Ich habe mein Bespiel noch etwas verbessert.

Den Vorgänger fand ich passender.

Einfacher:
1
#define de 1
2
#define en 2
3
#define LANG de
4
#if LANG == de
5
#define L(a,b) (a)
6
#endif
7
#if LANG == en
8
#define L(a,b) (b)
9
#endif
10

11
// und im Programm
12
printf(L("Hallo Welt","Hello world"));

Guido schrieb:
> Man kann die Sprachdateien auch in XML vorhalten,

Ich vermute mal, wr meint Microcontrollerprogramme: Kein Dateisystem, 
kein Xerces.
#4712589
Lesenswert?
• ▲
▼
@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.
Gast #4712738
Lesenswert?
• ▲
▼
> 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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren