提问人:mario 提问时间:4/17/2015 最后编辑:peterhmario 更新时间:2/22/2021 访问量:666
从PYZ中的虚拟目录获取GtkBuilder小部件的文本消息目录
Gettext message catalogues from virtual dir within PYZ for GtkBuilder widgets
问:
有没有一种既定的方法可以将 gettext 嵌入到 PYZ 包中?特别是让Gtks自动小部件翻译从ZIP存档中获取它们。locale/xy/LC_MESSAGES/*
对于其他嵌入式资源或/工作得足够好。但是系统和 Python gettext API 依赖于提供一个普通的旧 ;没有资源或字符串等。pkgutil.get_deta
inspect
get_source
bindtextdomain
localedir
因此,我无法设计出一个可行的甚至远程实用的解决方法:
虚拟
gvfs
/gio
路径
现在,使用 IRI 是直接从 zip 读取其他文件的替代方法。但是 glibs g_dgettext 仍然只是系统库的一个薄包装器。因此,任何此类 URL 都不能用作 .archive://file%3A%2F%2Fmypkg.pyz%2Fmessages%2F
localedir
部分提取 zip
我认为这就是 PyInstaller 的工作方式。但是,将某些东西捆绑为 .pyz 应用程序,只是在每次调用时都预先提取它,这当然有点荒谬。用户空间 gettext .
mo
/.po
提取
现在手动读出消息目录或仅使用琐碎的字典将是一种选择。但仅适用于应用程序内字符串。这同样是没有办法让Gtk/GtkBuilder隐式地拾取它们。
因此,我不得不手动遍历整个小部件树、标签、文本、内部小部件、markup_text等。可能,但是呵呵。保险丝安装
这将是超片状的。但是,当然,可以访问zip内容等。只是看起来是某种记忆猪。而且我怀疑它是否能保持可靠性,例如两个应用程序实例正在运行,或者之前一个不干净地终止。(我不知道,由于像 gettext 这样的系统库被一个脆弱的 zip 保险丝点绊倒了..)gvfs-mount
用于转换的Gtk信号/事件(?)
我发现对此很困惑,所以我有点确定Gtk/PyGtk/GI中没有其他小部件翻译机制。Gtk/Builder 期望并绑定到 gettext。
有没有更可靠的方法?
答:
这是我的示例 Glade/GtkBuilder/Gtk 应用程序。我定义了一个函数,该函数透明地转换 glade xml 文件并作为字符串传递给实例。xml_gettext
gtk.Builder
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()
我已将我的区域设置目录存档到其中,该目录包含在捆绑包中。
这是以下内容locale.zip
pyz
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')
为了使语言环境.zip作为文件系统,我使用fs中的ZipFS。
幸运的是,Python 不是 GNU gettext。 是纯 Python,它不使用 GNU gettext,而是模仿它。 具有两个核心功能和 .我在一个名为 .gettext
gettext
gettext
find
translation
mygettext
ZipFS
gettext
使用 ,并查找文件并打开它们,我将其替换为等效的文件 form 模块。os.path
os.path.exists
open
fs
这是我申请的内容。
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
由于文件前面有文本,通常是 shebang,因此在以二进制模式打开文件后,我会跳过这一行。应用程序中想要使用该函数的其他模块应改为导入 from 并将其设置为 的别名。pyz
pyz
gettext.gettext
zfs_gettext
mygettext
_
来了.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')
以下两个不应该被调用,因为它不使用 Python 。glade
gettext
glade.bindtextdomain('myapp','locale')
glade.textdomain('myapp')
评论
gettext