Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd00c185f2 | ||
|
|
f180069643 | ||
|
|
a0ec5812b1 | ||
|
|
e84840b473 | ||
|
|
94ae750e89 | ||
|
|
8c5cd67f38 |
@@ -2,6 +2,8 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
.vscode
|
||||
open-x4-sdk
|
||||
fs_
|
||||
lib/EpdFont/fontsrc
|
||||
lib/I18n/I18nKeys.h
|
||||
lib/I18n/I18nStrings.h
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "BookPages.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
// Coarse seed used only before any section has an exact or live count; replaced
|
||||
// by the calibrated average as soon as one is available.
|
||||
constexpr double DEFAULT_BYTES_PER_PAGE = 2000.0;
|
||||
// A section's serialized pageCount is a uint16_t, so no estimate needs to exceed this.
|
||||
constexpr int MAX_SECTION_PAGES = std::numeric_limits<uint16_t>::max();
|
||||
|
||||
int clampToInt(const uint64_t v) {
|
||||
return v > static_cast<uint64_t>(std::numeric_limits<int>::max()) ? std::numeric_limits<int>::max()
|
||||
: static_cast<int>(v);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
BookPagePosition computeBookPagePosition(const BookPageEntry* entries, const int sectionCount, const int spineIndex,
|
||||
const int pageInSection, const int liveSectionPages) {
|
||||
BookPagePosition pos;
|
||||
if (!entries || sectionCount <= 0) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Calibrate bytes-per-page from every section with an exact count (empty
|
||||
// known-0 chapters excluded), plus the live estimate for spineIndex when it
|
||||
// has no exact count yet.
|
||||
uint64_t knownBytes = 0;
|
||||
uint64_t knownPages = 0;
|
||||
for (int i = 0; i < sectionCount; ++i) {
|
||||
if (entries[i].pages > 0) {
|
||||
knownBytes += entries[i].bytes;
|
||||
knownPages += static_cast<uint32_t>(entries[i].pages);
|
||||
}
|
||||
}
|
||||
const bool useLive = spineIndex >= 0 && spineIndex < sectionCount && entries[spineIndex].pages < 0 &&
|
||||
liveSectionPages > 0 && entries[spineIndex].bytes > 0;
|
||||
if (useLive) {
|
||||
knownBytes += entries[spineIndex].bytes;
|
||||
knownPages += static_cast<uint32_t>(liveSectionPages);
|
||||
}
|
||||
const double bytesPerPage = (knownBytes > 0 && knownPages > 0)
|
||||
? static_cast<double>(knownBytes) / static_cast<double>(knownPages)
|
||||
: DEFAULT_BYTES_PER_PAGE;
|
||||
|
||||
uint64_t total = 0;
|
||||
uint64_t before = 0;
|
||||
bool exact = true;
|
||||
for (int i = 0; i < sectionCount; ++i) {
|
||||
uint32_t pages;
|
||||
if (entries[i].pages >= 0) {
|
||||
pages = static_cast<uint32_t>(entries[i].pages); // exact (0 = genuinely empty chapter)
|
||||
} else if (i == spineIndex && liveSectionPages > 0) {
|
||||
pages = static_cast<uint32_t>(std::min(liveSectionPages, MAX_SECTION_PAGES));
|
||||
exact = false;
|
||||
} else {
|
||||
const double raw = std::floor(static_cast<double>(entries[i].bytes) / bytesPerPage + 0.5);
|
||||
pages = static_cast<uint32_t>(std::clamp(raw, 1.0, static_cast<double>(MAX_SECTION_PAGES)));
|
||||
exact = false;
|
||||
}
|
||||
total += pages;
|
||||
if (i < spineIndex) {
|
||||
before += pages;
|
||||
}
|
||||
}
|
||||
|
||||
pos.totalPages = clampToInt(total);
|
||||
pos.currentPage = clampToInt(before + static_cast<uint64_t>(std::max(0, pageInSection)) + 1);
|
||||
pos.isEstimate = !exact;
|
||||
return pos;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Whole-book page accounting for book-global "page X of Y".
|
||||
//
|
||||
// The finalized section cache files (sections/*.bin) are the single source of
|
||||
// truth for exact per-section counts; the caller harvests them into an array of
|
||||
// BookPageEntry (nothing is persisted separately). Sections without an exact
|
||||
// count are estimated from their byte size, calibrated against the sections
|
||||
// whose counts are known — so the total gets more accurate as more of the book
|
||||
// is paginated, and becomes exact once every section is.
|
||||
|
||||
struct BookPageEntry {
|
||||
uint32_t bytes = 0; // uncompressed XHTML size, for estimating unknown sections
|
||||
int32_t pages = -1; // exact count from a finalized section cache; -1 = unknown (0 = empty chapter)
|
||||
};
|
||||
|
||||
struct BookPagePosition {
|
||||
int currentPage = 0;
|
||||
int totalPages = 0;
|
||||
bool isEstimate = true; // true until every section has an exact count
|
||||
};
|
||||
|
||||
// Book-global position: currentPage = pages before spineIndex + pageInSection + 1.
|
||||
// liveSectionPages is the in-progress build's estimate for spineIndex (see
|
||||
// Section::estimatedTotalPages); it is used for that section when it has no exact
|
||||
// count yet and folded into the bytes-per-page calibration. Pure function: no I/O.
|
||||
BookPagePosition computeBookPagePosition(const BookPageEntry* entries, int sectionCount, int spineIndex,
|
||||
int pageInSection, int liveSectionPages);
|
||||
+38
-26
@@ -39,8 +39,34 @@ constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) +
|
||||
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(bool) + sizeof(bool) +
|
||||
sizeof(uint8_t) + sizeof(bool) + sizeof(uint32_t) + sizeof(uint32_t) +
|
||||
sizeof(uint32_t) + sizeof(uint32_t);
|
||||
|
||||
// Read the render-parameter block of the header (everything between the version
|
||||
// byte and pageCount), in writeSectionFileHeader order. The file cursor must sit
|
||||
// just past the version byte.
|
||||
Section::RenderParams readHeaderRenderParams(HalFile& f) {
|
||||
Section::RenderParams p;
|
||||
serialization::readPod(f, p.fontId);
|
||||
serialization::readPod(f, p.lineCompression);
|
||||
serialization::readPod(f, p.extraParagraphSpacing);
|
||||
serialization::readPod(f, p.paragraphAlignment);
|
||||
serialization::readPod(f, p.viewportWidth);
|
||||
serialization::readPod(f, p.viewportHeight);
|
||||
serialization::readPod(f, p.hyphenationEnabled);
|
||||
serialization::readPod(f, p.embeddedStyle);
|
||||
serialization::readPod(f, p.imageRendering);
|
||||
serialization::readPod(f, p.focusReadingEnabled);
|
||||
return p;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool Section::RenderParams::operator==(const RenderParams& o) const {
|
||||
return fontId == o.fontId && lineCompression == o.lineCompression &&
|
||||
extraParagraphSpacing == o.extraParagraphSpacing && paragraphAlignment == o.paragraphAlignment &&
|
||||
viewportWidth == o.viewportWidth && viewportHeight == o.viewportHeight &&
|
||||
hyphenationEnabled == o.hyphenationEnabled && embeddedStyle == o.embeddedStyle &&
|
||||
imageRendering == o.imageRendering && focusReadingEnabled == o.focusReadingEnabled;
|
||||
}
|
||||
|
||||
// Out-of-line so the unique_ptr<ChapterHtmlSlimParser> in BuildContext can be
|
||||
// constructed/destroyed where the parser's full definition is visible.
|
||||
Section::Section(const std::shared_ptr<Epub>& epub, const int spineIndex, GfxRenderer& renderer)
|
||||
@@ -133,31 +159,11 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
|
||||
}
|
||||
filePartial = (version == SECTION_FILE_PARTIAL_VERSION);
|
||||
|
||||
int fileFontId;
|
||||
uint16_t fileViewportWidth, fileViewportHeight;
|
||||
float fileLineCompression;
|
||||
bool fileExtraParagraphSpacing;
|
||||
uint8_t fileParagraphAlignment;
|
||||
bool fileHyphenationEnabled;
|
||||
bool fileEmbeddedStyle;
|
||||
uint8_t fileImageRendering;
|
||||
bool fileFocusReadingEnabled;
|
||||
serialization::readPod(file, fileFontId);
|
||||
serialization::readPod(file, fileLineCompression);
|
||||
serialization::readPod(file, fileExtraParagraphSpacing);
|
||||
serialization::readPod(file, fileParagraphAlignment);
|
||||
serialization::readPod(file, fileViewportWidth);
|
||||
serialization::readPod(file, fileViewportHeight);
|
||||
serialization::readPod(file, fileHyphenationEnabled);
|
||||
serialization::readPod(file, fileEmbeddedStyle);
|
||||
serialization::readPod(file, fileImageRendering);
|
||||
serialization::readPod(file, fileFocusReadingEnabled);
|
||||
|
||||
if (fontId != fileFontId || lineCompression != fileLineCompression ||
|
||||
extraParagraphSpacing != fileExtraParagraphSpacing || paragraphAlignment != fileParagraphAlignment ||
|
||||
viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight ||
|
||||
hyphenationEnabled != fileHyphenationEnabled || embeddedStyle != fileEmbeddedStyle ||
|
||||
imageRendering != fileImageRendering || focusReadingEnabled != fileFocusReadingEnabled) {
|
||||
const RenderParams fileParams = readHeaderRenderParams(file);
|
||||
const RenderParams params = {fontId, lineCompression, extraParagraphSpacing, paragraphAlignment,
|
||||
viewportWidth, viewportHeight, hyphenationEnabled, embeddedStyle,
|
||||
imageRendering, focusReadingEnabled};
|
||||
if (!(fileParams == params)) {
|
||||
file.close();
|
||||
LOG_ERR("SCT", "Deserialization failed: Parameters do not match");
|
||||
clearCache();
|
||||
@@ -754,7 +760,7 @@ std::string Section::getTextFromSectionFile() {
|
||||
return fullText;
|
||||
}
|
||||
|
||||
std::optional<uint16_t> Section::getCachedPageCount() const {
|
||||
std::optional<uint16_t> Section::getCachedPageCount(const RenderParams* mustMatch) const {
|
||||
HalFile f;
|
||||
if (!Storage.openFileForRead("SCT", filePath, f)) {
|
||||
return std::nullopt;
|
||||
@@ -774,6 +780,12 @@ std::optional<uint16_t> Section::getCachedPageCount() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// A file cached under other render settings paginates differently; its count is
|
||||
// only stale-valid for rough mapping, so reject it when the caller needs a match.
|
||||
if (mustMatch && !(readHeaderRenderParams(f) == *mustMatch)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
f.seek(HEADER_SIZE - sizeof(uint32_t) * 4 - sizeof(uint16_t));
|
||||
uint16_t count;
|
||||
serialization::readPod(f, count);
|
||||
|
||||
+21
-1
@@ -78,6 +78,23 @@ class Section {
|
||||
std::unique_ptr<Page> loadPageDuringBuild(int page);
|
||||
|
||||
public:
|
||||
// The render parameters that determine pagination, in section cache header order
|
||||
// (see writeSectionFileHeader). A cached page count is only valid for one set.
|
||||
struct RenderParams {
|
||||
int fontId = 0;
|
||||
float lineCompression = 0.0f;
|
||||
bool extraParagraphSpacing = false;
|
||||
uint8_t paragraphAlignment = 0;
|
||||
uint16_t viewportWidth = 0;
|
||||
uint16_t viewportHeight = 0;
|
||||
bool hyphenationEnabled = false;
|
||||
bool embeddedStyle = false;
|
||||
uint8_t imageRendering = 0;
|
||||
bool focusReadingEnabled = false;
|
||||
|
||||
bool operator==(const RenderParams& o) const;
|
||||
};
|
||||
|
||||
uint16_t pageCount = 0;
|
||||
int currentPage = 0;
|
||||
|
||||
@@ -144,7 +161,10 @@ class Section {
|
||||
std::optional<uint16_t> findAnchorDuringBuild(const std::string& anchor) const;
|
||||
|
||||
// Get the page count from the section cache file without fully loading it.
|
||||
std::optional<uint16_t> getCachedPageCount() const;
|
||||
// Finalized files only (a partial's count is just a build watermark). When
|
||||
// `mustMatch` is given, the header's render parameters must equal it, so a
|
||||
// count cached under different settings is never trusted.
|
||||
std::optional<uint16_t> getCachedPageCount(const RenderParams* mustMatch = nullptr) const;
|
||||
|
||||
// Look up the page number for a synthetic paragraph index from XPath p[N].
|
||||
std::optional<uint16_t> getPageForParagraphIndex(uint16_t pIndex) const;
|
||||
|
||||
@@ -431,6 +431,11 @@ CssStyle CssParser::parseDeclarations(std::string_view declBlock) {
|
||||
// Rule processing
|
||||
|
||||
void CssParser::processRuleBlockWithStyle(std::string_view selectorGroup, const CssStyle& style) {
|
||||
// Skip rules that don't define any supported properties to save RAM.
|
||||
if (!style.defined.anySet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we've reached the rule limit before processing
|
||||
if (rulesBySelector_.size() >= MAX_RULES) {
|
||||
LOG_DBG("CSS", "Reached max rules limit (%zu), stopping CSS parsing", MAX_RULES);
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
class CssParser {
|
||||
public:
|
||||
// Bump when CSS cache format or rules change; section caches are invalidated when this changes
|
||||
static constexpr uint8_t CSS_CACHE_VERSION = 7;
|
||||
static constexpr uint8_t CSS_CACHE_VERSION = 8;
|
||||
|
||||
explicit CssParser(std::string cachePath) : cachePath(std::move(cachePath)) {}
|
||||
~CssParser() = default;
|
||||
|
||||
@@ -0,0 +1,394 @@
|
||||
_language_name: "العربية"
|
||||
_language_code: "AR"
|
||||
_order: "28"
|
||||
|
||||
STR_CROSSPOINT: "CrossPoint"
|
||||
STR_BOOTING: "جاري بدء التشغيل"
|
||||
STR_SLEEPING: "وضع السكون"
|
||||
STR_ENTERING_SLEEP: "جاري الدخول في وضع السكون"
|
||||
STR_BROWSE_FILES: "تصفح الملفات"
|
||||
STR_FILE_TRANSFER: "نقل الملفات"
|
||||
STR_SETTINGS_TITLE: "الإعدادات"
|
||||
STR_CONTINUE_READING: "متابعة القراءة"
|
||||
STR_NO_OPEN_BOOK: "لا يوجد كتاب مفتوح"
|
||||
STR_START_READING: "ابدأ القراءة أدناه"
|
||||
STR_NO_FILES_FOUND: "لم يتم العثور على ملفات"
|
||||
STR_SELECT_CHAPTER: "اختر الفصل"
|
||||
STR_NO_CHAPTERS: "لا توجد فصول"
|
||||
STR_END_OF_BOOK: "نهاية الكتاب"
|
||||
STR_EMPTY_CHAPTER: "فصل فارغ"
|
||||
STR_INDEXING: "جاري الفهرسة"
|
||||
STR_INDEX_FAILED: "فشلت الفهرسة - كتاب غير صالح"
|
||||
STR_MEMORY_ERROR: "خطأ في الذاكرة"
|
||||
STR_PAGE_LOAD_ERROR: "خطأ في تحميل الصفحة"
|
||||
STR_EMPTY_FILE: "ملف فارغ"
|
||||
STR_OUT_OF_BOUNDS: "خارج النطاق"
|
||||
STR_LOADING: "جاري التحميل..."
|
||||
STR_LOADING_POPUP: "جاري التحميل"
|
||||
STR_WIFI_NETWORKS: "شبكات Wi-Fi"
|
||||
STR_NO_NETWORKS: "لم يتم العثور على شبكات"
|
||||
STR_NETWORKS_FOUND: "تم العثور على %zu شبكة"
|
||||
STR_SCANNING: "جاري البحث..."
|
||||
STR_FINDING_SAVED_WIFI: "جاري البحث عن شبكة Wi-Fi محفوظة..."
|
||||
STR_CONNECTING: "جاري الاتصال..."
|
||||
STR_CONNECTING_SAVED_WIFI: "جاري الاتصال بشبكة Wi-Fi محفوظة..."
|
||||
STR_SHOW_NETWORKS: "عرض"
|
||||
STR_CONNECTED: "تم الاتصال!"
|
||||
STR_CONNECTION_FAILED: "فشل الاتصال"
|
||||
STR_FORGET_NETWORK: "نسيان هذه الشبكة؟"
|
||||
STR_SAVE_PASSWORD: "حفظ كلمة المرور للمرة القادمة؟"
|
||||
STR_PRESS_OK_SCAN: "اضغط موافق لإعادة البحث"
|
||||
STR_JOIN_NETWORK: "الانضمام إلى شبكة"
|
||||
STR_CREATE_HOTSPOT: "إنشاء نقطة اتصال"
|
||||
STR_JOIN_DESC: "الاتصال بشبكة Wi-Fi موجودة"
|
||||
STR_HOTSPOT_DESC: "إنشاء شبكة Wi-Fi يمكن للآخرين الانضمام إليها"
|
||||
STR_STARTING_HOTSPOT: "جاري تشغيل نقطة الاتصال..."
|
||||
STR_HOTSPOT_MODE: "وضع نقطة الاتصال"
|
||||
STR_CONNECT_WIFI_HINT: "قم بتوصيل جهازك بشبكة Wi-Fi هذه"
|
||||
STR_OPEN_URL_HINT: "افتح هذا العنوان في المتصفح"
|
||||
STR_OR_HTTP_PREFIX: "أو http://"
|
||||
STR_SCAN_QR_HINT: "أو امسح رمز QR بهاتفك:"
|
||||
STR_CALIBRE_WIRELESS: "اتصال Calibre اللاسلكي"
|
||||
STR_NETWORK_LEGEND: "* = مشفرة | + = محفوظة"
|
||||
STR_MAC_ADDRESS: "عنوان MAC:"
|
||||
STR_CHECKING_WIFI: "جاري فحص Wi-Fi..."
|
||||
STR_ENTER_WIFI_PASSWORD: "أدخل كلمة مرور الشبكة"
|
||||
STR_ADD_HIDDEN_NETWORK: "إضافة شبكة مخفية..."
|
||||
STR_ENTER_WIFI_SSID: "أدخل اسم الشبكة (SSID)"
|
||||
STR_TO_PREFIX: "إلى "
|
||||
STR_CALIBRE_RECEIVING: "جاري الاستلام: "
|
||||
STR_CALIBRE_RECEIVED: "تم الاستلام: "
|
||||
STR_CALIBRE_INSTRUCTION_1: "1) قم بتثبيت إضافة CrossPoint Reader"
|
||||
STR_CALIBRE_INSTRUCTION_2: "2) اتصل بنفس شبكة Wi-Fi"
|
||||
STR_CALIBRE_INSTRUCTION_3: "3) في Calibre: اختر \"إرسال إلى الجهاز\""
|
||||
STR_CALIBRE_INSTRUCTION_4: "اترك هذه الشاشة مفتوحة أثناء الإرسال"
|
||||
STR_CAT_DISPLAY: "الشاشة"
|
||||
STR_CAT_READER: "القراءة"
|
||||
STR_CAT_CONTROLS: "الأزرار"
|
||||
STR_CAT_SYSTEM: "النظام"
|
||||
STR_SLEEP_SCREEN: "شاشة السكون"
|
||||
STR_QUICK_RESUME_TIMEOUT: "استئناف سريع بعد المهلة"
|
||||
STR_SLEEP_COVER_MODE: "عرض الغلاف في وضع السكون"
|
||||
STR_HIDE_BATTERY: "إخفاء نسبة البطارية"
|
||||
STR_EXTRA_SPACING: "تباعد إضافي بين الفقرات"
|
||||
STR_TEXT_AA: "تنعيم حواف النص"
|
||||
STR_IMAGES: "الصور"
|
||||
STR_IMAGES_DISPLAY: "عرض"
|
||||
STR_IMAGES_PLACEHOLDER: "عنصر نائب"
|
||||
STR_IMAGES_SUPPRESS: "إخفاء"
|
||||
STR_EOB_HOME: "الرئيسية"
|
||||
STR_EOB_CONTINUE_WITH: "المتابعة إلى"
|
||||
STR_SHORT_PWR_BTN: "ضغطة قصيرة على زر التشغيل"
|
||||
STR_ORIENTATION: "اتجاه القراءة"
|
||||
STR_SIDE_BTN_LAYOUT: "تخطيط الأزرار الجانبية (أثناء القراءة)"
|
||||
STR_FRONT_BTN_FOLLOW_ORIENTATION: "تدوير الأزرار الأمامية مع الشاشة"
|
||||
STR_LONG_PRESS_BEHAVIOR: "سلوك الضغطة الطويلة"
|
||||
STR_LONG_PRESS_BEHAVIOR_OFF: "إيقاف"
|
||||
STR_LONG_PRESS_BEHAVIOR_SKIP: "تخطي الفصل"
|
||||
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "تغيير الاتجاه"
|
||||
STR_LONG_PRESS_MENU: "قائمة الضغطة الطويلة"
|
||||
STR_FONT_PREVIEW_TEXT: "نص حكيم له سر قاطع وذو شأن عظيم مكتوب على ثوب أخضر ومغلف بجلد أزرق"
|
||||
STR_FONT_FAMILY: "خط القراءة"
|
||||
STR_FONT_SIZE: "حجم الخط"
|
||||
STR_LINE_SPACING: "تباعد الأسطر"
|
||||
STR_SCREEN_MARGIN: "هوامش شاشة القراءة"
|
||||
STR_PARA_ALIGNMENT: "محاذاة الفقرات"
|
||||
STR_HYPHENATION: "تقسيم الكلمات"
|
||||
STR_TIME_TO_SLEEP: "مهلة الدخول في السكون"
|
||||
STR_SHOW_HIDDEN_FILES: "عرض الملفات المخفية"
|
||||
STR_REMOVE_READ_FROM_RECENTS: "إزالة الكتب المقروءة من قائمة الكتب الأخيرة"
|
||||
STR_MOVE_FINISHED_TO_READ: "نقل الكتب المنتهية إلى مجلد المقروءة"
|
||||
STR_REFRESH_FREQ: "معدل تحديث الشاشة"
|
||||
STR_KOREADER_SYNC: "مزامنة KOReader"
|
||||
STR_CHECK_UPDATES: "التحقق من التحديثات"
|
||||
STR_LANGUAGE: "اللغة"
|
||||
STR_CLEAR_READING_CACHE: "مسح ذاكرة القراءة المؤقتة"
|
||||
STR_USERNAME: "اسم المستخدم"
|
||||
STR_PASSWORD: "كلمة المرور"
|
||||
STR_SYNC_SERVER_URL: "عنوان خادم المزامنة"
|
||||
STR_DOCUMENT_MATCHING: "مطابقة المستندات"
|
||||
STR_SEND_METADATA: "إرسال البيانات الوصفية للمستند"
|
||||
STR_AUTHENTICATE: "تسجيل الدخول"
|
||||
STR_KOREADER_USERNAME: "اسم مستخدم KOReader"
|
||||
STR_KOREADER_PASSWORD: "كلمة مرور KOReader"
|
||||
STR_FILENAME: "اسم الملف"
|
||||
STR_BINARY: "ثنائي"
|
||||
STR_SET_CREDENTIALS_FIRST: "أدخل بيانات الاعتماد أولا"
|
||||
STR_WIFI_CONN_FAILED: "فشل الاتصال بالشبكة"
|
||||
STR_AUTHENTICATING: "جاري التحقق..."
|
||||
STR_AUTH_SUCCESS: "تم تسجيل الدخول بنجاح!"
|
||||
STR_KOREADER_AUTH: "تسجيل الدخول إلى KOReader"
|
||||
STR_SYNC_READY: "مزامنة KOReader جاهزة للاستخدام"
|
||||
STR_AUTH_FAILED: "فشل تسجيل الدخول"
|
||||
STR_DONE: "تم"
|
||||
STR_CLEAR_CACHE_WARNING_1: "سيؤدي هذا إلى مسح جميع بيانات الكتب المخزنة."
|
||||
STR_CLEAR_CACHE_WARNING_2: "سيتم فقدان كل تقدم القراءة!"
|
||||
STR_CLEAR_CACHE_WARNING_3: "ستحتاج الكتب إلى إعادة فهرسة"
|
||||
STR_CLEAR_CACHE_WARNING_4: "عند فتحها مرة أخرى."
|
||||
STR_CLEARING_CACHE: "جاري مسح الذاكرة المؤقتة..."
|
||||
STR_CACHE_CLEARED: "تم مسح الذاكرة المؤقتة"
|
||||
STR_ITEMS_REMOVED: "عناصر تمت إزالتها"
|
||||
STR_FAILED_LOWER: "فشل"
|
||||
STR_CLEAR_CACHE_FAILED: "فشل مسح الذاكرة المؤقتة"
|
||||
STR_CHECK_SERIAL_OUTPUT: "راجع مخرجات المنفذ التسلسلي للتفاصيل"
|
||||
STR_DARK: "داكن"
|
||||
STR_LIGHT: "فاتح"
|
||||
STR_CUSTOM: "مخصص"
|
||||
STR_COVER: "الغلاف"
|
||||
STR_NONE_OPT: "بدون"
|
||||
STR_FIT: "ملاءمة"
|
||||
STR_CROP: "قص"
|
||||
STR_NEVER: "أبدا"
|
||||
STR_IN_READER: "أثناء القراءة"
|
||||
STR_ALWAYS: "دائما"
|
||||
STR_IGNORE: "تجاهل"
|
||||
STR_SLEEP: "سكون"
|
||||
STR_PAGE_TURN: "تقليب الصفحة"
|
||||
STR_FORCE_REFRESH: "تحديث الشاشة"
|
||||
STR_PORTRAIT: "عمودي"
|
||||
STR_LANDSCAPE_CW: "أفقي (يمين)"
|
||||
STR_INVERTED: "عكس الألوان"
|
||||
STR_ORIENTATION_INVERTED: "عمودي 180°"
|
||||
STR_LANDSCAPE_CCW: "أفقي (يسار)"
|
||||
STR_PREV_NEXT: "السابق/التالي"
|
||||
STR_NEXT_PREV: "التالي/السابق"
|
||||
STR_KOSYNC: "KOSync"
|
||||
STR_BOOKMARK_OPTION: "إشارة مرجعية"
|
||||
STR_DISABLED: "معطل"
|
||||
STR_NOTO_SERIF: "Noto Serif"
|
||||
STR_NOTO_SANS: "Noto Sans"
|
||||
STR_SMALL: "صغير"
|
||||
STR_MEDIUM: "متوسط"
|
||||
STR_LARGE: "كبير"
|
||||
STR_X_LARGE: "كبير جدا"
|
||||
STR_TIGHT: "ضيق"
|
||||
STR_NORMAL: "عادي"
|
||||
STR_WIDE: "واسع"
|
||||
STR_JUSTIFY: "ضبط الطرفين"
|
||||
STR_ALIGN_LEFT: "يسار"
|
||||
STR_CENTER: "وسط"
|
||||
STR_ALIGN_RIGHT: "يمين"
|
||||
STR_PAGES_1: "صفحة واحدة"
|
||||
STR_PAGES_5: "5 صفحات"
|
||||
STR_PAGES_10: "10 صفحات"
|
||||
STR_PAGES_15: "15 صفحة"
|
||||
STR_PAGES_30: "30 صفحة"
|
||||
STR_UPDATE: "تحديث"
|
||||
STR_CHECKING_UPDATE: "جاري التحقق من التحديثات..."
|
||||
STR_NEW_UPDATE: "يتوفر تحديث جديد!"
|
||||
STR_CURRENT_VERSION: "الإصدار الحالي: "
|
||||
STR_NEW_VERSION: "الإصدار الجديد: "
|
||||
STR_UPDATING: "جاري التحديث..."
|
||||
STR_NO_UPDATE: "لا يتوفر تحديث"
|
||||
STR_UPDATE_FAILED: "فشل التحديث"
|
||||
STR_UPDATE_COMPLETE: "اكتمل التحديث"
|
||||
STR_POWER_ON_HINT: "اضغط مطولا على زر التشغيل لتشغيل الجهاز من جديد"
|
||||
STR_RESTARTING_HINT: "جاري إعادة التشغيل... إذا لم يعمل الجهاز، اضغط مطولا على زر التشغيل لعدة ثوان."
|
||||
STR_NO_ENTRIES: "لم يتم العثور على عناصر"
|
||||
STR_DOWNLOADING: "جاري التنزيل..."
|
||||
STR_DOWNLOAD_FAILED: "فشل التنزيل"
|
||||
STR_ERROR_MSG: "خطأ:"
|
||||
STR_UNNAMED: "بدون اسم"
|
||||
STR_HOLD_OPEN_TO_DELETE: "اضغط مطولا على فتح للحذف"
|
||||
STR_NO_SERVER_URL: "لم يتم تحديد عنوان الخادم"
|
||||
STR_FETCH_FEED_FAILED: "فشل جلب القائمة"
|
||||
STR_PARSE_FEED_FAILED: "فشل تحليل القائمة"
|
||||
STR_NEXT_PAGE: "الصفحة التالية"
|
||||
STR_PREV_PAGE: "الصفحة السابقة"
|
||||
STR_NETWORK_PREFIX: "الشبكة: "
|
||||
STR_IP_ADDRESS_PREFIX: "عنوان IP: "
|
||||
STR_ERROR_GENERAL_FAILURE: "خطأ: فشل عام"
|
||||
STR_ERROR_NETWORK_NOT_FOUND: "خطأ: الشبكة غير موجودة"
|
||||
STR_ERROR_CONNECTION_TIMEOUT: "خطأ: انتهت مهلة الاتصال"
|
||||
STR_SD_CARD: "بطاقة SD"
|
||||
STR_BACK: "رجوع »"
|
||||
STR_EXIT: "خروج »"
|
||||
STR_HOME: "الرئيسية »"
|
||||
STR_SELECT: "اختيار"
|
||||
STR_SELECTED: "محدد"
|
||||
STR_TOGGLE: "تبديل"
|
||||
STR_TOGGLE_BOOKMARK: "إضافة/إزالة إشارة مرجعية"
|
||||
STR_CONFIRM: "تأكيد"
|
||||
STR_CANCEL: "إلغاء"
|
||||
STR_CONNECT: "اتصال"
|
||||
STR_OPEN: "فتح"
|
||||
STR_DOWNLOAD: "تنزيل"
|
||||
STR_RETRY: "إعادة المحاولة"
|
||||
STR_YES: "نعم"
|
||||
STR_NO: "لا"
|
||||
STR_SHOW: "عرض"
|
||||
STR_HIDE: "إخفاء"
|
||||
STR_STATE_ON: "تشغيل"
|
||||
STR_STATE_OFF: "إيقاف"
|
||||
STR_NOT_SET: "غير محدد"
|
||||
STR_DIR_LEFT: "يسار"
|
||||
STR_DIR_RIGHT: "يمين"
|
||||
STR_DIR_UP: "أعلى"
|
||||
STR_DIR_DOWN: "أسفل"
|
||||
STR_OK_BUTTON: "موافق"
|
||||
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_XTC_STATUS_BAR: "شريط حالة XTC"
|
||||
STR_BOTTOM: "أسفل"
|
||||
STR_TOP: "أعلى"
|
||||
STR_CLOCK: "الساعة"
|
||||
STR_CLOCK_UTC_OFFSET: "فرق التوقيت عن UTC"
|
||||
STR_CLOCK_FORMAT: "تنسيق الساعة"
|
||||
STR_CLOCK_FORMAT_24H: "24 ساعة"
|
||||
STR_CLOCK_FORMAT_12H: "12 ساعة"
|
||||
STR_CURRENT_TIME: "الوقت الحالي:"
|
||||
STR_NEXT_FIELD: "التالي"
|
||||
STR_CLOCK_SYNC: "مزامنة الساعة"
|
||||
STR_CLOCK_SYNC_NOW: "مزامنة الساعة الآن"
|
||||
STR_CLOCK_SYNCING: "جاري المزامنة عبر NTP..."
|
||||
STR_CLOCK_SYNC_OK: "تمت مزامنة الساعة"
|
||||
STR_CLOCK_SYNC_FAIL: "فشلت المزامنة"
|
||||
STR_CLOCK_SYNC_NO_WIFI: "لا يوجد اتصال Wi-Fi"
|
||||
STR_CLOCK_SYNC_NO_WIFI_HINT: "اتصل بشبكة Wi-Fi أولا، ثم حاول مرة أخرى."
|
||||
STR_CLOCK_SYNCED: "تمت مزامنة الساعة"
|
||||
STR_UI_THEME: "مظهر الواجهة"
|
||||
STR_THEME_CLASSIC: "كلاسيكي"
|
||||
STR_THEME_LYRA: "Lyra"
|
||||
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
|
||||
STR_THEME_LYRA_EXTENDED: "Lyra موسع"
|
||||
STR_SUNLIGHT_FADING_FIX: "إصلاح بهتان الشاشة في الشمس"
|
||||
STR_REMAP_FRONT_BUTTONS: "تغيير وظائف الأزرار الأمامية"
|
||||
STR_BOOKMARKS: "الإشارات المرجعية"
|
||||
STR_BOOKMARK_ADDED: "تمت إضافة الإشارة المرجعية."
|
||||
STR_BOOKMARK_REMOVED: "تمت إزالة الإشارة المرجعية."
|
||||
STR_OPDS_BROWSER: "متصفح OPDS"
|
||||
STR_SEARCH: "بحث"
|
||||
STR_COVER_CUSTOM: "الغلاف + مخصص"
|
||||
STR_QUICK_RESUME: "استئناف سريع"
|
||||
STR_MENU_RECENT_BOOKS: "الكتب الأخيرة"
|
||||
STR_REMOVE_FROM_RECENTS: "إزالة من الكتب الأخيرة؟"
|
||||
STR_NO_RECENT_BOOKS: "لا توجد كتب أخيرة"
|
||||
STR_CALIBRE_DESC: "استخدم النقل اللاسلكي من Calibre"
|
||||
STR_FORGET_AND_REMOVE: "نسيان الشبكة وحذف كلمة المرور المحفوظة؟"
|
||||
STR_FORGET_BUTTON: "نسيان"
|
||||
STR_CALIBRE_STARTING: "جاري تشغيل Calibre..."
|
||||
STR_CALIBRE_SETUP: "الإعداد"
|
||||
STR_CALIBRE_STATUS: "الحالة"
|
||||
STR_CLEAR_BUTTON: "مسح"
|
||||
STR_DEFAULT_VALUE: "افتراضي"
|
||||
STR_REMAP_PROMPT: "اضغط زرا أماميا لكل وظيفة"
|
||||
STR_UNASSIGNED: "غير معين"
|
||||
STR_ALREADY_ASSIGNED: "معين مسبقا"
|
||||
STR_REMAP_RESET_HINT: "الزر الجانبي العلوي: استعادة التخطيط الافتراضي"
|
||||
STR_REMAP_CANCEL_HINT: "الزر الجانبي السفلي: إلغاء التغيير"
|
||||
STR_HW_BACK_LABEL: "رجوع (الزر 1)"
|
||||
STR_HW_CONFIRM_LABEL: "تأكيد (الزر 2)"
|
||||
STR_HW_LEFT_LABEL: "يسار (الزر 3)"
|
||||
STR_HW_RIGHT_LABEL: "يمين (الزر 4)"
|
||||
STR_GO_TO_PERCENT: "الانتقال إلى نسبة %"
|
||||
STR_GO_HOME_BUTTON: "العودة إلى الرئيسية"
|
||||
STR_SYNC_PROGRESS: "مزامنة التقدم"
|
||||
STR_DELETE_CACHE: "حذف ذاكرة الكتاب المؤقتة"
|
||||
STR_DELETE: "حذف"
|
||||
STR_CONFIRM_DELETE_BOOKMARK: "حذف هذه الإشارة المرجعية؟"
|
||||
STR_DISPLAY_QR: "عرض الصفحة كرمز QR"
|
||||
STR_CHAPTER_PREFIX: "الفصل: "
|
||||
STR_PAGES_SEPARATOR: " صفحات | "
|
||||
STR_BOOK_PREFIX: "الكتاب: "
|
||||
STR_CALIBRE_URL_HINT: "في Calibre، أضف /opds إلى العنوان"
|
||||
STR_SYNCING_TIME: "جاري مزامنة الوقت..."
|
||||
STR_CALC_HASH: "جاري حساب بصمة المستند..."
|
||||
STR_HASH_FAILED: "فشل حساب بصمة المستند"
|
||||
STR_FETCH_PROGRESS: "جاري جلب التقدم من الخادم..."
|
||||
STR_UPLOAD_PROGRESS: "جاري رفع التقدم..."
|
||||
STR_NO_CREDENTIALS_MSG: "لم يتم إعداد بيانات الاعتماد"
|
||||
STR_KOREADER_SETUP_HINT: "قم بإعداد حساب KOReader في الإعدادات"
|
||||
STR_PROGRESS_FOUND: "تم العثور على تقدم سابق!"
|
||||
STR_REMOTE_LABEL: "الخادم:"
|
||||
STR_LOCAL_LABEL: "الجهاز:"
|
||||
STR_PAGE_OVERALL_FORMAT: "صفحة %d، %.2f%% إجمالا"
|
||||
STR_PAGE_TOTAL_OVERALL_FORMAT: "صفحة %d من %d، %.2f%% إجمالا"
|
||||
STR_DEVICE_FROM_FORMAT: " من: %s"
|
||||
STR_APPLY_REMOTE: "استخدام تقدم الخادم"
|
||||
STR_UPLOAD_LOCAL: "رفع التقدم المحلي"
|
||||
STR_NO_REMOTE_MSG: "لا يوجد تقدم على الخادم"
|
||||
STR_UPLOAD_PROMPT: "رفع الموضع الحالي؟"
|
||||
STR_UPLOAD_SUCCESS: "تم رفع التقدم!"
|
||||
STR_SYNC_FAILED_MSG: "فشلت المزامنة"
|
||||
STR_SAVE_PROGRESS_FAILED: "تعذر حفظ التقدم"
|
||||
STR_SECTION_PREFIX: "القسم "
|
||||
STR_UPLOAD: "رفع"
|
||||
STR_BOOK_S_STYLE: "تنسيق الكتاب الأصلي"
|
||||
STR_EMBEDDED_STYLE: "التنسيق المضمن"
|
||||
STR_FOCUS_READING: "قراءة مركزة"
|
||||
STR_OPDS_SERVER_URL: "عنوان خادم OPDS"
|
||||
STR_PWR_BTN_FOOTNOTE_BACK: "عودة سريعة من الحواشي"
|
||||
STR_SET_SLEEP_COVER: "تعيين الغلاف"
|
||||
STR_FOOTNOTES: "الحواشي"
|
||||
STR_NO_FOOTNOTES: "لا توجد حواش في هذه الصفحة"
|
||||
STR_LINK: "[رابط]"
|
||||
STR_SCREENSHOT_BUTTON: "التقاط لقطة شاشة"
|
||||
STR_SLEEP_TIMER_VALUE_FORMAT: "%u دقيقة"
|
||||
STR_SLEEP_NEVER: "أبدا"
|
||||
STR_STEP_HINT_FRONT: "الأزرار الأمامية:"
|
||||
STR_STEP_HINT_SIDE: "الأزرار الجانبية:"
|
||||
STR_ADD_SERVER: "إضافة خادم"
|
||||
STR_SERVER_NAME: "اسم الخادم"
|
||||
STR_NO_SERVERS: "لم يتم إعداد خوادم OPDS"
|
||||
STR_DELETE_SERVER: "حذف الخادم"
|
||||
STR_OPDS_SERVERS: "خوادم OPDS"
|
||||
STR_AUTO_TURN_ENABLED: "التقليب التلقائي مفعل: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "التقليب التلقائي (صفحات في الدقيقة)"
|
||||
STR_MANAGE_FONTS: "إدارة الخطوط"
|
||||
STR_FONT_BROWSER: "متصفح الخطوط"
|
||||
STR_LOADING_FONT_LIST: "جاري تحميل قائمة الخطوط..."
|
||||
STR_NO_FONTS_AVAILABLE: "لا تتوفر خطوط"
|
||||
STR_FONT_INSTALLED: "تم تثبيت الخط!"
|
||||
STR_FONT_INSTALL_FAILED: "فشل تثبيت الخط"
|
||||
STR_INSTALLED: "مثبت"
|
||||
STR_DOWNLOAD_ALL: "تنزيل الكل"
|
||||
STR_UPDATE_ALL: "تحديث الكل"
|
||||
STR_UPDATE_AVAILABLE: "تحديث"
|
||||
STR_CRASH_TITLE: "تعطل النظام"
|
||||
STR_CRASH_DESCRIPTION: "تم حفظ تقرير مفصل في الملف crash_report.txt. يرجى إرفاق هذا الملف عند الإبلاغ عن المشكلة."
|
||||
STR_CRASH_REASON: "سبب التعطل:"
|
||||
STR_CRASH_NO_REASON: "(لم يتم تسجيل سبب)"
|
||||
STR_TILT_PAGE_TURN: "تقليب الصفحة بالإمالة"
|
||||
STR_KB_HINT_MOVE_CURSOR: "اضغط زر اليمين أو اليسار لتحريك المؤشر"
|
||||
STR_KB_HINT_RETURN_CURSOR: "اضغط زر اليسار للعودة إلى موضع المؤشر"
|
||||
STR_KB_HINT_HIDE_PASSWORD: "اضغط مطولا على زر اليمين ثم اضغط [***] لإخفاء كلمة المرور"
|
||||
STR_KB_HINT_SHOW_PASSWORD: "اضغط مطولا على زر اليمين ثم اضغط [abc] لإظهار كلمة المرور"
|
||||
STR_KB_HINT_TOGGLE_HIDE_PASSWORD: "اضغط [***] لإخفاء كلمة المرور"
|
||||
STR_KB_HINT_TOGGLE_SHOW_PASSWORD: "اضغط [abc] لإظهار كلمة المرور"
|
||||
STR_KB_HINT_EDIT_ENTRY: "اضغط مطولا على زر الأعلى للتعديل"
|
||||
STR_KB_TIPS: "نصائح:"
|
||||
STR_KB_HINT_RETURN_KEYBOARD: "اضغط زر الأسفل للعودة إلى لوحة المفاتيح"
|
||||
STR_KB_HINT_EXIT_URL_MODE: "اضغط ABC للخروج من وضع URL"
|
||||
STR_KB_HINT_CLEAR_TEXT: "اضغط مطولا على DEL لمسح كل النص"
|
||||
STR_KB_HINT_SECONDARY_CHAR: "اضغط مطولا على زر الاختيار للحرف الثانوي"
|
||||
STR_KB_HINT_UPPER_SECONDARY: "اضغط مطولا على زر الاختيار للحرف الكبير أو الثانوي"
|
||||
STR_KB_HINT_LOWER_SECONDARY: "اضغط مطولا على زر الاختيار للحرف الصغير أو الثانوي"
|
||||
STR_KB_HINT_URL_SNIPPETS: "اضغط URL للاختصارات"
|
||||
STR_SD_FIRMWARE_UPDATE: "تحديث البرنامج الثابت من بطاقة SD"
|
||||
STR_SELECT_FIRMWARE_FILE: "اختر ملف البرنامج الثابت (.bin)"
|
||||
STR_NO_BIN_FILES: "لم يتم العثور على ملفات .bin"
|
||||
STR_VALIDATING_FIRMWARE: "جاري التحقق من البرنامج الثابت..."
|
||||
STR_INVALID_FIRMWARE: "ملف برنامج ثابت غير صالح"
|
||||
STR_FIRMWARE_TOO_LARGE: "البرنامج الثابت أكبر من القسم المخصص له"
|
||||
STR_FIRMWARE_TOO_SMALL: "ملف البرنامج الثابت صغير جدا"
|
||||
STR_FIRMWARE_UPDATE_PROMPT: "تحديث البرنامج الثابت؟"
|
||||
STR_FIRMWARE_FILE_OPEN_FAILED: "تعذر فتح الملف"
|
||||
STR_FIRMWARE_WRITE_FAILED: "فشلت كتابة البرنامج الثابت"
|
||||
STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "لا تطفئ الجهاز!"
|
||||
STR_RECOVERY_MODE: "وضع الاستعادة"
|
||||
STR_RECOVERY_MODE_HINT: "ضع الملف firmware.bin في المجلد الرئيسي لبطاقة SD ثم اختره"
|
||||
@@ -229,7 +229,7 @@ STR_OK_BUTTON: "OK"
|
||||
STR_SLEEP_COVER_FILTER: "Sleep Screen Cover Filter"
|
||||
STR_FILTER_CONTRAST: "Contrast"
|
||||
STR_CUSTOMISE_STATUS_BAR: "Customise Status Bar"
|
||||
STR_CHAPTER_PAGE_COUNT: "Chapter Page Count"
|
||||
STR_CHAPTER_PAGE_COUNT: "Page Count"
|
||||
STR_BOOK_PROGRESS_PERCENTAGE: "Book Progress Percentage"
|
||||
STR_PROGRESS_BAR: "Progress Bar"
|
||||
STR_PROGRESS_BAR_THICKNESS: "Progress Bar Thickness"
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
_language_name: "Norsk bokmål"
|
||||
_language_code: "NB"
|
||||
_order: "26"
|
||||
|
||||
STR_CROSSPOINT: "CrossPoint"
|
||||
STR_BOOTING: "STARTER"
|
||||
STR_SLEEPING: "HVILER"
|
||||
STR_ENTERING_SLEEP: "Går i hvilemodus"
|
||||
STR_BROWSE_FILES: "Bla i filer"
|
||||
STR_FILE_TRANSFER: "Filoverføring"
|
||||
STR_SETTINGS_TITLE: "Innstillinger"
|
||||
STR_CONTINUE_READING: "Fortsett å lese"
|
||||
STR_NO_OPEN_BOOK: "Ingen åpen bok"
|
||||
STR_START_READING: "Start å lese nedenfor"
|
||||
STR_NO_FILES_FOUND: "Ingen filer funnet"
|
||||
STR_SELECT_CHAPTER: "Velg kapittel"
|
||||
STR_NO_CHAPTERS: "Ingen kapitler"
|
||||
STR_END_OF_BOOK: "Slutten av boken"
|
||||
STR_EMPTY_CHAPTER: "Tomt kapittel"
|
||||
STR_INDEXING: "Indekserer"
|
||||
STR_MEMORY_ERROR: "Minnefeil"
|
||||
STR_PAGE_LOAD_ERROR: "Feil ved sidelasting"
|
||||
STR_EMPTY_FILE: "Tom fil"
|
||||
STR_OUT_OF_BOUNDS: "Utenfor område"
|
||||
STR_LOADING: "Laster..."
|
||||
STR_LOADING_POPUP: "Laster"
|
||||
STR_WIFI_NETWORKS: "WiFi-nettverk"
|
||||
STR_NO_NETWORKS: "Ingen nettverk funnet"
|
||||
STR_NETWORKS_FOUND: "%zu nettverk funnet"
|
||||
STR_SCANNING: "Skanner..."
|
||||
STR_CONNECTING: "Kobler til..."
|
||||
STR_CONNECTED: "Tilkoblet!"
|
||||
STR_CONNECTION_FAILED: "Tilkobling mislyktes"
|
||||
STR_FORGET_NETWORK: "Glem nettverk?"
|
||||
STR_SAVE_PASSWORD: "Lagre passord til neste gang?"
|
||||
STR_PRESS_OK_SCAN: "Trykk OK for å skanne på nytt"
|
||||
STR_JOIN_NETWORK: "Koble til et nettverk"
|
||||
STR_CREATE_HOTSPOT: "Opprett hotspot"
|
||||
STR_JOIN_DESC: "Koble til et eksisterende WiFi-nettverk"
|
||||
STR_HOTSPOT_DESC: "Opprett et WiFi-nettverk andre kan koble seg til"
|
||||
STR_STARTING_HOTSPOT: "Starter hotspot..."
|
||||
STR_HOTSPOT_MODE: "Hotspot-modus"
|
||||
STR_CONNECT_WIFI_HINT: "Koble enheten din til dette WiFi-nettverket"
|
||||
STR_OPEN_URL_HINT: "Åpne denne URL-en i nettleseren din"
|
||||
STR_OR_HTTP_PREFIX: "eller http://"
|
||||
STR_SCAN_QR_HINT: "eller skann QR-kode med telefonen din:"
|
||||
STR_CALIBRE_WIRELESS: "Calibre Wireless"
|
||||
STR_NETWORK_LEGEND: "* = Kryptert | + = Lagret"
|
||||
STR_MAC_ADDRESS: "MAC-adresse:"
|
||||
STR_CHECKING_WIFI: "Sjekker WiFi..."
|
||||
STR_ENTER_WIFI_PASSWORD: "Skriv inn WiFi-passord"
|
||||
STR_TO_PREFIX: "til "
|
||||
STR_CALIBRE_RECEIVING: "Mottar: "
|
||||
STR_CALIBRE_RECEIVED: "Mottatt: "
|
||||
STR_CALIBRE_INSTRUCTION_1: "1) Installer CrossPoint Reader-plugin"
|
||||
STR_CALIBRE_INSTRUCTION_2: "2) Vær på samme WiFi-nettverk"
|
||||
STR_CALIBRE_INSTRUCTION_3: "3) I Calibre: \"Send til enhet\""
|
||||
STR_CALIBRE_INSTRUCTION_4: "\"Hold denne skjermen åpen mens du sender\""
|
||||
STR_CAT_DISPLAY: "Skjerm"
|
||||
STR_CAT_READER: "Leser"
|
||||
STR_CAT_CONTROLS: "Betjening"
|
||||
STR_CAT_SYSTEM: "System"
|
||||
STR_SLEEP_SCREEN: "Hvileskjerm"
|
||||
STR_QUICK_RESUME_TIMEOUT: "Hurtig gjenopptak ved tidsavbrudd"
|
||||
STR_SLEEP_COVER_MODE: "Omslagsmodus for hvileskjerm"
|
||||
STR_HIDE_BATTERY: "Skjul batteri %"
|
||||
STR_EXTRA_SPACING: "Ekstra avsnittsavstand"
|
||||
STR_TEXT_AA: "Tekstutjevning"
|
||||
STR_IMAGES: "Bilder"
|
||||
STR_IMAGES_DISPLAY: "Vis"
|
||||
STR_IMAGES_PLACEHOLDER: "Plassholder"
|
||||
STR_IMAGES_SUPPRESS: "Skjul"
|
||||
STR_SHORT_PWR_BTN: "Kort trykk på av/på-knapp"
|
||||
STR_ORIENTATION: "Leseretning"
|
||||
STR_SIDE_BTN_LAYOUT: "Sideknapp-oppsett (leser)"
|
||||
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orienter frontknapper"
|
||||
STR_LONG_PRESS_BEHAVIOR: "Atferd ved langt trykk"
|
||||
STR_LONG_PRESS_BEHAVIOR_OFF: "AV"
|
||||
STR_LONG_PRESS_BEHAVIOR_SKIP: "Kapittelhopp"
|
||||
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Endre orientering"
|
||||
STR_LONG_PRESS_MENU: "Langt trykk på Meny"
|
||||
STR_FONT_PREVIEW_TEXT: "Høvdingens kjære squaw får litt pizza i Mexico by"
|
||||
STR_FONT_FAMILY: "Skrifttype i leser"
|
||||
STR_FONT_SIZE: "Skriftstørrelse i leser"
|
||||
STR_LINE_SPACING: "Linjeavstand i leser"
|
||||
STR_SCREEN_MARGIN: "Skjermmarg i leser"
|
||||
STR_PARA_ALIGNMENT: "Avsnittsjustering i leser"
|
||||
STR_HYPHENATION: "Orddeling"
|
||||
STR_TIME_TO_SLEEP: "Tid før hvile"
|
||||
STR_SHOW_HIDDEN_FILES: "Vis skjulte filer"
|
||||
STR_REMOVE_READ_FROM_RECENTS: "Fjern leste bøker fra Nylig-listen"
|
||||
STR_MOVE_FINISHED_TO_READ: "Flytt fullførte bøker til Read-mappen"
|
||||
STR_REFRESH_FREQ: "Oppdateringsfrekvens"
|
||||
STR_KOREADER_SYNC: "KOReader Sync"
|
||||
STR_CHECK_UPDATES: "Se etter oppdateringer"
|
||||
STR_LANGUAGE: "Språk"
|
||||
STR_CLEAR_READING_CACHE: "Tøm lesebuffer"
|
||||
STR_USERNAME: "Brukernavn"
|
||||
STR_PASSWORD: "Passord"
|
||||
STR_SYNC_SERVER_URL: "URL til synk-server"
|
||||
STR_DOCUMENT_MATCHING: "Dokumentgjenkjenning"
|
||||
STR_AUTHENTICATE: "Godkjenn"
|
||||
STR_KOREADER_USERNAME: "KOReader-brukernavn"
|
||||
STR_KOREADER_PASSWORD: "KOReader-passord"
|
||||
STR_FILENAME: "Filnavn"
|
||||
STR_BINARY: "Binær"
|
||||
STR_SET_CREDENTIALS_FIRST: "Angi påloggingsinfo først"
|
||||
STR_WIFI_CONN_FAILED: "WiFi-tilkobling mislyktes"
|
||||
STR_AUTHENTICATING: "Godkjenner..."
|
||||
STR_AUTH_SUCCESS: "Godkjenning vellykket!"
|
||||
STR_KOREADER_AUTH: "KOReader-godkjenning"
|
||||
STR_SYNC_READY: "KOReader-synk er klar til bruk"
|
||||
STR_AUTH_FAILED: "Godkjenning mislyktes"
|
||||
STR_DONE: "Ferdig"
|
||||
STR_CLEAR_CACHE_WARNING_1: "Dette tømmer alle bufrede bokdata."
|
||||
STR_CLEAR_CACHE_WARNING_2: "All lesefremdrift går tapt!"
|
||||
STR_CLEAR_CACHE_WARNING_3: "Bøker må indekseres på nytt"
|
||||
STR_CLEAR_CACHE_WARNING_4: "når de åpnes igjen."
|
||||
STR_CLEARING_CACHE: "Tømmer buffer..."
|
||||
STR_CACHE_CLEARED: "Buffer tømt"
|
||||
STR_ITEMS_REMOVED: "elementer fjernet"
|
||||
STR_FAILED_LOWER: "mislyktes"
|
||||
STR_CLEAR_CACHE_FAILED: "Kunne ikke tømme buffer"
|
||||
STR_CHECK_SERIAL_OUTPUT: "Sjekk seriell utdata for detaljer"
|
||||
STR_DARK: "Mørk"
|
||||
STR_LIGHT: "Lys"
|
||||
STR_CUSTOM: "Egendefinert"
|
||||
STR_COVER: "Omslag"
|
||||
STR_NONE_OPT: "Ingen"
|
||||
STR_FIT: "Tilpass"
|
||||
STR_CROP: "Beskjær"
|
||||
STR_NEVER: "Aldri"
|
||||
STR_IN_READER: "I leseren"
|
||||
STR_ALWAYS: "Alltid"
|
||||
STR_IGNORE: "Ignorer"
|
||||
STR_SLEEP: "Hvile"
|
||||
STR_PAGE_TURN: "Bla om"
|
||||
STR_FORCE_REFRESH: "Oppdater skjerm"
|
||||
STR_PORTRAIT: "Stående"
|
||||
STR_LANDSCAPE_CW: "Liggende med klokken"
|
||||
STR_INVERTED: "Opp ned"
|
||||
STR_ORIENTATION_INVERTED: "Portrett 180°"
|
||||
STR_LANDSCAPE_CCW: "Liggende mot klokken"
|
||||
STR_PREV_NEXT: "Forrige/Neste"
|
||||
STR_NEXT_PREV: "Neste/Forrige"
|
||||
STR_KOSYNC: "KOSync"
|
||||
STR_BOOKMARK_OPTION: "Bokmerke"
|
||||
STR_DISABLED: "Deaktivert"
|
||||
STR_NOTO_SERIF: "Noto Serif"
|
||||
STR_NOTO_SANS: "Noto Sans"
|
||||
STR_SMALL: "Liten"
|
||||
STR_MEDIUM: "Middels"
|
||||
STR_LARGE: "Stor"
|
||||
STR_X_LARGE: "Ekstra stor"
|
||||
STR_TIGHT: "Tett"
|
||||
STR_NORMAL: "Normal"
|
||||
STR_WIDE: "Bred"
|
||||
STR_JUSTIFY: "Blokkjustert"
|
||||
STR_ALIGN_LEFT: "Venstre"
|
||||
STR_CENTER: "Midtstilt"
|
||||
STR_ALIGN_RIGHT: "Høyre"
|
||||
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: "Oppdater"
|
||||
STR_CHECKING_UPDATE: "Ser etter oppdatering..."
|
||||
STR_NEW_UPDATE: "Ny oppdatering tilgjengelig!"
|
||||
STR_CURRENT_VERSION: "Nåværende versjon: "
|
||||
STR_NEW_VERSION: "Ny versjon: "
|
||||
STR_UPDATING: "Oppdaterer..."
|
||||
STR_NO_UPDATE: "Ingen oppdatering tilgjengelig"
|
||||
STR_UPDATE_FAILED: "Oppdatering mislyktes"
|
||||
STR_UPDATE_COMPLETE: "Oppdatering fullført"
|
||||
STR_POWER_ON_HINT: "Trykk og hold av/på-knappen for å slå på igjen"
|
||||
STR_RESTARTING_HINT: "Starter på nytt... Hvis enheten ikke starter, hold av/på-knappen i noen sekunder."
|
||||
STR_NO_ENTRIES: "Ingen oppføringer funnet"
|
||||
STR_DOWNLOADING: "Laster ned..."
|
||||
STR_DOWNLOAD_FAILED: "Nedlasting mislyktes"
|
||||
STR_ERROR_MSG: "Feil:"
|
||||
STR_UNNAMED: "Uten navn"
|
||||
STR_HOLD_OPEN_TO_DELETE: "Hold Åpne for å slette"
|
||||
STR_NO_SERVER_URL: "Ingen server-URL konfigurert"
|
||||
STR_FETCH_FEED_FAILED: "Kunne ikke hente feed"
|
||||
STR_PARSE_FEED_FAILED: "Kunne ikke tolke feed"
|
||||
STR_NEXT_PAGE: "Neste side »"
|
||||
STR_PREV_PAGE: "« Forrige side"
|
||||
STR_NETWORK_PREFIX: "Nettverk: "
|
||||
STR_IP_ADDRESS_PREFIX: "IP-adresse: "
|
||||
STR_ERROR_GENERAL_FAILURE: "Feil: Generell feil"
|
||||
STR_ERROR_NETWORK_NOT_FOUND: "Feil: Nettverk ikke funnet"
|
||||
STR_ERROR_CONNECTION_TIMEOUT: "Feil: Tidsavbrudd for tilkobling"
|
||||
STR_SD_CARD: "SD-kort"
|
||||
STR_BACK: "« Tilbake"
|
||||
STR_EXIT: "« Avslutt"
|
||||
STR_HOME: "« Hjem"
|
||||
STR_SELECT: "Velg"
|
||||
STR_SELECTED: "Valgt"
|
||||
STR_TOGGLE: "Veksle"
|
||||
STR_TOGGLE_BOOKMARK: "Veksle bokmerke"
|
||||
STR_CONFIRM: "Bekreft"
|
||||
STR_CANCEL: "Avbryt"
|
||||
STR_CONNECT: "Koble til"
|
||||
STR_OPEN: "Åpne"
|
||||
STR_DOWNLOAD: "Last ned"
|
||||
STR_RETRY: "Prøv igjen"
|
||||
STR_YES: "Ja"
|
||||
STR_NO: "Nei"
|
||||
STR_SHOW: "Vis"
|
||||
STR_HIDE: "Skjul"
|
||||
STR_STATE_ON: "PÅ"
|
||||
STR_STATE_OFF: "AV"
|
||||
STR_NOT_SET: "Ikke angitt"
|
||||
STR_DIR_LEFT: "Venstre"
|
||||
STR_DIR_RIGHT: "Høyre"
|
||||
STR_DIR_UP: "Opp"
|
||||
STR_DIR_DOWN: "Ned"
|
||||
STR_OK_BUTTON: "OK"
|
||||
STR_SLEEP_COVER_FILTER: "Omslagsfilter for hvileskjerm"
|
||||
STR_FILTER_CONTRAST: "Kontrast"
|
||||
STR_CUSTOMISE_STATUS_BAR: "Tilpass statuslinje"
|
||||
STR_CHAPTER_PAGE_COUNT: "Sideantall i kapittel"
|
||||
STR_BOOK_PROGRESS_PERCENTAGE: "Bokfremdrift i prosent"
|
||||
STR_PROGRESS_BAR: "Fremdriftslinje"
|
||||
STR_PROGRESS_BAR_THICKNESS: "Tykkelse på fremdriftslinje"
|
||||
STR_PROGRESS_BAR_THIN: "Tynn"
|
||||
STR_PROGRESS_BAR_MEDIUM: "Middels"
|
||||
STR_PROGRESS_BAR_THICK: "Tykk"
|
||||
STR_BOOK: "Bok"
|
||||
STR_CHAPTER: "Kapittel"
|
||||
STR_EXAMPLE_CHAPTER: "Kapittel 21"
|
||||
STR_EXAMPLE_BOOK: "Boktittel"
|
||||
STR_PREVIEW: "Forhåndsvisning"
|
||||
STR_TITLE: "Tittel"
|
||||
STR_BATTERY: "Batteri"
|
||||
STR_XTC_STATUS_BAR: "XTC-statuslinje"
|
||||
STR_BOTTOM: "Nederst"
|
||||
STR_TOP: "Øverst"
|
||||
STR_CLOCK: "Klokke"
|
||||
STR_CLOCK_UTC_OFFSET: "UTC-forskyvning for klokke"
|
||||
STR_CLOCK_FORMAT: "Klokkeformat"
|
||||
STR_CLOCK_FORMAT_24H: "24-timers"
|
||||
STR_CLOCK_FORMAT_12H: "12-timers"
|
||||
STR_CURRENT_TIME: "Nåværende tid:"
|
||||
STR_NEXT_FIELD: "Neste"
|
||||
STR_CLOCK_SYNC: "Synkroniser klokke"
|
||||
STR_CLOCK_SYNC_NOW: "Synkroniser klokke nå"
|
||||
STR_CLOCK_SYNCING: "Synkroniserer fra NTP..."
|
||||
STR_CLOCK_SYNC_OK: "Klokke synkronisert"
|
||||
STR_CLOCK_SYNC_FAIL: "Synkronisering mislyktes"
|
||||
STR_CLOCK_SYNC_NO_WIFI: "WiFi ikke tilkoblet"
|
||||
STR_CLOCK_SYNC_NO_WIFI_HINT: "Koble til WiFi først, og prøv igjen."
|
||||
STR_CLOCK_SYNCED: "Klokke synkronisert"
|
||||
STR_UI_THEME: "Grensesnittstema"
|
||||
STR_THEME_CLASSIC: "Klassisk"
|
||||
STR_THEME_LYRA: "Lyra"
|
||||
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
|
||||
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
|
||||
STR_SUNLIGHT_FADING_FIX: "Retting for soluttoning"
|
||||
STR_REMAP_FRONT_BUTTONS: "Tilordne frontknapper på nytt"
|
||||
STR_BOOKMARKS: "Bokmerker"
|
||||
STR_BOOKMARK_ADDED: "Bokmerke lagt til."
|
||||
STR_BOOKMARK_REMOVED: "Bokmerke fjernet."
|
||||
STR_OPDS_BROWSER: "OPDS-leser"
|
||||
STR_SEARCH: "Søk"
|
||||
STR_COVER_CUSTOM: "Omslag + Egendefinert"
|
||||
STR_QUICK_RESUME: "Hurtig gjenopptak"
|
||||
STR_MENU_RECENT_BOOKS: "Nylige bøker"
|
||||
STR_REMOVE_FROM_RECENTS: "Fjern fra Nylige bøker?"
|
||||
STR_NO_RECENT_BOOKS: "Ingen nylige bøker"
|
||||
STR_CALIBRE_DESC: "Bruk trådløs enhetsoverføring med Calibre"
|
||||
STR_FORGET_AND_REMOVE: "Glem nettverk og fjern lagret passord?"
|
||||
STR_FORGET_BUTTON: "Glem"
|
||||
STR_CALIBRE_STARTING: "Starter Calibre..."
|
||||
STR_CALIBRE_SETUP: "Oppsett"
|
||||
STR_CALIBRE_STATUS: "Status"
|
||||
STR_CLEAR_BUTTON: "Tøm"
|
||||
STR_DEFAULT_VALUE: "Standard"
|
||||
STR_REMAP_PROMPT: "Trykk en frontknapp for hver rolle"
|
||||
STR_UNASSIGNED: "Ikke tilordnet"
|
||||
STR_ALREADY_ASSIGNED: "Allerede tilordnet"
|
||||
STR_REMAP_RESET_HINT: "Sideknapp opp: Nullstill til standardoppsett"
|
||||
STR_REMAP_CANCEL_HINT: "Sideknapp ned: Avbryt omtilordning"
|
||||
STR_HW_BACK_LABEL: "Tilbake (1. knapp)"
|
||||
STR_HW_CONFIRM_LABEL: "Bekreft (2. knapp)"
|
||||
STR_HW_LEFT_LABEL: "Venstre (3. knapp)"
|
||||
STR_HW_RIGHT_LABEL: "Høyre (4. knapp)"
|
||||
STR_GO_TO_PERCENT: "Gå til %"
|
||||
STR_GO_HOME_BUTTON: "Hjem"
|
||||
STR_SYNC_PROGRESS: "Synkroniser fremdrift"
|
||||
STR_DELETE_CACHE: "Slett bokbuffer"
|
||||
STR_DELETE: "Slett"
|
||||
STR_CONFIRM_DELETE_BOOKMARK: "Slette dette bokmerket?"
|
||||
STR_DISPLAY_QR: "Vis side som QR"
|
||||
STR_CHAPTER_PREFIX: "Kapittel: "
|
||||
STR_PAGES_SEPARATOR: " sider | "
|
||||
STR_BOOK_PREFIX: "Bok: "
|
||||
STR_CALIBRE_URL_HINT: "For Calibre, legg til /opds i URL-en din"
|
||||
STR_PERCENT_STEP_HINT: "Venstre/Høyre: 1% Opp/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 ekstern fremdrift..."
|
||||
STR_UPLOAD_PROGRESS: "Laster opp fremdrift..."
|
||||
STR_NO_CREDENTIALS_MSG: "Ingen påloggingsinfo angitt"
|
||||
STR_KOREADER_SETUP_HINT: "Sett opp KOReader-konto i Innstillinger"
|
||||
STR_PROGRESS_FOUND: "Fremdrift funnet!"
|
||||
STR_REMOTE_LABEL: "Ekstern:"
|
||||
STR_LOCAL_LABEL: "Lokal:"
|
||||
STR_PAGE_OVERALL_FORMAT: "Side %d, %.2f%% totalt"
|
||||
STR_PAGE_TOTAL_OVERALL_FORMAT: "Side %d/%d, %.2f%% totalt"
|
||||
STR_DEVICE_FROM_FORMAT: " Fra: %s"
|
||||
STR_APPLY_REMOTE: "Bruk ekstern fremdrift"
|
||||
STR_UPLOAD_LOCAL: "Last opp lokal fremdrift"
|
||||
STR_NO_REMOTE_MSG: "Ingen ekstern fremdrift funnet"
|
||||
STR_UPLOAD_PROMPT: "Last opp nåværende posisjon?"
|
||||
STR_UPLOAD_SUCCESS: "Fremdrift lastet opp!"
|
||||
STR_SYNC_FAILED_MSG: "Synkronisering mislyktes"
|
||||
STR_SAVE_PROGRESS_FAILED: "Kunne ikke lagre fremdrift"
|
||||
STR_SECTION_PREFIX: "Del "
|
||||
STR_UPLOAD: "Last opp"
|
||||
STR_BOOK_S_STYLE: "Bokens stil"
|
||||
STR_EMBEDDED_STYLE: "Innebygd stil"
|
||||
STR_FOCUS_READING: "Fokuslesing"
|
||||
STR_OPDS_SERVER_URL: "OPDS-server-URL"
|
||||
STR_PWR_BTN_FOOTNOTE_BACK: "Hurtig retur fra fotnoter"
|
||||
STR_SET_SLEEP_COVER: "Velg omslag"
|
||||
STR_FOOTNOTES: "Fotnoter"
|
||||
STR_NO_FOOTNOTES: "Ingen fotnoter på denne siden"
|
||||
STR_LINK: "[lenke]"
|
||||
STR_SCREENSHOT_BUTTON: "Ta skjermbilde"
|
||||
STR_SLEEP_TIMER_VALUE_FORMAT: "%u min"
|
||||
STR_SLEEP_NEVER: "Aldri"
|
||||
STR_SLEEP_TIMER_STEP_HINT: "Venstre/Høyre: 1 min Opp/Ned: 5 min"
|
||||
STR_ADD_SERVER: "Legg til server"
|
||||
STR_SERVER_NAME: "Servernavn"
|
||||
STR_NO_SERVERS: "Ingen OPDS-servere konfigurert"
|
||||
STR_DELETE_SERVER: "Slett server"
|
||||
STR_OPDS_SERVERS: "OPDS-servere"
|
||||
STR_AUTO_TURN_ENABLED: "Automatisk bla aktivert: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Automatisk bla (sider per minutt)"
|
||||
STR_MANAGE_FONTS: "Administrer skrifttyper"
|
||||
STR_FONT_BROWSER: "Bla i skrifttyper"
|
||||
STR_LOADING_FONT_LIST: "Laster skrifttypeliste..."
|
||||
STR_NO_FONTS_AVAILABLE: "Ingen skrifttyper tilgjengelig"
|
||||
STR_FONT_INSTALLED: "Skrifttype installert!"
|
||||
STR_FONT_INSTALL_FAILED: "Installasjon av skrifttype mislyktes"
|
||||
STR_INSTALLED: "Installert"
|
||||
STR_DOWNLOAD_ALL: "Last ned alle"
|
||||
STR_UPDATE_ALL: "Oppdater alle"
|
||||
STR_UPDATE_AVAILABLE: "Oppdater"
|
||||
STR_CRASH_TITLE: "Systemkrasj"
|
||||
STR_CRASH_DESCRIPTION: "En detaljert rapport ble lagret i crash_report.txt. Legg ved denne filen i feilrapporten din."
|
||||
STR_CRASH_REASON: "Krasjårsak:"
|
||||
STR_CRASH_NO_REASON: "(Ingen årsak ble registrert)"
|
||||
STR_TILT_PAGE_TURN: "Vipp for å bla"
|
||||
STR_KB_HINT_MOVE_CURSOR: "Trykk VENSTRE eller HØYRE for å flytte markøren"
|
||||
STR_KB_HINT_RETURN_CURSOR: "Trykk VENSTRE for å gå tilbake til markørposisjonen"
|
||||
STR_KB_HINT_HIDE_PASSWORD: "Hold HØYRE og trykk [***] for å skjule passord"
|
||||
STR_KB_HINT_SHOW_PASSWORD: "Hold HØYRE og trykk [abc] for å vise passord"
|
||||
STR_KB_HINT_TOGGLE_HIDE_PASSWORD: "Trykk [***] for å skjule passord"
|
||||
STR_KB_HINT_TOGGLE_SHOW_PASSWORD: "Trykk [abc] for å vise passord"
|
||||
STR_KB_HINT_EDIT_ENTRY: "Hold OPP for å redigere oppføring"
|
||||
STR_KB_TIPS: "Tips:"
|
||||
STR_KB_HINT_RETURN_KEYBOARD: "Trykk NED for å gå tilbake til tastaturet"
|
||||
STR_KB_HINT_EXIT_URL_MODE: "Trykk ABC for å avslutte URL-modus"
|
||||
STR_KB_HINT_CLEAR_TEXT: "Hold DEL for å slette all tekst"
|
||||
STR_KB_HINT_SECONDARY_CHAR: "Hold VELG for sekundærtegn"
|
||||
STR_KB_HINT_UPPER_SECONDARY: "Hold VELG for STORE BOKSTAVER eller sekundærtegn"
|
||||
STR_KB_HINT_LOWER_SECONDARY: "Hold VELG for små bokstaver eller sekundærtegn"
|
||||
STR_KB_HINT_URL_SNIPPETS: "Trykk URL for tekstbiter"
|
||||
STR_SD_FIRMWARE_UPDATE: "Fastvareoppdatering fra SD-kort"
|
||||
STR_SELECT_FIRMWARE_FILE: "Velg fastvarefil (.bin)"
|
||||
STR_NO_BIN_FILES: "Ingen .bin-filer funnet"
|
||||
STR_VALIDATING_FIRMWARE: "Validerer fastvare..."
|
||||
STR_INVALID_FIRMWARE: "Ugyldig fastvarefil"
|
||||
STR_FIRMWARE_TOO_LARGE: "Fastvaren er for stor for partisjonen"
|
||||
STR_FIRMWARE_TOO_SMALL: "Fastvarefilen er for liten"
|
||||
STR_FIRMWARE_UPDATE_PROMPT: "Oppdatere fastvare?"
|
||||
STR_FIRMWARE_FILE_OPEN_FAILED: "Kan ikke åpne filen"
|
||||
STR_FIRMWARE_WRITE_FAILED: "Skriving av fastvare mislyktes"
|
||||
STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "Ikke slå av!"
|
||||
STR_RECOVERY_MODE: "Gjenopprettingsmodus"
|
||||
STR_RECOVERY_MODE_HINT: "Legg firmware.bin i roten av SD-kortet og velg den"
|
||||
@@ -68,10 +68,10 @@ STR_SHORT_PWR_BTN: "Kısa Güç Tuşu Tıklaması"
|
||||
STR_ORIENTATION: "Okuma Yönü"
|
||||
STR_SIDE_BTN_LAYOUT: "Yan Tuş Dizilimi (okuyucu)"
|
||||
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Ön düğmeleri yönlendir"
|
||||
STR_LONG_PRESS_BEHAVIOR: "Long-press button behavior"
|
||||
STR_LONG_PRESS_BEHAVIOR_OFF: "OFF"
|
||||
STR_LONG_PRESS_BEHAVIOR_SKIP: "Chapter skip"
|
||||
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Orientation change"
|
||||
STR_LONG_PRESS_BEHAVIOR: "Uzun basma tuş davranışı"
|
||||
STR_LONG_PRESS_BEHAVIOR_OFF: "KAPALI"
|
||||
STR_LONG_PRESS_BEHAVIOR_SKIP: "Bölüm atlama"
|
||||
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Yön değiştirme"
|
||||
STR_FONT_PREVIEW_TEXT: "Pijamalı hasta yağız şoföre çabucak güvendi"
|
||||
STR_FONT_FAMILY: "Okuyucu Yazı Tipi Ailesi"
|
||||
STR_FONT_SIZE: "Arayüz Yazı Boyutu"
|
||||
@@ -306,3 +306,89 @@ STR_TITLE: "Başlık"
|
||||
STR_TILT_PAGE_TURN: "Eğerek sayfa çevirme"
|
||||
STR_ADD_HIDDEN_NETWORK: "Gizli ağ ekle..."
|
||||
STR_ENTER_WIFI_SSID: "Ağ adını girin (SSID)"
|
||||
STR_ADD_SERVER: "Sunucu Ekle"
|
||||
STR_BOOKMARKS: "Yer İmleri"
|
||||
STR_BOOKMARK_ADDED: "Yer imi eklendi."
|
||||
STR_BOOKMARK_OPTION: "Yer İmi"
|
||||
STR_BOTTOM: "Alt"
|
||||
STR_CLOCK: "Saat"
|
||||
STR_CLOCK_FORMAT: "Saat Biçimi"
|
||||
STR_CLOCK_FORMAT_12H: "12 saat"
|
||||
STR_CLOCK_FORMAT_24H: "24 saat"
|
||||
STR_CLOCK_SYNC: "Saati Eşitle"
|
||||
STR_CLOCK_SYNCED: "Saat Eşitlendi"
|
||||
STR_CLOCK_SYNCING: "NTP'den eşitleniyor..."
|
||||
STR_CLOCK_SYNC_FAIL: "Eşitleme başarısız"
|
||||
STR_CLOCK_SYNC_NOW: "Saati şimdi eşitle"
|
||||
STR_CLOCK_SYNC_NO_WIFI: "Wi-Fi bağlı değil"
|
||||
STR_CLOCK_SYNC_NO_WIFI_HINT: "Önce Wi-Fi'ye bağlanın, sonra tekrar deneyin."
|
||||
STR_CLOCK_SYNC_OK: "Saat eşitlendi"
|
||||
STR_CLOCK_UTC_OFFSET: "Saat UTC Farkı"
|
||||
STR_CONFIRM_DELETE_BOOKMARK: "Bu yer imi silinsin mi?"
|
||||
STR_CONNECTING_SAVED_WIFI: "Kayıtlı Wi-Fi'ye bağlanılıyor..."
|
||||
STR_CRASH_DESCRIPTION: "Ayrıntılı rapor crash_report.txt dosyasına kaydedildi. Lütfen hata bildiriminize bu dosyayı ekleyin."
|
||||
STR_CRASH_NO_REASON: "(Neden kaydedilmedi)"
|
||||
STR_CRASH_REASON: "Çökme nedeni:"
|
||||
STR_CRASH_TITLE: "Sistem Çökmesi"
|
||||
STR_CURRENT_TIME: "Şu anki saat:"
|
||||
STR_DELETE_SERVER: "Sunucuyu Sil"
|
||||
STR_DISABLED: "Devre dışı"
|
||||
STR_DOWNLOAD_ALL: "Tümünü İndir"
|
||||
STR_EOB_CONTINUE_WITH: "Şununla devam et"
|
||||
STR_EOB_HOME: "Ana Ekran"
|
||||
STR_FINDING_SAVED_WIFI: "Kayıtlı Wi-Fi aranıyor..."
|
||||
STR_FIRMWARE_FILE_OPEN_FAILED: "Dosya açılamıyor"
|
||||
STR_FIRMWARE_TOO_LARGE: "Firmware, bölümlemeye sığmıyor"
|
||||
STR_FIRMWARE_TOO_SMALL: "Firmware dosyası çok küçük"
|
||||
STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "Cihazı kapatmayın!"
|
||||
STR_FIRMWARE_UPDATE_PROMPT: "Firmware güncellensin mi?"
|
||||
STR_FIRMWARE_WRITE_FAILED: "Firmware yazılamadı"
|
||||
STR_FONT_BROWSER: "Yazı Tipi Tarayıcısı"
|
||||
STR_FONT_INSTALLED: "Yazı tipi yüklendi!"
|
||||
STR_FONT_INSTALL_FAILED: "Yazı tipi yüklenemedi"
|
||||
STR_FORCE_REFRESH: "Ekranı Yenile"
|
||||
STR_INDEX_FAILED: "Endeksleme başarısız - geçersiz kitap"
|
||||
STR_INSTALLED: "Yüklü"
|
||||
STR_INVALID_FIRMWARE: "Geçersiz firmware dosyası"
|
||||
STR_KB_HINT_CLEAR_TEXT: "Tüm metni silmek için DEL tuşunu basılı tutun"
|
||||
STR_KB_HINT_EDIT_ENTRY: "Girdiyi düzenlemek için UP tuşunu basılı tutun"
|
||||
STR_KB_HINT_EXIT_URL_MODE: "URL modundan çıkmak için ABC'ye basın"
|
||||
STR_KB_HINT_HIDE_PASSWORD: "Şifreyi gizlemek için RIGHT'ı basılı tutup [***] tuşuna basın"
|
||||
STR_KB_HINT_LOWER_SECONDARY: "Küçük harf veya ikincil karakter için SELECT'i basılı tutun"
|
||||
STR_KB_HINT_MOVE_CURSOR: "İmleci taşımak için LEFT veya RIGHT'a basın"
|
||||
STR_KB_HINT_RETURN_CURSOR: "İmleç konumuna dönmek için LEFT'e basın"
|
||||
STR_KB_HINT_RETURN_KEYBOARD: "Klavyeye dönmek için DOWN'a basın"
|
||||
STR_KB_HINT_SECONDARY_CHAR: "İkincil karakter için SELECT'i basılı tutun"
|
||||
STR_KB_HINT_SHOW_PASSWORD: "Şifreyi göstermek için RIGHT'ı basılı tutup [abc] tuşuna basın"
|
||||
STR_KB_HINT_TOGGLE_HIDE_PASSWORD: "Şifreyi gizlemek için [***] tuşuna basın"
|
||||
STR_KB_HINT_TOGGLE_SHOW_PASSWORD: "Şifreyi göstermek için [abc] tuşuna basın"
|
||||
STR_KB_HINT_UPPER_SECONDARY: "BÜYÜK harf veya ikincil karakter için SELECT'i basılı tutun"
|
||||
STR_KB_HINT_URL_SNIPPETS: "Hazır kalıplar için URL'ye basın"
|
||||
STR_KB_TIPS: "İpuçları:"
|
||||
STR_KOSYNC: "KOSync"
|
||||
STR_LOADING_FONT_LIST: "Yazı tipi listesi yükleniyor..."
|
||||
STR_LONG_PRESS_MENU: "Uzun Basma Menüsü"
|
||||
STR_MANAGE_FONTS: "Yazı Tiplerini Yönet"
|
||||
STR_NEXT_FIELD: "Sonraki"
|
||||
STR_NEXT_PAGE: "Sonraki Sayfa »"
|
||||
STR_NO_BIN_FILES: ".bin dosyası bulunamadı"
|
||||
STR_NO_FONTS_AVAILABLE: "Kullanılabilir yazı tipi yok"
|
||||
STR_NO_SERVERS: "Yapılandırılmış OPDS sunucusu yok"
|
||||
STR_OPDS_SERVERS: "OPDS Sunucuları"
|
||||
STR_PREV_PAGE: "« Önceki Sayfa"
|
||||
STR_PWR_BTN_FOOTNOTE_BACK: "Dipnottan hızlı dönüş"
|
||||
STR_RECOVERY_MODE: "Kurtarma Modu"
|
||||
STR_RECOVERY_MODE_HINT: "firmware.bin dosyasını SD kartın köküne koyup seçin"
|
||||
STR_RESTARTING_HINT: "Yeniden başlatılıyor... Cihaz yeniden başlamazsa güç düğmesini birkaç saniye basılı tutun."
|
||||
STR_SD_FIRMWARE_UPDATE: "SD Karttan Firmware Güncelleme"
|
||||
STR_SEARCH: "Ara"
|
||||
STR_SELECT_FIRMWARE_FILE: "Firmware dosyası seçin (.bin)"
|
||||
STR_SERVER_NAME: "Sunucu Adı"
|
||||
STR_SET_SLEEP_COVER: "Kapak Ayarla"
|
||||
STR_SHOW_NETWORKS: "Göster"
|
||||
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
|
||||
STR_TOP: "Üst"
|
||||
STR_UPDATE_ALL: "Tümünü Güncelle"
|
||||
STR_UPDATE_AVAILABLE: "Güncelle"
|
||||
STR_VALIDATING_FIRMWARE: "Firmware doğrulanıyor..."
|
||||
STR_XTC_STATUS_BAR: "XTC Durum Çubuğu"
|
||||
|
||||
@@ -58,6 +58,14 @@ class CrossPointSettings {
|
||||
HIDE_PROGRESS = 2,
|
||||
STATUS_BAR_PROGRESS_BAR_COUNT
|
||||
};
|
||||
// Values 0/1 keep the meaning of the old show/hide toggle (persisted under the
|
||||
// legacy "statusBarChapterPageCount" key), so existing settings files load as-is.
|
||||
enum STATUS_BAR_PAGE_COUNT {
|
||||
HIDE_PAGE_COUNT = 0,
|
||||
CHAPTER_PAGE_COUNT = 1,
|
||||
BOOK_PAGE_COUNT = 2,
|
||||
STATUS_BAR_PAGE_COUNT_COUNT
|
||||
};
|
||||
enum STATUS_BAR_PROGRESS_BAR_THICKNESS {
|
||||
PROGRESS_BAR_THIN = 0,
|
||||
PROGRESS_BAR_NORMAL = 1,
|
||||
@@ -189,7 +197,8 @@ class CrossPointSettings {
|
||||
uint8_t sleepScreenCoverFilter = NO_FILTER;
|
||||
// Status bar settings (statusBar retained for migration only)
|
||||
uint8_t statusBar = FULL;
|
||||
uint8_t statusBarChapterPageCount = 1;
|
||||
// STATUS_BAR_PAGE_COUNT; persisted under the legacy "statusBarChapterPageCount" key.
|
||||
uint8_t statusBarPageCount = CHAPTER_PAGE_COUNT;
|
||||
uint8_t statusBarBookProgressPercentage = 1;
|
||||
uint8_t statusBarProgressBar = HIDE_PROGRESS;
|
||||
uint8_t statusBarProgressBarThickness = PROGRESS_BAR_NORMAL;
|
||||
|
||||
@@ -21,35 +21,35 @@
|
||||
void applyLegacyStatusBarSettings(CrossPointSettings& settings) {
|
||||
switch (static_cast<CrossPointSettings::STATUS_BAR_MODE>(settings.statusBar)) {
|
||||
case CrossPointSettings::NONE:
|
||||
settings.statusBarChapterPageCount = 0;
|
||||
settings.statusBarPageCount = CrossPointSettings::HIDE_PAGE_COUNT;
|
||||
settings.statusBarBookProgressPercentage = 0;
|
||||
settings.statusBarProgressBar = CrossPointSettings::HIDE_PROGRESS;
|
||||
settings.statusBarTitle = CrossPointSettings::HIDE_TITLE;
|
||||
settings.statusBarBattery = 0;
|
||||
break;
|
||||
case CrossPointSettings::NO_PROGRESS:
|
||||
settings.statusBarChapterPageCount = 0;
|
||||
settings.statusBarPageCount = CrossPointSettings::HIDE_PAGE_COUNT;
|
||||
settings.statusBarBookProgressPercentage = 0;
|
||||
settings.statusBarProgressBar = CrossPointSettings::HIDE_PROGRESS;
|
||||
settings.statusBarTitle = CrossPointSettings::CHAPTER_TITLE;
|
||||
settings.statusBarBattery = 1;
|
||||
break;
|
||||
case CrossPointSettings::BOOK_PROGRESS_BAR:
|
||||
settings.statusBarChapterPageCount = 1;
|
||||
settings.statusBarPageCount = CrossPointSettings::CHAPTER_PAGE_COUNT;
|
||||
settings.statusBarBookProgressPercentage = 0;
|
||||
settings.statusBarProgressBar = CrossPointSettings::BOOK_PROGRESS;
|
||||
settings.statusBarTitle = CrossPointSettings::CHAPTER_TITLE;
|
||||
settings.statusBarBattery = 1;
|
||||
break;
|
||||
case CrossPointSettings::ONLY_BOOK_PROGRESS_BAR:
|
||||
settings.statusBarChapterPageCount = 1;
|
||||
settings.statusBarPageCount = CrossPointSettings::CHAPTER_PAGE_COUNT;
|
||||
settings.statusBarBookProgressPercentage = 0;
|
||||
settings.statusBarProgressBar = CrossPointSettings::BOOK_PROGRESS;
|
||||
settings.statusBarTitle = CrossPointSettings::HIDE_TITLE;
|
||||
settings.statusBarBattery = 0;
|
||||
break;
|
||||
case CrossPointSettings::CHAPTER_PROGRESS_BAR:
|
||||
settings.statusBarChapterPageCount = 0;
|
||||
settings.statusBarPageCount = CrossPointSettings::HIDE_PAGE_COUNT;
|
||||
settings.statusBarBookProgressPercentage = 1;
|
||||
settings.statusBarProgressBar = CrossPointSettings::CHAPTER_PROGRESS;
|
||||
settings.statusBarTitle = CrossPointSettings::CHAPTER_TITLE;
|
||||
@@ -57,7 +57,7 @@ void applyLegacyStatusBarSettings(CrossPointSettings& settings) {
|
||||
break;
|
||||
case CrossPointSettings::FULL:
|
||||
default:
|
||||
settings.statusBarChapterPageCount = 1;
|
||||
settings.statusBarPageCount = CrossPointSettings::CHAPTER_PAGE_COUNT;
|
||||
settings.statusBarBookProgressPercentage = 1;
|
||||
settings.statusBarProgressBar = CrossPointSettings::HIDE_PROGRESS;
|
||||
settings.statusBarTitle = CrossPointSettings::CHAPTER_TITLE;
|
||||
|
||||
+4
-2
@@ -233,8 +233,10 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
|
||||
},
|
||||
"koSendMetadata", StrId::STR_KOREADER_SYNC),
|
||||
// --- Status Bar Settings (web-only, uses StatusBarSettingsActivity) ---
|
||||
SettingInfo::Toggle(StrId::STR_CHAPTER_PAGE_COUNT, &CrossPointSettings::statusBarChapterPageCount,
|
||||
"statusBarChapterPageCount", StrId::STR_CUSTOMISE_STATUS_BAR),
|
||||
// Key kept from the old show/hide toggle so existing settings files load unchanged.
|
||||
SettingInfo::Enum(StrId::STR_CHAPTER_PAGE_COUNT, &CrossPointSettings::statusBarPageCount,
|
||||
{StrId::STR_HIDE, StrId::STR_CHAPTER, StrId::STR_BOOK}, "statusBarChapterPageCount",
|
||||
StrId::STR_CUSTOMISE_STATUS_BAR),
|
||||
SettingInfo::Toggle(StrId::STR_BOOK_PROGRESS_PERCENTAGE, &CrossPointSettings::statusBarBookProgressPercentage,
|
||||
"statusBarBookProgressPercentage", StrId::STR_CUSTOMISE_STATUS_BAR),
|
||||
SettingInfo::Enum(StrId::STR_PROGRESS_BAR, &CrossPointSettings::statusBarProgressBar,
|
||||
|
||||
@@ -44,6 +44,22 @@ constexpr int PAGE_TURN_RATES[] = {1, 1, 3, 6, 12};
|
||||
constexpr size_t initialBookmarkCacheCapacity = 16;
|
||||
constexpr float bookmarkProgressEpsilon = 0.0001f;
|
||||
|
||||
// The render parameters the reader paginates with, as passed to loadSectionFile /
|
||||
// createSectionFile / startBuild below. Used to validate other sections' cached
|
||||
// page counts and to detect when a settings change invalidates the harvest.
|
||||
Section::RenderParams readerRenderParams(const uint16_t viewportWidth, const uint16_t viewportHeight) {
|
||||
return {SETTINGS.getReaderFontId(),
|
||||
SETTINGS.getReaderLineCompression(),
|
||||
static_cast<bool>(SETTINGS.extraParagraphSpacing),
|
||||
SETTINGS.paragraphAlignment,
|
||||
viewportWidth,
|
||||
viewportHeight,
|
||||
static_cast<bool>(SETTINGS.hyphenationEnabled),
|
||||
static_cast<bool>(SETTINGS.embeddedStyle),
|
||||
SETTINGS.imageRendering,
|
||||
static_cast<bool>(SETTINGS.focusReadingEnabled)};
|
||||
}
|
||||
|
||||
int clampPercent(int percent) {
|
||||
if (percent < 0) {
|
||||
return 0;
|
||||
@@ -319,6 +335,25 @@ void EpubReaderActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Whole-book page counter: harvest one other section's cached page count per tick
|
||||
// (a header-only peek), so the total converges to exact without visiting every
|
||||
// chapter. Idle-priority — skipped whenever a render is pending or a build is
|
||||
// running. No re-render is requested; the counter refreshes on the next page turn.
|
||||
if (bookPages && bookPagesSweepIndex < epub->getSpineItemsCount() && !RenderLock::peek() &&
|
||||
!(section && section->isBuilding())) {
|
||||
RenderLock lock;
|
||||
// Re-check under the lock: render() may have just reset/reallocated the table.
|
||||
if (bookPages && bookPagesSweepIndex < epub->getSpineItemsCount()) {
|
||||
const int index = bookPagesSweepIndex++;
|
||||
if (bookPages[index].pages < 0) {
|
||||
const Section peekSection(epub, index, renderer);
|
||||
if (const auto count = peekSection.getCachedPageCount(&bookPagesParams)) {
|
||||
bookPages[index].pages = *count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End-of-Book screen reached (currentSpineIndex == spine count) means the book is
|
||||
// finished. Two independent finished-book features key off this same condition.
|
||||
const bool atEndOfBook = currentSpineIndex > 0 && currentSpineIndex >= epub->getSpineItemsCount();
|
||||
@@ -980,6 +1015,8 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
buildViewportWidth = viewportWidth;
|
||||
buildViewportHeight = viewportHeight;
|
||||
|
||||
ensureBookPages(viewportWidth, viewportHeight);
|
||||
|
||||
if (!section) {
|
||||
const auto filepath = epub->getSpineItem(currentSpineIndex).href;
|
||||
LOG_DBG("ERS", "Loading file: %s, index: %d", filepath.c_str(), currentSpineIndex);
|
||||
@@ -1222,6 +1259,8 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
// a plain resume / unchanged pagination). If still building, this defers to loop() on completion.
|
||||
applyDeferredReposition();
|
||||
|
||||
recordCurrentSectionPages();
|
||||
|
||||
renderer.clearScreen();
|
||||
|
||||
if (section->pageCount == 0) {
|
||||
@@ -1331,6 +1370,59 @@ bool EpubReaderActivity::applyDeferredReposition() {
|
||||
return changed;
|
||||
}
|
||||
|
||||
void EpubReaderActivity::ensureBookPages(const uint16_t viewportWidth, const uint16_t viewportHeight) {
|
||||
if (SETTINGS.statusBarPageCount != CrossPointSettings::STATUS_BAR_PAGE_COUNT::BOOK_PAGE_COUNT) {
|
||||
bookPages.reset();
|
||||
return;
|
||||
}
|
||||
const Section::RenderParams params = readerRenderParams(viewportWidth, viewportHeight);
|
||||
if (bookPages && params == bookPagesParams) {
|
||||
return;
|
||||
}
|
||||
// First render, or a render-params change (font/orientation/...): the harvested
|
||||
// counts are for the old pagination, so start over. The section caches themselves
|
||||
// are the persistence; there is nothing else to invalidate.
|
||||
bookPagesParams = params;
|
||||
bookPagesSweepIndex = 0;
|
||||
bookPages.reset();
|
||||
const int sectionCount = epub->getSpineItemsCount();
|
||||
if (sectionCount <= 0) {
|
||||
return;
|
||||
}
|
||||
// 8 bytes per spine item, held for the reading session; freed with the activity
|
||||
// or when the feature is switched off. On OOM stays null (chapter-local counts).
|
||||
bookPages = makeUniqueNoThrow<BookPageEntry[]>(sectionCount);
|
||||
if (!bookPages) {
|
||||
LOG_ERR("ERS", "OOM: book page table (%d sections)", sectionCount);
|
||||
return;
|
||||
}
|
||||
size_t prev = 0;
|
||||
for (int i = 0; i < sectionCount; ++i) {
|
||||
const size_t cum = epub->getCumulativeSpineItemSize(i);
|
||||
bookPages[i].bytes = static_cast<uint32_t>(cum >= prev ? cum - prev : 0);
|
||||
prev = cum;
|
||||
}
|
||||
}
|
||||
|
||||
void EpubReaderActivity::recordCurrentSectionPages() {
|
||||
// Only a finalized section's pageCount is the chapter total; a building or
|
||||
// partial section's is just its current watermark.
|
||||
if (!bookPages || !section || section->isBuilding() || section->isPartial()) {
|
||||
return;
|
||||
}
|
||||
if (currentSpineIndex >= 0 && currentSpineIndex < epub->getSpineItemsCount()) {
|
||||
bookPages[currentSpineIndex].pages = section->pageCount;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<BookPagePosition> EpubReaderActivity::bookPagePosition() const {
|
||||
if (!bookPages || !section) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return computeBookPagePosition(bookPages.get(), epub->getSpineItemsCount(), currentSpineIndex, section->currentPage,
|
||||
section->estimatedTotalPages());
|
||||
}
|
||||
|
||||
bool EpubReaderActivity::saveProgress(int spineIndex, int currentPage, int pageCount) {
|
||||
return EpubReaderUtils::saveProgress(*epub, spineIndex, currentPage, pageCount);
|
||||
}
|
||||
@@ -1512,10 +1604,21 @@ void EpubReaderActivity::renderStatusBar() const {
|
||||
// Calculate progress in book. Use the estimated total while a giant spine is still building so
|
||||
// "page X of Y" and the progress bar don't read off the small build watermark.
|
||||
const int currentPage = section->currentPage + 1;
|
||||
const float pageCount = section->estimatedTotalPages();
|
||||
const int pageCount = section->estimatedTotalPages();
|
||||
const float sectionChapterProg = (pageCount > 0) ? (static_cast<float>(currentPage) / pageCount) : 0;
|
||||
const float bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg) * 100;
|
||||
|
||||
// Page counter values: whole-book numbers when Page Count is set to Book (and the
|
||||
// table is alive), otherwise chapter-local. The progress bar stays on bookProgress.
|
||||
int counterPage = currentPage;
|
||||
int counterTotal = pageCount;
|
||||
bool counterIsEstimate = section->isBuilding();
|
||||
if (const auto book = bookPagePosition()) {
|
||||
counterPage = book->currentPage;
|
||||
counterTotal = book->totalPages;
|
||||
counterIsEstimate = book->isEstimate;
|
||||
}
|
||||
|
||||
std::string title;
|
||||
|
||||
int textYOffset = 0;
|
||||
@@ -1543,8 +1646,8 @@ void EpubReaderActivity::renderStatusBar() const {
|
||||
title = epub->getTitle();
|
||||
}
|
||||
|
||||
GUI.drawStatusBar(renderer, bookProgress, currentPage, pageCount, title, 0, textYOffset, true, currentPageBookmarked,
|
||||
section->isBuilding());
|
||||
GUI.drawStatusBar(renderer, bookProgress, counterPage, counterTotal, title, 0, textYOffset, true,
|
||||
currentPageBookmarked, counterIsEstimate);
|
||||
}
|
||||
|
||||
void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool savePosition) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <Epub.h>
|
||||
#include <Epub/BookPages.h>
|
||||
#include <Epub/FootnoteEntry.h>
|
||||
#include <Epub/Section.h>
|
||||
|
||||
@@ -23,6 +24,17 @@ class EpubReaderActivity final : public Activity {
|
||||
int pagesUntilFullRefresh = 0;
|
||||
int cachedSpineIndex = 0;
|
||||
int cachedChapterTotalPageCount = 0;
|
||||
// Whole-book page accounting ("page X of Y" across the whole book), active only
|
||||
// when the status bar Page Count is set to Book. Exact counts are harvested from
|
||||
// finalized section caches — nothing extra is persisted (see BookPages.h). Null
|
||||
// while inactive or on OOM; every book-page path degrades to chapter-local counts.
|
||||
// Shared between the render task and loop(): only touch under the RenderLock.
|
||||
std::unique_ptr<BookPageEntry[]> bookPages;
|
||||
// Render params the harvested counts are valid for; a change resets the harvest.
|
||||
Section::RenderParams bookPagesParams;
|
||||
// Next spine index for loop()'s background sweep that peeks other sections'
|
||||
// cached counts (one per tick); >= spine count once the sweep is done.
|
||||
int bookPagesSweepIndex = 0;
|
||||
unsigned long lastPageTurnTime = 0UL;
|
||||
unsigned long pageTurnDuration = 0UL;
|
||||
// Signals that the next render should reposition within the newly loaded section
|
||||
@@ -113,6 +125,14 @@ class EpubReaderActivity final : public Activity {
|
||||
// (used after a settings change re-paginates a chapter). Returns true if currentPage moved.
|
||||
// No-op while the section is still building or when the pagination is unchanged (plain resume).
|
||||
bool applyDeferredReposition();
|
||||
// (Re)allocate and reset bookPages when the feature turns on or the render params
|
||||
// change. Called from render() (under the RenderLock) where the viewport is known.
|
||||
void ensureBookPages(uint16_t viewportWidth, uint16_t viewportHeight);
|
||||
// Store the current section's exact page count once its pagination is final
|
||||
// (no-op while building or partial). Caller must hold the RenderLock.
|
||||
void recordCurrentSectionPages();
|
||||
// Book-global position for the current page, or nullopt while inactive.
|
||||
std::optional<BookPagePosition> bookPagePosition() const;
|
||||
bool saveProgress(int spineIndex, int currentPage, int pageCount);
|
||||
// Jump to a percentage of the book (0-100), mapping it to spine and page.
|
||||
void jumpToPercent(int percent);
|
||||
|
||||
@@ -64,6 +64,10 @@ std::string formatUtcOffset(uint8_t biasedQ) {
|
||||
snprintf(buf, sizeof(buf), "UTC%c%d:%02d", neg ? '-' : '+', hours, mins);
|
||||
return buf;
|
||||
}
|
||||
// Order follows STATUS_BAR_PAGE_COUNT (hide=0, chapter=1, book=2).
|
||||
constexpr int PAGE_COUNT_ITEMS = 3;
|
||||
const StrId pageCountNames[PAGE_COUNT_ITEMS] = {StrId::STR_HIDE, StrId::STR_CHAPTER, StrId::STR_BOOK};
|
||||
|
||||
constexpr int PROGRESS_BAR_ITEMS = 3;
|
||||
const StrId progressBarNames[PROGRESS_BAR_ITEMS] = {StrId::STR_BOOK, StrId::STR_CHAPTER, StrId::STR_HIDE};
|
||||
|
||||
@@ -90,7 +94,11 @@ void StatusBarSettingsActivity::onEnter() {
|
||||
selectedIndex = 0;
|
||||
visibleItemCount = halClock.isAvailable() ? FULL_MENU_ITEMS : BASE_MENU_ITEMS;
|
||||
|
||||
// Clamp statusBarProgressBar and statusBarTitle in case of corrupt/migrated data
|
||||
// Clamp enum-valued settings in case of corrupt/migrated data
|
||||
if (SETTINGS.statusBarPageCount >= PAGE_COUNT_ITEMS) {
|
||||
SETTINGS.statusBarPageCount = CrossPointSettings::STATUS_BAR_PAGE_COUNT::CHAPTER_PAGE_COUNT;
|
||||
}
|
||||
|
||||
if (SETTINGS.statusBarProgressBar >= PROGRESS_BAR_ITEMS) {
|
||||
SETTINGS.statusBarProgressBar = CrossPointSettings::STATUS_BAR_PROGRESS_BAR::HIDE_PROGRESS;
|
||||
}
|
||||
@@ -163,8 +171,12 @@ void StatusBarSettingsActivity::loop() {
|
||||
void StatusBarSettingsActivity::handleSelection() {
|
||||
switch (selectedIndex) {
|
||||
case ITEM_CHAPTER_PAGE_COUNT:
|
||||
SETTINGS.statusBarChapterPageCount = (SETTINGS.statusBarChapterPageCount + 1) % 2;
|
||||
break;
|
||||
optionPopup.show(StrId::STR_CHAPTER_PAGE_COUNT, pageCountNames, PAGE_COUNT_ITEMS, SETTINGS.statusBarPageCount,
|
||||
[this](int idx) {
|
||||
SETTINGS.statusBarPageCount = idx;
|
||||
SETTINGS.saveToFile();
|
||||
});
|
||||
return;
|
||||
case ITEM_BOOK_PROGRESS_PERCENTAGE:
|
||||
SETTINGS.statusBarBookProgressPercentage = (SETTINGS.statusBarBookProgressPercentage + 1) % 2;
|
||||
break;
|
||||
@@ -236,7 +248,7 @@ void StatusBarSettingsActivity::render(RenderLock&&) {
|
||||
[](int index) -> std::string {
|
||||
switch (index) {
|
||||
case ITEM_CHAPTER_PAGE_COUNT:
|
||||
return SETTINGS.statusBarChapterPageCount ? tr(STR_SHOW) : tr(STR_HIDE);
|
||||
return I18N.get(pageCountNames[SETTINGS.statusBarPageCount]);
|
||||
case ITEM_BOOK_PROGRESS_PERCENTAGE:
|
||||
return SETTINGS.statusBarBookProgressPercentage ? tr(STR_SHOW) : tr(STR_HIDE);
|
||||
case ITEM_PROGRESS_BAR:
|
||||
|
||||
@@ -132,7 +132,8 @@ int UITheme::getStatusBarHeight() {
|
||||
|
||||
// Add status bar margin
|
||||
const bool showStatusBar =
|
||||
SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage ||
|
||||
SETTINGS.statusBarPageCount != CrossPointSettings::STATUS_BAR_PAGE_COUNT::HIDE_PAGE_COUNT ||
|
||||
SETTINGS.statusBarBookProgressPercentage ||
|
||||
SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE || SETTINGS.statusBarBattery ||
|
||||
SETTINGS.statusBarClock != CrossPointSettings::STATUS_BAR_CLOCK_MODE::STATUS_BAR_CLOCK_HIDE;
|
||||
const bool showProgressBar =
|
||||
|
||||
@@ -27,7 +27,8 @@ constexpr int bookmarkStatusIconGap = 4;
|
||||
constexpr int bookmarkStatusIconTopCrop = 2;
|
||||
|
||||
bool statusBarTextLaneVisible() {
|
||||
return SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage ||
|
||||
return SETTINGS.statusBarPageCount != CrossPointSettings::STATUS_BAR_PAGE_COUNT::HIDE_PAGE_COUNT ||
|
||||
SETTINGS.statusBarBookProgressPercentage ||
|
||||
SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE || SETTINGS.statusBarBattery ||
|
||||
(SETTINGS.statusBarClock && halClock.isAvailable());
|
||||
}
|
||||
@@ -765,20 +766,23 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c
|
||||
int leftClusterWidth = 0;
|
||||
int rightClusterWidth = 0;
|
||||
|
||||
if (SETTINGS.statusBarBookProgressPercentage || SETTINGS.statusBarChapterPageCount) {
|
||||
const bool showPageCount = SETTINGS.statusBarPageCount != CrossPointSettings::STATUS_BAR_PAGE_COUNT::HIDE_PAGE_COUNT;
|
||||
if (SETTINGS.statusBarBookProgressPercentage || showPageCount) {
|
||||
// Right aligned text for progress counter
|
||||
char progressStr[32];
|
||||
|
||||
// Prefix the page count with "~" while a still-building spine only yields an estimated total.
|
||||
// Mark the total with "~" while it is only an estimate (a still-building spine's
|
||||
// watermark, or a whole-book total with not-yet-paginated chapters). The current
|
||||
// page is always exact, so the marker sits on the total.
|
||||
const char* estimatePrefix = pageCountEstimated ? "~" : "";
|
||||
|
||||
if (SETTINGS.statusBarBookProgressPercentage && SETTINGS.statusBarChapterPageCount) {
|
||||
snprintf(progressStr, sizeof(progressStr), "%s%d/%d %.0f%%", estimatePrefix, currentPage, pageCount,
|
||||
if (SETTINGS.statusBarBookProgressPercentage && showPageCount) {
|
||||
snprintf(progressStr, sizeof(progressStr), "%d/%s%d %.0f%%", currentPage, estimatePrefix, pageCount,
|
||||
bookProgress);
|
||||
} else if (SETTINGS.statusBarBookProgressPercentage) {
|
||||
snprintf(progressStr, sizeof(progressStr), "%.0f%%", bookProgress);
|
||||
} else {
|
||||
snprintf(progressStr, sizeof(progressStr), "%s%d/%d", estimatePrefix, currentPage, pageCount);
|
||||
snprintf(progressStr, sizeof(progressStr), "%d/%s%d", currentPage, estimatePrefix, pageCount);
|
||||
}
|
||||
|
||||
int progressTextWidth = renderer.getTextWidth(SMALL_FONT_ID, progressStr);
|
||||
|
||||
Reference in New Issue
Block a user