Difference between revisions of "Translations / i18n / localizations for programs/de"

From Free Pascal wiki
Jump to navigationJump to search
m (Text replace - "Delphi>" to "syntaxhighlight>")
m (Fixed syntax highlighting; deleted category included in page template)
 
(21 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
{{Translations_/_i18n_/_localizations_for_programs}}
 
{{Translations_/_i18n_/_localizations_for_programs}}
 
+
<br>
 +
Zurück zu den [[Additional information/de|Zusätzlichen Informationen]].<br>
 +
<br>
 +
__TOC__
 +
<br>
 
== Überblick ==
 
== Überblick ==
  
 
Hier wird gezeigt, wie Sie in Ihrem Programm verschiedene Strings für verschiedene Sprachen (Englisch, Deutsch, Chinesisch...) ausgeben können.
 
Hier wird gezeigt, wie Sie in Ihrem Programm verschiedene Strings für verschiedene Sprachen (Englisch, Deutsch, Chinesisch...) ausgeben können.
 
Dazu müssen Sie nur Folgendes tun: Einen ''resourcestring'' für jeden String, den Sie ausgeben möchten hinzufügen, kompilieren um .rst und/oder .po Dateien zu erhalten (kann die IDE automatisch erzeugen), für jede Sprache eine .po Datei erstellen (dazu gibt es graphische Programme), und zuletzt die richtige .po Datei beim Programmstart mittels der Unit ''translations'' laden.
 
Dazu müssen Sie nur Folgendes tun: Einen ''resourcestring'' für jeden String, den Sie ausgeben möchten hinzufügen, kompilieren um .rst und/oder .po Dateien zu erhalten (kann die IDE automatisch erzeugen), für jede Sprache eine .po Datei erstellen (dazu gibt es graphische Programme), und zuletzt die richtige .po Datei beim Programmstart mittels der Unit ''translations'' laden.
 +
 +
== Schnell i18n ==
 +
 +
Dies soll ein Schnelleinstig sein, der Übersetzungen kurz und bündig  vorstellt.
 +
 +
===poedit===
 +
Das bekannteste Werkzeug ist [https://poedit.net/ Poedit]. Poedit ist ein Übersetzungswerkzeug, das .po und .mo-Dateien erstellt.
 +
 +
===Resourcestrings übersetzen===
 +
Auf folgende Weise wird ein Ressourcestring einer Unit erzeugt:
 +
 +
<syntaxhighlight lang=pascal>
 +
resourcestring
 +
  Caption1 = 'Ein Text';
 +
  HelloWorld1 = 'Hello World';</syntaxhighlight>
 +
 +
Resourcestrings verhalten sich wie normale Stringkonstanten, das heißt, man kann sie einfach so verwenden:
 +
<syntaxhighlight lang=pascal>Label1.Caption := HelloWorld1;</syntaxhighlight>
 +
 +
===Beim Compilieren .po-Dateien erstellen===
 +
 +
Wenn man i18n in der Lazarus IDE einschaltet werden Resourcestrings zu .po-Dateien compiliert. i18n findet man unter Projekt > Projekteinstellungen > i18n > i18n einschalten. Beim erneuten compilieren werden die .po-Dateien aktualisiert. Man kann ebenfalls das Verzeichnis festlegen, in dem die .po-Dateien gespeichert werden. (Empfohlen wird '''po files''')
 +
 +
Die empfohlene voreingestellte Übersetzung oder Applications-Sprache sollte Englisch sein, da sie geladen wird, wenn keine passende Übersetzung gefunden wird.
 +
 +
Ist die '''project1.po'''-Datei erstellt, muss man sie in '''project1.<Sprache>.po''' umbenennen, wobei <Sprache> für das Kürzel der jeweiligen Sprache steht z.B: '''project1.es.po''' für Spanisch. Diese muss man nun zum Übersetzen geben.
 +
 +
Die Verzeichniss-Struktur sieht nun ungefähr so aus:
 +
 +
<syntaxhighlight lang=pascal>
 +
project1\po_files\
 +
project1\po_files\project1.po
 +
project1\po_files\project1.es.po
 +
</syntaxhighlight>
 +
 +
===.po-Dateien umwandeln zu .mo-Dateien===
 +
 +
Wenn die .po-Datei fertig übersetzt ist, sollte man sie in eine .mo-Datei compilieren, da dieses Binärformat schneller geladen wird. Für die Konvertierung in das .mo-Format eignet sich z.B. [https://poedit.net/ Poedit] ( Datei -> MO-Datei erstellen ... ).
 +
 +
===Automatische Übersetzung===
 +
 +
Wenn alle .mo-Dateien fertig sind, werden sie in das Verzeichnis '''locale''' oder '''languages''' direkt unterhalb des Anwendungsprogramms gespeichert. Dann wird noch die Unit '''DefaultTranslator''' eingebunden und das war schon alles. Die Übersetzung erfolgt automatisch.
 +
 +
<syntaxhighlight lang=pascal>
 +
uses
 +
  DefaultTranslator;
 +
</syntaxhighlight>
 +
 +
Es sollten nur die .mo-Dateien im '''locale''' or '''languages''' Verzeichnis ausgeliefert werden, da die .po-Dateien nur für die Anfertigung der Übersetzung und die Compilierung in .mo-Dateien benötigt werden.
 +
 +
Die Verzeichnissstruktur wird so aussehen:
 +
 +
<syntaxhighlight lang=pascal>
 +
project1\project1.exe
 +
project1\locale\
 +
project1\locale\project1.mo
 +
project1\locale\project1.es.mo
 +
</syntaxhighlight>
 +
 +
==Zusätzliche Informationen==
  
 
== Datums-, Zeit- und Zahlenformat ==
 
== Datums-, Zeit- und Zahlenformat ==
  
 
In der Unit [http://www.freepascal.org/docs-html/rtl/sysutils/index.html sysutils] sind verschiedene Format-Zeichen definiert, mit denen Datum und Zeit oder Dezimalzahlen formatiert werden. Unter POSIX-konformen Betriebssystemen wie Linux oder Mac OS X können diese von der Unit <nowiki>clocale</nowiki> automatisch aus der Standard C Library übernommen werden. Dazu muss die Unit nur in das Programm (.lpr) aufgenommen werden:
 
In der Unit [http://www.freepascal.org/docs-html/rtl/sysutils/index.html sysutils] sind verschiedene Format-Zeichen definiert, mit denen Datum und Zeit oder Dezimalzahlen formatiert werden. Unter POSIX-konformen Betriebssystemen wie Linux oder Mac OS X können diese von der Unit <nowiki>clocale</nowiki> automatisch aus der Standard C Library übernommen werden. Dazu muss die Unit nur in das Programm (.lpr) aufgenommen werden:
<syntaxhighlight>uses
+
 
 +
<syntaxhighlight lang=pascal>
 +
uses
 
   {$IFDEF UNIX}
 
   {$IFDEF UNIX}
 
   {$IFDEF UseCThreads}cthreads,{$ENDIF}
 
   {$IFDEF UseCThreads}cthreads,{$ENDIF}
Line 22: Line 88:
  
 
Eine Definition sieht so aus:
 
Eine Definition sieht so aus:
<syntaxhighlight>resourcestring
+
<syntaxhighlight lang=pascal>
 +
resourcestring
 
   Caption1 = 'Irgendein Text';
 
   Caption1 = 'Irgendein Text';
 
   HelloWorld1 = 'Hello World';</syntaxhighlight>
 
   HelloWorld1 = 'Hello World';</syntaxhighlight>
  
 
Nun kann man sie wie normale String-Konstanten verwenden. Wurden sie bereits übersetzt, wird nicht mehr der originale Inhalt sondern die Übersetzung zugewiesen:
 
Nun kann man sie wie normale String-Konstanten verwenden. Wurden sie bereits übersetzt, wird nicht mehr der originale Inhalt sondern die Übersetzung zugewiesen:
<syntaxhighlight>Label1.Caption := HelloWorld1;</syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>Label1.Caption := HelloWorld1;</syntaxhighlight>
  
 
Wenn der FPC eine Unit mit einem resourcestring-Abschnitt übersetzt, erzeugt er automatisch eine Datei '''unitname.rst''', in der alle Resourcestring-Daten (Name + Inhalt) zu dieser Unit gespeichert werden.
 
Wenn der FPC eine Unit mit einem resourcestring-Abschnitt übersetzt, erzeugt er automatisch eine Datei '''unitname.rst''', in der alle Resourcestring-Daten (Name + Inhalt) zu dieser Unit gespeichert werden.
Line 64: Line 132:
  
 
Mehrfache Einträge treten dann auf, wenn derselbe Text für verschiedene Ressourcenstrings als Übersetzung steht. Ein Beispiel dafür findet man in der Datei 'lazarus/ide/lazarusidestrconst.pas' für den 'Gutter' String:
 
Mehrfache Einträge treten dann auf, wenn derselbe Text für verschiedene Ressourcenstrings als Übersetzung steht. Ein Beispiel dafür findet man in der Datei 'lazarus/ide/lazarusidestrconst.pas' für den 'Gutter' String:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
   dlfMouseSimpleGutterSect = 'Gutter';
 
   dlfMouseSimpleGutterSect = 'Gutter';
 
   dlgMouseOptNodeGutter = 'Gutter';
 
   dlgMouseOptNodeGutter = 'Gutter';
Line 70: Line 139:
 
   dlgAddHiAttrGroupGutter  = 'Gutter';   
 
   dlgAddHiAttrGroupGutter  = 'Gutter';   
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
Eine konvertierte .rst-Datei für diese Ressourcenstrings würde ähnlich aussehen wie die folgende .po-Datei:
 
Eine konvertierte .rst-Datei für diese Ressourcenstrings würde ähnlich aussehen wie die folgende .po-Datei:
  
Line 101: Line 171:
 
(Das folgende Beispiel wurde aus der Lazarus history entnommen)<br>
 
(Das folgende Beispiel wurde aus der Lazarus history entnommen)<br>
 
Wenn z. B. ein Ressourcenstring anfänglich wie folgt definiert war:
 
Wenn z. B. ein Ressourcenstring anfänglich wie folgt definiert war:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
   dlgEdColor = 'Syntax highlight';
 
   dlgEdColor = 'Syntax highlight';
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
Dies erzeugt einen ähnlichen .po Eintrag wie diesen:
 
Dies erzeugt einen ähnlichen .po Eintrag wie diesen:
 
  #: lazarusidestrconsts.dlgedcolor
 
  #: lazarusidestrconsts.dlgedcolor
Line 113: Line 185:
 
  msgstr "Color"
 
  msgstr "Color"
 
Angenommen, dass zu einem späteren Zeitpunkt der Ressourcenstring geändert würde in
 
Angenommen, dass zu einem späteren Zeitpunkt der Ressourcenstring geändert würde in
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
   dlgEdColor = 'Colors';
 
   dlgEdColor = 'Colors';
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 120: Line 193:
 
  msgid "Colors"
 
  msgid "Colors"
 
  msgstr ""
 
  msgstr ""
Beachten Sie, dass zwar die ID 'lazarusidestrconsts.dlgedcolor' gleich geblieben ist, sich aber der String geändert hat von 'Syntax highlight' zu 'Colors', weil der String bereits überstzt war könnte die alte Übersetzung nicht zu der neuen Bedeutung passen. Und wirklich, für unseren neuen String wäre im Spanischen wahrscheinlich 'Colores' die bessere Übersetzung.  
+
Beachten Sie, dass zwar die ID 'lazarusidestrconsts.dlgedcolor' gleich geblieben ist, sich aber der String geändert hat, von 'Syntax highlight' zu 'Colors'. Weil der String bereits übersetzt war, könnte die alte Übersetzung nicht zu der neuen Bedeutung passen. Und wirklich, für unseren neuen String wäre im Spanischen wahrscheinlich 'Colores' die bessere Übersetzung.  
 
Das automatische Aktualisierungwerkzeug erkennt diese Situation und erzeugt einen Eintrag wie diesen:
 
Das automatische Aktualisierungwerkzeug erkennt diese Situation und erzeugt einen Eintrag wie diesen:
 
  #: lazarusidestrconsts.dlgedcolor
 
  #: lazarusidestrconsts.dlgedcolor
Line 139: Line 212:
 
Damit die Formulare zur Laufzeit tatsächlich übersetzt werden, müssen Sie der Objektvariablen 'LRSTranslator' (definiert in LResources) im Abschnitte initialization einer Ihrer Units ein Translator-Objekt zuweisen
 
Damit die Formulare zur Laufzeit tatsächlich übersetzt werden, müssen Sie der Objektvariablen 'LRSTranslator' (definiert in LResources) im Abschnitte initialization einer Ihrer Units ein Translator-Objekt zuweisen
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
...
 
...
 
uses
 
uses
Line 152: Line 225:
 
<s>Allerdings gibt es derzeit in der LCL keine TPoTranslator - Klasse (das ist eine Klasse, die unter Verwendung von .po Dateien übersetzt). Hier folgt eine mögliche Implementierung (teilweise entlehnt aus DefaultTranslator.pas in der LCL):</s>Der folgende Code wird nicht mehr länger gebraucht, wenn Sie einen aktuellen Lazarus 0.9.29 Snapshot einsetzen. Fügen Sie einfach 'DefaultTranslator' zur Uses-Klausel hinzu.
 
<s>Allerdings gibt es derzeit in der LCL keine TPoTranslator - Klasse (das ist eine Klasse, die unter Verwendung von .po Dateien übersetzt). Hier folgt eine mögliche Implementierung (teilweise entlehnt aus DefaultTranslator.pas in der LCL):</s>Der folgende Code wird nicht mehr länger gebraucht, wenn Sie einen aktuellen Lazarus 0.9.29 Snapshot einsetzen. Fügen Sie einfach 'DefaultTranslator' zur Uses-Klausel hinzu.
  
 
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
unit PoTranslator;
 
unit PoTranslator;
  
Line 214: Line 286:
 
Alternativ dazu können Sie (mit 'msgfmt') die .po Datei in eine .mo Datei umwandeln und einfach die Unit 'DefaultTranslator' verwenden
 
Alternativ dazu können Sie (mit 'msgfmt') die .po Datei in eine .mo Datei umwandeln und einfach die Unit 'DefaultTranslator' verwenden
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
...
 
...
 
uses
 
uses
Line 237: Line 309:
 
== Übersetzung beim Start eines Programms ==
 
== Übersetzung beim Start eines Programms ==
  
Für jede .po Datei müssen sie TranslateUnitResourceStrings aus der LCL translations Unit aufrufen. Zum Beispiel:
+
Für jede .po Datei müssen sie TranslateUnitResourceStrings aus der LCL Unit Translations aufrufen. Zum Beispiel:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
uses
 +
..., GetText, Translations;
  
<pascal>
+
procedure TForm1.FormCreate(Sender: TObject);
    {Zuallererst: fügen sie die "gettext" und "translations" Units zum uses Abschnitt hinzu}
+
var
    procedure TForm1.FormCreate(Sender: TObject);
+
  PODirectory, Lang, FallbackLang: String;
    var
+
begin
      PODirectory, Lang, FallbackLang: String;
+
  PODirectory := 'C:\Lazarus\lcl\languages\'; //Pfad entsprechend Installation anpassen !!!
    begin
+
  GetLanguageIDs(Lang, FallbackLang);
      PODirectory := '/Pfad/zu/lazarus/lcl/languages/';
+
  Translations.TranslateUnitResourceStrings('LCLStrConsts', PODirectory + 'lclstrconsts.%s.po', Lang, FallbackLang);
      GetLanguageIDs(Lang, FallbackLang); // in unit gettext
+
  // ... fügen Sie hier einen Aufruf von TranslateUnitResourceStrings für jede .po-Datei hinzu ...
      TranslateUnitResourceStrings('LCLStrConsts', PODirectory + 'lclstrconsts.%s.po', Lang, FallbackLang);
+
 
      MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
+
  // der folgende Dialog zeigt übersetzte Buttons:
    end;
+
  MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
</pascal>
+
end;
 +
</syntaxhighlight>
  
 
== .po Dateien in die Programmdatei kompilieren ==
 
== .po Dateien in die Programmdatei kompilieren ==
Line 262: Line 339:
 
</pre>
 
</pre>
 
Dadurch wird eine Include-Datei unit1.lrs erstellt, die folgendermaßen beginnt
 
Dadurch wird eine Include-Datei unit1.lrs erstellt, die folgendermaßen beginnt
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
LazarusResources.Add('unit1.de','PO',[
 
LazarusResources.Add('unit1.de','PO',[
 
   ...
 
   ...
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
*Fügen Sie den Code hinzu:
 
*Fügen Sie den Code hinzu:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
uses LResources, Translations;
 
uses LResources, Translations;
  
Line 279: Line 359:
 
begin
 
begin
 
   r:=LazarusResources.Find('unit1.de','PO');
 
   r:=LazarusResources.Find('unit1.de','PO');
   POFile:=TPOFile.Create;
+
   POFile:=TPOFile.Create(False); //if Full=True then you can get a crash (Issue #0026021)
 
   try
 
   try
 
     POFile.ReadPOText(r.Value);
 
     POFile.ReadPOText(r.Value);
Line 290: Line 370:
 
initialization
 
initialization
 
   {$I unit1.lrs}
 
   {$I unit1.lrs}
 +
</syntaxhighlight>
 +
* Rufen Sie TranslateUnitResourceStrings am Beginn des Programms auf. Wenn Sie wollen, können Sie das im Abschnitt "initialization" machen.
 +
 +
Leider wird dieser Code mit Lazarus 1.2.2 und früher nicht kompiliert, für diese Lazarusversionen können Sie wie folgt verfahren:
 +
 +
<syntaxhighlight lang=pascal>
 +
type
 +
  TTranslateFromResourceResult = (trSuccess, trResourceNotFound, trTranslationError);
  
 +
function TranslateFromResource(AResourceName, ALanguage : String): TTranslateFromResourceResult;
 +
var
 +
  LRes : TLResource;
 +
  POFile : TPOFile = nil;
 +
  SStream : TStringStream = nil;
 +
begin
 +
  Result := trResourceNotFound;
 +
  LRes := LazarusResources.Find(AResourceName + '.' + ALanguage, 'PO');
 +
  if LRes <> nil then
 +
  try
 +
    SStream := TStringStream.Create(LRes.Value);
 +
    POFile := TPoFile.Create(SStream, False);
 +
    try
 +
      if TranslateUnitResourceStrings(AResourceName, POFile) then Result := trSuccess
 +
      else Result := trTranslationError;
 +
    except
 +
      Result := trTranslationError;
 +
    end;
 +
  finally
 +
    if Assigned(SStream) then SStream.Free;
 +
    if Assigned(POFile) then POFile.Free;
 +
  end;
 +
end;
 +
</syntaxhighlight>
 +
 +
Anwendungsbeispiel:
 +
 +
<syntaxhighlight lang=pascal>
 +
initialization
 +
  {$I lclstrconsts.de.lrs}
 +
  TranslateFromResource('lclstrconsts', 'de');
 +
end.
 +
</syntaxhighlight>
 +
 +
== Cross-Plattform-Methode, um Systemsprache festzustellen ==
 +
 +
Die folgende Funktion liefert einen String, der die Sprache der Benutzeroberfläche darstellt. Sie unterstützt Linux, Mac OS X und Windows.
 +
 +
<syntaxhighlight lang=pascal>
 +
uses
 +
  Classes, SysUtils {fügen Sie zusätzliche Units, die von Ihrem Code genutzt werden hier ein}
 +
  {$IFDEF win32}
 +
  , Windows
 +
  {$ELSE}
 +
  , Unix
 +
    {$IFDEF LCLCarbon}
 +
  , MacOSAll
 +
    {$ENDIF}
 +
  {$ENDIF}
 +
  ;
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang=pascal>
 +
function GetOSLanguage: string;
 +
{Plattform-unabhängige Verfahren, um die Sprache der Bedienoberfläche zu lesen}
 +
var
 +
  l, fbl: string;
 +
  {$IFDEF LCLCarbon}
 +
  theLocaleRef: CFLocaleRef;
 +
  locale: CFStringRef;
 +
  buffer: StringPtr;
 +
  bufferSize: CFIndex;
 +
  encoding: CFStringEncoding;
 +
  success: boolean;
 +
  {$ENDIF}
 +
begin
 +
  {$IFDEF LCLCarbon}
 +
  theLocaleRef := CFLocaleCopyCurrent;
 +
  locale := CFLocaleGetIdentifier(theLocaleRef);
 +
  encoding := 0;
 +
  bufferSize := 256;
 +
  buffer := new(StringPtr);
 +
  success := CFStringGetPascalString(locale, buffer, bufferSize, encoding);
 +
  if success then
 +
    l := string(buffer^)
 +
  else
 +
    l := '';
 +
  fbl := Copy(l, 1, 2);
 +
  dispose(buffer);
 +
  {$ELSE}
 +
  {$IFDEF LINUX}
 +
  fbl := Copy(GetEnvironmentVariable('LC_CTYPE'), 1, 2);
 +
    {$ELSE}
 +
  GetLanguageIDs(l, fbl);
 +
    {$ENDIF}
 +
  {$ENDIF}
 +
  Result := fbl;
 +
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
* Rufen Sie TranslateUnitResourceStrings am Beginn des Programms auf. Wenn Sie wollen, können Sie das im Abschnitt "initialization" machen.
 
  
 
== Übersetzen der IDE ==
 
== Übersetzen der IDE ==
Line 315: Line 490:
 
==Siehe auch==
 
==Siehe auch==
  
[[IDE_Development#Translations.2C_i18n.2C_lrt_files.2C_po_files|IDE Development: Translations, i18n, lrt, po files]]
+
* [[IDE_Development#Translations.2C_i18n.2C_lrt_files.2C_po_files|IDE Development: Translations, i18n, lrt, po files]]
 
+
* [[Getting_translation_strings_right|Getting translation strings right]]
[[Category:Tutorials/de]]
+
* [[Lazarus_Documentation#Translating.2FInternationalization.2FLocalization|Translating/Internationalization/Localization]]
[[Category:Localization/de]]
+
* [[German_localization_notes]]

Latest revision as of 11:27, 1 March 2020

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) polski (pl) português (pt) русский (ru) 中文(中国大陆)‎ (zh_CN)

Zurück zu den Zusätzlichen Informationen.


Überblick

Hier wird gezeigt, wie Sie in Ihrem Programm verschiedene Strings für verschiedene Sprachen (Englisch, Deutsch, Chinesisch...) ausgeben können. Dazu müssen Sie nur Folgendes tun: Einen resourcestring für jeden String, den Sie ausgeben möchten hinzufügen, kompilieren um .rst und/oder .po Dateien zu erhalten (kann die IDE automatisch erzeugen), für jede Sprache eine .po Datei erstellen (dazu gibt es graphische Programme), und zuletzt die richtige .po Datei beim Programmstart mittels der Unit translations laden.

Schnell i18n

Dies soll ein Schnelleinstig sein, der Übersetzungen kurz und bündig vorstellt.

poedit

Das bekannteste Werkzeug ist Poedit. Poedit ist ein Übersetzungswerkzeug, das .po und .mo-Dateien erstellt.

Resourcestrings übersetzen

Auf folgende Weise wird ein Ressourcestring einer Unit erzeugt:

resourcestring
  Caption1 = 'Ein Text';
  HelloWorld1 = 'Hello World';

Resourcestrings verhalten sich wie normale Stringkonstanten, das heißt, man kann sie einfach so verwenden:

Label1.Caption := HelloWorld1;

Beim Compilieren .po-Dateien erstellen

Wenn man i18n in der Lazarus IDE einschaltet werden Resourcestrings zu .po-Dateien compiliert. i18n findet man unter Projekt > Projekteinstellungen > i18n > i18n einschalten. Beim erneuten compilieren werden die .po-Dateien aktualisiert. Man kann ebenfalls das Verzeichnis festlegen, in dem die .po-Dateien gespeichert werden. (Empfohlen wird po files)

Die empfohlene voreingestellte Übersetzung oder Applications-Sprache sollte Englisch sein, da sie geladen wird, wenn keine passende Übersetzung gefunden wird.

Ist die project1.po-Datei erstellt, muss man sie in project1.<Sprache>.po umbenennen, wobei <Sprache> für das Kürzel der jeweiligen Sprache steht z.B: project1.es.po für Spanisch. Diese muss man nun zum Übersetzen geben.

Die Verzeichniss-Struktur sieht nun ungefähr so aus:

project1\po_files\
project1\po_files\project1.po
project1\po_files\project1.es.po

.po-Dateien umwandeln zu .mo-Dateien

Wenn die .po-Datei fertig übersetzt ist, sollte man sie in eine .mo-Datei compilieren, da dieses Binärformat schneller geladen wird. Für die Konvertierung in das .mo-Format eignet sich z.B. Poedit ( Datei -> MO-Datei erstellen ... ).

Automatische Übersetzung

Wenn alle .mo-Dateien fertig sind, werden sie in das Verzeichnis locale oder languages direkt unterhalb des Anwendungsprogramms gespeichert. Dann wird noch die Unit DefaultTranslator eingebunden und das war schon alles. Die Übersetzung erfolgt automatisch.

uses
  DefaultTranslator;

Es sollten nur die .mo-Dateien im locale or languages Verzeichnis ausgeliefert werden, da die .po-Dateien nur für die Anfertigung der Übersetzung und die Compilierung in .mo-Dateien benötigt werden.

Die Verzeichnissstruktur wird so aussehen:

project1\project1.exe
project1\locale\
project1\locale\project1.mo
project1\locale\project1.es.mo

Zusätzliche Informationen

Datums-, Zeit- und Zahlenformat

In der Unit sysutils sind verschiedene Format-Zeichen definiert, mit denen Datum und Zeit oder Dezimalzahlen formatiert werden. Unter POSIX-konformen Betriebssystemen wie Linux oder Mac OS X können diese von der Unit clocale automatisch aus der Standard C Library übernommen werden. Dazu muss die Unit nur in das Programm (.lpr) aufgenommen werden:

uses
  {$IFDEF UNIX}
  {$IFDEF UseCThreads}cthreads,{$ENDIF}
  clocale,{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, Unit1
  { you can add units after this };

Ressourcenstrings

Ressourcenstrings verhalten sich ähnlich wie String-Konstanten, können aber zur Laufzeit übersetzt werden.

Eine Definition sieht so aus:

resourcestring
  Caption1 = 'Irgendein Text';
  HelloWorld1 = 'Hello World';

Nun kann man sie wie normale String-Konstanten verwenden. Wurden sie bereits übersetzt, wird nicht mehr der originale Inhalt sondern die Übersetzung zugewiesen:

Label1.Caption := HelloWorld1;

Wenn der FPC eine Unit mit einem resourcestring-Abschnitt übersetzt, erzeugt er automatisch eine Datei unitname.rst, in der alle Resourcestring-Daten (Name + Inhalt) zu dieser Unit gespeichert werden.

.po Dateien

.po Dateien sind genau wie die .rst Dateien einfache Textdateien, enthalten aber ein paar mehr Optionen, wie einen Header, in dem Felder für Autor, Kodierung (Zeichensatz), Sprache und Datum vorgesehen sind. Daher können sie mit jedem Texteditor bearbeitet werden. Es gibt aber viele freie, grafische Tools um .po Dateien um diese Aufgabe bequemer zu erledigen (z.B. mit kbabel, Poedit).

Jede fpc Installation enthält das Programm rstconv (unter Windows rstconv.exe). Dieses Werkzeug kann verwendet werden, um eine .rst Datei in eine .po Datei zu konvertieren. Die IDE kann dies aber automatisch erledigen (einzustellen in den Projektoptionen, siehe unten).

Beispiel wie rstconv direkt verwendet wird:

 rstconv -i unit1.rst -o unit1.po

Übersetzen

Für jede Sprache muss die .po-Datei kopiert und übersetzt werden. Einige Tipps zum Übersetzen finden Sie unter: Wie man Übersetzungsstrings richtig hinbekommt.

Die LCL Unit translation sucht nach den übersetzen .po-Dateien nach dem Schema: unitname.<Sprachkürzel>.po, wobei <Sprachkürzel> dem üblichen Sprachcode (en=English, de=Deutsch, it=Italienisch, ...) entspricht. Die deutsche Übersetzung für unit1.po wäre also in unit1.de.po zu finden. Legen Sie also für jede Sprache, die Sie anbieten wollen, eine Kopie der Originaldatei an und übersetzen Sie diese.

Anmerkung für Brasilianer/Portugiesen:: Lazarus IDE und LCL haben nur eine Übersetzung für brasilianisches Portugiesisch und diese Dateien enden auf 'pb.po' und nicht auf 'pt.po'.

IDE Einstellungen für automatische Updates der .po Dateien

  • Die Unit, die die Ressourcenstrings enthält, muss zum Package oder Projekt hinzugefügt sein.
  • Sie müssen einen .po Pfad angeben. Dies bedeutet ein separates Verzeichnis. Zum Beispiel: erzeugen sie ein Unterverzeichnis languages im Package / Projekt Verzeichnis.

Für Projekte wählen Sie im Menü Projekt > Projekteinstellungen -> i18n.
Für Packages wählen Sie in den Package-Einstellungen 'i18n'.

Wenn diese Optionen aktiviert sind, dann erzeugt bzw. aktualisiert die IDE die zugrundeliegende .po-Datei und benutzt dazu die Informationen aus den .rst und .lrt-Dateien (das rstconv Werkzeug ist dann nicht notwendig). Der Aktualisierungsvorgang beginnt damit, alle bestehenden Einträge aus der grundlegenden .po-Datei sowie aus den .rst und .lrt-Dateien zu sammeln, und wendet dann die nachfolgend gefundenen Merkmale an und aktualisiert so jede übersetzte .xx.po Datei.

Entfernen überflüssiger Einträge

Einträge aus der grundlegenden .po Datei, die nicht in .rst und .lrt-Dateien zu finden sind, werden entfernt. Anschließend werden ebenso all jene Einträge entfernt, die zwar in den übersetzten .xx.po Dateien vorkommen, aber nicht in der .po-Datei stehen. Auf diese Weise werden die .po Dateien nicht mit obsoleten Einträgen zugemüllt und die Übersetzer müssen keine Einträge bearbeiten, die gar nicht mehr verwendet werden.

Mehrfache Einträge

Mehrfache Einträge treten dann auf, wenn derselbe Text für verschiedene Ressourcenstrings als Übersetzung steht. Ein Beispiel dafür findet man in der Datei 'lazarus/ide/lazarusidestrconst.pas' für den 'Gutter' String:

  dlfMouseSimpleGutterSect = 'Gutter';
  dlgMouseOptNodeGutter = 'Gutter';
  dlgGutter = 'Gutter';
  dlgAddHiAttrGroupGutter   = 'Gutter';

Eine konvertierte .rst-Datei für diese Ressourcenstrings würde ähnlich aussehen wie die folgende .po-Datei:

#: lazarusidestrconsts.dlfmousesimpleguttersect
msgid "Gutter"
msgstr ""
#: lazarusidestrconsts.dlgaddhiattrgroupgutter
msgid "Gutter"
msgstr ""
etc.

Wobei die mit "#: " beginnenden Zeilen als Kommentare betrachtet werden und die zum Übersetzen dieser Einträge verwendeten Werkzeuge die wiederholten msgid "Gutter" Zeilen als mehrfache Einträge ansehen und Fehler oder Warnhinweise beim Laden oder Speichern produzieren. Mehrfache Einträge treten bei .po-Dateien häufig auf und deshalb ist es nötig, sie durch einen angehängten Kontext näher zu bestimmen. Das Schlüsselwort 'msgctxt' fügt einen Kontext an einen der mehrfachen Einträge an. Das Werkzeug zum automatischen Aktualisieren verwendet die Eintrags-ID (das ist der Text neben dem "#: " Präfix) als Kontext. Im vorigen Beispiel würde es ungefähr folgendes liefern:

#: lazarusidestrconsts.dlfmousesimpleguttersect
msgctxt "lazarusidestrconsts.dlfmousesimpleguttersect"
msgid "Gutter"
msgstr ""
#: lazarusidestrconsts.dlgaddhiattrgroupgutter
msgctxt "lazarusidestrconsts.dlgaddhiattrgroupgutter"
msgid "Gutter"
msgstr ""
etc.

Bei übersetzten .xx.po Dateien macht das automatische Tool eine zusätzliche Überprüfung: wenn der mehrfache Eintrag bereits übersetzt wurde, übernimmt der neue Eintrag die alte Übersetzung, sodass er wie automatisch übersetzt erscheint.

Die automatische Erkennung von Duplikaten ist noch nicht perfekt. Die Erkennung wird jedes Mal durchgeführt, wenn neue Einträge hinzugefügt werden. Es kann aber passieren, dass einige nicht übersetzte Einträge zuerst gelesen werden. Daher können mehrere Durchläufe notwendig sein, damit das Werkzeug alle Duplikate automatisch übersetzen kann.

Merkwürdige Einträge

Änderungen an Ressourcenstrings beeinflussen die Übersetzungen.
(Das folgende Beispiel wurde aus der Lazarus history entnommen)
Wenn z. B. ein Ressourcenstring anfänglich wie folgt definiert war:

  dlgEdColor = 'Syntax highlight';

Dies erzeugt einen ähnlichen .po Eintrag wie diesen:

#: lazarusidestrconsts.dlgedcolor
msgid "Syntax highlight"
msgstr ""

der in der spanischen Übersetzung resultiert in

#: lazarusidestrconsts.dlgedcolor
msgid "Syntax highlight"
msgstr "Color"

Angenommen, dass zu einem späteren Zeitpunkt der Ressourcenstring geändert würde in

  dlgEdColor = 'Colors';

dann ändert sich auch der .po Eintrag in

#: lazarusidestrconsts.dlgedcolor
msgid "Colors"
msgstr ""

Beachten Sie, dass zwar die ID 'lazarusidestrconsts.dlgedcolor' gleich geblieben ist, sich aber der String geändert hat, von 'Syntax highlight' zu 'Colors'. Weil der String bereits übersetzt war, könnte die alte Übersetzung nicht zu der neuen Bedeutung passen. Und wirklich, für unseren neuen String wäre im Spanischen wahrscheinlich 'Colores' die bessere Übersetzung. Das automatische Aktualisierungwerkzeug erkennt diese Situation und erzeugt einen Eintrag wie diesen:

#: lazarusidestrconsts.dlgedcolor
#, fuzzy
#| msgid "Syntax highlight"
msgctxt "lazarusidestrconsts.dlgedcolor"
msgid "Colors"
msgstr "Color"

Im Zusammenhang des .po Dateiformates, bedeutet das "#," Präfix, dass der Eintrag ein Flag (namens 'fuzzy') hat. Ein geeignetes Übersetzungsprogramm könnte dieses Flag auswerten. Der Übersetzer sieht dadurch bei diesem Eintrag sofort, dass die Übersetzung in ihrem derzeitigen Zustand fragwürdig ist und somit einer Überprüfung durch den Übersetzer bedarf.
Das "#|" Präfix weist darauf hin, dass der vorhergehende String unübersetzt ist und liefert auch einen Hinweis darauf, warum der Eintrag als 'fuzzy' markiert war.

Übersetzung von Formularen, Datenmodulen und Frames

Wenn die i18n Einstellung für das Projekt / Package aktiviert ist, dann erzeugt die IDE automatisch .lrt Dateien für jedes Formular. Die .lrt Dateien werden beim Speichern einer Unit erzeugt. Wenn sie die Einstellung zum ersten Mal aktivieren, dann müssen Sie jedes Formular einmal öffnen und eine Änderung vornehmen (damit es als geändert markiert ist) und dann speichern.

Beispiel: Wenn Sie ein Formular unit1.pas speichern, erstellt die IDE die Datei unit1.lrt. Beim Kompilieren sammelt die IDE alle Strings aus allen .lrt und .rst Dateien in einer einzigen .po Datei (projektname.po oder paketname.po) in dem i18n Verzeichnis.

Damit die Formulare zur Laufzeit tatsächlich übersetzt werden, müssen Sie der Objektvariablen 'LRSTranslator' (definiert in LResources) im Abschnitte initialization einer Ihrer Units ein Translator-Objekt zuweisen

...
uses
  ...
  LResources;
...
...
initialization
  LRSTranslator:=TPoTranslator.Create('/Pfad/zu/der/po/Datei');

Allerdings gibt es derzeit in der LCL keine TPoTranslator - Klasse (das ist eine Klasse, die unter Verwendung von .po Dateien übersetzt). Hier folgt eine mögliche Implementierung (teilweise entlehnt aus DefaultTranslator.pas in der LCL):Der folgende Code wird nicht mehr länger gebraucht, wenn Sie einen aktuellen Lazarus 0.9.29 Snapshot einsetzen. Fügen Sie einfach 'DefaultTranslator' zur Uses-Klausel hinzu.

unit PoTranslator;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, typinfo, Translations;

type

 { TPoTranslator }

 TPoTranslator=class(TAbstractTranslator)
 private
  FPOFile:TPOFile;
 public
  constructor Create(POFileName:string);
  destructor Destroy;override;
  procedure TranslateStringProperty(Sender:TObject; 
    const Instance: TPersistent; PropInfo: PPropInfo; var Content:string);override;
 end;

implementation

{ TPoTranslator }

constructor TPoTranslator.Create(POFileName: string);
begin
  inherited Create;
  FPOFile:=TPOFile.Create(POFileName);
end;

destructor TPoTranslator.Destroy;
begin
  FPOFile.Free;
  inherited Destroy;
end;

procedure TPoTranslator.TranslateStringProperty(Sender: TObject;
  const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
var
  s: String;
begin
  if not Assigned(FPOFile) then exit;
  if not Assigned(PropInfo) then exit;
{DO we really need this?}
  if Instance is TComponent then
   if csDesigning in (Instance as TComponent).ComponentState then exit;
{End DO :)}
  if (AnsiUpperCase(PropInfo^.PropType^.Name)<>'TTRANSLATESTRING') then exit;
  s:=FPOFile.Translate(Content, Content);
  if s<>'' then Content:=s;
end;

end.

Alternativ dazu können Sie (mit 'msgfmt') die .po Datei in eine .mo Datei umwandeln und einfach die Unit 'DefaultTranslator' verwenden

...
uses
   ...
   DefaultTranslator;

Diese sucht automatisch in verschiedenen Standard-Verzeichnissen nach einer .mo Datei (der Nachteil dabei ist, dass Sie beiderlei Dateien behalten müssen, die .mo Dateien für die DefaultTranslator-Unit und die .po Dateienfiles für TranslateUnitResourceStrings). Wenn Sie DefaultTranslator benutzen, wird er automatisch versuchen die Sprache anhand der Umgebungsvariablen LANG zu bestimmen. (Dies kann mit dem Befehlszeilenschalter --lang überschrieben werden). Anschließend wird an folgenden Stellen nach einer Übersetzung gesucht (hier bedeutet LANG die gesuchte Sprache):

  • <Application Directory>/LANG/<Application Filename>.mo
  • <Application Directory>/languages/LANG/<Application Filename>.mo
  • <Application Directory>/locale/LANG/<Application Filename>.mo
  • <Application Directory>/locale/LC_MESSAGES/LANG/<Application Filename>.mo

unter Unix-ähnliche Betriebssystemen wird auch gesucht in

  • /usr/share/locale/LANG/LC_MESSAGES/<Application Filename>.mo

ebenso wird die Kurzform des Sprachbezeichners probiert (wenn z.B. "es_ES" oder "es_ES.UTF-8" nicht existieren wird auch "es" versucht).

Übersetzung beim Start eines Programms

Für jede .po Datei müssen sie TranslateUnitResourceStrings aus der LCL Unit Translations aufrufen. Zum Beispiel:

uses
 ..., GetText, Translations;

procedure TForm1.FormCreate(Sender: TObject);
var
  PODirectory, Lang, FallbackLang: String;
begin
  PODirectory := 'C:\Lazarus\lcl\languages\';  //Pfad entsprechend Installation anpassen !!!
  GetLanguageIDs(Lang, FallbackLang);
  Translations.TranslateUnitResourceStrings('LCLStrConsts', PODirectory + 'lclstrconsts.%s.po', Lang, FallbackLang);
  // ... fügen Sie hier einen Aufruf von TranslateUnitResourceStrings für jede .po-Datei hinzu ... 

  // der folgende Dialog zeigt übersetzte Buttons:
  MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
end;

.po Dateien in die Programmdatei kompilieren

Wenn Sie die einzelnen .po Dateien nicht zusammen mit Ihrer Anwendung installieren, sondern mit in die Programmdatei packen wollen, gehen Sie wie folgt vor:

  • Erzeugen Sie eine neue Unit (kein Formular!).
  • Konvertieren Sie die .po Datei(en) in .lrs unter Verwendung von tools/lazres:
./lazres unit1.lrs unit1.de.po

Dadurch wird eine Include-Datei unit1.lrs erstellt, die folgendermaßen beginnt

LazarusResources.Add('unit1.de','PO',[
  ...
  • Fügen Sie den Code hinzu:
uses LResources, Translations;

resourcestring
  MyCaption = 'Caption';

function TranslateUnitResourceStrings: boolean;
var
  r: TLResource;
  POFile: TPOFile;
begin
  r:=LazarusResources.Find('unit1.de','PO');
  POFile:=TPOFile.Create(False);  //if Full=True then you can get a crash (Issue #0026021)
  try
    POFile.ReadPOText(r.Value);
    Result:=Translations.TranslateUnitResourceStrings('unit1',POFile);
  finally
    POFile.Free;
  end;
end;

initialization
  {$I unit1.lrs}
  • Rufen Sie TranslateUnitResourceStrings am Beginn des Programms auf. Wenn Sie wollen, können Sie das im Abschnitt "initialization" machen.

Leider wird dieser Code mit Lazarus 1.2.2 und früher nicht kompiliert, für diese Lazarusversionen können Sie wie folgt verfahren:

type
  TTranslateFromResourceResult = (trSuccess, trResourceNotFound, trTranslationError);

function TranslateFromResource(AResourceName, ALanguage : String): TTranslateFromResourceResult;
var
  LRes : TLResource;
  POFile : TPOFile = nil;
  SStream : TStringStream = nil;
begin
  Result := trResourceNotFound;
  LRes := LazarusResources.Find(AResourceName + '.' + ALanguage, 'PO');
  if LRes <> nil then
  try
    SStream := TStringStream.Create(LRes.Value);
    POFile := TPoFile.Create(SStream, False);
    try
      if TranslateUnitResourceStrings(AResourceName, POFile) then Result := trSuccess
      else Result := trTranslationError;
    except
      Result := trTranslationError;
    end;
  finally
    if Assigned(SStream) then SStream.Free;
    if Assigned(POFile) then POFile.Free;
  end;
end;

Anwendungsbeispiel:

initialization
  {$I lclstrconsts.de.lrs}
  TranslateFromResource('lclstrconsts', 'de');
end.

Cross-Plattform-Methode, um Systemsprache festzustellen

Die folgende Funktion liefert einen String, der die Sprache der Benutzeroberfläche darstellt. Sie unterstützt Linux, Mac OS X und Windows.

uses
  Classes, SysUtils {fügen Sie zusätzliche Units, die von Ihrem Code genutzt werden hier ein}
  {$IFDEF win32}
  , Windows
  {$ELSE}
  , Unix
    {$IFDEF LCLCarbon}
  , MacOSAll
    {$ENDIF}
  {$ENDIF}
  ;
function GetOSLanguage: string;
{Plattform-unabhängige Verfahren, um die Sprache der Bedienoberfläche zu lesen}
var
  l, fbl: string;
  {$IFDEF LCLCarbon}
  theLocaleRef: CFLocaleRef;
  locale: CFStringRef;
  buffer: StringPtr;
  bufferSize: CFIndex;
  encoding: CFStringEncoding;
  success: boolean;
  {$ENDIF}
begin
  {$IFDEF LCLCarbon}
  theLocaleRef := CFLocaleCopyCurrent;
  locale := CFLocaleGetIdentifier(theLocaleRef);
  encoding := 0;
  bufferSize := 256;
  buffer := new(StringPtr);
  success := CFStringGetPascalString(locale, buffer, bufferSize, encoding);
  if success then
    l := string(buffer^)
  else
    l := '';
  fbl := Copy(l, 1, 2);
  dispose(buffer);
  {$ELSE}
  {$IFDEF LINUX}
  fbl := Copy(GetEnvironmentVariable('LC_CTYPE'), 1, 2);
    {$ELSE}
  GetLanguageIDs(l, fbl);
    {$ENDIF}
  {$ENDIF}
  Result := fbl;
end;

Übersetzen der IDE

Dateien

Die .po Dateien der IDE befinden sich im Lazarus-Verzeichnis:

  • lazarus/languages/: Zeichenketten für die IDE
  • lcl/languages/: Zeichenketten für die LCL
  • ideintf/languages/: Zeichenketten für das IDE Interface

Übersetzer

  • Die deutsche Übersetzung wird von Jörg Braun gepflegt.
  • Die finnische Übersetzung wird von Seppo Suurtarla gepflegt.
  • Die russische Übersetzung wird von Maxim Ganetsky gepflegt.

Bevor Sie mit einer neuen Übersetzung beginnen, fragen Sie besser auf der Mailing-Liste, ob schon jemand daran arbeitet.

Bitte lesen Sie sorgfältig: Übersetzungen

Siehe auch