Dies ist mein Beispiel Glade/GtkBuilder/Gtk-Anwendung. Ich habe eine Funktion xml_gettext
definiert, die glade xml-Dateien transparent übersetzt und an gtk.Builder
Instanz als Zeichenfolge übergibt.
import mygettext as gettext
import os
import sys
import gtk
from gtk import glade
glade_xml = '''<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<signal name="delete-event" handler="onDeleteWindow" swapped="no"/>
<child>
<object class="GtkButton" id="button1">
<property name="label" translatable="yes">Welcome to Python!</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="pressed" handler="onButtonPressed" swapped="no"/>
</object>
</child>
</object>
</interface>'''
class Handler:
def onDeleteWindow(self, *args):
gtk.main_quit(*args)
def onButtonPressed(self, button):
print('locale: {}\nLANGUAGE: {}'.format(
gettext.find('myapp','locale'),os.environ['LANGUAGE']))
def main():
builder = gtk.Builder()
translated_xml = gettext.xml_gettext(glade_xml)
builder.add_from_string(translated_xml)
builder.connect_signals(Handler())
window = builder.get_object("window1")
window.show_all()
gtk.main()
if __name__ == '__main__':
main()
Ich habe meine locale Verzeichnisse in locale.zip
archiviert, die im pyz
Bündel enthalten ist.
Dies ist Inhalt locale.zip
(u'/locale/fr_FR/LC_MESSAGES/myapp.mo',
u'/locale/en_US/LC_MESSAGES/myapp.mo',
u'/locale/en_IN/LC_MESSAGES/myapp.mo')
Um die locale.zip als Dateisystem mache ich ZipFS von fs verwenden.
Zum Glück ist Python nicht GNU gettext. ist reines Python, es verwendet GNU gettext nicht, sondern imitiert es. hat zwei Kernfunktionen find
und translation
. Ich habe diese zwei in einem separaten Modul mit dem Namen mygettext
neu definiert, um sie Dateien aus der ZipFS
verwenden zu lassen.
gettext
verwendet os.path
, os.path.exists
und open
Dateien zu finden und öffnen Sie sie, die ich ersetzen die entsprechenden diejenigen fs
Modul bilden.
Dies ist Inhalt meiner Anwendung.
pyzzer.pyz -i glade_v1.pyz
# A zipped Python application
# Built with pyzzer
Archive contents:
glade_dist/glade_example.py
glade_dist/locale.zip
glade_dist/__init__.py
glade_dist/mygettext.py
__main__.py
Da pyz
Dateien Text, in der Regel eine shebang, vorangestellt, springe ich diese Zeile nach der pyz
Datei im Binär-Modus zu öffnen. Andere Module in der Anwendung, die die gettext.gettext
-Funktion verwenden möchten, sollten stattdessen zfs_gettext
von mygettext
importieren und es zu einem Alias zu _
machen.
Hier geht mygettext.py
.
from errno import ENOENT
from gettext import _expand_lang, _translations, _default_localedir
from gettext import GNUTranslations, NullTranslations
import gettext
import copy
import os
import sys
from xml.etree import ElementTree as ET
import zipfile
import fs
from fs.zipfs import ZipFS
zfs = None
if zipfile.is_zipfile(sys.argv[0]):
try:
myself = open(sys.argv[0],'rb')
next(myself)
zfs = ZipFS(ZipFS(myself,'r').open('glade_dist/locale.zip','rb'))
except:
pass
else:
try:
zfs = ZipFS('locale.zip','r')
except:
pass
if zfs:
os.path = fs.path
os.path.exists = zfs.exists
open = zfs.open
def find(domain, localedir=None, languages=None, all=0):
# Get some reasonable defaults for arguments that were not supplied
if localedir is None:
localedir = _default_localedir
if languages is None:
languages = []
for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
val = os.environ.get(envar)
if val:
languages = val.split(':')
break
if 'C' not in languages:
languages.append('C')
# now normalize and expand the languages
nelangs = []
for lang in languages:
for nelang in _expand_lang(lang):
if nelang not in nelangs:
nelangs.append(nelang)
# select a language
if all:
result = []
else:
result = None
for lang in nelangs:
if lang == 'C':
break
mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain)
mofile_lp = os.path.join("/usr/share/locale-langpack", lang,
'LC_MESSAGES', '%s.mo' % domain)
# first look into the standard locale dir, then into the
# langpack locale dir
# standard mo file
if os.path.exists(mofile):
if all:
result.append(mofile)
else:
return mofile
# langpack mofile -> use it
if os.path.exists(mofile_lp):
if all:
result.append(mofile_lp)
else:
return mofile
# langpack mofile -> use it
if os.path.exists(mofile_lp):
if all:
result.append(mofile_lp)
else:
return mofile_lp
return result
def translation(domain, localedir=None, languages=None,
class_=None, fallback=False, codeset=None):
if class_ is None:
class_ = GNUTranslations
mofiles = find(domain, localedir, languages, all=1)
if not mofiles:
if fallback:
return NullTranslations()
raise IOError(ENOENT, 'No translation file found for domain', domain)
# Avoid opening, reading, and parsing the .mo file after it's been done
# once.
result = None
for mofile in mofiles:
key = (class_, os.path.abspath(mofile))
t = _translations.get(key)
if t is None:
with open(mofile, 'rb') as fp:
t = _translations.setdefault(key, class_(fp))
# Copy the translation object to allow setting fallbacks and
# output charset. All other instance data is shared with the
# cached object.
t = copy.copy(t)
if codeset:
t.set_output_charset(codeset)
if result is None:
result = t
else:
result.add_fallback(t)
return result
def xml_gettext(xml_str):
root = ET.fromstring(xml_str)
labels = root.findall('.//*[@name="label"][@translatable="yes"]')
for label in labels:
label.text = _(label.text)
return ET.tostring(root)
gettext.find = find
gettext.translation = translation
_ = zfs_gettext = gettext.gettext
gettext.bindtextdomain('myapp','locale')
gettext.textdomain('myapp')
Die folgenden beiden nicht genannt werden sollte, weil glade
Python nicht gettext
verwenden.
glade.bindtextdomain('myapp','locale')
glade.textdomain('myapp')
Ich muss zugeben, ich verstehe nicht, was du meinst von "Glib ist eng an gettext angekettet". Das sind nur ein paar Makros, in GLib zwingt Sie gar nichts, sie zu benutzen oder gettext zu benutzen. – jku
Gut natürlich. Das ist auch, was die glib-Dokumentation behauptet ("erzwingt keine bestimmte Lokalisierungsmethode .."). Im Kontext von Gtk ist es einfach nicht so machbar. (Selbst wenn eine Neukompilierung eine Option wäre, würden die Makroschemata beispielsweise eine ICU-Substitution nicht leicht erlauben). Wenn Sie also alles außer gettext verwenden, müssen Sie alle Gtk-Widgets iterativ übersetzen. – mario
Wenn Ihr Problem darin besteht, dass GtkBuilder XML-Datei-Lokalisierung an gettext gebunden ist (was für mich ein gültiges Problem scheint), sollten Sie dies klar sagen. Wenn Sie implizieren, dass GLib (oder Gtk Application Code) Übersetzung irgendwie an gettext gebunden ist, macht es die Frage schwerer zu verstehen: Ich schätze, ich war nicht die Einzige, die sich am Kopf kratzte und dachte: "Diese Dinge haben nichts mit der Übersetzung zu tun ist tatsächlich gemacht: GTK Widgets kümmern sich nicht um die Übersetzung, die sie erwarten, um die übersetzte Zeichenkette "... – jku