Merge branch 'master' of https://github.com/jpirnay/crosspoint-reader into feat-koysnc-xpath
This commit is contained in:
@@ -858,3 +858,30 @@ float Epub::calculateProgress(const int currentSpineIndex, const float currentSp
|
||||
const float totalProgress = static_cast<float>(prevChapterSize) + sectionProgSize;
|
||||
return totalProgress / static_cast<float>(bookSize);
|
||||
}
|
||||
|
||||
int Epub::resolveHrefToSpineIndex(const std::string& href) const {
|
||||
if (!bookMetadataCache || !bookMetadataCache->isLoaded()) return -1;
|
||||
|
||||
// Extract filename (remove #anchor)
|
||||
std::string target = href;
|
||||
size_t hashPos = target.find('#');
|
||||
if (hashPos != std::string::npos) target = target.substr(0, hashPos);
|
||||
|
||||
// Same-file reference (anchor-only)
|
||||
if (target.empty()) return -1;
|
||||
|
||||
// Extract just the filename for comparison
|
||||
size_t targetSlash = target.find_last_of('/');
|
||||
std::string targetFilename = (targetSlash != std::string::npos) ? target.substr(targetSlash + 1) : target;
|
||||
|
||||
for (int i = 0; i < getSpineItemsCount(); i++) {
|
||||
const auto& spineHref = getSpineItem(i).href;
|
||||
// Try exact match first
|
||||
if (spineHref == target) return i;
|
||||
// Then filename-only match
|
||||
size_t spineSlash = spineHref.find_last_of('/');
|
||||
std::string spineFilename = (spineSlash != std::string::npos) ? spineHref.substr(spineSlash + 1) : spineHref;
|
||||
if (spineFilename == targetFilename) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -72,4 +72,5 @@ class Epub {
|
||||
size_t getBookSize() const;
|
||||
float calculateProgress(int currentSpineIndex, float currentSpineRead) const;
|
||||
CssParser* getCssParser() const { return cssParser.get(); }
|
||||
int resolveHrefToSpineIndex(const std::string& href) const;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
struct FootnoteEntry {
|
||||
char number[24];
|
||||
char href[64];
|
||||
|
||||
FootnoteEntry() {
|
||||
number[0] = '\0';
|
||||
href[0] = '\0';
|
||||
}
|
||||
};
|
||||
@@ -67,6 +67,18 @@ bool Page::serialize(FsFile& file) const {
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize footnotes (clamp to MAX_FOOTNOTES_PER_PAGE to match addFootnote/deserialize limits)
|
||||
const uint16_t fnCount = std::min<uint16_t>(footnotes.size(), MAX_FOOTNOTES_PER_PAGE);
|
||||
serialization::writePod(file, fnCount);
|
||||
for (uint16_t i = 0; i < fnCount; i++) {
|
||||
const auto& fn = footnotes[i];
|
||||
if (file.write(fn.number, sizeof(fn.number)) != sizeof(fn.number) ||
|
||||
file.write(fn.href, sizeof(fn.href)) != sizeof(fn.href)) {
|
||||
LOG_ERR("PGE", "Failed to write footnote");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,5 +104,24 @@ std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize footnotes
|
||||
uint16_t fnCount;
|
||||
serialization::readPod(file, fnCount);
|
||||
if (fnCount > MAX_FOOTNOTES_PER_PAGE) {
|
||||
LOG_ERR("PGE", "Invalid footnote count %u", fnCount);
|
||||
return nullptr;
|
||||
}
|
||||
page->footnotes.resize(fnCount);
|
||||
for (uint16_t i = 0; i < fnCount; i++) {
|
||||
auto& entry = page->footnotes[i];
|
||||
if (file.read(entry.number, sizeof(entry.number)) != sizeof(entry.number) ||
|
||||
file.read(entry.href, sizeof(entry.href)) != sizeof(entry.href)) {
|
||||
LOG_ERR("PGE", "Failed to read footnote %u", i);
|
||||
return nullptr;
|
||||
}
|
||||
entry.number[sizeof(entry.number) - 1] = '\0';
|
||||
entry.href[sizeof(entry.href) - 1] = '\0';
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "FootnoteEntry.h"
|
||||
#include "blocks/ImageBlock.h"
|
||||
#include "blocks/TextBlock.h"
|
||||
|
||||
@@ -57,6 +58,19 @@ class Page {
|
||||
public:
|
||||
// the list of block index and line numbers on this page
|
||||
std::vector<std::shared_ptr<PageElement>> elements;
|
||||
std::vector<FootnoteEntry> footnotes;
|
||||
static constexpr uint16_t MAX_FOOTNOTES_PER_PAGE = 16;
|
||||
|
||||
void addFootnote(const char* number, const char* href) {
|
||||
if (footnotes.size() >= MAX_FOOTNOTES_PER_PAGE) return; // Cap per-page footnotes
|
||||
FootnoteEntry entry;
|
||||
strncpy(entry.number, number, sizeof(entry.number) - 1);
|
||||
entry.number[sizeof(entry.number) - 1] = '\0';
|
||||
strncpy(entry.href, href, sizeof(entry.href) - 1);
|
||||
entry.href[sizeof(entry.href) - 1] = '\0';
|
||||
footnotes.push_back(entry);
|
||||
}
|
||||
|
||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
||||
bool serialize(FsFile& file) const;
|
||||
static std::unique_ptr<Page> deserialize(FsFile& file);
|
||||
|
||||
@@ -29,6 +29,7 @@ class TextBlock final : public Block {
|
||||
const BlockStyle& getBlockStyle() const { return blockStyle; }
|
||||
const std::vector<std::string>& getWords() const { return words; }
|
||||
bool isEmpty() override { return words.empty(); }
|
||||
size_t wordCount() const { return words.size(); }
|
||||
// given a renderer works out where to break the words into lines
|
||||
void render(const GfxRenderer& renderer, int fontId, int x, int y) const;
|
||||
BlockType getType() override { return TEXT_BLOCK; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// from
|
||||
// based on
|
||||
// https://github.com/atomic14/diy-esp32-epub-reader/blob/2c2f57fdd7e2a788d14a0bcb26b9e845a47aac42/lib/Epub/RubbishHtmlParser/htmlEntities.cpp
|
||||
|
||||
#include "htmlEntities.h"
|
||||
@@ -10,67 +10,105 @@ struct EntityPair {
|
||||
const char* value;
|
||||
};
|
||||
|
||||
static const EntityPair ENTITY_LOOKUP[] = {
|
||||
{""", "\""}, {"⁄", "⁄"}, {"&", "&"}, {"<", "<"}, {">", ">"},
|
||||
{"À", "À"}, {"Á", "Á"}, {"Â", "Â"}, {"Ã", "Ã"}, {"Ä", "Ä"},
|
||||
{"Å", "Å"}, {"Æ", "Æ"}, {"Ç", "Ç"}, {"È", "È"}, {"É", "É"},
|
||||
{"Ê", "Ê"}, {"Ë", "Ë"}, {"Ì", "Ì"}, {"Í", "Í"}, {"Î", "Î"},
|
||||
{"Ï", "Ï"}, {"Ð", "Ð"}, {"Ñ", "Ñ"}, {"Ò", "Ò"}, {"Ó", "Ó"},
|
||||
{"Ô", "Ô"}, {"Õ", "Õ"}, {"Ö", "Ö"}, {"Ø", "Ø"}, {"Ù", "Ù"},
|
||||
{"Ú", "Ú"}, {"Û", "Û"}, {"Ü", "Ü"}, {"Ý", "Ý"}, {"Þ", "Þ"},
|
||||
{"ß", "ß"}, {"à", "à"}, {"á", "á"}, {"â", "â"}, {"ã", "ã"},
|
||||
{"ä", "ä"}, {"å", "å"}, {"æ", "æ"}, {"ç", "ç"}, {"è", "è"},
|
||||
{"é", "é"}, {"ê", "ê"}, {"ë", "ë"}, {"ì", "ì"}, {"í", "í"},
|
||||
{"î", "î"}, {"ï", "ï"}, {"ð", "ð"}, {"ñ", "ñ"}, {"ò", "ò"},
|
||||
{"ó", "ó"}, {"ô", "ô"}, {"õ", "õ"}, {"ö", "ö"}, {"ø", "ø"},
|
||||
{"ù", "ù"}, {"ú", "ú"}, {"û", "û"}, {"ü", "ü"}, {"ý", "ý"},
|
||||
{"þ", "þ"}, {"ÿ", "ÿ"}, {" ", "\xC2\xA0"}, {"¡", "¡"}, {"¢", "¢"},
|
||||
{"£", "£"}, {"¤", "¤"}, {"¥", "¥"}, {"¦", "¦"}, {"§", "§"},
|
||||
{"¨", "¨"}, {"©", "©"}, {"ª", "ª"}, {"«", "«"}, {"¬", "¬"},
|
||||
{"­", ""}, {"®", "®"}, {"¯", "¯"}, {"°", "°"}, {"±", "±"},
|
||||
{"²", "²"}, {"³", "³"}, {"´", "´"}, {"µ", "µ"}, {"¶", "¶"},
|
||||
{"¸", "¸"}, {"¹", "¹"}, {"º", "º"}, {"»", "»"}, {"¼", "¼"},
|
||||
{"½", "½"}, {"¾", "¾"}, {"¿", "¿"}, {"×", "×"}, {"÷", "÷"},
|
||||
{"∀", "∀"}, {"∂", "∂"}, {"∃", "∃"}, {"∅", "∅"}, {"∇", "∇"},
|
||||
{"∈", "∈"}, {"∉", "∉"}, {"∋", "∋"}, {"∏", "∏"}, {"∑", "∑"},
|
||||
{"−", "−"}, {"∗", "∗"}, {"√", "√"}, {"∝", "∝"}, {"∞", "∞"},
|
||||
{"∠", "∠"}, {"∧", "∧"}, {"∨", "∨"}, {"∩", "∩"}, {"∪", "∪"},
|
||||
{"∫", "∫"}, {"∴", "∴"}, {"∼", "∼"}, {"≅", "≅"}, {"≈", "≈"},
|
||||
{"≠", "≠"}, {"≡", "≡"}, {"≤", "≤"}, {"≥", "≥"}, {"⊂", "⊂"},
|
||||
{"⊃", "⊃"}, {"⊄", "⊄"}, {"⊆", "⊆"}, {"⊇", "⊇"}, {"⊕", "⊕"},
|
||||
{"⊗", "⊗"}, {"⊥", "⊥"}, {"⋅", "⋅"}, {"Α", "Α"}, {"Β", "Β"},
|
||||
{"Γ", "Γ"}, {"Δ", "Δ"}, {"Ε", "Ε"}, {"Ζ", "Ζ"}, {"Η", "Η"},
|
||||
{"Θ", "Θ"}, {"Ι", "Ι"}, {"Κ", "Κ"}, {"Λ", "Λ"}, {"Μ", "Μ"},
|
||||
{"Ν", "Ν"}, {"Ξ", "Ξ"}, {"Ο", "Ο"}, {"Π", "Π"}, {"Ρ", "Ρ"},
|
||||
{"Σ", "Σ"}, {"Τ", "Τ"}, {"Υ", "Υ"}, {"Φ", "Φ"}, {"Χ", "Χ"},
|
||||
{"Ψ", "Ψ"}, {"Ω", "Ω"}, {"α", "α"}, {"β", "β"}, {"γ", "γ"},
|
||||
{"δ", "δ"}, {"ε", "ε"}, {"ζ", "ζ"}, {"η", "η"}, {"θ", "θ"},
|
||||
{"ι", "ι"}, {"κ", "κ"}, {"λ", "λ"}, {"μ", "μ"}, {"ν", "ν"},
|
||||
{"ξ", "ξ"}, {"ο", "ο"}, {"π", "π"}, {"ρ", "ρ"}, {"ς", "ς"},
|
||||
{"σ", "σ"}, {"τ", "τ"}, {"υ", "υ"}, {"φ", "φ"}, {"χ", "χ"},
|
||||
{"ψ", "ψ"}, {"ω", "ω"}, {"ϑ", "ϑ"}, {"ϒ", "ϒ"}, {"ϖ", "ϖ"},
|
||||
{"Œ", "Œ"}, {"œ", "œ"}, {"Š", "Š"}, {"š", "š"}, {"Ÿ", "Ÿ"},
|
||||
{"ƒ", "ƒ"}, {"ˆ", "ˆ"}, {"˜", "˜"}, {" ", " "}, {" ", " "},
|
||||
{" ", " "}, {"‌", ""}, {"‍", ""}, {"‎", ""}, {"‏", ""},
|
||||
{"–", "–"}, {"—", "—"}, {"‘", "‘"}, {"’", "’"}, {"‚", "‚"},
|
||||
{"“", "“"}, {"”", "”"}, {"„", "„"}, {"†", "†"}, {"‡", "‡"},
|
||||
{"•", "•"}, {"…", "…"}, {"‰", "‰"}, {"′", "′"}, {"″", "″"},
|
||||
{"‹", "‹"}, {"›", "›"}, {"‾", "‾"}, {"€", "€"}, {"™", "™"},
|
||||
{"←", "←"}, {"↑", "↑"}, {"→", "→"}, {"↓", "↓"}, {"↔", "↔"},
|
||||
{"↵", "↵"}, {"⌈", "⌈"}, {"⌉", "⌉"}, {"⌊", "⌊"}, {"⌋", "⌋"},
|
||||
{"◊", "◊"}, {"♠", "♠"}, {"♣", "♣"}, {"♥", "♥"}, {"♦", "♦"}};
|
||||
// Sorted lexicographically by key to allow binary search.
|
||||
static constexpr EntityPair ENTITY_LOOKUP[] = {
|
||||
{"Æ", "Æ"}, {"Á", "Á"}, {"Â", "Â"}, {"À", "À"}, {"Α", "Α"},
|
||||
{"Å", "Å"}, {"Ã", "Ã"}, {"Ä", "Ä"}, {"Β", "Β"}, {"Ç", "Ç"},
|
||||
{"Χ", "Χ"}, {"‡", "‡"}, {"Δ", "Δ"}, {"Ð", "Ð"}, {"É", "É"},
|
||||
{"Ê", "Ê"}, {"È", "È"}, {"Ε", "Ε"}, {"Η", "Η"}, {"Ë", "Ë"},
|
||||
{"Γ", "Γ"}, {"Í", "Í"}, {"Î", "Î"}, {"Ì", "Ì"}, {"Ι", "Ι"},
|
||||
{"Ï", "Ï"}, {"Κ", "Κ"}, {"Λ", "Λ"}, {"Μ", "Μ"}, {"Ñ", "Ñ"},
|
||||
{"Ν", "Ν"}, {"Œ", "Œ"}, {"Ó", "Ó"}, {"Ô", "Ô"}, {"Ò", "Ò"},
|
||||
{"Ω", "Ω"}, {"Ο", "Ο"}, {"Ø", "Ø"}, {"Õ", "Õ"}, {"Ö", "Ö"},
|
||||
{"Φ", "Φ"}, {"Π", "Π"}, {"″", "″"}, {"Ψ", "Ψ"}, {"Ρ", "Ρ"},
|
||||
{"Š", "Š"}, {"Σ", "Σ"}, {"Þ", "Þ"}, {"Τ", "Τ"}, {"Θ", "Θ"},
|
||||
{"Ú", "Ú"}, {"Û", "Û"}, {"Ù", "Ù"}, {"Υ", "Υ"}, {"Ü", "Ü"},
|
||||
{"Ξ", "Ξ"}, {"Ý", "Ý"}, {"Ÿ", "Ÿ"}, {"Ζ", "Ζ"}, {"á", "á"},
|
||||
{"â", "â"}, {"´", "´"}, {"æ", "æ"}, {"à", "à"}, {"α", "α"},
|
||||
{"&", "&"}, {"∧", "∧"}, {"∠", "∠"}, {"å", "å"}, {"≈", "≈"},
|
||||
{"ã", "ã"}, {"ä", "ä"}, {"„", "„"}, {"β", "β"}, {"¦", "¦"},
|
||||
{"•", "•"}, {"∩", "∩"}, {"ç", "ç"}, {"¸", "¸"}, {"¢", "¢"},
|
||||
{"χ", "χ"}, {"ˆ", "ˆ"}, {"♣", "♣"}, {"≅", "≅"}, {"©", "©"},
|
||||
{"↵", "↵"}, {"∪", "∪"}, {"¤", "¤"}, {"†", "†"}, {"↓", "↓"},
|
||||
{"°", "°"}, {"δ", "δ"}, {"♦", "♦"}, {"÷", "÷"}, {"é", "é"},
|
||||
{"ê", "ê"}, {"è", "è"}, {"∅", "∅"}, {" ", " "}, {" ", " "},
|
||||
{"ε", "ε"}, {"≡", "≡"}, {"η", "η"}, {"ð", "ð"}, {"ë", "ë"},
|
||||
{"€", "€"}, {"∃", "∃"}, {"ƒ", "ƒ"}, {"∀", "∀"}, {"½", "½"},
|
||||
{"¼", "¼"}, {"¾", "¾"}, {"⁄", "⁄"}, {"γ", "γ"}, {"≥", "≥"},
|
||||
{">", ">"}, {"↔", "↔"}, {"♥", "♥"}, {"…", "…"}, {"í", "í"},
|
||||
{"î", "î"}, {"¡", "¡"}, {"ì", "ì"}, {"∞", "∞"}, {"∫", "∫"},
|
||||
{"ι", "ι"}, {"¿", "¿"}, {"∈", "∈"}, {"ï", "ï"}, {"κ", "κ"},
|
||||
{"λ", "λ"}, {"«", "«"}, {"←", "←"}, {"⌈", "⌈"}, {"“", "\u201C"},
|
||||
{"≤", "≤"}, {"⌊", "⌊"}, {"∗", "∗"}, {"◊", "◊"}, {"‎", "\u200E"},
|
||||
{"‹", "‹"}, {"‘", "\u2018"}, {"<", "<"}, {"¯", "¯"}, {"—", "—"},
|
||||
{"µ", "µ"}, {"−", "−"}, {"μ", "μ"}, {"∇", "∇"}, {" ", "\xC2\xA0"},
|
||||
{"–", "–"}, {"≠", "≠"}, {"∋", "∋"}, {"¬", "¬"}, {"∉", "∉"},
|
||||
{"⊄", "⊄"}, {"ñ", "ñ"}, {"ν", "ν"}, {"ó", "ó"}, {"ô", "ô"},
|
||||
{"œ", "œ"}, {"ò", "ò"}, {"‾", "‾"}, {"ω", "ω"}, {"ο", "ο"},
|
||||
{"⊕", "⊕"}, {"∨", "∨"}, {"ª", "ª"}, {"º", "º"}, {"ø", "ø"},
|
||||
{"õ", "õ"}, {"⊗", "⊗"}, {"ö", "ö"}, {"¶", "¶"}, {"∂", "∂"},
|
||||
{"‰", "‰"}, {"⊥", "⊥"}, {"φ", "φ"}, {"π", "π"}, {"ϖ", "ϖ"},
|
||||
{"±", "±"}, {"£", "£"}, {"′", "′"}, {"∏", "∏"}, {"∝", "∝"},
|
||||
{"ψ", "ψ"}, {""", "\""}, {"√", "√"}, {"»", "»"}, {"→", "→"},
|
||||
{"⌉", "⌉"}, {"”", "\u201D"}, {"®", "®"}, {"⌋", "⌋"}, {"ρ", "ρ"},
|
||||
{"‏", "\u200F"}, {"›", "›"}, {"’", "\u2019"}, {"‚", "‚"}, {"š", "š"},
|
||||
{"⋅", "⋅"}, {"§", "§"}, {"­", "\xC2\xAD"}, {"σ", "σ"}, {"ς", "ς"},
|
||||
{"∼", "∼"}, {"♠", "♠"}, {"⊂", "⊂"}, {"⊆", "⊆"}, {"∑", "∑"},
|
||||
{"¹", "¹"}, {"²", "²"}, {"³", "³"}, {"⊃", "⊃"}, {"⊇", "⊇"},
|
||||
{"ß", "ß"}, {"τ", "τ"}, {"∴", "∴"}, {"θ", "θ"}, {"ϑ", "ϑ"},
|
||||
{" ", " "}, {"þ", "þ"}, {"˜", "˜"}, {"×", "×"}, {"™", "™"},
|
||||
{"ú", "ú"}, {"↑", "↑"}, {"û", "û"}, {"ù", "ù"}, {"¨", "¨"},
|
||||
{"ϒ", "ϒ"}, {"υ", "υ"}, {"ü", "ü"}, {"ξ", "ξ"}, {"ý", "ý"},
|
||||
{"¥", "¥"}, {"ÿ", "ÿ"}, {"ζ", "ζ"}, {"‍", "\u200D"}, {"‌", "\u200C"},
|
||||
};
|
||||
|
||||
static const size_t ENTITY_LOOKUP_COUNT = sizeof(ENTITY_LOOKUP) / sizeof(ENTITY_LOOKUP[0]);
|
||||
|
||||
// Lookup a single HTML entity and return its UTF-8 value
|
||||
const char* lookupHtmlEntity(const char* entity, int len) {
|
||||
for (size_t i = 0; i < ENTITY_LOOKUP_COUNT; i++) {
|
||||
const char* key = ENTITY_LOOKUP[i].key;
|
||||
// Verify the table is sorted at compile time.
|
||||
static constexpr int constexprStrcmp(const char* a, const char* b) {
|
||||
for (size_t i = 0;; i++) {
|
||||
if (a[i] != b[i]) return (unsigned char)a[i] < (unsigned char)b[i] ? -1 : 1;
|
||||
if (a[i] == '\0') return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isTableSorted() {
|
||||
for (size_t i = 1; i < ENTITY_LOOKUP_COUNT; i++) {
|
||||
if (constexprStrcmp(ENTITY_LOOKUP[i - 1].key, ENTITY_LOOKUP[i].key) >= 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static_assert(isTableSorted(), "ENTITY_LOOKUP must be sorted lexicographically by key");
|
||||
|
||||
// Lookup a single HTML entity and return its UTF-8 value.
|
||||
const char* lookupHtmlEntity(const char* entity, size_t len) {
|
||||
if (entity == nullptr || len == 0) return nullptr;
|
||||
|
||||
size_t lo = 0;
|
||||
size_t hi = ENTITY_LOOKUP_COUNT;
|
||||
|
||||
while (lo < hi) {
|
||||
const size_t mid = lo + (hi - lo) / 2;
|
||||
const char* key = ENTITY_LOOKUP[mid].key;
|
||||
const size_t keyLen = strlen(key);
|
||||
if (static_cast<size_t>(len) == keyLen && memcmp(entity, key, keyLen) == 0) {
|
||||
return ENTITY_LOOKUP[i].value;
|
||||
const size_t cmpLen = (len < keyLen) ? len : keyLen;
|
||||
int cmp = memcmp(entity, key, cmpLen);
|
||||
if (cmp == 0) {
|
||||
// safety net: if prefix equal, shorter string is considered smaller
|
||||
if (len < keyLen)
|
||||
cmp = -1;
|
||||
else if (len > keyLen)
|
||||
cmp = 1;
|
||||
else
|
||||
cmp = 0;
|
||||
}
|
||||
|
||||
if (cmp == 0) return ENTITY_LOOKUP[mid].value;
|
||||
if (cmp < 0)
|
||||
hi = mid;
|
||||
else
|
||||
lo = mid + 1;
|
||||
}
|
||||
|
||||
return nullptr; // Entity not found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// from
|
||||
// based on
|
||||
// https://github.com/atomic14/diy-esp32-epub-reader/blob/2c2f57fdd7e2a788d14a0bcb26b9e845a47aac42/lib/Epub/RubbishHtmlParser/htmlEntities.cpp
|
||||
|
||||
#pragma once
|
||||
@@ -6,4 +6,4 @@
|
||||
|
||||
// Lookup a single HTML entity (including & and ;) and return its UTF-8 value
|
||||
// Returns nullptr if entity is not found
|
||||
const char* lookupHtmlEntity(const char* entity, int len);
|
||||
const char* lookupHtmlEntity(const char* entity, size_t len);
|
||||
|
||||
@@ -49,6 +49,24 @@ bool matches(const char* tag_name, const char* possible_tags[], const int possib
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* getAttribute(const XML_Char** atts, const char* attrName) {
|
||||
if (!atts) return nullptr;
|
||||
for (int i = 0; atts[i]; i += 2) {
|
||||
if (strcmp(atts[i], attrName) == 0) return atts[i + 1];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isInternalEpubLink(const char* href) {
|
||||
if (!href || href[0] == '\0') return false;
|
||||
if (strncmp(href, "http://", 7) == 0 || strncmp(href, "https://", 8) == 0) return false;
|
||||
if (strncmp(href, "mailto:", 7) == 0) return false;
|
||||
if (strncmp(href, "ftp://", 6) == 0) return false;
|
||||
if (strncmp(href, "tel:", 4) == 0) return false;
|
||||
if (strncmp(href, "javascript:", 11) == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isHeaderOrBlock(const char* name) {
|
||||
return matches(name, HEADER_TAGS, NUM_HEADER_TAGS) || matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS);
|
||||
}
|
||||
@@ -121,6 +139,7 @@ void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) {
|
||||
makePages();
|
||||
}
|
||||
currentTextBlock.reset(new ParsedText(extraParagraphSpacing, hyphenationEnabled, blockStyle));
|
||||
wordsExtractedInBlock = 0;
|
||||
}
|
||||
|
||||
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
|
||||
@@ -430,6 +449,50 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
|
||||
}
|
||||
}
|
||||
|
||||
// Detect internal <a href="..."> links (footnotes, cross-references)
|
||||
// Note: <aside epub:type="footnote"> elements are rendered as normal content
|
||||
// without special handling. Links pointing to them are collected as footnotes.
|
||||
if (strcmp(name, "a") == 0) {
|
||||
const char* href = getAttribute(atts, "href");
|
||||
|
||||
bool isInternalLink = isInternalEpubLink(href);
|
||||
|
||||
// Special case: javascript:void(0) links with data attributes
|
||||
// Example: <a href="javascript:void(0)"
|
||||
// data-xyz="{"name":"OPS/ch2.xhtml","frag":"id46"}">
|
||||
if (href && strncmp(href, "javascript:", 11) == 0) {
|
||||
isInternalLink = false;
|
||||
// TODO: Parse data-* attributes to extract actual href
|
||||
}
|
||||
|
||||
if (isInternalLink) {
|
||||
// Flush buffer before style change
|
||||
if (self->partWordBufferIndex > 0) {
|
||||
self->flushPartWordBuffer();
|
||||
self->nextWordContinues = true;
|
||||
}
|
||||
self->insideFootnoteLink = true;
|
||||
self->footnoteLinkDepth = self->depth;
|
||||
strncpy(self->currentFootnoteLinkHref, href, sizeof(self->currentFootnoteLinkHref) - 1);
|
||||
self->currentFootnoteLinkHref[sizeof(self->currentFootnoteLinkHref) - 1] = '\0';
|
||||
self->currentFootnoteLinkText[0] = '\0';
|
||||
self->currentFootnoteLinkTextLen = 0;
|
||||
|
||||
// Apply underline style to visually indicate the link
|
||||
self->underlineUntilDepth = std::min(self->underlineUntilDepth, self->depth);
|
||||
StyleStackEntry entry;
|
||||
entry.depth = self->depth;
|
||||
entry.hasUnderline = true;
|
||||
entry.underline = true;
|
||||
self->inlineStyleStack.push_back(entry);
|
||||
self->updateEffectiveInlineStyle();
|
||||
|
||||
// Skip CSS resolution — we already handled styling for this <a> tag
|
||||
self->depth += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute CSS style for this element
|
||||
CssStyle cssStyle;
|
||||
if (self->cssParser) {
|
||||
@@ -582,6 +645,19 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect footnote link display text (for the number label)
|
||||
// Skip whitespace and brackets to normalize noterefs like "[1]" → "1"
|
||||
if (self->insideFootnoteLink) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned char c = static_cast<unsigned char>(s[i]);
|
||||
if (isWhitespace(c) || c == '[' || c == ']') continue;
|
||||
if (self->currentFootnoteLinkTextLen < static_cast<int>(sizeof(self->currentFootnoteLinkText)) - 1) {
|
||||
self->currentFootnoteLinkText[self->currentFootnoteLinkTextLen++] = c;
|
||||
self->currentFootnoteLinkText[self->currentFootnoteLinkTextLen] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (isWhitespace(s[i])) {
|
||||
// Currently looking at whitespace, if there's anything in the partWordBuffer, flush it
|
||||
@@ -685,7 +761,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
||||
void XMLCALL ChapterHtmlSlimParser::defaultHandlerExpand(void* userData, const XML_Char* s, const int len) {
|
||||
// Check if this looks like an entity reference (&...;)
|
||||
if (len >= 3 && s[0] == '&' && s[len - 1] == ';') {
|
||||
const char* utf8Value = lookupHtmlEntity(s, len);
|
||||
const char* utf8Value = lookupHtmlEntity(s, static_cast<size_t>(len));
|
||||
if (utf8Value != nullptr) {
|
||||
// Known entity: expand to its UTF-8 value
|
||||
characterData(userData, utf8Value, strlen(utf8Value));
|
||||
@@ -743,6 +819,21 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
|
||||
|
||||
self->depth -= 1;
|
||||
|
||||
// Closing a footnote link — create entry from collected text and href
|
||||
if (self->insideFootnoteLink && self->depth == self->footnoteLinkDepth) {
|
||||
if (self->currentFootnoteLinkText[0] != '\0' && self->currentFootnoteLinkHref[0] != '\0') {
|
||||
FootnoteEntry entry;
|
||||
strncpy(entry.number, self->currentFootnoteLinkText, sizeof(entry.number) - 1);
|
||||
entry.number[sizeof(entry.number) - 1] = '\0';
|
||||
strncpy(entry.href, self->currentFootnoteLinkHref, sizeof(entry.href) - 1);
|
||||
entry.href[sizeof(entry.href) - 1] = '\0';
|
||||
int wordIndex =
|
||||
self->wordsExtractedInBlock + (self->currentTextBlock ? static_cast<int>(self->currentTextBlock->size()) : 0);
|
||||
self->pendingFootnotes.push_back({wordIndex, entry});
|
||||
}
|
||||
self->insideFootnoteLink = false;
|
||||
}
|
||||
|
||||
// Leaving skip
|
||||
if (self->skipUntilDepth == self->depth) {
|
||||
self->skipUntilDepth = INT_MAX;
|
||||
@@ -910,6 +1001,15 @@ void ChapterHtmlSlimParser::addLineToPage(std::shared_ptr<TextBlock> line) {
|
||||
currentPageNextY = 0;
|
||||
}
|
||||
|
||||
// Track cumulative words to assign footnotes to the page containing their anchor
|
||||
wordsExtractedInBlock += line->wordCount();
|
||||
auto footnoteIt = pendingFootnotes.begin();
|
||||
while (footnoteIt != pendingFootnotes.end() && footnoteIt->first <= wordsExtractedInBlock) {
|
||||
currentPage->addFootnote(footnoteIt->second.number, footnoteIt->second.href);
|
||||
++footnoteIt;
|
||||
}
|
||||
pendingFootnotes.erase(pendingFootnotes.begin(), footnoteIt);
|
||||
|
||||
// Apply horizontal left inset (margin + padding) as x position offset
|
||||
const int16_t xOffset = line->getBlockStyle().leftInset();
|
||||
currentPage->elements.push_back(std::make_shared<PageLine>(line, xOffset, currentPageNextY));
|
||||
@@ -947,6 +1047,16 @@ void ChapterHtmlSlimParser::makePages() {
|
||||
renderer, fontId, effectiveWidth,
|
||||
[this](const std::shared_ptr<TextBlock>& textBlock) { addLineToPage(textBlock); });
|
||||
|
||||
// Fallback: transfer any remaining pending footnotes to current page.
|
||||
// Normally addLineToPage handles this via word-index tracking, but this catches
|
||||
// edge cases where a footnote's word index equals the exact block size.
|
||||
if (!pendingFootnotes.empty() && currentPage) {
|
||||
for (const auto& [idx, fn] : pendingFootnotes) {
|
||||
currentPage->addFootnote(fn.number, fn.href);
|
||||
}
|
||||
pendingFootnotes.clear();
|
||||
}
|
||||
|
||||
// Apply bottom spacing after the paragraph (stored in pixels)
|
||||
if (blockStyle.marginBottom > 0) {
|
||||
currentPageNextY += blockStyle.marginBottom;
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include <climits>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "../FootnoteEntry.h"
|
||||
#include "../ParsedText.h"
|
||||
#include "../blocks/ImageBlock.h"
|
||||
#include "../blocks/TextBlock.h"
|
||||
@@ -66,6 +68,15 @@ class ChapterHtmlSlimParser {
|
||||
int tableRowIndex = 0;
|
||||
int tableColIndex = 0;
|
||||
|
||||
// Footnote link tracking
|
||||
bool insideFootnoteLink = false;
|
||||
int footnoteLinkDepth = -1;
|
||||
char currentFootnoteLinkText[24] = {};
|
||||
int currentFootnoteLinkTextLen = 0;
|
||||
char currentFootnoteLinkHref[64] = {};
|
||||
std::vector<std::pair<int, FootnoteEntry>> pendingFootnotes; // <wordIndex, entry>
|
||||
int wordsExtractedInBlock = 0;
|
||||
|
||||
void updateEffectiveInlineStyle();
|
||||
void startNewTextBlock(const BlockStyle& blockStyle);
|
||||
void flushPartWordBuffer();
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
_language_name: "Dansk"
|
||||
_language_code: "DA"
|
||||
_order: "15"
|
||||
|
||||
STR_CROSSPOINT: "CrossPoint"
|
||||
STR_BOOTING: "STARTER"
|
||||
STR_SLEEPING: "HVILE"
|
||||
STR_ENTERING_SLEEP: "Går i hvile"
|
||||
STR_BROWSE_FILES: "Gennemsøg filer"
|
||||
STR_FILE_TRANSFER: "Filoverførelse"
|
||||
STR_SETTINGS_TITLE: "Indstillinger"
|
||||
STR_CALIBRE_LIBRARY: "Calibre bibliotek"
|
||||
STR_CONTINUE_READING: "Fortsæt med at læse"
|
||||
STR_NO_OPEN_BOOK: "Ingen åben bog"
|
||||
STR_START_READING: "Start læsning nedenfor"
|
||||
STR_BOOKS: "Bøger"
|
||||
STR_NO_BOOKS_FOUND: "Ingen bøger fundet"
|
||||
STR_SELECT_CHAPTER: "Vælg kapitel"
|
||||
STR_NO_CHAPTERS: "Ingen kapitler"
|
||||
STR_END_OF_BOOK: "Bogen er færdig"
|
||||
STR_EMPTY_CHAPTER: "Tomt kapitel"
|
||||
STR_INDEXING: "Indekserer"
|
||||
STR_MEMORY_ERROR: "Hukommelsesfejl"
|
||||
STR_PAGE_LOAD_ERROR: "Fejl ved sideindlæsning"
|
||||
STR_EMPTY_FILE: "Tom fil"
|
||||
STR_OUT_OF_BOUNDS: "Uden for grænsen"
|
||||
STR_LOADING: "Indlæser..."
|
||||
STR_LOADING_POPUP: "Indlæser"
|
||||
STR_LOAD_XTC_FAILED: "Mislykkedes at indlæse XTC"
|
||||
STR_LOAD_TXT_FAILED: "Mislykkedes at indlæse TXT"
|
||||
STR_LOAD_EPUB_FAILED: "Mislykkedes at indlæse EPUB"
|
||||
STR_SD_CARD_ERROR: "SD kort fejl"
|
||||
STR_WIFI_NETWORKS: "Trådløse netværk"
|
||||
STR_NO_NETWORKS: "Intet netværk fundet"
|
||||
STR_NETWORKS_FOUND: "%zu netværk fundet"
|
||||
STR_SCANNING: "Skanner..."
|
||||
STR_CONNECTING: "Forbinder..."
|
||||
STR_CONNECTED: "Forbundet!"
|
||||
STR_CONNECTION_FAILED: "Forbindelsen mislykkedes"
|
||||
STR_CONNECTION_TIMEOUT: "Forbindelses timeout"
|
||||
STR_FORGET_NETWORK: "Glem netværk?"
|
||||
STR_SAVE_PASSWORD: "Gem adgangskode til næste gang?"
|
||||
STR_REMOVE_PASSWORD: "Fjern gemt adgangskode?"
|
||||
STR_PRESS_OK_SCAN: "Tryk OK for at scanne igen"
|
||||
STR_PRESS_ANY_CONTINUE: "Tryk på en knap for at fortsætte"
|
||||
STR_SELECT_HINT: "VENSTRE/HØJRE: Vælg | OK: Bekræft"
|
||||
STR_HOW_CONNECT: "Hvordan vil du oprette forbindelse?"
|
||||
STR_JOIN_NETWORK: "Tilslut netværk"
|
||||
STR_CREATE_HOTSPOT: "Opret Hotspot"
|
||||
STR_JOIN_DESC: "Opret forbindelse til et eksisterende WiFi-netværk"
|
||||
STR_HOTSPOT_DESC: "Opret et WiFi-netværk andre kan tilslutte sig"
|
||||
STR_STARTING_HOTSPOT: "Starter Hotspot..."
|
||||
STR_HOTSPOT_MODE: "Hotspot-tilstand"
|
||||
STR_CONNECT_WIFI_HINT: "Opret forbindelse fra din enhed til dette WiFi-netværk"
|
||||
STR_OPEN_URL_HINT: "Åbn denne URL i din browser"
|
||||
STR_OR_HTTP_PREFIX: "eller http://"
|
||||
STR_SCAN_QR_HINT: "eller scan QR-kode med din telefon:"
|
||||
STR_CALIBRE_WIRELESS: "Calibre Wireless"
|
||||
STR_CALIBRE_WEB_URL: "Calibre Web URL"
|
||||
STR_CONNECT_WIRELESS: "Opret forbindelse som trådløs enhed"
|
||||
STR_NETWORK_LEGEND: "* = Krypteret | + = Gemt"
|
||||
STR_MAC_ADDRESS: "MAC-adresse:"
|
||||
STR_CHECKING_WIFI: "Tjekker WiFi..."
|
||||
STR_ENTER_WIFI_PASSWORD: "Indtast WiFi-adgangskode"
|
||||
STR_ENTER_TEXT: "Indtast tekst"
|
||||
STR_TO_PREFIX: "til "
|
||||
STR_CALIBRE_DISCOVERING: "Opdager Calibre..."
|
||||
STR_CALIBRE_CONNECTING_TO: "Forbinder til "
|
||||
STR_CALIBRE_CONNECTED_TO: "Forbundet til "
|
||||
STR_CALIBRE_WAITING_COMMANDS: "Venter på kommandoer..."
|
||||
STR_CONNECTION_FAILED_RETRYING: "(Forbindelsen mislykkedes, prøver igen)"
|
||||
STR_CALIBRE_DISCONNECTED: "Calibre afbrudt"
|
||||
STR_CALIBRE_WAITING_TRANSFER: "Venter på overførelse..."
|
||||
STR_CALIBRE_TRANSFER_HINT: "Hvis overførslen mislykkes, aktiver\\n'Ignorer ledig plads' i Calibres\\nSmartDevice-plugin-indstillinger."
|
||||
STR_CALIBRE_RECEIVING: "Modtager: "
|
||||
STR_CALIBRE_RECEIVED: "Modtaget: "
|
||||
STR_CALIBRE_WAITING_MORE: "Venter på mere..."
|
||||
STR_CALIBRE_FAILED_CREATE_FILE: "Kunne ikke oprette fil"
|
||||
STR_CALIBRE_PASSWORD_REQUIRED: "Adgangskode påkrævet"
|
||||
STR_CALIBRE_TRANSFER_INTERRUPTED: "Overførelse afbrudt"
|
||||
STR_CALIBRE_INSTRUCTION_1: "1) Installer CrossPoint Reader-plugin"
|
||||
STR_CALIBRE_INSTRUCTION_2: "2) Vær på det samme WiFi-netværk"
|
||||
STR_CALIBRE_INSTRUCTION_3: "3) I Calibre: \"Send til enhed\""
|
||||
STR_CALIBRE_INSTRUCTION_4: "\"Hold denne skærm åben under afsendelse\""
|
||||
STR_CAT_DISPLAY: "Skærm"
|
||||
STR_CAT_READER: "Læser"
|
||||
STR_CAT_CONTROLS: "Brugerflade"
|
||||
STR_CAT_SYSTEM: "System"
|
||||
STR_SLEEP_SCREEN: "Hvile-skærm"
|
||||
STR_SLEEP_COVER_MODE: "Hvile-skærm omslag-tilstand"
|
||||
STR_STATUS_BAR: "Statuslinje"
|
||||
STR_HIDE_BATTERY: "Skjul batteri %"
|
||||
STR_EXTRA_SPACING: "Ekstra afsnitsafstand"
|
||||
STR_TEXT_AA: "Tekst Anti-Aliasing"
|
||||
STR_SHORT_PWR_BTN: "Kort tryk på tænd/sluk-knap"
|
||||
STR_ORIENTATION: "Læseretning"
|
||||
STR_FRONT_BTN_LAYOUT: "Knaplayout foran"
|
||||
STR_SIDE_BTN_LAYOUT: "Knaplayout på siden (læser)"
|
||||
STR_LONG_PRESS_SKIP: "Langt tryk spring kapitel over"
|
||||
STR_FONT_FAMILY: "Læser skrifttype"
|
||||
STR_EXT_READER_FONT: "Ekstern læserskrifttype"
|
||||
STR_EXT_CHINESE_FONT: "Læserskrifttype"
|
||||
STR_EXT_UI_FONT: "Brugergrænseflade skrifttype"
|
||||
STR_FONT_SIZE: "Brugergrænseflade skriftstørrelse"
|
||||
STR_LINE_SPACING: "Linjeafstand"
|
||||
STR_ASCII_LETTER_SPACING: "ASCII bogstavafstand"
|
||||
STR_ASCII_DIGIT_SPACING: "ASCII cifreafstand"
|
||||
STR_CJK_SPACING: "CJK afstand"
|
||||
STR_COLOR_MODE: "Farvetilstand"
|
||||
STR_SCREEN_MARGIN: "Skærmmargen"
|
||||
STR_PARA_ALIGNMENT: "Afsnitsjustering"
|
||||
STR_HYPHENATION: "Orddeling"
|
||||
STR_TIME_TO_SLEEP: "Tid til hvile"
|
||||
STR_REFRESH_FREQ: "Opdateringsfrekvens"
|
||||
STR_CALIBRE_SETTINGS: "Calibre-indstillinger"
|
||||
STR_KOREADER_SYNC: "KOReader Sync"
|
||||
STR_CHECK_UPDATES: "Søg efter opdateringer"
|
||||
STR_LANGUAGE: "Sprog"
|
||||
STR_SELECT_WALLPAPER: "Vælg baggrundsbillede"
|
||||
STR_CLEAR_READING_CACHE: "Ryd læsecache"
|
||||
STR_CALIBRE: "Calibre"
|
||||
STR_USERNAME: "Brugernavn"
|
||||
STR_PASSWORD: "Adgangskode"
|
||||
STR_SYNC_SERVER_URL: "Synkroniseringsserver-URL"
|
||||
STR_DOCUMENT_MATCHING: "Dokumentsammenkobling"
|
||||
STR_AUTHENTICATE: "Godkend"
|
||||
STR_KOREADER_USERNAME: "KOReader brugernavn"
|
||||
STR_KOREADER_PASSWORD: "KOReader adgangskode"
|
||||
STR_FILENAME: "Filnavn"
|
||||
STR_BINARY: "Binær"
|
||||
STR_SET_CREDENTIALS_FIRST: "Angiv legitimationsoplysninger først"
|
||||
STR_WIFI_CONN_FAILED: "WiFi-forbindelsen mislykkedes"
|
||||
STR_AUTHENTICATING: "Godkender..."
|
||||
STR_AUTH_SUCCESS: "Godkendt!"
|
||||
STR_KOREADER_AUTH: "KOReader-godkendelse"
|
||||
STR_SYNC_READY: "KOReader-synkronisering er klar til brug"
|
||||
STR_AUTH_FAILED: "Godkendelse mislykkedes"
|
||||
STR_DONE: "Færdig"
|
||||
STR_CLEAR_CACHE_WARNING_1: "Dette vil rydde alle cachelagrede bogdata."
|
||||
STR_CLEAR_CACHE_WARNING_2: "Al læsefremskridt vil gå tabt!"
|
||||
STR_CLEAR_CACHE_WARNING_3: "Bøger skal indekseres igen"
|
||||
STR_CLEAR_CACHE_WARNING_4: "når de åbnes igen."
|
||||
STR_CLEARING_CACHE: "Rydder cache..."
|
||||
STR_CACHE_CLEARED: "Cache ryddet"
|
||||
STR_ITEMS_REMOVED: "elementer fjernet"
|
||||
STR_FAILED_LOWER: "mislykkedes"
|
||||
STR_CLEAR_CACHE_FAILED: "Kunne ikke rydde cache"
|
||||
STR_CHECK_SERIAL_OUTPUT: "Tjek serielt output for detaljer"
|
||||
STR_DARK: "Mørk"
|
||||
STR_LIGHT: "Lys"
|
||||
STR_CUSTOM: "Brugerdefineret"
|
||||
STR_COVER: "Omslag"
|
||||
STR_NONE_OPT: "Ingen"
|
||||
STR_FIT: "Tilpas"
|
||||
STR_CROP: "Beskær"
|
||||
STR_NO_PROGRESS: "Ingen fremskridt"
|
||||
STR_FULL_OPT: "Fuld"
|
||||
STR_NEVER: "Aldrig"
|
||||
STR_IN_READER: "I læseren"
|
||||
STR_ALWAYS: "Altid"
|
||||
STR_IGNORE: "Ignorer"
|
||||
STR_SLEEP: "Hvile"
|
||||
STR_PAGE_TURN: "Sideskift"
|
||||
STR_PORTRAIT: "Portræt"
|
||||
STR_LANDSCAPE_CW: "Liggende med uret"
|
||||
STR_INVERTED: "Inverteret"
|
||||
STR_LANDSCAPE_CCW: "Liggende mod uret"
|
||||
STR_FRONT_LAYOUT_BCLR: "Bck, Cnfrm, Lft, Rght"
|
||||
STR_FRONT_LAYOUT_LRBC: "Lft, Rght, Bck, Cnfrm"
|
||||
STR_FRONT_LAYOUT_LBCR: "Lft, Bck, Cnfrm, Rght"
|
||||
STR_PREV_NEXT: "Forrige/Næste"
|
||||
STR_NEXT_PREV: "Næste/Forrige"
|
||||
STR_BOOKERLY: "Bookerly"
|
||||
STR_NOTO_SANS: "Noto Sans"
|
||||
STR_OPEN_DYSLEXIC: "Open Dyslexic"
|
||||
STR_SMALL: "Lille"
|
||||
STR_MEDIUM: "Mellem"
|
||||
STR_LARGE: "Stor"
|
||||
STR_X_LARGE: "Ekstra stor"
|
||||
STR_TIGHT: "Tæt"
|
||||
STR_NORMAL: "Normal"
|
||||
STR_WIDE: "Bred"
|
||||
STR_JUSTIFY: "Justeret"
|
||||
STR_ALIGN_LEFT: "Venstre"
|
||||
STR_CENTER: "Centreret"
|
||||
STR_ALIGN_RIGHT: "Højre"
|
||||
STR_MIN_1: "1 min"
|
||||
STR_MIN_5: "5 min"
|
||||
STR_MIN_10: "10 min"
|
||||
STR_MIN_15: "15 min"
|
||||
STR_MIN_30: "30 min"
|
||||
STR_PAGES_1: "1 side"
|
||||
STR_PAGES_5: "5 sider"
|
||||
STR_PAGES_10: "10 sider"
|
||||
STR_PAGES_15: "15 sider"
|
||||
STR_PAGES_30: "30 sider"
|
||||
STR_UPDATE: "Opdater"
|
||||
STR_CHECKING_UPDATE: "Søger efter opdatering..."
|
||||
STR_NEW_UPDATE: "Ny opdatering tilgængelig!"
|
||||
STR_CURRENT_VERSION: "Nuværende version: "
|
||||
STR_NEW_VERSION: "Ny version: "
|
||||
STR_UPDATING: "Opdaterer..."
|
||||
STR_NO_UPDATE: "Ingen opdatering tilgængelig"
|
||||
STR_UPDATE_FAILED: "Opdatering mislykkedes"
|
||||
STR_UPDATE_COMPLETE: "Opdatering færdig"
|
||||
STR_POWER_ON_HINT: "Hold tænd/sluk-knappen nede for at tænde igen"
|
||||
STR_EXTERNAL_FONT: "Ekstern skrifttype"
|
||||
STR_BUILTIN_DISABLED: "Indbygget (deaktiveret)"
|
||||
STR_NO_ENTRIES: "Ingen poster fundet"
|
||||
STR_DOWNLOADING: "Downloader..."
|
||||
STR_DOWNLOAD_FAILED: "Download mislykkedes"
|
||||
STR_ERROR_MSG: "Fejl:"
|
||||
STR_UNNAMED: "Unavngivet"
|
||||
STR_NO_SERVER_URL: "Ingen server-URL konfigureret"
|
||||
STR_FETCH_FEED_FAILED: "Kunne ikke hente feed"
|
||||
STR_PARSE_FEED_FAILED: "Kunne ikke fortolke feed"
|
||||
STR_NETWORK_PREFIX: "Netværk: "
|
||||
STR_IP_ADDRESS_PREFIX: "IP-adresse: "
|
||||
STR_SCAN_QR_WIFI_HINT: "eller scan QR-kode med din telefon for at oprette forbindelse til WiFi."
|
||||
STR_ERROR_GENERAL_FAILURE: "Fejl: Generel fejl"
|
||||
STR_ERROR_NETWORK_NOT_FOUND: "Fejl: Netværk ikke fundet"
|
||||
STR_ERROR_CONNECTION_TIMEOUT: "Fejl: Forbindelses timeout"
|
||||
STR_SD_CARD: "SD-kort"
|
||||
STR_BACK: "« Tilbage"
|
||||
STR_EXIT: "« Afslut"
|
||||
STR_HOME: "« Hjem"
|
||||
STR_SAVE: "« Gem"
|
||||
STR_SELECT: "Vælg"
|
||||
STR_TOGGLE: "Skift"
|
||||
STR_CONFIRM: "Bekræft"
|
||||
STR_CANCEL: "Annuller"
|
||||
STR_CONNECT: "Forbind"
|
||||
STR_OPEN: "Åbn"
|
||||
STR_DOWNLOAD: "Download"
|
||||
STR_RETRY: "Prøv igen"
|
||||
STR_YES: "Ja"
|
||||
STR_NO: "Nej"
|
||||
STR_STATE_ON: "TÆNDT"
|
||||
STR_STATE_OFF: "SLUKKET"
|
||||
STR_SET: "Indstil"
|
||||
STR_NOT_SET: "Ikke indstillet"
|
||||
STR_DIR_LEFT: "Venstre"
|
||||
STR_DIR_RIGHT: "Højre"
|
||||
STR_DIR_UP: "Op"
|
||||
STR_DIR_DOWN: "Ned"
|
||||
STR_CAPS_ON: "CAPS"
|
||||
STR_CAPS_OFF: "caps"
|
||||
STR_OK_BUTTON: "OK"
|
||||
STR_ON_MARKER: "[ON]"
|
||||
STR_SLEEP_COVER_FILTER: "Hvile-skærm omslag-filter"
|
||||
STR_FILTER_CONTRAST: "Kontrast"
|
||||
STR_STATUS_BAR_FULL_PERCENT: "Fuld m/ procent"
|
||||
STR_STATUS_BAR_FULL_BOOK: "Fuld m/ boglinje"
|
||||
STR_STATUS_BAR_BOOK_ONLY: "Kun boglinje"
|
||||
STR_STATUS_BAR_FULL_CHAPTER: "Fuld m/ kapitellinje"
|
||||
STR_UI_THEME: "Brugergrænseflade tema"
|
||||
STR_THEME_CLASSIC: "Klassisk"
|
||||
STR_THEME_LYRA: "Lyra"
|
||||
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
|
||||
STR_SUNLIGHT_FADING_FIX: "Sollysfading-rettelse"
|
||||
STR_REMAP_FRONT_BUTTONS: "Omtildel frontknapper"
|
||||
STR_OPDS_BROWSER: "OPDS Browser"
|
||||
STR_COVER_CUSTOM: "Omslag + Brugerdefineret"
|
||||
STR_RECENTS: "Seneste"
|
||||
STR_MENU_RECENT_BOOKS: "Seneste bøger"
|
||||
STR_NO_RECENT_BOOKS: "Ingen seneste bøger"
|
||||
STR_CALIBRE_DESC: "Brug Calibre trådløs enhedsoverførelse"
|
||||
STR_FORGET_AND_REMOVE: "Glem netværk og fjern gemt adgangskode?"
|
||||
STR_FORGET_BUTTON: "Glem"
|
||||
STR_CALIBRE_STARTING: "Starter Calibre..."
|
||||
STR_CALIBRE_SETUP: "Opsætning"
|
||||
STR_CALIBRE_STATUS: "Status"
|
||||
STR_CLEAR_BUTTON: "Ryd"
|
||||
STR_DEFAULT_VALUE: "Standard"
|
||||
STR_REMAP_PROMPT: "Tryk på en frontknap for hver rolle"
|
||||
STR_UNASSIGNED: "Ikke tildelt"
|
||||
STR_ALREADY_ASSIGNED: "Allerede tildelt"
|
||||
STR_REMAP_RESET_HINT: "Sideknap op: Nulstil til standardlayout"
|
||||
STR_REMAP_CANCEL_HINT: "Sideknap ned: Annuller omtildeling"
|
||||
STR_HW_BACK_LABEL: "Tilbage (1. knap)"
|
||||
STR_HW_CONFIRM_LABEL: "Bekræft (2. knap)"
|
||||
STR_HW_LEFT_LABEL: "Venstre (3. knap)"
|
||||
STR_HW_RIGHT_LABEL: "Højre (4. knap)"
|
||||
STR_GO_TO_PERCENT: "Gå til %"
|
||||
STR_GO_HOME_BUTTON: "Gå til start"
|
||||
STR_SYNC_PROGRESS: "Synkroniser fremskridt"
|
||||
STR_DELETE_CACHE: "Slet bogcache"
|
||||
STR_CHAPTER_PREFIX: "Kapitel: "
|
||||
STR_PAGES_SEPARATOR: " sider | "
|
||||
STR_BOOK_PREFIX: "Bog: "
|
||||
STR_KBD_SHIFT: "shift"
|
||||
STR_KBD_SHIFT_CAPS: "SHIFT"
|
||||
STR_KBD_LOCK: "LOCK"
|
||||
STR_CALIBRE_URL_HINT: "Tilføj /opds til din URL for Calibre"
|
||||
STR_PERCENT_STEP_HINT: "Venstre/Højre: 1% Op/Ned: 10%"
|
||||
STR_SYNCING_TIME: "Synkroniserer tid..."
|
||||
STR_CALC_HASH: "Beregner dokument-hash..."
|
||||
STR_HASH_FAILED: "Kunne ikke beregne dokument-hash"
|
||||
STR_FETCH_PROGRESS: "Henter fjernfremskridt..."
|
||||
STR_UPLOAD_PROGRESS: "Uploader fremskridt..."
|
||||
STR_NO_CREDENTIALS_MSG: "Ingen legitimationsoplysninger konfigureret"
|
||||
STR_KOREADER_SETUP_HINT: "Opsæt KOReader-konto i Indstillinger"
|
||||
STR_PROGRESS_FOUND: "Fremskridt fundet!"
|
||||
STR_REMOTE_LABEL: "Ekstern:"
|
||||
STR_LOCAL_LABEL: "Lokal:"
|
||||
STR_PAGE_OVERALL_FORMAT: "Side %d, %.2f%% samlet"
|
||||
STR_PAGE_TOTAL_OVERALL_FORMAT: "Side %d/%d, %.2f%% samlet"
|
||||
STR_DEVICE_FROM_FORMAT: " Fra: %s"
|
||||
STR_APPLY_REMOTE: "Anvend fjernfremskridt"
|
||||
STR_UPLOAD_LOCAL: "Upload lokalt fremskridt"
|
||||
STR_NO_REMOTE_MSG: "Ingen fjernfremskridt fundet"
|
||||
STR_UPLOAD_PROMPT: "Upload nuværende position?"
|
||||
STR_UPLOAD_SUCCESS: "Fremskridt uploadet!"
|
||||
STR_SYNC_FAILED_MSG: "Synkronisering mislykkedes"
|
||||
STR_SECTION_PREFIX: "Afsnit "
|
||||
STR_UPLOAD: "Upload"
|
||||
STR_BOOK_S_STYLE: "Bogens stil"
|
||||
STR_EMBEDDED_STYLE: "Indlejret stil"
|
||||
STR_OPDS_SERVER_URL: "OPDS Server URL"
|
||||
STR_SCREENSHOT_BUTTON: "Tag skærmbillede"
|
||||
@@ -331,4 +331,7 @@ STR_UPLOAD: "Upload"
|
||||
STR_BOOK_S_STYLE: "Book's Style"
|
||||
STR_EMBEDDED_STYLE: "Embedded Style"
|
||||
STR_OPDS_SERVER_URL: "OPDS Server URL"
|
||||
STR_SCREENSHOT_BUTTON: "Take screenshot"
|
||||
STR_FOOTNOTES: "Footnotes"
|
||||
STR_NO_FOOTNOTES: "No footnotes on this page"
|
||||
STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Take screenshot"
|
||||
|
||||
@@ -63,7 +63,7 @@ STR_MAC_ADDRESS: "Adres MAC:"
|
||||
STR_CHECKING_WIFI: "Sprawdzanie WiFi..."
|
||||
STR_ENTER_WIFI_PASSWORD: "Wprowadź hasło WiFi"
|
||||
STR_ENTER_TEXT: "Wprowadź tekst"
|
||||
STR_TO_PREFIX: "do "
|
||||
STR_TO_PREFIX: "Z "
|
||||
STR_CALIBRE_DISCOVERING: "Odkrywanie Calibre..."
|
||||
STR_CALIBRE_CONNECTING_TO: "Łączenie do "
|
||||
STR_CALIBRE_CONNECTED_TO: "Podłączony do "
|
||||
@@ -87,21 +87,21 @@ STR_CAT_READER: "Czytnik"
|
||||
STR_CAT_CONTROLS: "Sterowanie"
|
||||
STR_CAT_SYSTEM: "System"
|
||||
STR_SLEEP_SCREEN: "Wygaszacz ekranu"
|
||||
STR_SLEEP_COVER_MODE: "Tryb ekranu uśpienia z okładką"
|
||||
STR_SLEEP_COVER_MODE: "Okładki wygaszacza"
|
||||
STR_STATUS_BAR: "Status Bar"
|
||||
STR_HIDE_BATTERY: "Ukryj % baterii"
|
||||
STR_EXTRA_SPACING: "Dodatkowe odstępy paragrafów"
|
||||
STR_TEXT_AA: "Wygładzanie tekstu"
|
||||
STR_SHORT_PWR_BTN: "Krótkie naciśnięcie przycisku zasilania"
|
||||
STR_SHORT_PWR_BTN: "Krótkie naciśnięcie zasilania"
|
||||
STR_ORIENTATION: "Układ czytania"
|
||||
STR_FRONT_BTN_LAYOUT: "Układ przednich przycisków"
|
||||
STR_SIDE_BTN_LAYOUT: "Układ przycisków bocznych(czytnik)"
|
||||
STR_SIDE_BTN_LAYOUT: "Układ przycisków bocznych"
|
||||
STR_LONG_PRESS_SKIP: "Przytrzymaj aby przeskoczyć rozdział"
|
||||
STR_FONT_FAMILY: "Czcionka"
|
||||
STR_EXT_READER_FONT: "Zewnętrzna czcionka czytnika"
|
||||
STR_EXT_CHINESE_FONT: "Czcionka czytnika"
|
||||
STR_EXT_UI_FONT: "Czcionka UI"
|
||||
STR_FONT_SIZE: "Rozmiar czcionki UI"
|
||||
STR_FONT_SIZE: "Rozmiar czcionki"
|
||||
STR_LINE_SPACING: "Odstępy między wierszami"
|
||||
STR_ASCII_LETTER_SPACING: "Odstępy liter ASCII"
|
||||
STR_ASCII_DIGIT_SPACING: "Odstępy cyfr ASCII"
|
||||
@@ -160,7 +160,7 @@ STR_IN_READER: "W czytniku"
|
||||
STR_ALWAYS: "Zawsze"
|
||||
STR_IGNORE: "Ignoruj"
|
||||
STR_SLEEP: "Uśpienie"
|
||||
STR_PAGE_TURN: "Obrót strony"
|
||||
STR_PAGE_TURN: "Nast. str."
|
||||
STR_PORTRAIT: "Pionowo"
|
||||
STR_LANDSCAPE_CW: "Poziomo L"
|
||||
STR_INVERTED: "Odwrócony"
|
||||
@@ -168,8 +168,8 @@ STR_LANDSCAPE_CCW: "Poziomo P"
|
||||
STR_FRONT_LAYOUT_BCLR: "Wstecz, Potwierdź, Lewo, Prawo"
|
||||
STR_FRONT_LAYOUT_LRBC: "Lewo, Prawo, Wstecz, Potwierdź"
|
||||
STR_FRONT_LAYOUT_LBCR: "Lewo, Wstecz, Potwierdź, Prawo"
|
||||
STR_PREV_NEXT: "Poprzedni/Następny"
|
||||
STR_NEXT_PREV: "Następny/Poprzedni"
|
||||
STR_PREV_NEXT: "Poprz./Nast."
|
||||
STR_NEXT_PREV: "Nast./Poprz."
|
||||
STR_BOOKERLY: "Bookerly"
|
||||
STR_NOTO_SANS: "Noto Sans"
|
||||
STR_OPEN_DYSLEXIC: "Open Dyslexic"
|
||||
@@ -177,13 +177,13 @@ STR_SMALL: "Mały"
|
||||
STR_MEDIUM: "Średni"
|
||||
STR_LARGE: "Duży"
|
||||
STR_X_LARGE: "B. duży"
|
||||
STR_TIGHT: "Ciasno"
|
||||
STR_NORMAL: "Normalnie"
|
||||
STR_WIDE: "Szeroko"
|
||||
STR_JUSTIFY: "Wyrównanie"
|
||||
STR_ALIGN_LEFT: "Lewej"
|
||||
STR_CENTER: "Środkuj"
|
||||
STR_ALIGN_RIGHT: "Prawej"
|
||||
STR_TIGHT: "Małe"
|
||||
STR_NORMAL: "Normalne"
|
||||
STR_WIDE: "Duże"
|
||||
STR_JUSTIFY: "Wyrównane"
|
||||
STR_ALIGN_LEFT: "Lewo"
|
||||
STR_CENTER: "Środek"
|
||||
STR_ALIGN_RIGHT: "Prawo"
|
||||
STR_MIN_1: "1 min"
|
||||
STR_MIN_5: "5 min"
|
||||
STR_MIN_10: "10 min"
|
||||
@@ -220,13 +220,13 @@ STR_SCAN_QR_WIFI_HINT: "albo skanuj kod QR telefonem aby połączyć się do Wif
|
||||
STR_ERROR_GENERAL_FAILURE: "Błąd: Ogólny"
|
||||
STR_ERROR_NETWORK_NOT_FOUND: "Błąd: Sieci nie znaleziono"
|
||||
STR_ERROR_CONNECTION_TIMEOUT: "Błąd: Przekroczono limit czasu połączenia"
|
||||
STR_SD_CARD: "karta S"
|
||||
STR_SD_CARD: "Karta SD"
|
||||
STR_BACK: "« Wstecz"
|
||||
STR_EXIT: "« Wyjdź"
|
||||
STR_HOME: "« Home"
|
||||
STR_SAVE: "« Zapisz"
|
||||
STR_SELECT: "Wybierz"
|
||||
STR_TOGGLE: "Przełącz"
|
||||
STR_TOGGLE: "Zmień"
|
||||
STR_CONFIRM: "Potwierdź"
|
||||
STR_CANCEL: "Anuluj"
|
||||
STR_CONNECT: "Połącz"
|
||||
@@ -237,8 +237,8 @@ STR_YES: "Tak"
|
||||
STR_NO: "Nie"
|
||||
STR_SHOW: "Pokaż"
|
||||
STR_HIDE: "Ukryj"
|
||||
STR_STATE_ON: "ON"
|
||||
STR_STATE_OFF: "OFF"
|
||||
STR_STATE_ON: "Wł."
|
||||
STR_STATE_OFF: "Wył."
|
||||
STR_SET: "Ustawiono"
|
||||
STR_NOT_SET: "Nie ustawiono"
|
||||
STR_DIR_LEFT: "Lewo"
|
||||
@@ -249,11 +249,11 @@ STR_CAPS_ON: "CAPS"
|
||||
STR_CAPS_OFF: "caps"
|
||||
STR_OK_BUTTON: "OK"
|
||||
STR_ON_MARKER: "[ON]"
|
||||
STR_SLEEP_COVER_FILTER: "Filtr okładki uśpionego ekranu"
|
||||
STR_SLEEP_COVER_FILTER: "Filtr okładek wygaszacza"
|
||||
STR_FILTER_CONTRAST: "Kontrast"
|
||||
STR_CUSTOMISE_STATUS_BAR: "Dostosowanie paska statusu"
|
||||
STR_CHAPTER_PAGE_COUNT: "Strona rozdziału"
|
||||
STR_BOOK_PROGRESS_PERCENTAGE: "Pasek postępu książki"
|
||||
STR_BOOK_PROGRESS_PERCENTAGE: "Postęp książki"
|
||||
STR_PROGRESS_BAR: "Pasek postępu"
|
||||
STR_PROGRESS_BAR_THICKNESS: "Grubość paska postępu"
|
||||
STR_PROGRESS_BAR_THIN: "Cienki"
|
||||
@@ -306,9 +306,9 @@ STR_KBD_SHIFT: "shift"
|
||||
STR_KBD_SHIFT_CAPS: "SHIFT"
|
||||
STR_KBD_LOCK: "LOCK"
|
||||
STR_CALIBRE_URL_HINT: "Dla Calibre, dodaj /opds do adresu URL"
|
||||
STR_PERCENT_STEP_HINT: "Left/Right: 1% Up/Down: 10%"
|
||||
STR_PERCENT_STEP_HINT: "Lewo/Prawo: 1% Góra/Dół: 10%"
|
||||
STR_SYNCING_TIME: "Synchronizacja czasu..."
|
||||
STR_CALC_HASH: "Obliczanie sumu kontrolnekj..."
|
||||
STR_CALC_HASH: "Obliczanie sumy kontrolnej..."
|
||||
STR_HASH_FAILED: "Błąd obliczania sumy kontrolnej"
|
||||
STR_FETCH_PROGRESS: "Pobieranie zdalnego postępu.."
|
||||
STR_UPLOAD_PROGRESS: "Postęp wysyłania..."
|
||||
@@ -329,6 +329,6 @@ STR_SYNC_FAILED_MSG: "Synchronizacja nieudana"
|
||||
STR_SECTION_PREFIX: "Sekcja "
|
||||
STR_UPLOAD: "Wyślij"
|
||||
STR_BOOK_S_STYLE: "Styl książki"
|
||||
STR_EMBEDDED_STYLE: "Styl wbudowany"
|
||||
STR_EMBEDDED_STYLE: "Style wbudowane w EPUB"
|
||||
STR_OPDS_SERVER_URL: "URL serwera OPDS"
|
||||
STR_SCREENSHOT_BUTTON: "Zrób zrzut ekranu"
|
||||
|
||||
@@ -235,6 +235,8 @@ STR_DOWNLOAD: "Descarcă"
|
||||
STR_RETRY: "Reîncercare"
|
||||
STR_YES: "Da"
|
||||
STR_NO: "Nu"
|
||||
STR_SHOW: "Afișează"
|
||||
STR_HIDE: "Ascunde"
|
||||
STR_STATE_ON: "Pornit"
|
||||
STR_STATE_OFF: "Oprit"
|
||||
STR_SET: "Setare"
|
||||
@@ -249,6 +251,21 @@ STR_OK_BUTTON: "OK"
|
||||
STR_ON_MARKER: "[ON]"
|
||||
STR_SLEEP_COVER_FILTER: "Filtru ecran de repaus"
|
||||
STR_FILTER_CONTRAST: "Contrast"
|
||||
STR_CUSTOMISE_STATUS_BAR: "Customizaţi bara de stare"
|
||||
STR_CHAPTER_PAGE_COUNT: "Număr de pagini în capitol"
|
||||
STR_BOOK_PROGRESS_PERCENTAGE: "Progres carte procentual"
|
||||
STR_PROGRESS_BAR: "Bară de progres"
|
||||
STR_PROGRESS_BAR_THICKNESS: "Grosime bară de progres"
|
||||
STR_PROGRESS_BAR_THIN: "Subţire"
|
||||
STR_PROGRESS_BAR_MEDIUM: "Medie"
|
||||
STR_PROGRESS_BAR_THICK: "Groasă"
|
||||
STR_BOOK: "Carte"
|
||||
STR_CHAPTER: "Capitol"
|
||||
STR_EXAMPLE_CHAPTER: "Capitolul 21"
|
||||
STR_EXAMPLE_BOOK: "Titlul cărţii"
|
||||
STR_PREVIEW: "Previzualizare"
|
||||
STR_TITLE: "Titlu"
|
||||
STR_BATTERY: "Baterie"
|
||||
STR_UI_THEME: "Tema UI"
|
||||
STR_THEME_CLASSIC: "Clasic"
|
||||
STR_THEME_LYRA: "Lyra"
|
||||
@@ -281,7 +298,7 @@ STR_GO_TO_PERCENT: "Săriţi la %"
|
||||
STR_GO_HOME_BUTTON: "Acasă"
|
||||
STR_SYNC_PROGRESS: "Progres sincronizare"
|
||||
STR_DELETE_CACHE: "Ştergere cache cărţi"
|
||||
STR_CHAPTER_PREFIX: "Capitol: "
|
||||
STR_DISPLAY_QR: "Afișați pagina ca cod QR"
|
||||
STR_PAGES_SEPARATOR: " pagini | "
|
||||
STR_BOOK_PREFIX: "Carte: "
|
||||
STR_KBD_SHIFT: "shift"
|
||||
|
||||
@@ -235,6 +235,8 @@ STR_DOWNLOAD: "Скачать"
|
||||
STR_RETRY: "Повторить"
|
||||
STR_YES: "Да"
|
||||
STR_NO: "Нет"
|
||||
STR_SHOW: "Показать"
|
||||
STR_HIDE: "Скрыть"
|
||||
STR_STATE_ON: "ВКЛ"
|
||||
STR_STATE_OFF: "ВЫКЛ"
|
||||
STR_SET: "Установлено"
|
||||
@@ -249,6 +251,20 @@ STR_OK_BUTTON: "OK"
|
||||
STR_ON_MARKER: "[ВКЛ]"
|
||||
STR_SLEEP_COVER_FILTER: "Фильтр экрана сна"
|
||||
STR_FILTER_CONTRAST: "Контраст"
|
||||
STR_CUSTOMISE_STATUS_BAR: "Настройка строки состояния"
|
||||
STR_CHAPTER_PAGE_COUNT: "Количество страниц главы"
|
||||
STR_BOOK_PROGRESS_PERCENTAGE: "% прочтения книги"
|
||||
STR_PROGRESS_BAR: "Полоса прогресса"
|
||||
STR_PROGRESS_BAR_THICKNESS: "Толщина индикатора прогресса"
|
||||
STR_PROGRESS_BAR_THIN: "Тонкий"
|
||||
STR_PROGRESS_BAR_MEDIUM: "Средний"
|
||||
STR_PROGRESS_BAR_THICK: "Толстый"
|
||||
STR_BOOK: "Книга"
|
||||
STR_CHAPTER: "Глава"
|
||||
STR_EXAMPLE_BOOK: "Название книги"
|
||||
STR_PREVIEW: "Предпросмотр"
|
||||
STR_TITLE: "Заглавие"
|
||||
STR_BATTERY: "Батарея"
|
||||
STR_UI_THEME: "Тема интерфейса"
|
||||
STR_THEME_CLASSIC: "Классическая"
|
||||
STR_THEME_LYRA: "Lyra"
|
||||
@@ -282,6 +298,7 @@ STR_GO_HOME_BUTTON: "На главную"
|
||||
STR_SYNC_PROGRESS: "Синхронизировать прогресс"
|
||||
STR_DELETE_CACHE: "Удалить кэш книги"
|
||||
STR_CHAPTER_PREFIX: "Глава:"
|
||||
STR_DISPLAY_QR: "Показать страницу в виде QR-кода"
|
||||
STR_PAGES_SEPARATOR: "стр. |"
|
||||
STR_BOOK_PREFIX: "Книга:"
|
||||
STR_KBD_SHIFT: "shift"
|
||||
|
||||
@@ -234,6 +234,8 @@ STR_OPEN: "Відкрити"
|
||||
STR_DOWNLOAD: "Завант."
|
||||
STR_RETRY: "Повтор."
|
||||
STR_YES: "Так"
|
||||
STR_SHOW: "Показати"
|
||||
STR_HIDE: "Сховати"
|
||||
STR_NO: "Ні"
|
||||
STR_STATE_ON: "УВІМК"
|
||||
STR_STATE_OFF: "ВИМК"
|
||||
@@ -249,6 +251,21 @@ STR_OK_BUTTON: "OK"
|
||||
STR_ON_MARKER: "[УВІМК]"
|
||||
STR_SLEEP_COVER_FILTER: "Фільтр обкладинки екрана сну"
|
||||
STR_FILTER_CONTRAST: "Контраст"
|
||||
STR_CUSTOMISE_STATUS_BAR: "Налаштувати рядок стану"
|
||||
STR_CHAPTER_PAGE_COUNT: "Кількість сторінок розділу"
|
||||
STR_BOOK_PROGRESS_PERCENTAGE: "Відсоток прочитаного"
|
||||
STR_PROGRESS_BAR: "Рядок прогресу"
|
||||
STR_PROGRESS_BAR_THICKNESS: "Товщина рядку прогресу"
|
||||
STR_PROGRESS_BAR_THIN: "Тонкий"
|
||||
STR_PROGRESS_BAR_MEDIUM: "Середній"
|
||||
STR_PROGRESS_BAR_THICK: "Жирний"
|
||||
STR_BOOK: "Книга"
|
||||
STR_CHAPTER: "Розділ"
|
||||
STR_EXAMPLE_CHAPTER: "Розділ 21"
|
||||
STR_EXAMPLE_BOOK: "Назва книги"
|
||||
STR_PREVIEW: "Перегляд"
|
||||
STR_TITLE: "Назва"
|
||||
STR_BATTERY: "Акумулятор"
|
||||
STR_UI_THEME: "Тема інтерфейсу"
|
||||
STR_THEME_CLASSIC: "Класична"
|
||||
STR_THEME_LYRA: "Lyra"
|
||||
@@ -281,6 +298,7 @@ STR_GO_TO_PERCENT: "Перейти до %"
|
||||
STR_GO_HOME_BUTTON: "На головну"
|
||||
STR_SYNC_PROGRESS: "Прогрес синхронізації"
|
||||
STR_DELETE_CACHE: "Видалити кеш книги"
|
||||
STR_DISPLAY_QR: "Показати сторінку як QR-код"
|
||||
STR_CHAPTER_PREFIX: "Розділ: "
|
||||
STR_PAGES_SEPARATOR: " сторінок | "
|
||||
STR_BOOK_PREFIX: "Книга: "
|
||||
@@ -313,4 +331,7 @@ STR_UPLOAD: "Завантажити"
|
||||
STR_BOOK_S_STYLE: "Стиль книги"
|
||||
STR_EMBEDDED_STYLE: "Вбудований стиль"
|
||||
STR_OPDS_SERVER_URL: "URL сервера OPDS"
|
||||
STR_FOOTNOTES: "Зноски"
|
||||
STR_NO_FOOTNOTES: "На цій сторінці немає зносок"
|
||||
STR_LINK: "[посилання]"
|
||||
STR_SCREENSHOT_BUTTON: "Знімок екрана"
|
||||
|
||||
Reference in New Issue
Block a user