mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Mehrsprachige Strings ohne Hilfsprogramme?


Autor: Simpel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie lassen sich Zeichenketten dynamisch mehrsprachig verwenden, ohne im 
Quelltext mit Indizes (kryptisch) oder Symbolen (aufwendig) arbeiten zu 
müssen?
lang = "de";
print("Hallo Welt!"); // Hallo Welt!
lang = "en";
print("Hallo Welt!"); // Hello world!

Als alternative Syntax evtl. direkt mit Übersetzung(en)
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?

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meinst du jetzt dynamisch zur Compile-zeit oder zur Laufzeit?

Autor: Simpel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
print("Hallo!"); // Hello! (aus gleichen Daten)
print("Du bist {} schlaue(r) Programmierer!"); // "10 smart programmers."

Autor: Peter II (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Amateur (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fix wenn über Makros geregelt oder such mal nach I18N und L10N.

Autor: Simpel (Gast)
Datum:

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

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Simpel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Cyblord -. (cyblord)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cyblord -. schrieb:
> Du bist lustig.

ich finde, er hat recht ;)

Autor: c-hater (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Simpel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)?

Autor: Daniel A. (daniel-a)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Getestet, nur ne Idee. Wie war das noch gleich mit designated 
initialisern bei arrays in c?
#ifndef LOCALISATION_H
#define LOCALISATION_H

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

enum langs {
  DE, EN,
  LANG_COUNT
};

extern enum langs lang;

#endif
#include <stdio.h>
#include "localisation.h"

enum langs lang = EN;

int main(){
  puts(L((
    [EN] = "Hello World!",
    [DE] = "Hallo Welt!"
  )));
  return 0;
}

: Bearbeitet durch User
Autor: c-hater (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: W.S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sven B. (scummos)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Daniel A. (daniel-a)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe mein Bespiel noch etwas verbessert.

Autor: Bastler (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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]);

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht 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".

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?"
7.1.3 Reserved identifiers
[…]
— All identifiers that begin with an underscore and either an uppercase letter or another
underscore are always reserved for any use.
[…]
2 No other identifiers are reserved. If the program declares or defines an identifier in a
context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved
identifier as a macro name, the behavior is undefined.

Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Simpel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
Hallo Welt!
Hello World!
???

Kennt jemand eine Implementierung von
> einen Hash des Textes und zusätzlich einen gewichteten Binärbaum zur
> schnellen Suche in den Hashes.
?

Autor: Simpel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also: Danke, Daniel!

Autor: Frank M. (ukw) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Hans-Georg L. (h-g-l)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Simpel (Gast)
Datum:

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

Nein. Aber ich habe heute Jeans an.

Autor: MaWin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel A. schrieb:
> Ich habe mein Bespiel noch etwas verbessert.

Den Vorgänger fand ich passender.

Einfacher:
#define de 1
#define en 2
#define LANG de
#if LANG == de
#define L(a,b) (a)
#endif
#if LANG == en
#define L(a,b) (b)
#endif

// und im Programm
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.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der to hat doch ausdrücklich nach einer dynamischen Auswahl zur Laufzeit 
gefragt!

Autor: klick klack (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wo bitte ist das Problem ?

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: MaWin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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))

Autor: Daniel A. (daniel-a)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.