1
0
mirror of https://github.com/gryf/ebook-converter.git synced 2026-01-02 08:32:26 +01:00

Cleanup get_path usage in favor of pkg_resources.

This commit is contained in:
2020-04-12 18:33:23 +02:00
parent 0bf43ec6e8
commit 5aa0b1a0eb
17 changed files with 532 additions and 277 deletions

View File

@@ -0,0 +1,149 @@
/*
** Book Jacket generation
**
** The template for Book Jackets is template.xhtml
** This CSS is inserted into the generated HTML at conversion time
**
** Users can control parts of the presentation of a generated book jacket by
** editing this file and template.xhtml
**
** The general form of a generated Book Jacket:
**
** Title
** Series: series [series_index]
** Published: year_of_publication
** Rating: #_of_stars
** Tags: tag1, tag2, tag3 ...
**
** Comments
**
** If a book does not have Series information, a date of publication, a rating or tags
** the corresponding row is automatically removed from the generated book jacket.
*/
/*
** Banner
** Only affects EPUB, kindle ignores this type of formatting
*/
.cbj_banner {
background: #eee;
color: black;
border: thin solid black;
margin: 1em;
padding: 1em;
border-radius:8px;
}
/*
** Title
*/
table.cbj_header td.cbj_title {
font-size: 1.5em;
font-style: italic;
text-align: center;
}
/*
** Series
*/
table.cbj_header td.cbj_series {
text-align: center;
}
/*
** Author
*/
table.cbj_header td.cbj_author {
text-align: center;
}
/*
** Publisher/published
*/
table.cbj_header td.cbj_pubdata {
text-align: center;
}
/*
** Table containing Rating and Tags
*/
table.cbj_header {
width: 100%;
}
/*
** General formatting for banner labels
*/
table.cbj_header td.cbj_label {
font-family: sans-serif;
text-align: right;
width: 33%;
}
/*
** General formatting for banner content
*/
table.cbj_header td.cbj_content {
font-family: sans-serif;
text-align: left;
width:67%;
}
/*
** Metadata divider
*/
hr.metadata_divider {
width:90%;
margin-left:5%;
border-top: solid white 0px;
border-right: solid white 0px;
border-bottom: solid black 1px;
border-left: solid white 0px;
}
/*
** To skip a banner item (Series|Published|Rating|Tags),
** edit the appropriate CSS rule below.
*/
table.cbj_header tr.cbj_series {
/* Uncomment the next line to remove 'Series' from banner section */
/* display:none; */
}
table.cbj_header tr.cbj_pubdata {
/* Uncomment the next line to remove 'Published (year of publication)' from banner section */
/* display:none; */
}
table.cbj_header tr.cbj_rating {
/* Uncomment the next line to remove 'Rating' from banner section */
/* display:none; */
}
table.cbj_header tr.cbj_tags {
/* Uncomment the next line to remove 'Tags' from banner section */
/* display:none; */
}
hr {
/* This rule controls formatting for any hr elements contained in the jacket */
border-top: 0px solid white;
border-right: 0px solid white;
border-bottom: 2px solid black;
border-left: 0px solid white;
margin-left: 10%;
width: 80%;
}
.cbj_footer {
font-family: sans-serif;
font-size: 0.8em;
margin-top: 8px;
text-align: center;
}
.cbj_comments {
font-family: sans-serif;
}

View File

@@ -0,0 +1,58 @@
<html xmlns="{xmlns}">
<head>
<title>{title_str}</title>
<meta name="calibre-content" content="jacket"/>
<style type="text/css">{css}</style>
</head>
<body>
<div class="cbj_banner">
<table class="cbj_header">
<tr>
<td class="cbj_title" colspan="2">{title}</td>
</tr>
<tr>
<!-- If you do not want the series number to be formatted using roman numerals
change {series.roman} to {series}. You can also access the raw series name and number
using {series.name}, {series.number} or {series.roman_number} -->
<td class="cbj_series" colspan="2">{series.roman}</td>
</tr>
<tr>
<td class="cbj_author" colspan="2">{author}</td>
</tr>
<tr>
<td class="cbj_pubdata" colspan="2">{publisher} ({pubdate})</td>
</tr>
<tr>
<td class="cbj_author" colspan="2"><hr class="metadata_divider" /></td>
</tr>
<tr class="cbj_rating">
<td class="cbj_label">{rating_label}:</td>
<td class="cbj_content">{rating}</td>
</tr>
<tr class="cbj_tags">
<td class="cbj_label">{tags_label}:</td>
<!-- If you want the tags to be alphabetical, change {tags} to
{tags.alphabetical} -->
<td class="cbj_content">{tags}</td>
</tr>
<tr data-calibre-jacket-searchable-tags="1" style="color:white; display:none"><td colspan="2">{searchable_tags}</td></tr>
</table>
<div class="cbj_footer">{footer}</div>
</div>
<hr class="cbj_kindle_banner_hr" />
<!--
In addition you can add code to show the values of custom columns here.
The value is available as _column_name and the title as
_column_name_label. For example, if you have a custom column with
label #genre, you can add it to this template with _genre_label and
_genre. Note that the # is replaced by an underscore. For example
<div><b>{_genre_label}:</b> {_genre}</div>
-->
<div class="cbj_comments">{comments}</div>
</body>
</html>

View File

@@ -0,0 +1,210 @@
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:c="calibre"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:opf="http://www.idpf.org/2007/opf"
xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"
extension-element-prefixes="c"
xsl:version = "1.1"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<package version="2.0">
<metadata>
<xsl:call-template name="make-metadata"/>
</metadata>
<manifest>
<xsl:call-template name="make-manifest"/>
</manifest>
<spine toc="ncx">
<xsl:call-template name="make-spine"/>
</spine>
</package>
<xsl:call-template name="make-ncx"/>
<xsl:call-template name="make-css"/>
<xsl:for-each select="//Page">
<xsl:call-template name="make-page"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="make-css">
<xsl:for-each select="//TextStyle|//BlockStyle">
<c:styles/>
</xsl:for-each>
</xsl:template>
<xsl:template name="make-page">
<xsl:variable name="pid" select="@objid"/>
<xsl:document href="{$pid}.xhtml" method="xml" indent="yes">
<html>
<head>
<title><xsl:value-of select="//Title"/></title>
<link rel="stylesheet" type="text/css" href="styles.css"/>
</head>
<body class="body">
<xsl:apply-templates />
</body>
</html>
</xsl:document>
</xsl:template>
<xsl:template match="RuledLine">
<c:ruled-line/>
</xsl:template>
<xsl:template match="TextBlock">
<c:text-block/>
</xsl:template>
<xsl:template match="ImageBlock">
<c:image-block/>
</xsl:template>
<xsl:template match="Canvas">
<c:canvas/>
</xsl:template>
<xsl:template name="make-metadata">
<xsl:for-each select='//BookInformation/Info/BookInfo'>
<xsl:apply-templates select="Title"/>
<xsl:apply-templates select="Author"/>
<xsl:apply-templates select="Publisher"/>
<xsl:apply-templates select="Category|Classification"/>
</xsl:for-each>
<xsl:for-each select='//BookInformation/Info/DocInfo'>
<xsl:apply-templates select="Language"/>
<xsl:apply-templates select="Producer"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="Title">
<xsl:element name="dc:title">
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Author">
<xsl:element name="dc:creator">
<xsl:attribute name="opf:role">aut</xsl:attribute>
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Publisher">
<xsl:element name="dc:publisher">
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Producer">
<xsl:element name="dc:creator">
<xsl:attribute name="opf:role">bkp</xsl:attribute>
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Language">
<xsl:element name="dc:language">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Category|Classification">
<xsl:if test=".!=''">
<xsl:element name="dc:subject">
<xsl:value-of select="."/>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template name="make-manifest">
<xsl:for-each select='//Page'>
<xsl:element name="item">
<xsl:attribute name="id"><xsl:value-of select="@objid"/></xsl:attribute>
<xsl:attribute name="media-type"><xsl:text>application/xhtml+xml</xsl:text></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="@objid"/><xsl:text>.xhtml</xsl:text></xsl:attribute>
</xsl:element>
</xsl:for-each>
<xsl:for-each select="//ImageStream">
<xsl:element name="item">
<xsl:attribute name="id"><xsl:value-of select="@objid"/></xsl:attribute>
<xsl:attribute name="media-type"><c:media-type/></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="@file"/></xsl:attribute>
</xsl:element>
</xsl:for-each>
<xsl:for-each select="//RegistFont">
<xsl:element name="item">
<xsl:attribute name="id"><xsl:value-of select="@objid"/></xsl:attribute>
<xsl:attribute name="media-type"><c:media-type/></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="@file"/></xsl:attribute>
</xsl:element>
</xsl:for-each>
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
<item id="styles" href="styles.css" media-type="text/css" />
</xsl:template>
<xsl:template name="make-spine">
<xsl:for-each select='//Page'>
<xsl:element name="itemref">
<xsl:attribute name="idref"><xsl:value-of select="@objid"/></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="*">
<xsl:message>
<xsl:text>no match for element: "</xsl:text>
<xsl:value-of select="name(.)"/>
<xsl:text>" &#xA;</xsl:text>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="make-ncx">
<xsl:document href="toc.ncx" method="xml" indent="yes">
<ncx version="2005-1"
xmlns="http://www.daisy.org/z3986/2005/ncx/"
xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"
>
<head>
<meta name="dtb:uid" content="uid"/>
<meta name="dtb:depth" content="1"/>
<meta name="dtb:generator" content="calibre"/>
<meta name="dtb:totalPageCount" content="0"/>
<meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle><text>Table of Contents</text></docTitle>
<navMap>
<xsl:for-each select="//TOC/TocLabel">
<xsl:element name="navPoint">
<xsl:attribute name="id"><xsl:value-of select="count(preceding-sibling::*)"/></xsl:attribute>
<xsl:attribute name="playOrder"><xsl:value-of select="count(preceding-sibling::*)+1"/></xsl:attribute>
<navLabel><text><xsl:value-of select="."/></text></navLabel>
<xsl:element name="content">
<xsl:attribute name="src">
<xsl:value-of select="@refpage"/>.xhtml#<xsl:value-of select="@refobj"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:for-each>
</navMap>
</ncx>
</xsl:document>
</xsl:template>
</xsl:stylesheet>

