Hallo zusammen,
In unsrerm aktuellen C-Programm für eine MCU werden Meldetexte in
englischer Sprache auf einem Textdisplay ausgegeben:
lcd_clear();
lcd_puts("Cylinder stroke");
lcd_goto (1,0);
lcd_puts("in mm:");
Jetzt kommt die Forderung, das Programm mehrsprachig zu gestalten.
Wie würde man soetwas am Besten umsetzen ? Es sind viele Texte
vorhanden.
char eng,span,potugisisch;
eng = 0;
span = 1;
portugisisch=0;
lcd_clear();
if (eng=1) lcd_puts("Cylinder stroke");
if (spanisch=1) lcd_puts("Text in Spanisch");
if (portugisisch=1) lcd_puts("Text in Portugisisch");
lcd_goto (1,0);
if (eng=1) lcd_puts("in mm:");
if (spanisch=1) lcd_puts("Text in Spanisch");
if (portugisisch=1) lcd_puts("Text in Portugisisch");
Gibt es keinen besseren Weg ?
Also mit so einer Art Texttabellen arbeiten, oder so ?
Bitte um Vorschläge...
Danke
Such mal nach der Stichworten:
Internationalisierung (I18N)
oder:
Lokalisierung (L10N)
Es gibt dazu einen Satz an Tools, da nicht nur Du ein Problem haben
könntest, sondern auch der Anwender.
Üblicherweise definiert man dann Strings bzw. arrays mit entsprechenden
Strings je Sprache und nutzt dann je nach Einstellung das eine oder
andere. (also keine hard codierten Texte mehr!)
Kommt ein wenig auf den verfügbaren Speicher an, ob man alle oder nur
eine/ausgewählte Sprache(n) mit installiert (linkt). Oder man macht das
über Header-files, d.h. je nach gewählter Sprache wird ein anderes
header-file genutzt.
... was Dir aber kein Tool abnimmt ist die Problematik, die sich daraus
ergibt, dass die eine Sprache "langatmig" ist - im wahrsten Sinne des
Wortes - und die andere kurz und prägnant.
Also ein Menü immer für den "ungünstigsten" Fall ausgelegt sein muss.
@ DirkF (Gast)
>Jetzt kommt die Forderung, das Programm mehrsprachig zu gestalten.>Wie würde man soetwas am Besten umsetzen ? Es sind viele Texte>vorhanden.>char eng,span,potugisisch;>eng = 0;>span = 1;>portugisisch=0;
so schon mal nicht ;-)
>lcd_clear();>if (eng=1) lcd_puts("Cylinder stroke");>if (spanisch=1) lcd_puts("Text in Spanisch");>if (portugisisch=1) lcd_puts("Text in Portugisisch");>lcd_goto (1,0);>if (eng=1) lcd_puts("in mm:");>if (spanisch=1) lcd_puts("Text in Spanisch");>if (portugisisch=1) lcd_puts("Text in Portugisisch");
Um Gottes Willen, NEIN!
>Gibt es keinen besseren Weg ?
Sicher. Große Frameworks ala QT bieten dafür integrierte Funktionen an.
Kleine Programme in reinem C müssen das per Hand machen.
>Also mit so einer Art Texttabellen arbeiten, oder so ?
Ja.
Ich hab das mal so gemacht. (Sind nur Ausschnitte)
globals.h
1
// display texts
2
3
typedefstruct{
4
PGM_Pmsg_netto;
5
PGM_Pmsg_brutto;
6
PGM_Pmsg_SPL;
7
PGM_Pmsg_UW1;
8
PGM_Pmsg_UW2;
9
PGM_Pmsg_PRF;
10
PGM_Pmsg_dis_normal;
11
PGM_Pmsg_dis_flip;
12
PGM_Pmsg_video_on;
13
PGM_Pmsg_video_off;
14
PGM_Pmsg_result;
15
PGM_Pmsg_halftime_A;
16
PGM_Pmsg_halftime_B;
17
PGM_Pmsg_penalty;
18
PGM_Pmsg_timeout;
19
PGM_Pmsg_half_time;
20
PGM_Pmenus[25];
21
}language_t;
Ein Struct, welches Zeiger auf alle Texte des Programms enthält.
Die Texte stehen dann in
globals.c
Am Ende gibt es ein Array von typ language_t, wo alle Sprachen
zusammengefasst sind, der Index gibt die Sprache an.
Im Programm hat man dann keine direkten Texte mehr, nur noch Verweise
auf die Member von language_t. Etwa so.
Das sieht durch die pgm_read_word() Funktion vom avr gcc ein wenig wild
aus, kann man aber mit einer modernen Compilerversion deutlich
entschärfen. Der Sprachenindex ist eine Variable.
Sicher nicht die perfekte Lösung (Weil man keine echten Texgte mehr im
Programm sieht), aber für kleine bis mittlere Sachen ein brauchbarer
Kompromiss.
DirkF schrieb:> In unsrerm aktuellen C-Programm für eine MCU werden Meldetexte in> englischer Sprache auf einem Textdisplay ausgegeben:> Jetzt kommt die Forderung, das Programm mehrsprachig zu gestalten.> Wie würde man soetwas am Besten umsetzen ? Es sind viele Texte> vorhanden.
Komische Frage, schließlich ist das kein ganz neues Problem. Natürlich
benutzt man dazu Lookup-Tables, ist doch logisch, naheliegend und
überaus trivial. Das einzige Problem ist hier der mit der Zahl der
unterstützten Sprachen wachsende Speicherbedarf für die konstanten
Strings und die konstanten Lookup-Tabellen. Also der Flash-Space.
Komplizierter ist schon, wenn es nicht nur darum geht, konstante Strings
in der richtigen Sprache auszugeben, sondern diese Strings auch noch
eingebettete variable Bestandteile haben und diese dann auch noch in der
"richtigen" (zur Sprache passenden) Lokalisierung ausgegeben werden
müssen.
Aber auch dann ist man weiter bei Lookup-Tabellen, nur daß diese auch
noch Funktions-Zeiger enthalten müssen. Letztlich (programmiertechnisch
gesehen) aber ebenfalls absolut trivialer Scheiß, nur viel Fleißarbeit.
Der Hauptaufwand entsteht bei der "Übersetzung". Wenn das nicht jemand
macht, der 1. versteht, worum es sachlich überhaupt geht, und 2. den für
die Anwendung einschlägigen Slang sowohl in der Quellsprache als auch in
der Zielsprache beherrscht, dann wird quasi unvermeidlich für den
Anwender unverständliche Scheiße dabei rauskommen.
Wir als Deutsche können das Ergebnis lausiger Lokalisierungen aus der
Quellsprache Englisch oft genug selber erleben, und das obwohl Englisch
und Deutsch vergleichsweise nahe Verwandte sind und es sicher in beiden
Sprachräumen kein Problem ist, einen Muttersprachler des jeweils anderen
aufzutreiben.
Bei "exotischere" Sprachen gibt es genau denselben Effekt, nur noch viel
stärker ausgeprägt.
In Linux wird das z.B. mit gettext gemacht
https://de.wikipedia.org/wiki/GNU_gettext
Im Prinzip gibt es nur die englische Ausgabe, wo dann mit einer
Stringersetzung die entsprechende Übersetzung ausgegeben wird.
Interessant wird das, wenn man die Grammatik (Reihenfolge von Elementen)
berücksichtigen muss. Also z.B. das Datumsformat "yyyy/dd/mm" (US)
gegenüber "dd.mm.yyyy" (DE).
Wenn man sowieso schon eine printf()-Bibliothek hat, kann man sich
daraus kleine Templates mit positionsgebundenen Argumenten bauen (z.B.
"%1$d/%2$d/%3$d" fuer englisches, "%3$d.%2$d.%1$d" für deutsches Datum).
Und zum Schluss: Portugiesisch schreibt man mit "ie" in der Mitte.
S. R. schrieb:> Wenn man sowieso schon eine printf()-Bibliothek hat, kann man sich> daraus kleine Templates mit positionsgebundenen Argumenten bauen (z.B.> "%1$d/%2$d/%3$d" fuer englisches, "%3$d.%2$d.%1$d" für deutsches Datum).
Aber das gibt es doch schon längst, nennt sich gettext und läuft auch
nicht nur unter Unix; inkl. Tools um die Übersetzungen zu pflegen.
Dabei dient eine Übersetzung als Schlüssel (muss nicht die Englisch
sein):
1
lcd_clear();
2
lcd_puts(_("Cylinder stroke"));
3
lcd_goto (1,0);
4
lcd_puts(_("in mm:"));
_ ist hier eine Funktion, die das übergebene Stringliteral als Schlüssel
für die Suche nach der entsprechenden Übersetzung sucht.
Vorteil ist, dass man auf eine bereits gut getestete Lösung zurück
greifen kann mit jeder Menge Tools und die meisten Softwareentwickler
werden diese Lösung auf anhieb verstehen.
Torsten R. schrieb:> Aber das gibt es doch schon längst, nennt sich gettext und läuft auch> nicht nur unter Unix; inkl. Tools um die Übersetzungen zu pflegen.
Richtig, allerdings habe ich gettext auf einem nackten AVR nicht zur
Verfügung, im Gegensatz zu printf. (Ich gehöre zu der Fraktion, die den
Wert von printf größer als dessen Codegröße schätzt.)
Hallo Torsten.
>>_ ist hier eine Funktion, die das übergebene Stringliteral als Schlüssel>>für die Suche nach der entsprechenden Übersetzung sucht.
Tja, aber dann wird meine Zykluszeit drastisch ansteigen, wenn er bei
jeder Textausgabe erst in einer "Datenbank" suchen muss.
Gruß Dirk
Dirk F schrieb:> Hallo Torsten.>>>_ ist hier eine Funktion, die das übergebene Stringliteral als Schlüssel>>>für die Suche nach der entsprechenden Übersetzung sucht.>> Tja, aber dann wird meine Zykluszeit drastisch ansteigen, wenn er bei> jeder Textausgabe erst in einer "Datenbank" suchen muss.
Naja, Du must die Daten ja nicht in einer "Datenbank" ablegen. Du
könntest Sie z.B. auch im Flash ablegen. Wenn Du deinem Linker erklären
kannst, dass er alle Duplikate von String-Literalen entfernen soll, dann
reicht sogar die Adresse des Literals als Key.
Wenn Du einfach gerne das Rad neu erfinden möchtest, dann mach das doch
einfach. Macht evtl. viel Spaß und man lernt ne Menge dabei ;-)
S. R. schrieb:> gegenüber "dd.mm.yyyy" (DE).
Guck dir mal die aktuelle Normung für Datumsformate an, als da wären
ISO 8601 und EN 28601. Die DIN 1355 aus dem Jahre 1943 mit deinem
DE-Datumsformat wurde schon vor 20 Jahren durch die EN 28601 mit u.a.
dem Format "yyyy-mm-dd" abgelöst.
Oder schreibst du sonst bei Zahlen auch die höchstwertige Stelle
irgendwo weit hinten? ;-)
Wolfgang schrieb:> Die DIN 1355 aus dem Jahre 1943 mit deinem> DE-Datumsformat wurde schon vor 20 Jahren durch die EN 28601 mit u.a.> dem Format "yyyy-mm-dd" abgelöst.
Das ist mir - hoeflich formuliert - schei..egal.
Ich will in einer Anzeige das Datum als dd.mm.yyyy sehen.(Punkt)
In der Verwendung als Dateinamen sieht das ganz anders aus, klar.
wendelsberg
Wolfgang schrieb:> Guck dir mal die aktuelle Normung für Datumsformate an, als da wären> ISO 8601 und EN 28601. Die DIN 1355 aus dem Jahre 1943 mit deinem> DE-Datumsformat wurde schon vor 20 Jahren durch die EN 28601 mit u.a.> dem Format "yyyy-mm-dd" abgelöst.
Schön für ISO 8601 und EN 28601, relativ egal für mich. Innerhalb
Deutschlands ist dd.mm.yyyy das gebräuchliche Format, also zeige ich ein
Datum in deutsch lokalisierter Software auch so an.
Offiziell hat auch UK das metrische System. Verkehrsschilder geben
Entfernungen trotzdem in Yards und Meilen an.
wendelsberg schrieb:> In der Verwendung als Dateinamen sieht das ganz anders aus, klar.
Was hat die DIN ISO 8601 jetzt speziell mit Dateinamen zu tun.
Aber natürlich kannst du auf deiner Anzeige das Datum so darstellen, wie
du möchtest, auch mit "(Punkt)".
Wolfgang schrieb:> wendelsberg schrieb:>> In der Verwendung als Dateinamen sieht das ganz anders aus, klar.>> Was hat die DIN ISO 8601 jetzt speziell mit Dateinamen zu tun.
Dort verwende ich yyyy_mm_dd.xxx weil diese sich von selbst sortieren.
Die Verwendung von:
S. R. schrieb:> Datumsformat "yyyy/dd/mm" (US)
ist allerdings vollkommener Bloedsinn, deswegen kommt das fuer mich
nirgendwo in Frage.
wendelsberg
Hallo zusammen,
also das hört sich ja alles recht kompliziert an. Es schein so zu sein,
dass es keine fertige Lösung gibt, um die Sprache während der Laufzeit
umzuschalten.
Ich könnte auch damit leben, wenn für eine andere Sprache das Programm
neu kompiliert werden muss.
Irgendwie bekomme ich die Syntax aber für so etwas nich hin:
DirkF schrieb:> Wer kann helfen ?
Du dir selbst.
Was ist der augenfälligste Unterschied zwischne
1
#define TEXT1 Text1_auf_spanisch
und
1
#define Text1 "Text1_auf_deutsch"
Welches ist die richtige Version?
Ersetz mal in beiden Fällen in
1
lcd_puts(TEXT1);
das Makro.
Das eine mal kommt raus
1
lcd_puts(Text1_auf_spanisch);
und das andere mal kommt raus
1
lcd_puts("Text1_auf_deutsch");
(Makros sind nur Textersetzungen. Ein Text wird an der verwendenden
Stelle durch einen anderen Text ersetzt)
Welche von beiden Versionen ist syntaktisch korrekt und warum?
Wie muss daher das Makro aussehen?
(Wenn du mit Makros arbeitest und nicht verstehst, warum du einen Fehler
kriegst, dann mach ersetz einfach an der verwendenden Stelle den
Makronamen mit dem beim Makro angegebenen Ersatztext und sieh dir dieses
Ergebnis an, was daran falsch ist. Was anderes machen Präprozessor und
Compiler auch nicht. D.h.: im wesentlichen)
DirkF schrieb:> Sorry bin halt noch nicht so gut im C-Programmieren....
Hat mit 'gut sein' eher weniger zu tun.
Sondern mit 'gut bzw. aufmerksam beobachten'.
Aber das wirst du schon noch bis zum Erbrechen feststellen, dass 70% in
der Entwicklungsarbeit darin besteht, Dinge zu bemerken, die Lieschen
Müller nicht sieht.
Hallo Spock,
danke für Deinen Vorschlag.
Sehe ich dass richtig, dass bei Deiner Lösung die SPrache auch während
der Laufzeit umgestellt werden könnte, also nicht nur vor dem
Compilerlauf ?
DirkF schrieb:> Hallo Spock,> danke für Deinen Vorschlag.> Sehe ich dass richtig, dass bei Deiner Lösung die SPrache auch während> der Laufzeit umgestellt werden könnte, also nicht nur vor dem> Compilerlauf ?
Schon.
Aber auf Kosten von Speicher und Laufzeit.
Auf einem PC kein Problem. Auf einem kleinen µC wirds das aber ganz
schnell.
@ Karl Heinz (kbuchegg) (Moderator)
>Aber auf Kosten von Speicher und Laufzeit.
Oh WEHH!!
>Auf einem PC kein Problem. Auf einem kleinen µC wirds das aber ganz>schnell.
Laute, übertreibt's mal nicht! Auch "kleine" uC haben heute ziemlich
viel Speicher! Wir reden hier nicht über 1kB Flash PIC12 oder so!
Diese Methode läuft prima und ist kaum langsamer als ein normales
Printf!
Beitrag "Re: C: Programm mit Textanzeige für mehrere Sprachen"
Dass man für jede Sprache nochmal grob den gleichen Spreicher für die
Texte braucht ist wohl klar. So what! Was kostet ein 128kB Flash uC
heute?
Wenn es nicht zur Laufzeit geschehen muß, ist die Sache nicht gewaltig
aufwendig. Bei mir gibt es einfach eine Datei texts_deu.h, die
mitgelinkt wird. Bei einer anderen Sprachversion eine texts_eng.h. Darin
steht etwas wie:
1
constflashcharMT_NULL[]={'\0'};
2
constflashcharMT_RETURN[]="zurück";
3
constflashcharMT_YES[]="Ja";
4
constflashcharMT_NO[]="Nein";
5
constflashcharMT_CANCEL[]="Abbrechen";
6
constflashcharMT_ABORT[]="Abbrechen";
7
constflashcharMT_RETRY[]="Nochmal versuchen";
8
constflashcharMT_CONTINUE[]="Weiter";
9
constflashcharMT_PREFSMENU[]="Einstellungen";
10
constflashcharMT_INFOSCREEN[]="Firmware-Info";
11
constflashcharMT_PRESSKEYTOLEAVE[]="Beenden mit Taster";
Und im Programm die entsprechenden printf-Anweisungen. Das hat selbst
bei einem rein deutschsprachigen Programm den Vorteil, daß im Quelltext
die kurzen englischen Bezeichner stehen (kein
Deutsch-Englisch-Mischmasch) und die Quelltexte dann besser lesbar sind.
// ansatz für eine bei der compilation berücksichtigte sprache
// vorteil: es steht nur das im code was drin stehen soll
// nachteil: kein wechsel durch anwender nach kompilation
im makefile dann das compiler flagg -D entsprechend
für deutsch
-DDEUTSCH
für englisch
-DENGISCH
für sonstige ( oder anpassen )
-DENGLISCH
language_paket.h
Karl H. schrieb:> Schon.> Aber auf Kosten von Speicher und Laufzeit.>> Auf einem PC kein Problem. Auf einem kleinen µC wirds das aber ganz> schnell.
Speicher wird's das Gleiche benötigen, Laufzeit kommt auf den Controller
drauf an, man kann's aber natürlich noch optimieren.
Der Vorteil ist schon mal falls irgendwo die Übersetzung nicht da ist
das dann wenigstens auf das Fallback zurückgegriffen wird.
Effizienter kann man's bezüglich dem Speicher nicht mehr gestalten (jede
extra IF Abfrage wäre zudem zusätzlicher Speicherverbrauch), aber wie
schon von jemand anderem erwähnt -- in diesem Fall sind die paar Bytes
mehr oder weniger absolut kein Grund.
Man kann auch mit einem Index arbeiten und sich das strcmp sparen, ist
dann halt nicht so komfortabel zum Programmieren...
#include <stdio.h>
#define HALLO_WELT 0
struct lang {
char *german;
char *english;
} foo[]={
[HALLO_WELT]={"hallo", "Welt"}
};
#define GERMAN 0
#define ENGLISH 1
int language=GERMAN;
int printf_t(int idx) {
switch(language) {
case GERMAN:
printf("%s\n",foo[idx].german);
break;
case ENGLISH:
printf("%s\n",foo[idx].english);
break;
}
return 0;
}
int main() {
printf("%s\n", foo[HALLO_WELT].german);
printf("%s\n", foo[HALLO_WELT].english);
printf_t(HALLO_WELT);
language=ENGLISH;
printf_t(HALLO_WELT);
return 0;
}
push#
noch ein vorteil
kommt jetzt einer weitere sprache dazu, ergänzt du das
language_paket.h
1
..
2
..
3
4
#ifdef DEUTSCH
5
#define Cylinder_stroke "Zylinderhub"
6
#define in_mm "in mm:"
7
// usw
8
#endif
9
10
#ifdef ENGLISCH
11
#define Cylinder_stroke "Cylinder Stroke"
12
#define in_mm "in mm:" // oder andere einheit
13
// mit umrechnungsfunktion in inches
14
// 100 mm sind ca 3,93701 inches also
15
// du brauchst dann float! als ergebnis
16
// formel mm / 25,4 = inches
17
#define mm2inch .....
18
19
// usw
20
#endif
21
22
23
#ifdef PORTUGAL
24
#define Cylinder_stroke "Cylinder Stroke"
25
#define in_mm "in mm:" // oder andere einheit wie oben
26
#endif
und am programm quelltext muss nichts geändert werden.
den ansatz für in der laufzeit änderebare sprache müsstest du das ganze
soweit abändern, dass du dir ein enum anlegst:
> char eng,span,potugisisch;> eng = 0;> span = 1;> portugisisch=0;
so denke ich würde ich es nicht machen, da würde ich enum verwenden
1
enumlanguage{eng,span,portugisisch=0};
und über eine 'setup variable' am anfang des programms die sprache
einstellen, den text dann über ein array of struct vorhalten und über
den idx ( index ) des gewählten enum ausgeben.
ändert der benutzer über setup funktion die sprache, ändert sich nur der
idx zur passenden array of struct passage.
..
.. schrieb:> dann über ein array of struct vorhalten
Nö.
Man muß nur ein einziges Struct vorhalten, das alle Texte in einer
Sprache enthält und beim Start aus dem Flash geladen wird. Dann spart
man sich die zusätzliche Indizierung.
Also so:
1
printf(txt.abort);
und nicht
1
printf(txt[lang].abort);
Es sei denn, man ist extrem eng im SRAM und das txt-Struct paßt nicht
mehr hinein - dann muß man den Index die ganze Zeit mitschleppen.
speicher optimierung:
du könntest dann mit dynamischer speicherverwaltung arbeiten,
und zur laufzeit und bedarfszeit mit malloc() / calloc()
realloc() arbeiten, und dabei immer wieder den alten speicher
entweder neu nutzen, oder freigeben und neu anfordern.
somit ist immer nur ein sprachpaket im speicher.
ich würde sogar ein standard sprachpaket setzen ( englisch )
und bei bedarf den belegeten speicher mit deutsch neu belegen..
es macht also keinen unterschied, nur das file selbst wird
etwas grösser für das flash, was aber ins ram oder auf dem stack
kommt, entscheidest dann du.
..
Walter T. schrieb:> .. schrieb:>> dann über ein array of struct vorhalten>> Nö.
hast ja recht, bin doch schon bei der speicher optimierung,
eins nach dem anderen ;-)
..
Hallo zusammen,
vielen Dank für die zahlreichen Antworten. Hat mir sehr geholfen.
Habe mich jetzt für diese Version entschieden, ist leicht umzusetzen.
Dann muss halt eine Sonderversion für eine andere Sprache compiliert
werden und im Werk vor Auslieferung neu geflashed werden.
Kann man dann auch so bequem an ein Übersetzungsbüro zum Ausfüllen
senden.
Und am wichtigsten: Der Quelltext bleibt gut lesbar, auch für einen
Kollegen in der Urlaubszeit !!!!
//Nur die gewünschte Sprache auskommentieren und neu kompilieren
#define ENGLISH // Standardsprache
//#define SPANISH
//#define PORTUGUESE
//#define GERMAN
#ifdef ENGLISH //
// Diese Spalte Hier Sprachtext eintragen
// nicht verändern! "...max.20 Zeichen..."
#define temperature "temperature "
#define in_Celsius "in Celsius:"
#define Contact_force "Contact force"
#endif
lcd_puts(temperature);
Gruß Dirk
DirkF schrieb:>> //Nur die gewünschte Sprache auskommentieren und neu kompilieren
das könntest du mit dem compiler flag -D erledigen Dirk.
-D steht für Define und macht nichts anderes als du
deine zu brauchende Sprache im programm code zu definieren mit
#define DEUTSCH
=
gcc -DDeutsch ......
Beitrag "c code beta, release je nach gesetzter auswahl compilieren"
und es muss gar keiner an den code
sondern nur das werk das makefile aufrufen.
wenn du ein datenbankmangement einsetzt, kannst du über die adresse
des kunden und des auftrags sogar ein automatisches makefile anlegen.
so mache ich das z.b. mit serialisierung / unique id
-DAUFTRAG
Falk B. schrieb:> Was kostet ein 128kB Flash uC> heute?
na das vierfache oder mehr
Arduino m328p 5,- (oder viel billiger)
Arduino mighty m1284p 22,-€ +FTDI
Wolfgang schrieb:> S. R. schrieb:>> gegenüber "dd.mm.yyyy" (DE).>> Guck dir mal die aktuelle Normung für Datumsformate an, als da wären> ISO 8601 und EN 28601. Die DIN 1355 aus dem Jahre 1943 mit deinem> DE-Datumsformat wurde schon vor 20 Jahren durch die EN 28601 mit u.a.> dem Format "yyyy-mm-dd" abgelöst.
ISO8601 beschreibt ein Format für den Datenaustausch. Damit sind
Maschinen gemeint, die Daten z.B. über XML, CSV oder fixed Format
austauschen, keine Menschen. Für den Menschen hat ein Datum in der
landesüblichen Form ausgegeben zu werden.