mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Infrastructure for supporting translations of plugins
This commit is contained in:
parent
e4557408a8
commit
6d08762344
@ -92,6 +92,11 @@ The first thing to note is that this zip file has a lot more files in it, explai
|
||||
**about.txt**
|
||||
A text file with information about the plugin
|
||||
|
||||
**translations**
|
||||
A folder containing .mo files with the translations of the user
|
||||
interface of your plugin into different languages. See below for
|
||||
details.
|
||||
|
||||
Now let's look at the code.
|
||||
|
||||
__init__.py
|
||||
@ -175,6 +180,42 @@ You can see the ``prefs`` object being used in main.py:
|
||||
.. literalinclude:: plugin_examples/interface_demo/main.py
|
||||
:pyobject: DemoDialog.config
|
||||
|
||||
Adding translations to your plugin
|
||||
--------------------------------------
|
||||
|
||||
You can have all the user interface strings in your plugin translated and
|
||||
displayed in whatever language is set for the main calibre user interface.
|
||||
|
||||
The first step is to go through your plugin's source code and mark all user
|
||||
visible strings as translatable, by surrounding them in _(). For example::
|
||||
|
||||
action_spec = (_('My plugin'), None, _('My plugin is cool'), None)
|
||||
|
||||
Then use some program to generate .po files from your plugin source code. There
|
||||
should be one .po file for every language you want to translate into. For
|
||||
example: de.po for German, fr.po for French and so on. You can use the
|
||||
`poedit <http://www.poedit.net/>`_ program for this.
|
||||
|
||||
Send these .po files to your translators. Once you get them back, compile them
|
||||
into .mo files. You can again use poedit for that, or just do::
|
||||
|
||||
calibre-debug -c "from calibre.translations.msgfmt import main; main()" filename.po
|
||||
|
||||
Put the .mo files into the ``translations`` folder in your plugin.
|
||||
|
||||
The last step is to simply call the function `load_translations()` at the top
|
||||
of your plugin's .py files. For performance reasons you should only call this
|
||||
function in those .py files that actually have translatable strings. So in a
|
||||
typical User Interface plugin you would call it at the top of ``ui.py`` but not
|
||||
``__init__.py``.
|
||||
|
||||
You can test the translations of your plugins by changing the user interface
|
||||
language in calibre under Preferences->Look & Feel or by running calibre like
|
||||
this::
|
||||
|
||||
CALIBRE_OVERRIDE_LANG=de calibre
|
||||
|
||||
Replace ``de`` with the language code of the language you want to test.
|
||||
|
||||
The plugin API
|
||||
--------------------------------
|
||||
|
@ -81,6 +81,30 @@ def get_icons(zfp, name_or_list_of_names):
|
||||
ians = ians.pop(names[0])
|
||||
return ians
|
||||
|
||||
_translations_cache = {}
|
||||
|
||||
def load_translations(namespace, zfp):
|
||||
null = object()
|
||||
trans = _translations_cache.get(zfp, null)
|
||||
if trans is None:
|
||||
return
|
||||
if trans is null:
|
||||
from calibre.utils.localization import get_lang
|
||||
lang = get_lang()
|
||||
if not lang or lang == 'en': # performance optimization
|
||||
_translations_cache[zfp] = None
|
||||
return
|
||||
mo = get_resources(zfp, 'translations/%s.mo' % lang)
|
||||
if mo is None:
|
||||
_translations_cache[zfp] = None
|
||||
return
|
||||
from gettext import GNUTranslations
|
||||
from io import BytesIO
|
||||
trans = _translations_cache[zfp] = GNUTranslations(BytesIO(mo))
|
||||
|
||||
namespace['_'] = trans.gettext
|
||||
namespace['ngettext'] = trans.ngettext
|
||||
|
||||
class PluginLoader(object):
|
||||
|
||||
def __init__(self):
|
||||
@ -147,11 +171,11 @@ class PluginLoader(object):
|
||||
import_name), 'exec', dont_inherit=True)
|
||||
mod.__dict__['get_resources'] = partial(get_resources, zfp)
|
||||
mod.__dict__['get_icons'] = partial(get_icons, zfp)
|
||||
mod.__dict__['load_translations'] = partial(load_translations, mod.__dict__, zfp)
|
||||
exec compiled in mod.__dict__
|
||||
|
||||
return mod
|
||||
|
||||
|
||||
def load(self, path_to_zip_file):
|
||||
if not os.access(path_to_zip_file, os.R_OK):
|
||||
raise PluginNotFound('Cannot access %r'%path_to_zip_file)
|
||||
@ -193,7 +217,6 @@ class PluginLoader(object):
|
||||
del self.loaded_plugins[plugin_name]
|
||||
raise
|
||||
|
||||
|
||||
def _locate_code(self, zf, path_to_zip_file):
|
||||
names = [x if isinstance(x, unicode) else x.decode('utf-8') for x in
|
||||
zf.namelist()]
|
||||
|
@ -184,7 +184,11 @@ def make(filename, outfile):
|
||||
# Compute output
|
||||
output = generate()
|
||||
|
||||
try:
|
||||
outfile.write(output)
|
||||
except AttributeError:
|
||||
with open(outfile, 'wb') as f:
|
||||
f.write(output)
|
||||
|
||||
|
||||
def main():
|
||||
|
Loading…
x
Reference in New Issue
Block a user