Forum: Mikrocontroller und Digitale Elektronik Mehrsprachige Strings ohne Hilfsprogramme?


von Simpel (Gast)


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?

von Guido (Gast)


Lesenswert?

Meinst du jetzt dynamisch zur Compile-zeit oder zur Laufzeit?

von Simpel (Gast)


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."

von Peter II (Gast)


Lesenswert?

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

von Amateur (Gast)


Lesenswert?

Fix wenn über Makros geregelt oder such mal nach I18N und L10N.

von Simpel (Gast)


Lesenswert?

Danke erstmal, http://www.mikrocontroller.net/search?query=gettext 
ergibt schon einige Threads.

von Guido (Gast)


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.

von Simpel (Gast)


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.

von Cyblord -. (cyblord)


Lesenswert?

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.

von Guido (Gast)


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!

von Guido (Gast)


Lesenswert?

Cyblord -. schrieb:
> Du bist lustig.

ich finde, er hat recht ;)

von c-hater (Gast)


Lesenswert?

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.

von Simpel (Gast)


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)?

von Daniel A. (daniel-a)


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
}

: Bearbeitet durch User
von c-hater (Gast)


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.

von W.S. (Gast)


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.

von Sven B. (scummos)


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?

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Ich habe mein Bespiel noch etwas verbessert.

von Bastler (Gast)


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]);

von Joachim D. (Firma: JDCC) (scheppertreiber)


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.

von Guido (Gast)


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.

von Axel S. (a-za-z0-9)


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".

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Simpel (Gast)


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.
?

von Simpel (Gast)


Lesenswert?

Also: Danke, Daniel!

von Frank M. (ukw) (Moderator) Benutzerseite


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.

von Hans-Georg L. (h-g-l)


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

: Bearbeitet durch User
von Simpel (Gast)


Lesenswert?

Frank M. schrieb:
> Ahso, Du brauchst es also für AVR. Die Info fehlte bisher.

Nein. Aber ich habe heute Jeans an.

von MaWin (Gast)


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.

von Stefan F. (Gast)


Lesenswert?

Der to hat doch ausdrücklich nach einer dynamischen Auswahl zur Laufzeit 
gefragt!

von klick klack (Gast)


Lesenswert?

Wo bitte ist das Problem ?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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?

von MaWin (Gast)


Lesenswert?

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))

von Daniel A. (daniel-a)


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.

von Stefan F. (Gast)


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?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.