View File

@@ -0,0 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
<head>
<title>Navigation</title>
</head>
<body>
</body>
</html>

View File

@@ -6,6 +6,7 @@ __copyright__ = '2008, Anatoly Shipitsin <norguhtar at gmail.com>'
Convert .fb2 files to .lrf
"""
import os, re
import pkg_resources
from ebook_converter.customize.conversion import InputFormatPlugin, OptionRecommendation
from ebook_converter import guess_type
@@ -86,8 +87,9 @@ class FB2Input(InputFormatPlugin):
css = re.sub(r'name\s*=\s*', 'class=', css)
self.extract_embedded_content(doc)
log.debug('Converting XML to HTML...')
with open(P('templates/fb2.xsl'), 'rb') as f:
ss = f.read().decode('utf-8')
with open(pkg_resources.resource_filename('ebook_converter',
'data/fb2.xsl')) as f:
ss = f.read().decode()
ss = ss.replace("__FB_NS__", fb_ns)
if options.no_inline_fb2_toc:
log('Disabling generation of inline FB2 TOC')

View File

@@ -6,6 +6,7 @@ __docformat__ = 'restructuredtext en'
import os, re, shutil
from os.path import dirname, abspath, relpath as _relpath, exists, basename
import pkg_resources
from ebook_converter.customize.conversion import OutputFormatPlugin, OptionRecommendation
from ebook_converter import CurrentDir
@@ -95,19 +96,31 @@ class HTMLOutput(OutputFormatPlugin):
with open(opts.template_html_index, 'rb') as f:
template_html_index_data = f.read()
else:
template_html_index_data = P('templates/html_export_default_index.tmpl', data=True)
with open(pkg_resources.
resource_filename('ebook_converter',
'data/html_export_default_index.tmpl')
) as fobj:
template_html_index_data = fobj.read().decode()
if opts.template_html is not None:
with open(opts.template_html, 'rb') as f:
template_html_data = f.read()
else:
template_html_data = P('templates/html_export_default.tmpl', data=True)
with open(pkg_resources.
resource_filename('ebook_converter',
'data/html_export_default.tmpl')
) as fobj:
template_html_data = fobj.read().decode()
if opts.template_css is not None:
with open(opts.template_css, 'rb') as f:
template_css_data = f.read()
else:
template_css_data = P('templates/html_export_default.css', data=True)
with open(pkg_resources.
resource_filename('ebook_converter',
'data/html_export_default.css')
) as fobj:
template_css_data = fobj.read().decode()
template_html_index_data = template_html_index_data.decode('utf-8')
template_html_data = template_html_data.decode('utf-8')

View File

@@ -7,6 +7,8 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys
import pkg_resources
from ebook_converter.customize.conversion import InputFormatPlugin
@@ -54,7 +56,12 @@ class LRFInput(InputFormatPlugin):
plot_map[ro] = imgstr[0].get('file')
self.log('Converting XML to HTML...')
styledoc = safe_xml_fromstring(P('templates/lrf.xsl', data=True))
with open(pkg_resources.
resource_filename('ebook_converter',
'data/lrf.xsl')) as fobj:
# TODO(gryf): change this nonsense to etree.parse() instead.
styledoc = safe_xml_fromstring(fobj.read())
media_type = MediaType()
styles = Styles()
text_block = TextBlock(styles, char_button_map, plot_map, log)

View File

@@ -5,29 +5,40 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
from PIL import ImageFont
from ebook_converter.utils.fonts.scanner import font_scanner
'''
Default fonts used in the PRS500
'''
LIBERATION_FONT_MAP = {
'Swis721 BT Roman' : 'LiberationSans-Regular',
'Dutch801 Rm BT Roman' : 'LiberationSerif-Regular',
'Courier10 BT Roman' : 'LiberationMono-Regular',
}
LIBERATION_FONT_MAP = {'Swis721 BT Roman': 'Liberation Sans Regular',
'Dutch801 Rm BT Roman': 'Liberation Serif Regular',
'Courier10 BT Roman': 'Liberation Mono Regular'}
_LIB_CACHE = {}
FONT_FILE_MAP = {}
def get_font(name, size, encoding='unic'):
'''
"""
Get an ImageFont object by name.
@param size: Font height in pixels. To convert from pts:
sz in pixels = (dpi/72) * size in pts
@param encoding: Font encoding to use. E.g. 'unic', 'symbol', 'ADOB', 'ADBE', 'aprm'
@param manager: A dict that will store the PersistentTemporary
'''
@param encoding: Font encoding to use. E.g. 'unic', 'symbol', 'ADOB',
'ADBE', 'aprm'
"""
if name in LIBERATION_FONT_MAP:
return ImageFont.truetype(P('fonts/liberation/%s.ttf' % LIBERATION_FONT_MAP[name]), size, encoding=encoding)
if not _LIB_CACHE:
for key in font_scanner.cache['fonts']:
record = font_scanner.cache['fonts'][key]
_LIB_CACHE[record['family_name'] + ' ' +
record['subfamily_name']] = record['path']
fpath = _LIB_CACHE.get(LIBERATION_FONT_MAP[name])
if not fpath:
raise ValueError('There is no liberation font existing in the '
'system. Please install them before converter '
'use.')
return ImageFont.truetype(fpath, size, encoding=encoding)
elif name in FONT_FILE_MAP:
return ImageFont.truetype(FONT_FILE_MAP[name], size, encoding=encoding)

View File

@@ -10,6 +10,7 @@ import re
from collections import Counter, OrderedDict
from functools import partial
from operator import itemgetter
import pkg_resources
from lxml import etree
from lxml.builder import ElementMaker
@@ -690,7 +691,10 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None, previous_nav=None)
if previous_nav is not None:
root = previous_nav[1]
else:
root = container.parse_xhtml(P('templates/new_nav.html', data=True).decode('utf-8'))
with open(pkg_resources.
resource_filename('ebook_converter',
'data/new_nav.html')) as fobj:
root = container.parse_xhtml(fobj.read())
container.replace(tocname, root)
else:
root = container.parsed(tocname)

View File

@@ -9,13 +9,13 @@ __docformat__ = 'restructuredtext en'
import sys, os, re
from xml.sax.saxutils import escape
from string import Formatter
import pkg_resources
from ebook_converter import guess_type, strftime
from ebook_converter.constants import iswindows
from ebook_converter.ebooks.oeb.base import XPath, XHTML_NS, XHTML, xml2text, urldefrag, urlnormalize
from ebook_converter.library.comments import comments_to_html, markdown
from ebook_converter.utils.date import is_date_undefined, as_local_time
from ebook_converter.utils.icu import sort_key
from ebook_converter.ebooks.chardet import strip_encoding_declarations
from ebook_converter.ebooks.metadata import fmt_sidx, rating_to_stars
from ebook_converter.polyglot.builtins import unicode_type, map
@@ -196,7 +196,7 @@ class Tags(unicode_type):
def __new__(self, tags, output_profile):
tags = [escape(x) for x in tags or ()]
t = unicode_type.__new__(self, ', '.join(tags))
t.alphabetical = ', '.join(sorted(tags, key=sort_key))
t.alphabetical = ', '.join(sorted(tags))
t.tags_list = tags
return t
@@ -232,8 +232,14 @@ def postprocess_jacket(root, output_profile, has_data):
def render_jacket(mi, output_profile,
alt_title=_('Unknown'), alt_tags=[], alt_comments='',
alt_publisher='', rescale_fonts=False, alt_authors=None):
css = P('jacket/stylesheet.css', data=True).decode('utf-8')
template = P('jacket/template.xhtml', data=True).decode('utf-8')
with open(pkg_resources.resource_filename('ebook_converter',
'data/jacket/stylesheet.css'),
'rb') as fobj:
css = fobj.read().decode()
with open(pkg_resources.resource_filename('ebook_converter',
'data/jacket/template.xhtml'),
'rb') as fobj:
template = fobj.read().decode()
template = re.sub(r'<!--.*?-->', '', template, flags=re.DOTALL)
css = re.sub(r'/\*.*?\*/', '', css, flags=re.DOTALL)

View File

@@ -6,6 +6,8 @@ __license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
from collections import namedtuple
import json
import pkg_resources
from ebook_converter.utils.localization import canonicalize_lang
@@ -17,9 +19,31 @@ ccodes, ccodemap, country_names = None, None, None
def get_codes():
global ccodes, ccodemap, country_names
if ccodes is None:
from ebook_converter.utils.serialize import msgpack_loads
data = msgpack_loads(P('localization/iso3166.calibre_msgpack', allow_user_override=False, data=True))
ccodes, ccodemap, country_names = data['codes'], data['three_map'], data['names']
src = pkg_resources.resource_filename('ebook_converter',
'data/iso_3166-1.json')
with open(src, 'rb') as f:
db = json.load(f)
codes = set()
three_map = {}
name_map = {}
unicode_type = type(u'')
for x in db['3166-1']:
two = x.get('alpha_2')
if two:
two = unicode_type(two)
codes.add(two)
name_map[two] = x.get('name')
if name_map[two]:
name_map[two] = unicode_type(name_map[two])
three = x.get('alpha_3')
if three:
three_map[unicode_type(three)] = two
data = {'names': name_map,
'codes': frozenset(codes),
'three_map': three_map}
ccodes, ccodemap, country_names = (data['codes'], data['three_map'],
data['names'])
return ccodes, ccodemap

View File

@@ -6,6 +6,7 @@ import os, re, traceback, numbers
from functools import partial
from collections import defaultdict
from copy import deepcopy
import pkg_resources
from ebook_converter.utils.lock import ExclusiveFile
from ebook_converter.constants import config_dir, CONFIG_DIR_MODE, ispy3, preferred_encoding, filesystem_encoding, iswindows
@@ -634,7 +635,9 @@ def read_custom_tweaks():
def default_tweaks_raw():
return P('default_tweaks.py', data=True, allow_user_override=False)
with open(pkg_resources.resource_filename('ebook_converter',
'data/default_tweaks.py')) as f:
return f.read().encode()
def read_tweaks():

View File

@@ -195,8 +195,7 @@ class FontScanner(Thread):
def __init__(self, folders=[], allowed_extensions={'ttf', 'otf'}):
Thread.__init__(self)
self.folders = folders + font_dirs() + [os.path.join(config_dir, 'fonts'),
P('fonts/liberation')]
self.folders = folders + font_dirs()
self.folders = [os.path.normcase(os.path.abspath(font)) for font in
self.folders]
self.font_families = ()

View File

@@ -451,6 +451,7 @@ def get_font_for_text(text, candidate_font_data=None):
def test_glyph_ids():
from ebook_converter.utils.fonts.free_type import FreeType
# TODO(gryf): move this test to test files
data = P('fonts/liberation/LiberationSerif-Regular.ttf', data=True)
ft = FreeType()
font = ft.load_font(data)
@@ -462,6 +463,7 @@ def test_glyph_ids():
def test_supports_text():
# TODO(gryf): move this test to test files
data = P('fonts/calibreSymbols.otf', data=True)
if not supports_text(data, '.★½'):
raise RuntimeError('Incorrectly returning that text is not supported')
@@ -470,6 +472,7 @@ def test_supports_text():
def test_find_font():
# TODO(gryf): move this test to test files
from ebook_converter.utils.fonts.scanner import font_scanner
abcd = '诶比西迪'
family = font_scanner.find_font_for_text(abcd)[0]

View File

@@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, locale, re, io, sys
import re, io, sys
import json
from gettext import GNUTranslations, NullTranslations
import pkg_resources
@@ -12,64 +12,6 @@ from ebook_converter.polyglot.builtins import is_py3, iteritems, unicode_type
_available_translations = None
def available_translations():
global _available_translations
if _available_translations is None:
stats = P('localization/stats.calibre_msgpack', allow_user_override=False)
if os.path.exists(stats):
from ebook_converter.utils.serialize import msgpack_loads
with open(stats, 'rb') as f:
stats = msgpack_loads(f.read())
else:
stats = {}
_available_translations = [x for x in stats if stats[x] > 0.1]
return _available_translations
def get_system_locale():
from ebook_converter.constants import iswindows, isosx, plugins
lang = None
if iswindows:
try:
from ebook_converter.constants import get_windows_user_locale_name
lang = get_windows_user_locale_name()
lang = lang.strip()
if not lang:
lang = None
except:
pass # Windows XP does not have the GetUserDefaultLocaleName fn
elif isosx:
try:
lang = plugins['usbobserver'][0].user_locale() or None
except:
# Fallback to environment vars if something bad happened
import traceback
traceback.print_exc()
if lang is None:
try:
envvars = ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES', 'LANG']
lang = locale.getdefaultlocale(envvars)[0]
# lang is None in two cases: either the environment variable is not
# set or it's "C". Stop looking for a language in the latter case.
if lang is None:
for var in envvars:
if os.environ.get(var) == 'C':
lang = 'en_US'
break
except:
pass # This happens on Ubuntu apparently
if lang is None and 'LANG' in os.environ: # Needed for OS X
try:
lang = os.environ['LANG']
except:
pass
if lang:
lang = lang.replace('-', '_')
lang = '_'.join(lang.split('_')[:2])
return lang
def sanitize_lang(lang):
if lang:
match = re.match('[a-z]{2,3}(_[A-Z]{2}){0,1}', lang)
@@ -83,101 +25,16 @@ def sanitize_lang(lang):
def get_lang():
'Try to figure out what language to display the interface in'
from ebook_converter.utils.config_base import prefs
lang = prefs['language']
lang = os.environ.get('CALIBRE_OVERRIDE_LANG', lang)
if lang:
return lang
try:
lang = get_system_locale()
except:
import traceback
traceback.print_exc()
lang = None
return sanitize_lang(lang)
return 'en_US'
def is_rtl():
return get_lang()[:2].lower() in {'he', 'ar'}
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))
_lang_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'))
yield lang, GNUTranslations(buf)
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 = '{}/{}.mo'.format(mpath, which)
data = zf.read(path)
buf = io.BytesIO(data)
try:
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))
def get_iso639_translator(lang):
lang = sanitize_lang(lang)
mpath = get_lc_messages_path(lang) if lang else None
return get_single_translator(mpath, 'iso639') if mpath else None
def get_translator(bcp_47_code):
parts = bcp_47_code.replace('-', '_').split('_')[:2]
parts[0] = lang_as_iso639_1(parts[0].lower()) or 'en'
if len(parts) > 1:
parts[1] = parts[1].upper()
lang = '_'.join(parts)
lang = {'pt':'pt_BR', 'zh':'zh_CN'}.get(lang, lang)
available = available_translations()
found = True
if lang == 'en' or lang.startswith('en_'):
return found, lang, NullTranslations()
if lang not in available:
lang = {'pt':'pt_BR', 'zh':'zh_CN'}.get(parts[0], parts[0])
if lang not in available:
lang = get_lang()
if lang not in available:
lang = 'en'
found = False
if lang == 'en':
return True, lang, NullTranslations()
return found, lang, get_single_translator(lang)
lcdata = {
'abday': ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'),
'abmon': ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
@@ -208,62 +65,9 @@ def load_po(path):
def set_translators():
global _lang_trans, lcdata
# To test different translations invoke as
# CALIBRE_OVERRIDE_LANG=de_DE.utf8 program
lang = get_lang()
t = buf = iso639 = None
if 'CALIBRE_TEST_TRANSLATION' in os.environ:
buf = load_po(os.path.expanduser(os.environ['CALIBRE_TEST_TRANSLATION']))
if lang:
mpath = get_lc_messages_path(lang)
if buf is None and mpath and os.access(mpath + '.po', os.R_OK):
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 mpath == 'nds':
mpath = 'de'
isof = mpath + '/iso639.mo'
try:
iso639 = io.BytesIO(zf.read(isof))
except:
pass # No iso639 translations for this lang
if buf is not None:
from ebook_converter.utils.serialize import msgpack_loads
try:
lcdata = msgpack_loads(zf.read(mpath + '/lcdata.calibre_msgpack'))
except:
pass # No lcdata
if buf is not None:
t = GNUTranslations(buf)
if iso639 is not None:
iso639 = _lang_trans = GNUTranslations(iso639)
t.add_fallback(iso639)
if t is None:
t = NullTranslations()
try:
set_translators.lang = t.info().get('language')
except Exception:
pass
if is_py3:
t.install(names=('ngettext',))
else:
t.install(unicode=True, names=('ngettext',))
# Now that we have installed a translator, we have to retranslate the help
# for the global prefs object as it was instantiated in get_lang(), before
# the translator was installed.
from ebook_converter.utils.config_base import prefs
prefs.retranslate_help()
t = NullTranslations()
set_translators.lang = t.info().get('language')
t.install(names=('ngettext',))
set_translators.lang = None
@@ -535,53 +339,5 @@ def get_udc():
return _udc
def user_manual_stats():
stats = getattr(user_manual_stats, 'stats', None)
if stats is None:
import json
try:
stats = json.loads(P('user-manual-translation-stats.json', allow_user_override=False, data=True))
except EnvironmentError:
stats = {}
user_manual_stats.stats = stats
return stats
def localize_user_manual_link(url):
#lc = lang_as_iso639_1(get_lang())
# if lc == 'en':
return url
# stats = user_manual_stats()
# if stats.get(lc, 0) < 0.3:
# return url
# from polyglot.urllib import urlparse, urlunparse
# parts = urlparse(url)
# path = re.sub(r'/generated/[a-z]+/', '/generated/%s/' % lc, parts.path or '')
# path = '/%s%s' % (lc, path)
# parts = list(parts)
# parts[2] = path
# return urlunparse(parts)
def website_languages():
stats = getattr(website_languages, 'stats', None)
if stats is None:
try:
stats = frozenset(P('localization/website-languages.txt', allow_user_override=False, data=True).split())
except EnvironmentError:
stats = frozenset()
website_languages.stats = stats
return stats
def localize_website_link(url):
lc = lang_as_iso639_1(get_lang())
langs = website_languages()
if lc == 'en' or lc not in langs:
return url
from ebook_converter.polyglot.urllib import urlparse, urlunparse
parts = urlparse(url)
path = '/{}{}'.format(lc, parts.path)
parts = list(parts)
parts[2] = path
return urlunparse(parts)

View File

@@ -34,6 +34,7 @@ install_requires =
msgpack
html5-parser
odfpy
setuptools
[options.entry_points]
console_scripts =