我用
gettext
冻结了一个 pyinstaller --onefile palc.py
本地化(英语和法语,但将来可能更多)Python 脚本,它可以完美编译,但是当我尝试运行它时,它尝试使用存储在 locales
中的语言环境
目录(这意味着如果我不使用 locales
目录分发包,它就找不到它们)。正如您可以想象的那样,这是一个主要缺点,并且几乎破坏了 PyInstaller 的意义 - 为了分发它,我必须提供一个目录和包才能使其工作 - 不过,正如我将要告诉你,即使这样也行不通。
是否有可能(最好不要太难或需要大量重写)让 PyInstaller 使用
gettext
语言环境编译 Python 脚本?
palc.spec
,这是新版本:# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['palc.py'],
pathex=['~/python-text-calculator'],
binaries=[],
datas=[('~/python-text-calculator/locales/*', 'locales')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='palc',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='palc')
这是编译包的输出:
>>> ./palc
--------------------------------------------------------------------------
Language Selection
--------------------------------------------------------------------------
1 - English // Anglais
2 - Francais // French
Type: 1
Traceback (most recent call last):
File "/Users/computer/python-text-calculator/palc.py", line 30, in <module>
l_translations = gettext.translation('base', localedir='locales', languages=["en"])
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/gettext.py", line 514, in translation
raise OSError(ENOENT, 'No translation file found for domain', domain)
FileNotFoundError: [Errno 2] No translation file found for domain: 'base'
[19393] Failed to execute script palc
这与没有编辑
palc.spec
时的输出完全相同。另外,它使编译后的包成为一个directory(我在dist中的./palc
目录中运行了palc
),所以我仍然需要分发一个目录。我需要的是一个像here那样的单个文件。
有人可以帮忙吗?谢谢! :D
事情是:
您的新
.spec
文件是正确的,您正在说明您想要在捆绑包中包含哪些文件以及要将它们放在捆绑包中的位置。
正是你访问它们的方式导致了你的痛苦。
错误代码中的行
l_translations = gettext.translation('base', localedir='locales', languages=["en"])
建议您列出文件所在的目录 locales
这实际上是有意义的,因为您说过它们应该在那里......但是 PyInstaller
的工作方式有点不同。因为您将其捆绑为一个文件,所以它实际上是通过 bootloader
在其他地方打开的。locales
将其更改为:
from os import path
bundle_dir = getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__))) # get the bundle dir if bundled or simply the __file__ dir if not bundled
locales_dir = path.abspath(path.join(bundle_dir, 'locales'))
现在
locales_dir
指向您捆绑的目录PyInstaller
附注
如果这导致错误(不应该),请将您的
Analasis
数据部分编辑为 datas=[('~/python-text-calculator/locales', 'locales')],
首先,生成规范文件后,将规范文件提供给 pysintaller 而不是 Python 文件:运行
pyinstaller palc.spec
而不是 pyinstaller palc.py
。否则,pyinstaller 每次都会重置spec 文件。
然后,为了为 onefile 应用程序生成正确的规范文件,请使用
pyi-makespec --onefile palc.py
。它生成一个没有 COLLECT
步骤和不同 EXE
步骤的规范文件。
然后您可以在您的spec文件中使用自定义python函数来为您的语言环境构建
datas
(请记住,spec文件只是一个带有自定义文件扩展名的Python文件):
def get_locales_data():
locales_data = []
for locale in os.listdir(os.path.join('./locales')):
locales_data.append((
os.path.join('./locales', locale, 'LC_MESSAGES/*.mo'),
os.path.join('locales', locale, 'LC_MESSAGES')
))
return locales_data
然后使用该函数的返回值作为
Analysis
步骤中datas参数的值:
a = Analysis(['palc.py'],
...
datas=get_locales_data(),
...)
然后你将不得不调整你的代码以在正确的位置查找语言环境文件(根据运行时环境:打包与否),但我没有更多的时间来开发这部分答案,所以这里是一个线程讨论这个。 ;)
为了方便起见,下面是使用 pyi-makespec 生成并更改为包含语言环境的正确规范文件的示例:
# -*- mode: python ; coding: utf-8 -*-
import os
block_cipher = None
def get_locales_data():
locales_data = []
for locale in os.listdir(os.path.join('./locales')):
locales_data.append((
os.path.join('./locales', locale, 'LC_MESSAGES/*.mo'),
os.path.join('locales', locale, 'LC_MESSAGES')
))
return locales_data
a = Analysis(['palc.py'],
pathex=['.'],
binaries=[],
datas=get_locales_data(),
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='palc',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True )
import os
import sys
def pyinstaller_path_fix(path: str) -> str:
base_path = getattr(sys, '_MEIPASS', os.path.abspath("."))
return os.path.join(base_path, path)
--添加数据“../res:res”
--添加数据“../common_tools/locale/ru/LC_MESSAGES:common_tools/locale/ru/LC_MESSAGES”