+- with ZipFile(get_path('localization/locales.zip',
+- allow_user_override=False), 'r') as zf:
+- try:
+- buf = io.BytesIO(zf.read(mpath + '/messages.mo'))
+- except Exception:
+- pass
+- else:
+- trans = GNUTranslations(buf)
+- _CACHE[lang] = trans
++ p = os.path.join(mpath, 'calibre.mo')
++ if os.path.exists(p):
++ trans = GNUTranslations(open(p, 'rb'))
++ _CACHE[lang] = trans
+ if trans is None:
+ return getattr(__builtins__, '_', lambda x: x)(text)
+ return trans.gettext(text)
+diff -urNp -x '*.orig' calibre-4.20.0.org/src/calibre/utils/localization.py calibre-4.20.0/src/calibre/utils/localization.py
+--- calibre-4.20.0.org/src/calibre/utils/localization.py 2020-07-03 04:11:13.000000000 +0200
++++ calibre-4.20.0/src/calibre/utils/localization.py 2021-06-23 14:08:43.642418411 +0200
+@@ -104,51 +104,42 @@ def is_rtl():
+ return get_lang()[:2].lower() in {'he', 'ar'}
+
+
++def messages_path(lang):
++ return ('/usr/share/locale/%s/LC_MESSAGES'%lang)
++
+ def get_lc_messages_path(lang):
+ hlang = None
+- if zf_exists():
+- if lang in available_translations():
+- hlang = lang
+- else:
+- xlang = lang.split('_')[0].lower()
+- if xlang in available_translations():
+- hlang = xlang
+- return hlang
+-
+-
+-def zf_exists():
+- return os.path.exists(P('localization/locales.zip',
+- allow_user_override=False))
++ if lang in available_translations():
++ hlang = lang
++ else:
++ xlang = lang.split('_')[0]
++ if xlang in available_translations():
++ hlang = xlang
++ if hlang is not None:
++ return messages_path(hlang)
++ return None
+
+
+ _lang_trans = _country_trans = None
+
+
+ def get_all_translators():
+- from zipfile import ZipFile
+- with ZipFile(P('localization/locales.zip', allow_user_override=False), 'r') as zf:
+- for lang in available_translations():
+- mpath = get_lc_messages_path(lang)
+- if mpath is not None:
+- buf = io.BytesIO(zf.read(mpath + '/messages.mo'))
++ for lang in available_translations():
++ mpath = get_lc_messages_path(lang)
++ if mpath is not None:
++ try:
++ buf = open(os.path.join(mpath, 'calibre.mo'), 'rb')
+ yield lang, GNUTranslations(buf)
++ except:
++ pass
+
+
+ def get_single_translator(mpath, which='messages'):
+- from zipfile import ZipFile
+- with ZipFile(P('localization/locales.zip', allow_user_override=False), 'r') as zf:
+- path = f'{mpath}/{which}.mo'
+- data = zf.read(path)
+- buf = io.BytesIO(data)
+ try:
++ buf = open(os.path.join(mpath, '/%s.mo' % which), 'rb')
+ return GNUTranslations(buf)
+ except Exception as e:
+- import traceback
+- traceback.print_exc()
+- import hashlib
+- sig = hashlib.sha1(data).hexdigest()
+- raise ValueError('Failed to load translations for: {} (size: {} and signature: {}) with error: {}'.format(
+- path, len(data), sig, e))
++ pass # No translations for this lang
+
+
+ def get_iso639_translator(lang):
+@@ -219,27 +210,25 @@ def translator_for_lang(lang):
+ buf = load_po(mpath + '.po')
+
+ if mpath is not None:
+- from zipfile import ZipFile
+- with ZipFile(P('localization/locales.zip',
+- allow_user_override=False), 'r') as zf:
+- if buf is None:
+- buf = io.BytesIO(zf.read(mpath + '/messages.mo'))
++ if buf is None:
++ try: