Translations / i18n / localizations for programs/de

From Free Pascal wiki
Revision as of 13:46, 3 September 2010 by Swen (talk | contribs) (Reverted edits by Chronos (Talk); changed back to last version by Swen)
Jump to navigationJump to search

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

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

Datums-, Zeit- und Zahlenformat

Under Linux, BSD, Mac OS X there are several locales defining things like time and date format or the thousand separator. In order to initialize the RTL you need to iclude the clocale unit in the uses section of your program (lpr file).

Ressourcenstrings

Zum Beispiel

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

Diese sind wie normale string Konstanten. Das bedeutet, daß sie sie jedem string zuweisen können, etwa

 Label1.Caption := HelloWorld1;

Wenn FPC diese kompiliert, dann wird für jede Unit eine Datei unitname.rst erzeugt, welche die Resourcestring Daten (Name + Inhalt) enthält.

.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 Installtion 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 muß die .po Datei kopiert und übersetzt werden. Die LCL translation Unit verwendet die üblichen Sprachcodes (en=english, de=deutsch, it=italienisch, ...) für die Suche. Zum Beispiel wäre die deutsche Übersetzung von unit1.po die unit1.de.po. This means, copy the unit1.po file to unit1.de.po, unit1.it.po, and whatever language you want to support and then the translators can edit their specific .po file.

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

IDE Einstellungen für automatische Updates der .po Dateien

  • Die Unit, welche die Resourcenstrings enthält, muß 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 language 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'.

When this options are enabled, the IDE generates or updates the base .po file using the information contained in .rst and .lrt files (das rstconv Werkzeug ist dann nicht notwendig). The update process begins by collecting all existing entries found in base .po file and in .rst and .lrt files and then applying the following features it finds and brings up to date any translated .xx.po file.

Entfernen überflüssiger Einträge

Entries in base .po file not found in .rst and .lrt files are removed, subsequently, all entries found in translated .xx.po files not found in base .po file are also removed. This way .po files are not cluttered with obsolete entries and translators don't have to translate entries that are not used.

Mehrfache Einträge

Duplicate entries occur when for some reason the same text is used for diferent resource strings, a random example of this is the file lazarus/ide/lazarusidestrconst.pas for the 'Gutter' string: <Delphi>

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

</Delphi> A converted .rst file for this resource strings would look similar to this in a .po file:

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

Where the lines starting with "#: " are considered comments and the tools used to translate this entries see the repeated msgid "Gutter" lines like duplicated entries and produce errors or warnings on loading or saving. Duplicate entries are considered a normal eventuality on .po files and they need to have some context attached to them. The msgctxt keyword is used to add context to duplicated entries and the automatic update tool use the entry ID (the text next to "#: " prefix) as the context, for the previous example it would produce something like this:

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

On translated .xx.po files the automatic tool does one additional check: if the duplicated entry was already translated, the new entry gets the old translation, so it appears like being translated automatically.

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, daß einige nicht übersetzte Einträge zuerst gelesen werden. Daher kann es mehrere Durchläufe brauchen, damit das Werkzeug alle Duplikate automatisch übersetzen kann.

Merkwürdige Einträge

Changes in resource strings affect translations, for example if initially a resource string was defined like: <Delphi>

 dlgEdColor = 'Syntax highlight';

</Delphi> this would produce a .po entry similar to this

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

which if translated to spanish langauge (this sample was taken from lazarus history), may result in

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

Suppose then that at a later time, the resource string has been changed to <Delphi>

 dlgEdColor = 'Colors';

</Delphi> the resulting .po entry may become

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

Note that while ID remained the same lazarusidestrconsts.dlgedcolor the string has changed from 'Syntax highlight' to 'Colors', as the string was already translated the old translation may not match the new the new meaning. Indeed, for the new string probably 'Colores' may be a better translation. The automatic update tool notices this situation and produce an entry like this:

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

In terms of .po file format, the "#," prefix means the entry has a flag (fuzzy) and translator programs may present to the translator user a special gui for this item, in this case the flag would mean that the translation in its current state is doubtful and needs to be reviewed more careful by translator. The "#|" prefix indicates what was the previous untranslated string of this entry and gives the translator a hint why the entry was marked as fuzzy.

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

For the forms to be actually translated at runtime, you have to assign a translator to LRSTranslator (defined in LResources) in the initialization section to one of your units

<Delphi> ... uses

 ...
 LResources;

... ... initialization

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

</Delphi>

However there's no TPoTranslator class (i.e a class that translates using .po files) available in the LCL. This is a possible implementation (partly lifted from DefaultTranslator.pas in the LCL):

<Delphi> 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. </Delphi>

Alternatively you can transform the .po file into .mo (using msgfmt) and simply use the DefaultTranslator unit

<Delphi> ... uses

  ...
  DefaultTranslator;

</Delphi>

which will automatically look in several standard places for a .mo file (the disadvantage is that you'll have to keep around both the .mo files for the DefaultTranslator unit and the .po files for TranslateUnitResourceStrings). If you use DefaultTranslator, it will try to automatically detect the language based on the LANG environment variable (overridable using the --lang command line switch), then look in these places for the translation (LANG stands for the desired language):

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

under unix-like systems it will also look in

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

as well as using the short part of the language (e.g. if it is "es_ES" or "es_ES.UTF-8" and it doesn't exist it will also try "es")

Übersetzung beim Start eines Programms

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

<pascal>

   {Zuallererst: fügen sie die "gettext" und "translations" Units zum uses Abschnitt hinzu}
   procedure TForm1.FormCreate(Sender: TObject);
   var
     PODirectory, Lang, FallbackLang: String;
   begin
     PODirectory := '/Pfad/zu/lazarus/lcl/languages/';
     GetLanguageIDs(Lang, FallbackLang); // in unit gettext
     TranslateUnitResourceStrings('LCLStrConsts', PODirectory + 'lclstrconsts.%s.po', Lang, FallbackLang);
     MessageDlg('Title', 'Text', mtInformation, [mbOk, mbCancel, mbYes], 0);
   end;

</pascal>

.po Dateien in die Programmdatei kompilieren

Wenn Sie die einzelnen .po Dateien nicht zusammen mit Ihrer Anwendung installieren, sonder 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 <pascal> LazarusResources.Add('unit1.de','PO',[

 ...

</pascal>

  • Fügen Sie den Code hinzu:

<pascal> uses LResources, Translations;

resourcestring

 MyCaption = 'Caption';

function TranslateUnitResourceStrings: boolean; var

 r: TLResource;
 POFile: TPOFile;

begin

 r:=LazarusResources.Find('unit1.de','PO');
 POFile:=TPOFile.Create;
 try
   POFile.ReadPOText(r.Value);
   Result:=Translations.TranslateUnitResourceStrings('unit1',POFile);
 finally
   POFile.Free;
 end;

end;

initialization

 {$I unit1.lrs}

</pascal>

  • Rufen sie TranslateUnitResourceStrings am Beginn des Programms auf. Sie können das im initialization Abschnitt machen wenn sie wollen.

Ü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

Future work / ToDos

IDE Development: Translations, i18n, lrt, po files