Buchempfehlung
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Umfassend, aber leicht verständlich führt dieses Buch in die Programmierung von ATMEL AVR Mikrocontrollern ein. [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Code-Beispiel

Code-Beispiele » Stringfunktionen

Mehrsprachige Programme (I18N = Internationalization)

Lizenz:Erster Autor:Letzte Bearbeitung:
LGPLMitgliedTJF 23.08.2011

Oft ist es sinnvoll die Textausgabe von Programmen in unterschiedlichen Sprachen vorzusehen. Dies gilt sowohl für Programme, welche über die Kommandozeile gesteuert werden und dort ihre Ausgaben tätigen, als auch (und besonders) für solche Programme, welche über eine grafische Benutzerschnittstelle verfügen.

Diese Zielstellung kann in FB durch die Verwendung des GNU-Standard-Tools libintl realisiert werden. Unter LINUX ist die Funktionalität in der libc bereits enthalten, für windows müssen die Bibliotheken libintl.dll und libiconv.dll installiert werden (und für die Kompilierung auch deren .a Versionen). Die Bibliothek libintl versucht während der Laufzeit des Programmes eine zur Systemeinstellung passende Übersetzungsdatei zu finden und verwendet diese ggf. Gelingt dies nicht, so werden die Texte entsprechend dem Quelltext ausgegeben.

Dieses Codebeispiel betrifft ein einfaches "Hallo Welt!" Programm (windows / LINUX). Es werden die grundsätzlichen Schritte zur Realisierung von I18N beschrieben.

Zunächst wird ein Header zur Anbindung der Bibliotheken benötigt (libintl.bi)

' This is file libintl.bi
' (FreeBasic binding for libintl version 0.18)
'
' translated with help of h_2_bi.bas by
' Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net.
'
' Licence:
' (C) 2011 Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net
'
' This library binding is free software; you can redistribute it
' and/or modify it under the terms of the GNU Lesser General Public
' License as published by the Free Software Foundation; either
' version 2 of the License, or (at your option) ANY later version.
'
' This binding is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
' Lesser General Public License for more details, refer to:
' http://www.gnu.org/licenses/lgpl.html
'
'
' Original license text:
'
'/* Message catalogs for internationalization.
   'Copyright (C) 1995-2002, 2004, 2005 Free Software Foundation, Inc.
   'This file is part of the GNU C Library.
   'This file is derived from the file libgettext.h in the GNU gettext package.

   'The GNU C Library is free software; you can redistribute it and/or
   'modify it under the terms of the GNU Lesser General Public
   'License as published by the Free Software Foundation; either
   'version 2.1 of the License, or (at your option) any later version.

   'The GNU C Library is distributed in the hope that it will be useful,
   'but WITHOUT ANY WARRANTY; without even the implied warranty of
   'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   'Lesser General Public License for more details.

   'You should have received a copy of the GNU Lesser General Public
   'License along with the GNU C Library; if not, write to the Free
   'Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   '02111-1307 USA.  */

#IFNDEF __LIBINTL_TJF__
#DEFINE __LIBINTL_TJF__

#IFDEF __FB_WIN32__
 #PRAGMA push(msbitfields)
 #INCLIB "iconv"
 #INCLIB "intl"
#ENDIF

#LANG "fb"
EXTERN "C" ' (h_2_bi -P_oCD option)

' 001 start from: libintl.h2bi ==> libintl.h

#IFNDEF _LIBINTL_H
#DEFINE _LIBINTL_H 1

' file not found: features.h

#DEFINE __USE_GNU_GETTEXT 1
#DEFINE __GNU_GETTEXT_SUPPORTED_REVISION(major) IIF((major)= 0 , 1 , -1)

DECLARE FUNCTION gettext(BYVAL AS CONST ZSTRING PTR) AS ZSTRING PTR
DECLARE FUNCTION dgettext(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR) AS ZSTRING PTR
DECLARE FUNCTION __dgettext(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR) AS ZSTRING PTR
DECLARE FUNCTION dcgettext(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR, BYVAL AS INTEGER) AS ZSTRING PTR
DECLARE FUNCTION __dcgettext(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR, BYVAL AS INTEGER) AS ZSTRING PTR
DECLARE FUNCTION ngettext(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR, BYVAL AS ULONGINT) AS ZSTRING PTR
DECLARE FUNCTION dngettext(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR, BYVAL AS ULONGINT) AS ZSTRING PTR
DECLARE FUNCTION dcngettext(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR, BYVAL AS ULONGINT, BYVAL AS INTEGER) AS ZSTRING PTR
DECLARE FUNCTION textdomain(BYVAL AS CONST ZSTRING PTR) AS ZSTRING PTR
DECLARE FUNCTION bindtextdomain(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR) AS ZSTRING PTR
DECLARE FUNCTION bind_textdomain_codeset(BYVAL AS CONST ZSTRING PTR, BYVAL AS CONST ZSTRING PTR) AS ZSTRING PTR

#IF DEFINED (__OPTIMIZE__) AND NOT DEFINED (__cplusplus)
#DEFINE __need_NULL
#INCLUDE ONCE "crt/stddef.bi" '__HEADERS__: stddef.h

' file not found: locale.h

#DEFINE gettext(msgid) dgettext (NULL, msgid)
#DEFINE dgettext(domainname, msgid) dcgettext (domainname, msgid, LC_MESSAGES)
#DEFINE ngettext(msgid1, msgid2, n) dngettext (NULL, msgid1, msgid2, n)
#DEFINE dngettext(domainname, msgid1, msgid2, n) dcngettext (domainname, msgid1, msgid2, n, LC_MESSAGES)
#ENDIF ' DEFINED __OPTIM...
#ENDIF ' _LIBINTL_H

' 001 back from libintl.h2bi ==> libintl.h

DECLARE FUNCTION setlocale(BYVAL AS INTEGER, BYVAL AS CONST ZSTRING PTR) AS ZSTRING PTR

END EXTERN ' (h_2_bi -P_oCD option)
' 000 start from: libintl.h2bi, section __END_BI__

#IFDEF __FB_WIN32__
#PRAGMA pop(msbitfields)
#ENDIF

#DEFINE LC_CTYPE 0
#DEFINE LC_NUMERIC 1
#DEFINE LC_TIME 2
#DEFINE LC_COLLATE 3
#DEFINE LC_MONETARY 4
#DEFINE LC_MESSAGES 5
#DEFINE LC_ALL 6
#DEFINE LC_PAPER 7
#DEFINE LC_NAME 8
#DEFINE LC_ADDRESS 9
#DEFINE LC_TELEPHONE 10
#DEFINE LC_MEASUREMENT 11
#DEFINE LC_IDENTIFICATION 12

#DEFINE __(_T_) gettext(_T_)

#ENDIF ' __LIBINTL_TJF__

' Translated at 11-01-28 11:16:38, by h_2_bi (version 0.2.0.1,
' released under GPLv3 by Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net)

'   Protocol: libintl.bi
' Parameters:
'                                  Process time [s]: 0.006257920642383397
'                                  Bytes translated: 2184
'                                      Maximum deep: 1
'                                SUB/FUNCTION names: 11
'                                mangled TYPE names: 0
'                                        files done: 0
'                                      files missed: 2
' features.h
' locale.h
'                                       #DEFINE FOLDERS#DEFINE : 0
'                                        #DEFINE MACROS#DEFINE : 5
' 1: #define #DEFINE BEGIN_DECLS
' 1: #define #DEFINE END_DECLS
' 11: #define #DEFINE THROW
' 33: #define #DEFINE const const
' 8: #define #DEFINE attribute_format_arg#DEFINE (x)
'                                       #DEFINE HEADERS#DEFINE : 0
'                                         #DEFINE TYPES#DEFINE : 0
'                                     #DEFINE POST_REPS#DEFINE : 0

Dieser wird in den Quelltext eingebunden und wenige Funktionsaufrufe genügen, um der Bibliothek den Namen und den Zugriffspfad zu den Dateien mit den übersetzten Texten mitzuteilen

#INCLUDE "libintl.bi"

' set then system locale / die Systemsprache setzen
VAR t = setlocale(LC_ALL, "")
?*t ' show locale setting / Ausgabe Spracheeinstellung

' your project file name / Name des Programmes
CONST PROJ_NAME = "I18N"

' set the path where the translations are located / Zugriffspfad
bindtextdomain(PROJ_NAME, EXEPATH & "/locale")
' set the character encoding / Zeichencodierung
bind_textdomain_codeset(PROJ_NAME, "UTF-8")
' set the filename for the translations / Dateiname
textdomain(PROJ_NAME)


' use a translatable text / Anwendung
?*__("Hello world!")

Wenn dieser Quelltext kompiliert und gestartet wird, sucht libintl nach einer passenden Übersetzungsdatei mit dem Namen I18N.mo in dem Unterverzeichnis locale. Im Falle eines Systems mit deutscher Spracheinstellung wird in dem weiteren Unterordner de gesucht. In diesem können sich - ebenfalls in Unterordnern - verschiedene Informationen befinden, z. B. das landesspezifische Ausgabeformat für Währung, Datum oder Telefonnummern. In unserem Fall handelt es sich um eine Textausgabe, welche im Unterordner LC_MESSAGES gesucht wird. Damit die passende Übersetzungsdatei für I18N.bas gefunden werden kann, muss diese also folgendermaßen gespeichert sein

locale/de/LC_MESSAGES/I18N.mo

Zur Erstellung der Übersetzungsdatei I18N.mo werden zwei Hilfsmittel aus dem libintl Baukasten eingesetzt. Zunächst werden die übersetzbaren Texte aus dem Quelltext extrahiert. Hierzu dient das Kommandozeilenprogramm xgettext. Dieses ist derzeit (Stand 8/2011) nicht speziell für die Verwendung von FB Quelltext vorbereitet. Als Notlösung empfehle ich die Einstellung awk Quelltext und die Definition der verwendeten Funktionen. Dazu wird folgender Befehl im Ordner mit I18N.bas ausgeführt

xgettext --output=I18N.pot --no-wrap --from-code=utf-8 --package-name=I18N --package-version=0.0 --msgid-bugs-address=Email@provider.country --copyright-holder=me --keyword= --keyword=__  --language=awk --force-po I18N.bas

Der Vollständigkeit halber hier noch eine Version mit allen Funktionen (wird für I18N.bas nicht benötigt)

xgettext --output=I18N.pot --no-wrap --from-code=utf-8 --package-name=I18N --package-version=0.0 --msgid-bugs-address=Email@provider.country --copyright-holder=me --keyword= --keyword=__ --keyword=gettext --keyword=dgettext:2 --keyword=dcgettext:2 --keyword=ngettext:1,2 --keyword=dngettext:2,3 --keyword=dcngettext:2,3 --keyword=gettext_noop --keyword=pgettext:1c,2 --keyword=dpgettext:2c,3 --keyword=dcpgettext:2c,3 --keyword=npgettext:1c,2,3 --keyword=dnpgettext:2c,3,4--keyword=dcnpgettext:2c,3,4 --language=awk --force-po I18N.bas

(Auch C Syntax kann verwendet werden, führt jedoch bei umfangreichen Programmen zu der Ausgabe von vielen Warnungen bzgl. unvollständiger Quelltextzeilen.)

Nach erfolgreicher Anwendung von xgettext befindet sich im Projektordner eine neue Datei namens I18N.pot mit folgendem Inhalt

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR me
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: I18N 0.0\n"
"Report-Msgid-Bugs-To: Email@provider.country\n"
"POT-Creation-Date: 2011-08-22 18:16+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: I18N.bas:21
msgid "Hello world!"
msgstr ""

Unter den Informationen zu dem Projekt (welche wir mit der Kommandozeile für xgettext spezifiziert haben) finden wir den Text aus I18N.bas. Um diesen zu übersetzen wird ein weiteres Hilfmittel namens poedit eingesetzt. Mit diesem grafischen Editor kann die Datei I18N.pot eingelesen und Text für Text übersetzt werden. Beim Abspeichern der Übersetzung sollte auf die Verwendung des o. g. Pfades (locale/de/LC_MESSAGES/) geachtet werden. Dabei werden die beiden Dateien I18N.po und I18N.mo erzeugt. Wie oben erwähnt ist die .mo Datei für die Verwendung zur Laufzeit gedacht und enthält auch binäre Daten. I18N.po ist der Ausgangsdatei I18N.pot sehr ähnlich, enthält aber weitere Informationen zum Übersetzerteam und natürlich die übersetzen Texte.

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: I18N 0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-08-22 16:33+0200\n"
"PO-Revision-Date: 2011-08-22 16:37+0100\n"
"Last-Translator: Thomas Freiherr <Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net>\n"
"Language-Team: awd <SAF>\n"
"Language: German\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: German\n"
"X-Poedit-Country: Germany\n"
"X-Poedit-SourceCharset: utf-8\n"

#: I18N.bas:21
msgid "Hello world!"
msgstr "Hallo Welt!"

Jetzt kann unser Programm gestartet werden und sollte auf einem System mit deutscher Spracheinstellung "Hallo Welt!" ausgeben.

Das Programm kann natürlich auch in der Originalsprache ausgeführt werden (ist zum Debuggen sinnvoll), was mit der Umgebungsvariablen LANG gesteuert wird

SprachewindowsLINUX
QuelltextSET LANG=C & I18N.exeLANG=C ./I18N
DeutschSET LANG=de_DE.UTF-8 & I18N.exe LANG=de_DE.UTF-8 ./I18N

Für I18N Programme wird die Verwendung von UTF-8 Zeichenkodierung dringend empfohlen.

Neben der Übersetzungsfunktion für einfache Texte bietet libintl weitere Funktionen für die formatierte Ausgabe von Texten und die Verwendung von Pluralformen. Deren Beschreibung sprengt den Rahmen dieses Codebeispieles und kann in der Externer Link!Originaldokumentation (en) nachgeschlagen werden.

Um weitere Übersetzungen zu generieren wird die Datei I18N.pot aus dem Projektordner an die jeweiligen Übersetzer versendet, damit diese länderspezifische _xyz_/LC_MESSAGES/I18N.mo Dateien für den locale Unterordner erzeugen.

Nach Änderungen der übersetzbaren Texte im Quelltext wird erneut xgettext eingesetzt und die I18N.pot Datei aktualisiert. Dann wird in poedit die Datei locale/de/LC_MESSAGES/I18N.po geladen und im Menü "Katalog -> Aus POT-Datei aktualisieren" ausgeführt. Dabei werden bereits übersetzte Text weiter verwendet. Nur die neuen oder veränderten Texte müssen übersetzt werden.

Für Programme mit grafischer Benutzeroberfläche (GUI) wird die Verwendung von GTK, Glade3 und GladeToBac empfohlen. Hier müssen Texte aus verschiedenen Quelltextdateien (*.bas, *.bi, *.ui oder *.glade) extrahiert und sinnvollerweise in einer einzigen .pot Datei zusammengefasst werden.

English

Externer Link!See english forum.


Zusätzliche Informationen und Funktionen
  • Das Code-Beispiel wurde am 23.08.2011 von MitgliedTJF angelegt.
  • Die aktuellste Version wurde am 23.08.2011 von MitgliedTJF gespeichert.
  Bearbeiten Bearbeiten  

  Versionen Versionen