Compare commits

..

15 Commits

Author SHA1 Message Date
github-actions[bot] f53546cc01 chore: Release v2.1.1 2026-07-28 04:28:46 +00:00
Rajarshee Chatterjee 594b078fad fix: Harden Database Migration Recovery 2026-07-28 07:32:55 +05:30
Rajarshee Chatterjee ac1b2f5ce4 feat: Add Durable Completion Notifications 2026-07-27 22:27:04 +05:30
Rajarshee Chatterjee 648d21062f feat: Add Parallel Task Queues With Per-Plugin Download Limits 2026-07-27 22:15:02 +05:30
Rajarshee Chatterjee 89d715ce6b docs: Replace Beta Badge With Build Preview Status 2026-07-27 21:54:45 +05:30
Rajarshee Chatterjee bd527a02ef feat: Improve Backup Selection, Notifications, And Plugin Restore 2026-07-27 20:03:03 +05:30
Rajarshee Chatterjee 11117c1b54 feat: Add GitHub-Style Embed For Nightly Notifications 2026-07-27 17:12:50 +05:30
Patrick Loser ba25d5b8fe feat: Add Rozenite DevTools Integration (#1912)
* add rozenite

* add opsqlite adapter

* fix lint

* Update scripts/generate-env-file.cjs

Co-authored-by: Rajarshee Chatterjee <rajarshee.adm@gmail.com>

---------

Co-authored-by: Rajarshee Chatterjee <rajarshee.adm@gmail.com>
2026-07-27 17:07:41 +05:30
Rajarshee Chatterjee 31cb4b994a fix: Align Bottom Navigation With Material 3 Specs 2026-07-27 15:11:38 +05:30
Rajarshee Chatterjee 72ef618af1 fix: Preserve Existing Data During Novel Counter Migration 2026-07-27 15:06:36 +05:30
Rajarshee Chatterjee 936fd223bb feat: Add Discord Notifications For Nightly Builds 2026-07-27 10:49:30 +05:30
Rajarshee Chatterjee 6d26438d2d test: Update Expected Database Migrations 2026-07-27 08:47:32 +05:30
Rajarshee Chatterjee 5eb5a55c4c fix: Repair Skipped Novel Counter Migrations 2026-07-27 08:41:55 +05:30
Rajarshee Chatterjee 6cd86d1422 chore: Update Translations (#1906)
* New translations strings.json (Romanian)

[ci skip]
[ci skip]

* New translations strings.json (French)

[ci skip]
[ci skip]

* New translations strings.json (Spanish)

[ci skip]
[ci skip]

* New translations strings.json (Afrikaans)

[ci skip]
[ci skip]

* New translations strings.json (Arabic)

[ci skip]
[ci skip]

* New translations strings.json (Catalan)

[ci skip]
[ci skip]

* New translations strings.json (Czech)

[ci skip]
[ci skip]

* New translations strings.json (Danish)

[ci skip]
[ci skip]

* New translations strings.json (German)

[ci skip]
[ci skip]

* New translations strings.json (Greek)

[ci skip]
[ci skip]

* New translations strings.json (Finnish)

[ci skip]
[ci skip]

* New translations strings.json (Hebrew)

[ci skip]
[ci skip]

* New translations strings.json (Hungarian)

[ci skip]
[ci skip]

* New translations strings.json (Italian)

[ci skip]
[ci skip]

* New translations strings.json (Japanese)

[ci skip]
[ci skip]

* New translations strings.json (Korean)

[ci skip]
[ci skip]

* New translations strings.json (Dutch)

[ci skip]
[ci skip]

* New translations strings.json (Norwegian)

[ci skip]
[ci skip]

* New translations strings.json (Polish)

[ci skip]
[ci skip]

* New translations strings.json (Portuguese)

[ci skip]
[ci skip]

* New translations strings.json (Russian)

[ci skip]
[ci skip]

* New translations strings.json (Albanian)

[ci skip]
[ci skip]

* New translations strings.json (Serbian (Cyrillic))

[ci skip]
[ci skip]

* New translations strings.json (Swedish)

[ci skip]
[ci skip]

* New translations strings.json (Turkish)

[ci skip]
[ci skip]

* New translations strings.json (Ukrainian)

[ci skip]
[ci skip]

* New translations strings.json (Chinese Simplified)

[ci skip]
[ci skip]

* New translations strings.json (Chinese Traditional)

[ci skip]
[ci skip]

* New translations strings.json (Vietnamese)

[ci skip]
[ci skip]

* New translations strings.json (Portuguese, Brazilian)

[ci skip]
[ci skip]

* New translations strings.json (Indonesian)

[ci skip]
[ci skip]

* New translations strings.json (Hindi)

[ci skip]
[ci skip]

* New translations strings.json (Assamese)

[ci skip]
[ci skip]

* New translations strings.json (Odia)

[ci skip]
[ci skip]
2026-07-27 08:19:16 +05:30
Patrick Loser 9783c4d5e3 fix: Normalize Repository Back Navigation (#1905)
* remove useBackHandler

* Remove popToTop
2026-07-27 00:53:11 +05:30
94 changed files with 9386 additions and 1471 deletions
+2 -2
View File
@@ -45,7 +45,7 @@ body:
label: LNReader version
description: You can find your LNReader version in **More → About**.
placeholder: |
Example: "2.1.0"
Example: "2.1.1"
validations:
required: true
@@ -88,7 +88,7 @@ body:
required: true
- label: If this is an issue with a plugin, I should be opening an issue in the [plugins repository](https://github.com/lnreader/lnreader-plugins/issues/new/choose).
required: true
- label: I have updated the app to version **[2.1.0](https://github.com/lnreader/lnreader/releases/latest)**.
- label: I have updated the app to version **[2.1.1](https://github.com/lnreader/lnreader/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true
+1 -1
View File
@@ -34,7 +34,7 @@ body:
required: true
- label: If this is an issue with a source, I should be opening an issue in the [sources repository](https://github.com/lnreader/lnreader-sources/issues/new/choose).
required: true
- label: I have updated the app to version **[2.1.0](https://github.com/lnreader/lnreader/releases/latest)**.
- label: I have updated the app to version **[2.1.1](https://github.com/lnreader/lnreader/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true
+32
View File
@@ -149,6 +149,7 @@ jobs:
"app/build/outputs/apk/preRelease/$PRERELEASE_APK_NAME"
- name: Upload Preview APK
id: upload-nightly
uses: actions/upload-artifact@v4
with:
name: ${{ env.PRERELEASE_ARTIFACT_NAME }}
@@ -156,3 +157,34 @@ jobs:
if-no-files-found: error
retention-days: 14
compression-level: 0
- name: Notify Discord
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }}
ARTIFACT_URL: ${{ steps.upload-nightly.outputs.artifact-url }}
run: |
short_sha="${GITHUB_SHA::8}"
message="[${GITHUB_REPOSITORY}] New nightly build published: \`${short_sha}\`"
payload=$(jq -n \
--arg title "$message" \
--arg url "$ARTIFACT_URL" \
'{
username: "GitHub",
avatar_url: "https://github.com/github.png",
embeds: [{
author: {
name: "github-actions[bot]",
icon_url: "https://github.com/github.png"
},
title: $title,
url: $url,
color: 3092790
}]
}')
curl --fail-with-body \
--request POST \
--header "Content-Type: application/json" \
--data "$payload" \
"$DISCORD_WEBHOOK_URL"
-5
View File
@@ -1,5 +0,0 @@
# AGENTS.md
## Repository Conventions
- Do not add or modify tests unless explicitly requested.
+4
View File
@@ -19,9 +19,12 @@ import Main from './src/navigators/Main';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { useInitDatabase } from '@database/db';
import { useInitializeAppServices } from '@hooks/common/useInitializeAppServices';
import { opSqliteAdapter } from './src/rozenite/opSqliteAdapter';
import { useRozeniteSqlitePlugin } from '@rozenite/sqlite-plugin';
import { ThemeProvider, useTheme } from '@hooks/persisted/useTheme';
enableFreeze(true);
const sqliteAdapters = __DEV__ && opSqliteAdapter ? [opSqliteAdapter] : [];
const ThemedPaperProvider = ({ children }: PropsWithChildren) => {
const theme = useTheme();
@@ -41,6 +44,7 @@ const ThemedPaperProvider = ({ children }: PropsWithChildren) => {
};
const App = () => {
useRozeniteSqlitePlugin({ adapters: sqliteAdapters });
const { success: databaseReady, error: databaseError } = useInitDatabase();
const { ready: servicesReady, error: servicesError } =
useInitializeAppServices(Boolean(databaseReady));
+2 -2
View File
@@ -35,8 +35,8 @@
<a href="https://github.com/lnreader/lnreader/releases/latest">
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/lnreader/lnreader?label=Stable&labelColor=0d7377&color=084c4e&style=flat">
</a>
<a href="https://github.com/lnreader/lnreader/releases/latest">
<img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/lnreader/lnreader?include_prereleases&sort=semver&label=Beta&labelColor=3d3d5c&color=2a2a47&style=flat">
<a href="https://github.com/lnreader/lnreader/actions/workflows/build.yml">
<img alt="Build Preview" src="https://github.com/lnreader/lnreader/actions/workflows/build.yml/badge.svg">
</a>
</p>
+2 -2
View File
@@ -1,6 +1,6 @@
{
"expo": {
"version": "2.1.0",
"version": "2.1.1",
"name": "LNReader",
"slug": "LNReader",
"scheme": "lnreader",
@@ -47,7 +47,7 @@
]
],
"android": {
"versionCode": 4196352,
"versionCode": 4196353,
"package": "com.rajarsheechatterjee.LNReader",
"adaptiveIcon": {
"foregroundImage": "./assets/native/ic_launcher_foreground.png",
@@ -0,0 +1,101 @@
DROP TRIGGER IF EXISTS `update_novel_stats`;--> statement-breakpoint
DROP TRIGGER IF EXISTS `update_novel_stats_on_update`;--> statement-breakpoint
DROP TRIGGER IF EXISTS `update_novel_stats_on_delete`;--> statement-breakpoint
CREATE TABLE IF NOT EXISTS `__migration_Novel` AS SELECT * FROM `Novel`;--> statement-breakpoint
CREATE TABLE IF NOT EXISTS `__migration_Chapter` AS SELECT * FROM `Chapter`;--> statement-breakpoint
CREATE TABLE IF NOT EXISTS `__migration_NovelCategory` AS SELECT * FROM `NovelCategory`;--> statement-breakpoint
DROP TABLE IF EXISTS `__new_Novel`;--> statement-breakpoint
CREATE TABLE `__new_Novel` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`path` text NOT NULL,
`pluginId` text NOT NULL,
`name` text NOT NULL,
`cover` text,
`summary` text,
`author` text,
`artist` text,
`status` text DEFAULT 'Unknown',
`genres` text,
`inLibrary` integer DEFAULT false,
`isLocal` integer DEFAULT false,
`totalPages` integer DEFAULT 0,
`chaptersDownloaded` integer DEFAULT 0,
`chaptersUnread` integer DEFAULT 0,
`totalChapters` integer DEFAULT 0,
`lastReadAt` text,
`lastUpdatedAt` text
);
--> statement-breakpoint
INSERT INTO `__new_Novel` (
`id`,
`path`,
`pluginId`,
`name`,
`cover`,
`summary`,
`author`,
`artist`,
`status`,
`genres`,
`inLibrary`,
`isLocal`,
`totalPages`,
`chaptersDownloaded`,
`chaptersUnread`,
`totalChapters`,
`lastReadAt`,
`lastUpdatedAt`
)
SELECT
`__migration_Novel`.`id`,
`__migration_Novel`.`path`,
`__migration_Novel`.`pluginId`,
`__migration_Novel`.`name`,
`__migration_Novel`.`cover`,
`__migration_Novel`.`summary`,
`__migration_Novel`.`author`,
`__migration_Novel`.`artist`,
`__migration_Novel`.`status`,
`__migration_Novel`.`genres`,
`__migration_Novel`.`inLibrary`,
`__migration_Novel`.`isLocal`,
`__migration_Novel`.`totalPages`,
(
SELECT COUNT(*)
FROM `__migration_Chapter`
WHERE `__migration_Chapter`.`novelId` = `__migration_Novel`.`id`
AND `__migration_Chapter`.`isDownloaded` = 1
),
(
SELECT COUNT(*)
FROM `__migration_Chapter`
WHERE `__migration_Chapter`.`novelId` = `__migration_Novel`.`id`
AND `__migration_Chapter`.`unread` = 1
),
(
SELECT COUNT(*)
FROM `__migration_Chapter`
WHERE `__migration_Chapter`.`novelId` = `__migration_Novel`.`id`
),
(
SELECT MAX(`__migration_Chapter`.`readTime`)
FROM `__migration_Chapter`
WHERE `__migration_Chapter`.`novelId` = `__migration_Novel`.`id`
),
(
SELECT MAX(`__migration_Chapter`.`updatedTime`)
FROM `__migration_Chapter`
WHERE `__migration_Chapter`.`novelId` = `__migration_Novel`.`id`
)
FROM `__migration_Novel`;--> statement-breakpoint
DELETE FROM `NovelCategory`;--> statement-breakpoint
DELETE FROM `Chapter`;--> statement-breakpoint
DROP TABLE IF EXISTS `Novel`;--> statement-breakpoint
ALTER TABLE `__new_Novel` RENAME TO `Novel`;--> statement-breakpoint
INSERT OR REPLACE INTO `Chapter` SELECT * FROM `__migration_Chapter`;--> statement-breakpoint
INSERT OR REPLACE INTO `NovelCategory` SELECT * FROM `__migration_NovelCategory`;--> statement-breakpoint
DROP TABLE `__migration_Novel`;--> statement-breakpoint
DROP TABLE `__migration_Chapter`;--> statement-breakpoint
DROP TABLE `__migration_NovelCategory`;--> statement-breakpoint
CREATE UNIQUE INDEX `novel_path_plugin_unique` ON `Novel` (`path`,`pluginId`);--> statement-breakpoint
CREATE INDEX `NovelIndex` ON `Novel` (`pluginId`,`path`,`id`,`inLibrary`);
+7 -5
View File
@@ -3,11 +3,13 @@
import m0000 from './20251222152612_past_mandrill/migration.sql';
import m0001 from './20260612232322_normal_saracen/migration.sql';
import m0002 from './20260719143427_long_moondragon/migration.sql';
import m0003 from './20260727081855_calm_chimera/migration.sql';
export default {
migrations: {
"20251222152612_past_mandrill": m0000,
"20260612232322_normal_saracen": m0001,
"20260719143427_long_moondragon": m0002
}
}
'20251222152612_past_mandrill': m0000,
'20260612232322_normal_saracen': m0001,
'20260719143427_long_moondragon': m0002,
'20260727081855_calm_chimera': m0003,
},
};
+8 -2
View File
@@ -1,3 +1,6 @@
const { withRozenite } = require('@rozenite/metro');
const { withRozeniteExpoAtlasPlugin } = require('@rozenite/expo-atlas-plugin');
const { getDefaultConfig } = require('expo/metro-config');
const fs = require('fs');
const path = require('path');
@@ -44,7 +47,7 @@ config.server.enhanceMiddleware = metroMiddleware => {
response.setHeader(
'Content-Type',
readerAssetContentTypes[path.extname(assetPath)] ||
'application/octet-stream',
'application/octet-stream',
);
if (request.method === 'HEAD') {
@@ -55,4 +58,7 @@ config.server.enhanceMiddleware = metroMiddleware => {
};
};
module.exports = config;
module.exports = withRozenite(config, {
enabled: process.env.WITH_ROZENITE === 'true',
enhanceMetroConfig: metroConfig => withRozeniteExpoAtlasPlugin(metroConfig),
});
@@ -25,8 +25,28 @@ interface BackgroundTaskDao {
@Query("UPDATE background_tasks SET state = :state, updatedAt = :updatedAt WHERE id = :id AND state = 'running'")
suspend fun finishRunning(id: String, state: String, updatedAt: Long)
@Query("UPDATE background_tasks SET state = :state, attempt = attempt + 1, updatedAt = :updatedAt WHERE id = :id")
suspend fun markRunning(id: String, state: String, updatedAt: Long)
@Query(
"""
UPDATE background_tasks
SET state = :state, attempt = attempt + 1, updatedAt = :updatedAt
WHERE id = :id
AND state = 'queued'
AND (
type != 'DOWNLOAD_CHAPTER'
OR (
SELECT COUNT(*)
FROM background_tasks
WHERE type = 'DOWNLOAD_CHAPTER' AND state = 'running'
) < :maxConcurrentDownloads
)
""",
)
suspend fun tryMarkRunning(
id: String,
state: String,
updatedAt: Long,
maxConcurrentDownloads: Int,
): Int
@Query("UPDATE background_tasks SET progress = :progress, progressText = :progressText, updatedAt = :updatedAt WHERE id = :id")
suspend fun updateProgress(id: String, progress: Double?, progressText: String?, updatedAt: Long)
@@ -36,4 +56,4 @@ interface BackgroundTaskDao {
@Query("UPDATE background_tasks SET workId = :workId, updatedAt = :updatedAt WHERE id = :id")
suspend fun assignWork(id: String, workId: String, updatedAt: Long)
}
}
@@ -2,10 +2,12 @@ package expo.modules.nativebackgroundtasks
import android.content.Context
import androidx.room.Database
import androidx.room.migration.Migration
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
@Database(entities = [BackgroundTaskEntity::class], version = 1, exportSchema = false)
@Database(entities = [BackgroundTaskEntity::class], version = 2, exportSchema = false)
abstract class BackgroundTaskDatabase : RoomDatabase() {
abstract fun tasks(): BackgroundTaskDao
@@ -18,7 +20,18 @@ abstract class BackgroundTaskDatabase : RoomDatabase() {
context.applicationContext,
BackgroundTaskDatabase::class.java,
"lnreader-background-tasks.db",
).build().also { instance = it }
).addMigrations(MIGRATION_1_2)
.build()
.also { instance = it }
}
private val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"ALTER TABLE background_tasks ADD COLUMN queueName TEXT NOT NULL " +
"DEFAULT 'lnreader-background-task-queue'",
)
}
}
}
}
}
@@ -10,6 +10,7 @@ data class BackgroundTaskEntity(
val payload: String,
val title: String,
val description: String,
val queueName: String,
val state: String,
val progress: Double?,
val progressText: String?,
@@ -29,4 +30,4 @@ object BackgroundTaskState {
const val CANCELLED = "cancelled"
val active = listOf(QUEUED, RUNNING, PAUSED)
}
}
@@ -10,7 +10,6 @@ import androidx.work.WorkManager
import java.util.UUID
object BackgroundTaskScheduler {
private const val QUEUE_NAME = "lnreader-background-task-queue"
const val TASK_ID = "taskId"
suspend fun enqueue(context: Context, taskId: String): UUID {
@@ -28,7 +27,7 @@ object BackgroundTaskScheduler {
BackgroundTaskDatabase.get(context).tasks()
.assignWork(taskId, request.id.toString(), System.currentTimeMillis())
WorkManager.getInstance(context).enqueueUniqueWork(
QUEUE_NAME,
task.queueName,
ExistingWorkPolicy.APPEND_OR_REPLACE,
request,
)
@@ -52,6 +51,7 @@ object BackgroundTaskScheduler {
payload = """{"name":"$LIBRARY_UPDATE_TASK_TYPE"}""",
title = title,
description = description,
queueName = "lnreader-background-task:task:$LIBRARY_UPDATE_TASK_TYPE",
state = BackgroundTaskState.QUEUED,
progress = null,
progressText = null,
@@ -21,7 +21,19 @@ class LNReaderTaskWorker(
return Result.success()
}
dao.markRunning(taskId, BackgroundTaskState.RUNNING, System.currentTimeMillis())
val claimed = dao.tryMarkRunning(
taskId,
BackgroundTaskState.RUNNING,
System.currentTimeMillis(),
MAX_CONCURRENT_DOWNLOADS,
)
if (claimed == 0) {
return if (dao.get(taskId)?.state == BackgroundTaskState.QUEUED) {
Result.retry()
} else {
Result.success()
}
}
val runningTask = dao.get(taskId) ?: return Result.failure()
setForeground(createForegroundInfo(runningTask))
val execution = TaskExecutionRegistry.register(taskId)
@@ -48,7 +60,9 @@ class LNReaderTaskWorker(
return Result.success()
}
dao.finishRunning(taskId, BackgroundTaskState.SUCCEEDED, System.currentTimeMillis())
dao.get(taskId)?.let { TaskNotificationFactory.update(applicationContext, it) }
dao.get(taskId)?.let {
TaskNotificationFactory.postTerminal(applicationContext, it)
}
Result.success()
}
is TaskExecutionResult.Failure -> {
@@ -61,7 +75,9 @@ class LNReaderTaskWorker(
Result.retry()
} else {
dao.updateState(taskId, BackgroundTaskState.FAILED, System.currentTimeMillis())
dao.get(taskId)?.let { TaskNotificationFactory.update(applicationContext, it) }
dao.get(taskId)?.let {
TaskNotificationFactory.postTerminal(applicationContext, it)
}
Result.success()
}
}
@@ -86,4 +102,8 @@ class LNReaderTaskWorker(
ForegroundInfo(id, notification)
}
}
}
companion object {
private const val MAX_CONCURRENT_DOWNLOADS = 3
}
}
@@ -27,7 +27,7 @@ class NativeBackgroundTasksModule : Module() {
reactContextRef = null
}
AsyncFunction("enqueue") { type: String, payload: String, title: String, description: String, allowsDuplicates: Boolean ->
AsyncFunction("enqueue") { type: String, payload: String, title: String, description: String, allowsDuplicates: Boolean, queueName: String ->
runBlocking(Dispatchers.IO) {
if (!allowsDuplicates) {
dao.getActiveByType(type)?.let { return@runBlocking it.id }
@@ -39,6 +39,7 @@ class NativeBackgroundTasksModule : Module() {
payload = payload,
title = title,
description = description,
queueName = queueName,
state = BackgroundTaskState.QUEUED,
progress = null,
progressText = null,
@@ -130,10 +131,12 @@ class NativeBackgroundTasksModule : Module() {
}
}
AsyncFunction("complete") { taskId: String ->
AsyncFunction("complete") { taskId: String, completionText: String ->
runBlocking(Dispatchers.IO) {
dao.updateCheckpoint(taskId, null, System.currentTimeMillis())
dao.finishRunning(taskId, BackgroundTaskState.SUCCEEDED, System.currentTimeMillis())
val now = System.currentTimeMillis()
dao.updateCheckpoint(taskId, null, now)
dao.updateProgress(taskId, null, completionText, now)
dao.finishRunning(taskId, BackgroundTaskState.SUCCEEDED, now)
TaskExecutionRegistry.complete(taskId, TaskExecutionResult.Success)
}
}
@@ -15,6 +15,7 @@ object TaskNotificationFactory {
const val ACTION_RESUME = "expo.modules.nativebackgroundtasks.RESUME"
const val ACTION_CANCEL = "expo.modules.nativebackgroundtasks.CANCEL"
const val EXTRA_TASK_ID = "taskId"
private const val TERMINAL_NOTIFICATION_MASK = 0x40000000
fun ensureChannel(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
@@ -33,6 +34,9 @@ object TaskNotificationFactory {
fun notificationId(taskId: String): Int = taskId.hashCode().and(Int.MAX_VALUE).coerceAtLeast(1)
fun terminalNotificationId(taskId: String): Int =
notificationId(taskId).xor(TERMINAL_NOTIFICATION_MASK).coerceAtLeast(1)
fun build(context: Context, task: BackgroundTaskEntity): Notification {
ensureChannel(context)
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
@@ -66,7 +70,13 @@ object TaskNotificationFactory {
.setContentText(contentText)
.setContentIntent(contentIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
.setCategory(
if (task.state in listOf(BackgroundTaskState.SUCCEEDED, BackgroundTaskState.FAILED)) {
NotificationCompat.CATEGORY_STATUS
} else {
NotificationCompat.CATEGORY_PROGRESS
},
)
.setOngoing(task.state == BackgroundTaskState.RUNNING || task.state == BackgroundTaskState.QUEUED)
.setAutoCancel(task.state == BackgroundTaskState.SUCCEEDED || task.state == BackgroundTaskState.FAILED)
.setOnlyAlertOnce(true)
@@ -78,15 +88,22 @@ object TaskNotificationFactory {
val progress = task.progress
if (task.state == BackgroundTaskState.SUCCEEDED) {
builder.setContentText("Completed").setProgress(0, 0, false)
builder.setContentText(contentText).setProgress(0, 0, false)
} else if (task.state == BackgroundTaskState.FAILED) {
builder.setContentText("Failed: $contentText").setProgress(0, 0, false)
builder.setContentText(contentText).setProgress(0, 0, false)
} else if (progress == null) {
builder.setProgress(100, 0, true)
} else {
builder.setProgress(100, (progress.coerceIn(0.0, 1.0) * 100).toInt(), false)
}
if (
task.state in listOf(BackgroundTaskState.SUCCEEDED, BackgroundTaskState.FAILED) &&
contentText.isNotBlank()
) {
builder.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
}
if (
task.type == "UPDATE_LIBRARY" &&
task.state in listOf(BackgroundTaskState.QUEUED, BackgroundTaskState.RUNNING, BackgroundTaskState.PAUSED) &&
@@ -107,9 +124,17 @@ object TaskNotificationFactory {
.notify(notificationId(task.id), build(context, task))
}
fun dismiss(context: Context, taskId: String) {
fun postTerminal(context: Context, task: BackgroundTaskEntity) {
require(task.state in listOf(BackgroundTaskState.SUCCEEDED, BackgroundTaskState.FAILED))
context.getSystemService(NotificationManager::class.java)
.cancel(notificationId(taskId))
.notify(terminalNotificationId(task.id), build(context, task))
}
fun dismiss(context: Context, taskId: String) {
context.getSystemService(NotificationManager::class.java).apply {
cancel(notificationId(taskId))
cancel(terminalNotificationId(taskId))
}
}
private fun actionIntent(
@@ -22,6 +22,7 @@ type NativeBackgroundTasksModule = {
title: string,
description: string,
allowsDuplicates: boolean,
queueName: string,
): Promise<string>;
getTasks(): Promise<NativeBackgroundTaskRecord[]>;
pause(taskId: string): Promise<void>;
@@ -33,7 +34,7 @@ type NativeBackgroundTasksModule = {
progressText: string,
): Promise<void>;
updateCheckpoint(taskId: string, checkpoint: string): Promise<void>;
complete(taskId: string): Promise<void>;
complete(taskId: string, completionText: string): Promise<void>;
fail(taskId: string, error: string, shouldRetry: boolean): Promise<void>;
scheduleLibraryUpdates(
intervalHours: number,
+4 -1
View File
@@ -1,6 +1,6 @@
{
"name": "lnreader",
"version": "2.1.0",
"version": "2.1.1",
"private": true,
"scripts": {
"dev:android:release": "pnpm run generate:env:release && npx expo run:android --variant release",
@@ -108,6 +108,9 @@
"@babel/preset-env": "^7.29.7",
"@babel/runtime": "^7.29.7",
"@eslint/eslintrc": "^3.3.6",
"@rozenite/expo-atlas-plugin": "^1.13.0",
"@rozenite/metro": "^1.13.0",
"@rozenite/sqlite-plugin": "^1.13.0",
"@testing-library/react-native": "^13.3.3",
"@types/better-sqlite3": "^7.6.13",
"@types/color": "^4.2.1",
+1225 -621
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -169,12 +169,14 @@ const releaseDate = args['release-date'] || formatUtcDate(new Date());
const nodeEnv =
args['node-env'] ||
(buildType.toLowerCase().includes('release') ? 'production' : 'development');
const rozeniteEnabled = nodeEnv !== 'production';
const generatedEnvContent = [
formatEnvEntry('BUILD_TYPE', buildType),
formatEnvEntry('GIT_HASH', gitHash),
formatEnvEntry('RELEASE_DATE', releaseDate),
formatEnvEntry('NODE_ENV', nodeEnv),
formatEnvEntry('WITH_ROZENITE', rozeniteEnabled),
formatEnvEntry('MYANIMELIST_CLIENT_ID', myanimelistClientId),
formatEnvEntry('ANILIST_CLIENT_ID', anilistClientId),
'',
@@ -243,6 +245,7 @@ try {
GIT_HASH: gitHash,
RELEASE_DATE: releaseDate,
NODE_ENV: nodeEnv,
WITH_ROZENITE: rozeniteEnabled ? 'enabled' : 'disabled',
MYANIMELIST_CLIENT_ID: myanimelistClientId
? 'configured'
: 'not configured',
+1 -2
View File
@@ -53,7 +53,7 @@ function CustomBottomTabBar({
styles.container,
{
backgroundColor: theme.surface2 || theme.surface,
paddingBottom: insets?.bottom || 0,
paddingBottom: 16 + (insets?.bottom || 0),
},
]}
>
@@ -148,7 +148,6 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 6,
paddingHorizontal: 4,
position: 'relative',
},
+46 -4
View File
@@ -5,6 +5,7 @@ import {
StyleSheet,
Text,
TextStyle,
View,
ViewStyle,
} from 'react-native';
import { Checkbox as PaperCheckbox } from 'react-native-paper';
@@ -20,6 +21,8 @@ interface CheckboxProps {
theme: ThemeColors;
viewStyle?: StyleProp<ViewStyle>;
labelStyle?: StyleProp<TextStyle>;
description?: string;
descriptionStyle?: StyleProp<TextStyle>;
}
export const Checkbox: React.FC<CheckboxProps> = ({
@@ -30,8 +33,17 @@ export const Checkbox: React.FC<CheckboxProps> = ({
onPress,
viewStyle,
labelStyle,
description,
descriptionStyle,
}) => (
<Pressable
accessibilityHint={description}
accessibilityLabel={label}
accessibilityRole="checkbox"
accessibilityState={{
checked: status === 'indeterminate' ? 'mixed' : status,
disabled,
}}
android_ripple={{ color: theme.rippleColor }}
style={[styles.pressable, viewStyle]}
onPress={onPress}
@@ -53,9 +65,28 @@ export const Checkbox: React.FC<CheckboxProps> = ({
uncheckedColor={theme.onSurfaceVariant}
disabled={disabled}
/>
<Text style={[styles.defaultLabel, { color: theme.onSurface }, labelStyle]}>
{label}
</Text>
<View style={styles.textContainer}>
<Text
style={[styles.defaultLabel, { color: theme.onSurface }, labelStyle]}
>
{label}
</Text>
{description ? (
<Text
style={[
styles.description,
{
color: disabled
? theme.onSurfaceDisabled
: theme.onSurfaceVariant,
},
descriptionStyle,
]}
>
{description}
</Text>
) : null}
</View>
</Pressable>
);
@@ -85,7 +116,13 @@ export const SortItem = ({ label, status, onPress, theme }: SortItemProps) => (
const styles = StyleSheet.create({
defaultLabel: {
marginStart: 12,
flexShrink: 1,
},
description: {
flexShrink: 1,
fontSize: 13,
lineHeight: 18,
marginTop: 2,
},
icon: {
alignSelf: 'center',
@@ -99,4 +136,9 @@ const styles = StyleSheet.create({
paddingVertical: 6,
},
sortItem: { paddingVertical: 16, paddingStart: 64 },
textContainer: {
flex: 1,
justifyContent: 'center',
marginStart: 12,
},
});
@@ -0,0 +1,51 @@
import { render, screen } from '@testing-library/react-native';
import { Checkbox } from '../Checkbox/Checkbox';
import type { ThemeColors } from '../../theme/types';
const mockTheme = {
onSurface: '#111111',
onSurfaceDisabled: '#777777',
onSurfaceVariant: '#555555',
primary: '#6200ee',
rippleColor: '#eeeeee',
} as ThemeColors;
describe('Checkbox', () => {
it('renders an accessible description below the label', () => {
render(
<Checkbox
description="Include app preferences"
label="Settings"
onPress={() => {}}
status
theme={mockTheme}
/>,
);
const checkbox = screen.getByRole('checkbox', { name: 'Settings' });
expect(checkbox.props.accessibilityState).toEqual({
checked: true,
disabled: undefined,
});
expect(checkbox.props.accessibilityHint).toBe('Include app preferences');
expect(screen.getByText('Include app preferences')).toBeOnTheScreen();
});
it('centers the checkbox against the label and description block', () => {
render(
<Checkbox
description="Include app preferences"
label="Settings"
status={false}
theme={mockTheme}
/>,
);
expect(screen.getByRole('checkbox', { name: 'Settings' })).toHaveStyle({
alignItems: 'center',
flexDirection: 'row',
});
});
});
+285 -11
View File
@@ -6,6 +6,7 @@ import { schema } from '@database/schema';
import {
getPendingMigrations,
repairInterruptedNovelMigration,
repairMigrationHistory,
runDatabaseBootstrap,
} from '@database/db';
@@ -71,6 +72,32 @@ const MIGRATION_STATEMENTS = [
`CREATE UNIQUE INDEX IF NOT EXISTS repository_url_unique ON Repository (url)`,
];
const LEGACY_NOVEL_TABLE_STATEMENT = `CREATE TABLE IF NOT EXISTS Novel (
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
path text NOT NULL,
pluginId text NOT NULL,
name text NOT NULL,
cover text,
summary text,
author text,
artist text,
status text DEFAULT 'Unknown',
genres text,
inLibrary integer DEFAULT false,
isLocal integer DEFAULT false,
totalPages integer DEFAULT 0
)`;
const createSchema = (sqlite: DB, legacyNovel = false) => {
for (const statement of MIGRATION_STATEMENTS) {
sqlite.executeSync(
legacyNovel && statement.startsWith('CREATE TABLE IF NOT EXISTS Novel (')
? LEGACY_NOVEL_TABLE_STATEMENT
: statement.trim(),
);
}
};
const createExecutor = (sqlite: DB) => ({
executeSync: (sql: string, params?: unknown[]) => {
sqlite.executeSync(sql, params as any[]);
@@ -131,9 +158,7 @@ describe('runDatabaseBootstrap', () => {
(sqlite as any).executeAsync ??= sqlite.execute;
(sqlite as any).executeRawAsync ??= sqlite.executeRaw;
try {
for (const statement of MIGRATION_STATEMENTS) {
sqlite.executeSync(statement.trim());
}
createSchema(sqlite);
sqlite.executeSync('PRAGMA journal_mode = WAL');
runDatabaseBootstrap(createExecutor(sqlite));
@@ -174,9 +199,42 @@ describe('production migrations', () => {
(sqlite as any).executeAsync ??= sqlite.execute;
(sqlite as any).executeRawAsync ??= sqlite.executeRaw;
try {
for (const statement of MIGRATION_STATEMENTS) {
sqlite.executeSync(statement.trim());
}
createSchema(sqlite);
sqlite.executeSync(`
INSERT INTO Category (id, name, sort)
VALUES (1, 'Existing category', 1)
`);
sqlite.executeSync(`
INSERT INTO Novel (id, path, pluginId, name, inLibrary)
VALUES (1, '/existing', 'existing-plugin', 'Existing novel', 1)
`);
sqlite.executeSync(`
INSERT INTO Chapter (
id,
novelId,
path,
name,
unread,
isDownloaded,
readTime,
updatedTime
)
VALUES (
1,
1,
'/existing/chapter-1',
'Existing chapter',
1,
1,
'2026-07-26T10:00:00.000Z',
'2026-07-26T11:00:00.000Z'
)
`);
sqlite.executeSync(`
INSERT INTO NovelCategory (id, novelId, categoryId)
VALUES (1, 1, 1)
`);
const drizzleDb = drizzle(sqlite, { schema });
await migrate(drizzleDb, getPendingMigrations(sqlite));
@@ -194,20 +252,226 @@ describe('production migrations', () => {
'Repository',
]),
);
expect(
sqlite.executeSync(
'SELECT id, name, chaptersDownloaded, chaptersUnread, totalChapters FROM Novel',
).rows,
).toEqual([
{
id: 1,
name: 'Existing novel',
chaptersDownloaded: 1,
chaptersUnread: 1,
totalChapters: 1,
},
]);
expect(
sqlite.executeSync('SELECT id, novelId, name FROM Chapter').rows,
).toEqual([{ id: 1, novelId: 1, name: 'Existing chapter' }]);
expect(
sqlite.executeSync('SELECT id, novelId, categoryId FROM NovelCategory')
.rows,
).toEqual([{ id: 1, novelId: 1, categoryId: 1 }]);
} finally {
sqlite.close();
}
});
it('recovers when the scanlator column exists without a migration record', async () => {
it('repairs a legacy Novel table that is missing counter columns', async () => {
const sqlite = open({ name: ':memory:' });
(sqlite as any).executeAsync ??= sqlite.execute;
(sqlite as any).executeRawAsync ??= sqlite.executeRaw;
try {
for (const statement of MIGRATION_STATEMENTS) {
sqlite.executeSync(statement.trim());
}
createSchema(sqlite, true);
sqlite.executeSync(`
INSERT INTO Category (id, name, sort)
VALUES (1, 'Existing category', 1)
`);
sqlite.executeSync(`
INSERT INTO Novel (id, path, pluginId, name, inLibrary)
VALUES (1, '/legacy', 'legacy-plugin', 'Legacy novel', 1)
`);
sqlite.executeSync(`
INSERT INTO Chapter (
id,
novelId,
path,
name,
unread,
isDownloaded,
readTime,
updatedTime
)
VALUES
(
1,
1,
'/legacy/chapter-1',
'Chapter 1',
0,
1,
'2026-07-25T10:00:00.000Z',
'2026-07-25T11:00:00.000Z'
),
(
2,
1,
'/legacy/chapter-2',
'Chapter 2',
1,
0,
NULL,
'2026-07-26T11:00:00.000Z'
)
`);
sqlite.executeSync(`
INSERT INTO NovelCategory (id, novelId, categoryId)
VALUES (1, 1, 1)
`);
const drizzleDb = drizzle(sqlite, { schema });
await migrate(drizzleDb, getPendingMigrations(sqlite));
expect(
sqlite.executeSync(
`SELECT
id,
chaptersDownloaded,
chaptersUnread,
totalChapters,
lastReadAt,
lastUpdatedAt
FROM Novel`,
).rows,
).toEqual([
{
id: 1,
chaptersDownloaded: 1,
chaptersUnread: 1,
totalChapters: 2,
lastReadAt: '2026-07-25T10:00:00.000Z',
lastUpdatedAt: '2026-07-26T11:00:00.000Z',
},
]);
expect(
sqlite.executeSync('SELECT id FROM Chapter ORDER BY id').rows,
).toEqual([{ id: 1 }, { id: 2 }]);
expect(
sqlite.executeSync('SELECT novelId, categoryId FROM NovelCategory')
.rows,
).toEqual([{ novelId: 1, categoryId: 1 }]);
} finally {
sqlite.close();
}
});
it('resumes after Novel was dropped during the previous repair migration', async () => {
const sqlite = open({ name: ':memory:' });
(sqlite as any).executeAsync ??= sqlite.execute;
(sqlite as any).executeRawAsync ??= sqlite.executeRaw;
try {
createSchema(sqlite);
sqlite.executeSync('ALTER TABLE Chapter ADD scanlator text');
sqlite.executeSync('ALTER TABLE Chapter ADD timeSpent integer DEFAULT 0');
sqlite.executeSync(`
INSERT INTO Category (id, name, sort)
VALUES (1, 'Existing category', 1)
`);
sqlite.executeSync(`
INSERT INTO Novel (id, path, pluginId, name, inLibrary)
VALUES (1, '/interrupted', 'legacy-plugin', 'Interrupted novel', 1)
`);
sqlite.executeSync(`
INSERT INTO Chapter (
id,
novelId,
path,
name,
unread,
isDownloaded,
scanlator,
timeSpent
)
VALUES (
1,
1,
'/interrupted/chapter-1',
'Chapter 1',
1,
1,
'Group',
12
)
`);
sqlite.executeSync(`
INSERT INTO NovelCategory (id, novelId, categoryId)
VALUES (1, 1, 1)
`);
sqlite.executeSync('CREATE TABLE __new_Novel AS SELECT * FROM Novel');
sqlite.executeSync(
'CREATE TABLE __migration_Chapter AS SELECT * FROM Chapter',
);
sqlite.executeSync(
'CREATE TABLE __migration_NovelCategory AS SELECT * FROM NovelCategory',
);
sqlite.executeSync('DELETE FROM NovelCategory');
sqlite.executeSync('DELETE FROM Chapter');
sqlite.executeSync('DROP TABLE Novel');
repairInterruptedNovelMigration(sqlite);
sqlite.executeSync(`
CREATE TABLE __drizzle_migrations (
id INTEGER PRIMARY KEY,
hash text NOT NULL,
created_at numeric,
name text,
applied_at text
)
`);
sqlite.executeSync(`
INSERT INTO __drizzle_migrations (hash, created_at, name)
VALUES ('', 1766417172000, NULL)
`);
repairMigrationHistory(sqlite);
const drizzleDb = drizzle(sqlite, { schema });
await migrate(drizzleDb, getPendingMigrations(sqlite));
expect(
sqlite.executeSync(
'SELECT id, name, chaptersDownloaded, chaptersUnread, totalChapters FROM Novel',
).rows,
).toEqual([
{
id: 1,
name: 'Interrupted novel',
chaptersDownloaded: 1,
chaptersUnread: 1,
totalChapters: 1,
},
]);
expect(
sqlite.executeSync('SELECT id, scanlator, timeSpent FROM Chapter').rows,
).toEqual([{ id: 1, scanlator: 'Group', timeSpent: 12 }]);
expect(
sqlite.executeSync('SELECT novelId, categoryId FROM NovelCategory')
.rows,
).toEqual([{ novelId: 1, categoryId: 1 }]);
} finally {
sqlite.close();
}
});
it('recovers when Chapter columns exist without migration records', async () => {
const sqlite = open({ name: ':memory:' });
(sqlite as any).executeAsync ??= sqlite.execute;
(sqlite as any).executeRawAsync ??= sqlite.executeRaw;
try {
createSchema(sqlite);
sqlite.executeSync('ALTER TABLE Chapter ADD scanlator text');
sqlite.executeSync('ALTER TABLE Chapter ADD timeSpent integer DEFAULT 0');
sqlite.executeSync(`
CREATE TABLE __drizzle_migrations (
id INTEGER PRIMARY KEY,
@@ -229,6 +493,11 @@ describe('production migrations', () => {
"SELECT name FROM __drizzle_migrations WHERE name = '20260612232322_normal_saracen'",
).rows,
).toHaveLength(1);
expect(
sqlite.executeSync(
"SELECT name FROM __drizzle_migrations WHERE name = '20260719143427_long_moondragon'",
).rows,
).toHaveLength(1);
const drizzleDb = drizzle(sqlite, { schema });
await migrate(drizzleDb, getPendingMigrations(sqlite));
@@ -237,6 +506,10 @@ describe('production migrations', () => {
.executeRawSync('PRAGMA table_info(Chapter)')
.filter(column => column[1] === 'scanlator');
expect(scanlatorColumns).toHaveLength(1);
const timeSpentColumns = sqlite
.executeRawSync('PRAGMA table_info(Chapter)')
.filter(column => column[1] === 'timeSpent');
expect(timeSpentColumns).toHaveLength(1);
const appliedMigrations = sqlite.executeSync(
'SELECT name FROM __drizzle_migrations ORDER BY created_at',
@@ -244,7 +517,8 @@ describe('production migrations', () => {
expect(appliedMigrations.map(row => row.name)).toEqual([
'20251222152612_past_mandrill',
'20260612232322_normal_saracen',
'20260719143427_long_moondragon'
'20260719143427_long_moondragon',
'20260727081855_calm_chimera',
]);
} finally {
sqlite.close();
+57 -5
View File
@@ -30,6 +30,21 @@ const INITIAL_MIGRATION_NAME = '20251222152612_past_mandrill';
const INITIAL_MIGRATION_CREATED_AT = 1766417172000;
const SCANLATOR_MIGRATION_NAME = '20260612232322_normal_saracen';
const SCANLATOR_MIGRATION_CREATED_AT = 1781306602000;
const TIME_SPENT_MIGRATION_NAME = '20260719143427_long_moondragon';
const TIME_SPENT_MIGRATION_CREATED_AT = 1784471667000;
const CHAPTER_COLUMN_MIGRATIONS = [
{
columnName: 'scanlator',
migrationName: SCANLATOR_MIGRATION_NAME,
createdAt: SCANLATOR_MIGRATION_CREATED_AT,
},
{
columnName: 'timeSpent',
migrationName: TIME_SPENT_MIGRATION_NAME,
createdAt: TIME_SPENT_MIGRATION_CREATED_AT,
},
] as const;
/**
* Raw SQLite database instance
@@ -60,6 +75,39 @@ type MigrationExecutor = {
executeSync: (sql: string) => unknown;
};
/**
* Recovers the one interruption point where the previous repair migration
* could have dropped Novel before renaming its populated replacement.
*/
export const repairInterruptedNovelMigration = (
executor: MigrationExecutor,
) => {
const tableNames = new Set(
executor
.executeRawSync("SELECT name FROM sqlite_master WHERE type = 'table';")
.map(row => row[0]),
);
const hasNovel = tableNames.has('Novel');
const hasNovelSnapshot = tableNames.has('__migration_Novel');
const hasNewNovel = tableNames.has('__new_Novel');
const hasMigrationState =
hasNovelSnapshot ||
hasNewNovel ||
tableNames.has('__migration_Chapter') ||
tableNames.has('__migration_NovelCategory');
if (!hasMigrationState || hasNovel || hasNovelSnapshot) {
return;
}
if (!hasNewNovel) {
throw new Error(
'Cannot recover interrupted Novel migration: no source table remains',
);
}
executor.executeSync("ALTER TABLE '__new_Novel' RENAME TO 'Novel';");
};
/**
* Repairs migration metadata created by older Drizzle versions and interrupted
* migrations. SQLite cannot add a column conditionally, so an existing column
@@ -94,17 +142,20 @@ export const repairMigrationHistory = (executor: MigrationExecutor) => {
`);
const chapterColumns = executor.executeRawSync('PRAGMA table_info(Chapter);');
const hasScanlator = chapterColumns.some(row => row[1] === 'scanlator');
const chapterColumnNames = new Set(chapterColumns.map(row => row[1]));
if (hasScanlator) {
for (const migration of CHAPTER_COLUMN_MIGRATIONS) {
if (!chapterColumnNames.has(migration.columnName)) {
continue;
}
executor.executeSync(`
INSERT INTO __drizzle_migrations
(hash, created_at, name, applied_at)
SELECT '', ${SCANLATOR_MIGRATION_CREATED_AT},
'${SCANLATOR_MIGRATION_NAME}', datetime('now')
SELECT '', ${migration.createdAt},
'${migration.migrationName}', datetime('now')
WHERE NOT EXISTS (
SELECT 1 FROM __drizzle_migrations
WHERE name = '${SCANLATOR_MIGRATION_NAME}'
WHERE name = '${migration.migrationName}'
);
`);
}
@@ -180,6 +231,7 @@ let initialization: Promise<void> | undefined;
export const initializeDatabase = () => {
if (!initialization) {
setPragmas(_db);
repairInterruptedNovelMigration(_db);
repairMigrationHistory(_db);
initialization = migrate(drizzleDb, getPendingMigrations(_db)).then(() => {
runDatabaseBootstrap(_db);
+2
View File
@@ -48,6 +48,7 @@ export default function useDownload() {
data: {
novelName: novel.name,
novelId: novel.id,
pluginId: novel.pluginId,
chapters: [{ chapterId: chapter.id, chapterName: chapter.name }],
},
});
@@ -57,6 +58,7 @@ export default function useDownload() {
data: {
novelName: novel.name,
novelId: novel.id,
pluginId: novel.pluginId,
chapters: chapters.map(chapter => ({
chapterId: chapter.id,
chapterName: chapter.name,
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+151 -14
View File
@@ -22,16 +22,22 @@
"dataManagement": "إدارة البيانات",
"deleteReadChapters": "حذف الفصول المقروءة",
"deleteReadChaptersDialogTitle": "حذف جميع الفصول التي تم قراءتها",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "استيراد EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "استيراد رواية",
"importStaticFiles": "استيراد الملفات الثابتة",
"novelInsertFailed": "فشل إدراج الرواية",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "وكيل المستخدم",
"recreateDBIndexes": "إعادة إنشاء فهارس قاعدة البيانات",
"recreateDBIndexesToast": "تمت إعادة إنشاء فهارس قاعدة البيانات",
"recreateDBIndexesDialogTitle": "سيتم إعادة إنشاء جميع فهارس قاعدة البيانات.\nقد يستغرق هذا بعض الوقت.",
"recreateDBIndexesDesc": "يعيد إنشاء فهارس قاعدة البيانات. قد يؤدي هذا إلى تحسين الأداء على الأجهزة الأبطأ."
"recreateDBIndexesDesc": "يعيد إنشاء فهارس قاعدة البيانات. قد يؤدي هذا إلى تحسين الأداء على الأجهزة الأبطأ.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "مظهر",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "الافتراضي",
"appTheme": "مظهر التطبيق",
"darkTheme": "المظهر الداكن",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "إخفاء الخلفية",
"lightTheme": "المظهر الفاتح",
"navbar": "شريط التنقل",
"novelInfo": "معلومات الرواية",
"pureBlackDarkMode": "الأسود القاتم للوضع الداكن",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "إظهار التاريخ في شريط التنقل",
"showUpdatesInTheNav": "إظهار التحديثات في شريط التنقل",
"themeMode": "وضع السمة",
@@ -75,7 +86,7 @@
"backupCreated": "Backup created successfully",
"backupRestored": "Backup restored successfully",
"savingBackup": "Saving Backup",
"categoriesRestored": "",
"categoriesRestored": "Restored %{count} categories",
"categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)",
"categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}",
"categoryFileNotFound": "Category file not found in backup",
@@ -136,30 +147,58 @@
"available": "متاح",
"deletePluginMessage": "هل أنت متأكد أنك تريد إلغاء تثبيت %{name}؟",
"discover": "استكشف",
"editPluginSetting": "Edit %{name}",
"globalSearch": "بحث عام",
"installPlugin": "Install %{name}",
"installFailed": "فشل التثبيت: %{name}",
"installed": "تم التثبيت",
"installedPlugin": "تم تثبيت %{name}",
"installedPlugins": "الإضافات المثبتة",
"language": "Language",
"lastUsed": "اخر استخدام",
"latest": "الاخير",
"listEmpty": "تفعيل اللغات من الاعدادات",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "تم تثبيت %{name} في الأعلى",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "الإضافات المثبتة في الأعلى",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "تمت إزالته من المكتبة",
"repositories": "Repositories",
"searchbar": "البحث في المصادر",
"searchResults": "نتائج البحث",
"selectNovel": "اختر الرواية",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "تم إلغاء تثبيت %{name}",
"unpinnedPlugin": "تم إلغاء التثبيت في الأعلى لـ %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "فشل التحديث",
"updatesPending": "Updates pending",
"updatedTo": "تم التحديث إلى %{version}",
"settings": {
"title": "إعدادات الإضافة",
@@ -196,6 +235,7 @@
"about": "حول",
"add": "إضافة",
"all": "الكل",
"back": "Back",
"backup": "نسخة احتياطية",
"cancel": "إلغاء",
"categories": "تصنيفات",
@@ -213,10 +253,13 @@
"filter": "تصفية",
"globally": "عالمياً",
"install": "تثبيت",
"imported": "Imported",
"logout": "تسجيل الخروج",
"name": "اسم",
"newUpdateAvailable": "تحديث جديد متاح",
"none": "None",
"ok": "حسنآ",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "أعد المحاولة",
"save": "حفظ",
"saved": "Saved",
"search": "بحث",
"searchFor": "بحث عن",
"searchResults": "نتائج البحث",
@@ -235,11 +279,13 @@
"sort": "فرز",
"submit": "إرسال",
"loading": "جارٍ التحميل...",
"moreOptions": "More options",
"warning": "تحذير"
},
"webview": {
"refresh": "تحديث",
"share": "مشاركة",
"openInWebView": "Open in WebView",
"openInBrowser": "فتح في المتصفح",
"clearCookies": "مسح ملفات تعريف الارتباط",
"cookiesCleared": "تم مسح ملفات تعريف الارتباط",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "إما أن الفصل فارغ أو لم يتمكن التطبيق من كشطه",
"chapterName": "الفصل: %{name}",
"completed": "اكتمل التنزيل",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "جارٍ التنزيل",
"downloadingNovel": "جارٍ تنزيل: %{name}",
"downloadsLower": "التنزيلات",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(ترتيب تصاعدي)",
"autoDownload": "التحميل التلقائي",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "حسب المصدر",
"chapterSort": "الفرز الافتراضي للفصول",
"desc": "(ترتيب تنازلي)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "قد يحسن الأداء على الأجهزة الأبطأ",
"disableHapticFeedback": "تعطيل الاهتزاز",
"disableHapticFeedbackDescription": "إيقاف الاهتزازات عند اللمس.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "وضع العرض",
"downloadNewChapters": "تنزيل فصول جديدة",
"epub": "EPUB",
"epubLocation": "موقع EPUB",
"epubLocationDescription": "المكان الذي تفتح فيه وتصدر إليه ملفات EPUB الخاصة بك.",
"globalUpdate": "تحديث شامل",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "حجم الشبكة",
"gridSizeDesc": "%{num} في كل صف",
"itemsPerRow": "العناصر في كل صف",
@@ -294,11 +357,21 @@
"novelSort": "فرز الروايات",
"refreshMetadata": "تحديث البيانات الوصفية تلقائياً",
"refreshMetadataDescription": "التحقق من الغلاف الجديد والتفاصيل عند تحديث المكتبة",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "ترتيب الفرز",
"updateLibrary": "تحديث المكتبة عند التشغيل",
"updateLibraryDesc": "لا ينصح به للأجهزة الضعيفة",
"updateTime": "إظهار وقت آخر تحديث",
"useFAB": "استخدام الزر العائم (FAB) في المكتبة"
"useFAB": "استخدام الزر العائم (FAB) في المكتبة",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "جميع المصادر",
@@ -310,6 +383,8 @@
"clearHistorWarning": "هل أنت متأكد؟ سيتم مسح كل السجل.",
"deleted": "تم حذف السجل.",
"nothingReadRecently": "لم يتم قراءة شيء مؤخراً",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "سجل البحث"
},
"library": "المكتبة",
@@ -346,6 +421,7 @@
}
},
"empty": "مكتبتك فارغة. أضف سلاسل إلى مكتبتك من التصفح.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "استيراد EPUB",
"openRandom": "فتح مدخل عشوائي",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "محفوظ",
"downloaded": "منزل",
"unread": "غير مقروء"
"unread": "غير مقروء",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "حسب اسم الفصل",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "فشل تصدير EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "تم تصدير %{chapters} فصلاً بنجاح كملف EPUB",
"noDownloadedChapters": "لم يتم العثور على فصول محملة. يرجى تحميل الفصول قبل التصدير.",
"noNovelSelected": "لم يتم اختيار رواية للتصدير"
"noNovelSelected": "لم يتم اختيار رواية للتصدير",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "اسم الفصل",
"chapterNumber": "رقم الفصل",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "أدخل اسم فصل صحيح",
"validChapterNumber": "أدخل رَقْم فصل صحيح"
},
"jumpToChapter": "انتقل إلى الفصل",
"openChapter": "فتح الفصل"
"openChapter": "فتح الفصل",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "نقل",
"noSummary": "لا يوجد ملخص",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "تحديد النص",
"autoscroll": "تمرير تلقائي",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "قراءة بيونية",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "النقر للتمرير",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "اللون",
"display": "Display",
"fontStyle": "نمط الخط",
"fullscreen": "ملء الشاشة",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "ارتفاع السطر",
"navigation": "Navigation",
"padding": "تباعد",
"pageReader": "قراءة بصفحات (تجريبي)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "إزالة المسافات الزائدة",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "مقدار التمرير (افتراضياً طول الشاشة)",
"showBatteryAndTime": "البطارية والوقت",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "تقدم القراءة",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "السحب بين الفصول",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "محاذاة النص",
"textSize": "حجم الخط",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "السحب لفتح الدرج",
"verticalSeekbar": "شريط تمرير رأسي",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "إبقاء الشاشة مضاءة",
"volumeButtonsScroll": "التمرير بأزرار الصوت"
"volumeButtonsScroll": "التمرير بأزرار الصوت",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "التمرير للأسفل",
@@ -489,7 +598,9 @@
"finished": "انتهى",
"nextChapter": "التالي: %{name}",
"noNextChapter": "لا يوجد فصل تالي",
"noPreviousChapter": "لا يوجد فصل سابق"
"noPreviousChapter": "لا يوجد فصل سابق",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "فاصل التمرير (ثواني)",
@@ -514,7 +625,8 @@
"textColor": "لون النص",
"textColorModal": "لون النص",
"title": "القارئ",
"verticalSeekbarDesc": "استخدم شريط تمرير رأسي"
"verticalSeekbarDesc": "استخدم شريط تمرير رأسي",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "لم يتم العثور على أي نتائج"
@@ -528,10 +640,16 @@
"title": "الإحصائيات",
"titlesInLibrary": "العناوين في المكتبة",
"totalChapters": "إجمالي الفصول",
"unreadChapters": "الفصول غير المقروءة"
"unreadChapters": "الفصول غير المقروءة",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "التتبع",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "تسجيل الخروج من %{name}؟",
"revalidate": "إعادة التحقق",
"services": "الخدمات"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "جاري استيراد EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "جاري تحديث المكتبة",
"DRIVE_BACKUP": "نسخ احتياطي لجوجل درايف",
"DRIVE_RESTORE": "استعادة من جوجل درايف",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "استعادة من نسخة احتياطية محلية",
"MIGRATE_NOVEL": "جاري نقل الرواية",
"DOWNLOAD_CHAPTER": "جاري تنزيل الفصل"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "আবিষ্কাৰ কৰক",
"editPluginSetting": "Edit %{name}",
"globalSearch": "বিশ্বব্যাপী সন্ধান",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "শেষত ব্যৱহৃত",
"latest": "সাম্প্ৰতিক",
"listEmpty": "ছেটিংছৰ পৰা ভাষা সক্ৰিয় কৰক",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "পুথিভঁৰালৰ পৰা আঁতৰ কৰা হৈছে",
"repositories": "Repositories",
"searchbar": "উৎসত সন্ধান কৰক",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "যোগ কৰক",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "বাতিল",
"categories": "শ্ৰেণীবিভাজন",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "বিশ্বব্যাপী",
"install": "স্থাপন",
"imported": "Imported",
"logout": "Logout",
"name": "নাম",
"newUpdateAvailable": "নতুন আপডেট উপলব্ধ",
"none": "None",
"ok": "হ’ব",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "পুনৰ চেষ্টা কৰক",
"save": "সংৰক্ষণ",
"saved": "Saved",
"search": "সন্ধান",
"searchFor": "সন্ধান কৰক",
"searchResults": "সন্ধান ফলাফল",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "জমা দিয়ক",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "ডাউনলোডসমূহ",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "সকলো উৎস",
@@ -310,6 +383,8 @@
"clearHistorWarning": "আপুনি নিশ্চিত? সকলো ইতিহাস হেৰাই যাব।",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "ইতিহাসত সন্ধান কৰক"
},
"library": "পুথিভঁৰাল",
@@ -346,6 +421,7 @@
}
},
"empty": "আপোনাৰ পুথিভঁৰাল খালী। ‘ব্ৰাউজ’ৰ পৰা শৃংখলা যোগ কৰক।",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "অধ্যায়ৰ নাম",
"chapterNumber": "অধ্যায় নম্বৰ",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "বৈধ অধ্যায়ৰ নাম দিন",
"validChapterNumber": "বৈধ অধ্যায় নম্বৰ দিন"
},
"jumpToChapter": "অধ্যায়লৈ যাওক",
"openChapter": "অধ্যায় খুলক"
"openChapter": "অধ্যায় খুলক",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "স্থানান্তৰিত কৰক",
"noSummary": "কোনো সংক্ষিপ্তসাৰ নাই",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "ৰঙ",
"display": "Display",
"fontStyle": "ফণ্টৰ শৈলী",
"fullscreen": "সম্পূৰ্ণপর্দা",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "স্ক্ৰল পৰিমাণ (প্ৰদৰ্শন উচ্চতা অনুযায়ী)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "আখৰৰ আকাৰ",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "তললৈ স্ক্ৰল কৰক",
@@ -489,7 +598,9 @@
"finished": "শেষ",
"nextChapter": "Next: %{name}",
"noNextChapter": "পৰবর্তী অধ্যায় নাই",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "কোনো ফলাফল পোৱা নাযায়"
@@ -528,10 +640,16 @@
"title": "পৰিসংখ্যা",
"titlesInLibrary": "গ্ৰন্থাগাৰত শিৰোনামসমূহ",
"totalChapters": "মুঠ অধ্যায়সমূহ",
"unreadChapters": "নপঢ়া অধ্যায়সমূহ"
"unreadChapters": "নপঢ়া অধ্যায়সমূহ",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Objevit",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Globální vyhledávání",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Naposledy použité",
"latest": "Poslední",
"listEmpty": "Povolit jazyky z nastavení",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Odstraněno z knihovny",
"repositories": "Repositories",
"searchbar": "Zdroje hledání",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Přidat",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Zrušit",
"categories": "Kategorie",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globálně",
"install": "Instalovat",
"imported": "Imported",
"logout": "Logout",
"name": "Jméno",
"newUpdateAvailable": "Nová aktualizace k dispozici",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Opakovat",
"save": "Uložit",
"saved": "Saved",
"search": "Hledat",
"searchFor": "Hledat",
"searchResults": "Výsledky hledání",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Odeslat",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "stažené",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "všechny zdroje",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Jste si jistí? Celá historie bude stracena.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Historie hledání"
},
"library": "Knihovna",
@@ -346,6 +421,7 @@
}
},
"empty": "Vaše knihovna je prázdná. Přidejte sérii do knihovny z procházet.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Název kapitoly",
"chapterNumber": "Číslo kapitoly",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Zadejte platný název kapitoly",
"validChapterNumber": "Zadejte platné číslo kapitoly"
},
"jumpToChapter": "Přeskočit na kapitolu",
"openChapter": "Otevřít kapitolu"
"openChapter": "Otevřít kapitolu",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrovat",
"noSummary": "Žádné shrnutí",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Barva",
"display": "Display",
"fontStyle": "Styl písma",
"fullscreen": "Celá obrazovka",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Vzdálenost posunu (jako výchozí velikost výška obrazovky)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Velikost textu",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Přejít na konec",
@@ -489,7 +598,9 @@
"finished": "Dokončeno",
"nextChapter": "Next: %{name}",
"noNextChapter": "Nejsou žádné další kapitoly",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Nebyly nalezeny žádné výsledky"
@@ -528,10 +640,16 @@
"title": "Statistiky",
"titlesInLibrary": "Názvy v knihovně",
"totalChapters": "Kapitol dohromady",
"unreadChapters": "Nepřečtené kapitoly"
"unreadChapters": "Nepřečtené kapitoly",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Gelesene Kapitel löschen",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Standard",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Entdecke",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Globale Suche",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Zuletzt genutzt",
"latest": "Aktuell",
"listEmpty": "Sprachen in Einstellungen aktivieren",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Aus der Bibliothek entfernt",
"repositories": "Repositories",
"searchbar": "Quellen durchsuchen",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Hinzufügen",
"all": "Alle",
"back": "Back",
"backup": "Backup",
"cancel": "Abbrechen",
"categories": "Kategorien",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "global",
"install": "Installieren",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "Neues Update verfügbar",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Erneut versuchen",
"save": "Sichern",
"saved": "Saved",
"search": "Suchen",
"searchFor": "Suche nach",
"searchResults": "Suchergebnisse",
@@ -235,11 +279,13 @@
"sort": "Sortieren",
"submit": "Bestätigen",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "Downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Aufsteigend)",
"autoDownload": "Automatisch herunterladen",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Nach Quelle",
"chapterSort": "Standard Kapitel-Sortierung",
"desc": "(Absteigend)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Haptisches Feedback deaktivieren",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Anzeigemodus",
"downloadNewChapters": "Neue Kapitel herunterladen",
"epub": "EPUB",
"epubLocation": "EPUB Speicherort",
"epubLocationDescription": "Der Speicherort, an dem Sie Ihre EPUB Dateien öffnen und exportieren.",
"globalUpdate": "Globale Aktualisierung",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Bücher pro Reihe",
@@ -294,11 +357,21 @@
"novelSort": "Bücher Sortierung",
"refreshMetadata": "Metadaten automatisch aktualisieren",
"refreshMetadataDescription": "Suche nach neuen Cover und Details beim Aktualisieren der Bibliothek",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Bibliothek beim Starten aktualisieren",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Zeige den Zeitpunkt der letzten Aktualisierung",
"useFAB": "FAB in der Bibliothek verwenden"
"useFAB": "FAB in der Bibliothek verwenden",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "Alle Quellen",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Bist du sicher? Der gesamte Verlauf geht verloren.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Suchverlauf"
},
"library": "Bibliothek",
@@ -346,6 +421,7 @@
}
},
"empty": "Deine Bibliothek ist leer. Füge Serien über Browse zu deiner Bibliothek hinzu.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Kapitelname",
"chapterNumber": "Kapitelnummer",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Geben Sie einen gültigen Kapitelnamen ein",
"validChapterNumber": "Geben Sie eine gültige Kapitelnummer ein"
},
"jumpToChapter": "Zum Kapitel springen",
"openChapter": "Kapitel öffnen"
"openChapter": "Kapitel öffnen",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrieren",
"noSummary": "Keine Beschreibung",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Farbe",
"display": "Display",
"fontStyle": "Schriftart",
"fullscreen": "Vollbildmodus",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Höhe des Bildlaufs (standardmäßig Bildschirmhöhe)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Schriftgröße",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Zum Ende springen",
@@ -489,7 +598,9 @@
"finished": "Beendet",
"nextChapter": "Next: %{name}",
"noNextChapter": "Es gibt kein weiteres Kapitel",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Textfarbe",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Keine Ergebnisse gefunden"
@@ -528,10 +640,16 @@
"title": "Statistiken",
"titlesInLibrary": "Bücher in der Bibliothek",
"totalChapters": "Gesamte Kapitel",
"unreadChapters": "Ungelesene Kapitel"
"unreadChapters": "Ungelesene Kapitel",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Τελευταία χρήση",
"latest": "Latest",
"listEmpty": "Ενεργοποιήστε γλώσσες από τις ρυθμίσεις",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Καταργήθηκε από τη βιβλιοθήκη",
"repositories": "Repositories",
"searchbar": "Αναζήτηση πηγών",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Προσθήκη",
"all": "Όλα",
"back": "Back",
"backup": "Backup",
"cancel": "Ακύρωση",
"categories": "Κατηγορίες",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Εγκατάσταση",
"imported": "Imported",
"logout": "Logout",
"name": "Όνομα",
"newUpdateAvailable": "Νέα ενημέρωση διαθέσιμη",
"none": "None",
"ok": "Οκ",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Επανάληψη",
"save": "Αποθήκευση",
"saved": "Saved",
"search": "Αναζήτηση",
"searchFor": "Αναζήτηση για",
"searchResults": "Αποτελέσματα αναζήτησης",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Υποβολή",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Είστε σίγουροι; Όλο το ιστορικό θα χαθεί.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Βιβλιοθήκη",
@@ -346,6 +421,7 @@
}
},
"empty": "Η βιβλιοθήκη σας είναι άδεια. Προσθέστε σειρές στη βιβλιοθήκη σας από την Περιήγηση.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Χρώμα",
"display": "Display",
"fontStyle": "Στυλ γραμματοσειράς",
"fullscreen": "Πλήρης Οθόνη",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Μέγεθος κειμένου",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Κύλιση προς τα κάτω",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "Δεν υπάρχει επόμενο κεφάλαιο",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+54 -1
View File
@@ -84,10 +84,24 @@
"backupScreen": {
"backupName": "Backup name",
"backupCreated": "Backup created successfully",
"backupCreatedWithWarnings": "Backup created with warnings: %{warnings}",
"backupRestored": "Backup restored successfully",
"backupRestoredSelectedWithWarnings": "Backup restored with warnings: %{warnings}",
"backupRestoredSummary": {
"one": "Restored %{count} novel",
"other": "Restored %{count} novels"
},
"backupRestoredWithWarnings": {
"one": "Restored %{count} novel with warnings: %{warnings}",
"other": "Restored %{count} novels with warnings: %{warnings}"
},
"savingBackup": "Saving Backup",
"categoriesRestored": "Restored %{count} categories",
"categoriesRestoredWithErrors": "Restored %{count} categories (%{failedCount} failed)",
"categoriesRestoreFailedSummary": {
"one": "%{count} category failed",
"other": "%{count} categories failed"
},
"categoryRestoreFailed": "Failed to restore category: %{categoryName} - %{error}",
"categoryFileNotFound": "Category file not found in backup",
"categoryFileReadFailed": "Failed to read category file: %{error}",
@@ -100,14 +114,34 @@
"failed": "Backup failed",
"novelsRestored": "Restored %{count} novels",
"novelsRestoredWithErrors": "Restored %{count} novels (%{failedCount} failed)",
"novelsBackupFailedSummary": {
"one": "%{count} novel failed",
"other": "%{count} novels failed"
},
"novelsRestoreFailedSummary": {
"one": "%{count} novel failed",
"other": "%{count} novels failed"
},
"novelBackupFailed": "Failed to backup novel: %{novelName} - %{error}",
"novelRestoreFailed": "Failed to restore novel: %{novelName} - %{error}",
"novelDirectoryNotFound": "Novel directory not found in backup",
"novelDirectoryReadFailed": "Failed to read novel directory: %{error}",
"restoringCategories": "Restoring Categories",
"restoringCategoriesProgress": "Restoring categories (%{current}/%{total})",
"restoringNovels": "Restoring Novels",
"restoringNovelsProgress": "Restoring novels (%{current}/%{total})",
"restoringSettings": "Restoring Settings",
"missingPluginsAfterRestore": "Missing plugins: %{plugins}",
"sectionsRestoreFailedSummary": {
"one": "%{count} backup section failed",
"other": "%{count} backup sections failed"
},
"sectionsBackupFailedSummary": {
"one": "%{count} backup section failed",
"other": "%{count} backup sections failed"
},
"settingsRestored": "Settings restored",
"settingsRestoreFailedSummary": "settings failed",
"settingsFileNotFound": "Settings file not found in backup",
"settingsRestoreFailed": "Failed to restore settings: %{error}",
"settingsFileWriteFailed": "Failed to write settings file: %{error}",
@@ -124,7 +158,21 @@
"invalidBackupFolder": "Invalid backup folder",
"localBackup": "Local Backup",
"noBackupFound": "No backup found",
"options": {
"title": "Backup contents",
"description": "Choose what to include in this backup.",
"selectAll": "Select all",
"library": "Library data",
"libraryDescription": "Novels, chapter lists, reading progress, bookmarks, and categories.",
"settings": "Settings",
"settingsDescription": "App, reader, and update preferences.",
"plugins": "Plugins",
"pluginsDescription": "Installed source plugin files.",
"downloadedFiles": "Downloaded chapters and covers",
"downloadedFilesDescription": "Downloaded chapter content and saved novel covers. This may be large."
},
"preparingData": "Preparing Data",
"preparingSelectedFiles": "Preparing selected files",
"remote": {
"backup": "Self Host Backup",
"host": "Host",
@@ -136,9 +184,11 @@
"restoreLargeBackupsWarning": "Restoring large backups may freeze the app until restoring is finished",
"restorinBackup": "Restoring backup",
"restoringData": "Restoring Data",
"restoringSelectedFiles": "Restoring selected files",
"selfHost": "Self Host",
"selfHostDesc": "Backup to your server",
"uploadingData": "Uploading Data",
"uploadingSelectedFiles": "Uploading selected files",
"uploadingDownloadedFiles": "Uploading Downloaded Files"
},
"browse": "Browse",
@@ -686,7 +736,10 @@
"LOCAL_BACKUP": "Local Backup",
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
"DOWNLOAD_CHAPTER": "Downloading Chapter",
"taskQueued": "%{task} queued",
"taskCompleted": "Completed",
"taskFailed": "Failed: %{error}"
},
"time": {
"seconds": {
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Administración de datos",
"deleteReadChapters": "Eliminar capítulos leídos",
"deleteReadChaptersDialogTitle": "Todos los capítulos marcados como leídos serán borrados.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Importar EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Importar novela",
"importStaticFiles": "Importar archivos estáticos",
"novelInsertFailed": "inserción de novela fallida",
"networking": "Networking",
"useFAB": "Usar FAB en lugar del botón",
"userAgent": "Agente de Usuario",
"recreateDBIndexes": "Recrear índices de la Base de Datos",
"recreateDBIndexesToast": "Índices de la Base de Datos Recreados",
"recreateDBIndexesDialogTitle": "Todos los índices de la base de datos serán Recreados. Esto puede tomar un tiempo.",
"recreateDBIndexesDesc": "Recrea los índices de la Base de Datos. Esto puede mejorar el rendimiento en dispositivos más lentos."
"recreateDBIndexesDesc": "Recrea los índices de la Base de Datos. Esto puede mejorar el rendimiento en dispositivos más lentos.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Apariencia",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "Tema de la aplicación",
"darkTheme": "Tema Oscuro",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Ocultar fondo",
"lightTheme": "Tema Claro",
"navbar": "Barra de navegación",
"novelInfo": "Información de la Novela",
"pureBlackDarkMode": "Modo negro puro",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Mostrar historial en el navegador",
"showUpdatesInTheNav": "Mostrar actualizaciones en el navegador",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Disponible",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Explorar",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Búsqueda global",
"installPlugin": "Install %{name}",
"installFailed": "Instalación fallida: %{name}",
"installed": "Instalado",
"installedPlugin": "%{name} Instalado",
"installedPlugins": "Complementos instalados",
"language": "Language",
"lastUsed": "Última usada",
"latest": "Últimos",
"listEmpty": "Habilitar idiomas desde la configuración",
"migration": {
"cover": "Cover",
"dialogMessage": "¿Migrar %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "La novela ya está en la biblioteca",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Seleccionar fuente",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Eliminar de la biblioteca",
"repositories": "Repositories",
"searchbar": "Buscar fuente",
"searchResults": "Search results",
"selectNovel": "Seleccionar novela",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Desinstalado %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Actualización fallida",
"updatesPending": "Updates pending",
"updatedTo": "Actualizado a %{version}",
"settings": {
"title": "Ajustes del plugin",
@@ -196,6 +235,7 @@
"about": "Acerca de",
"add": "Añadir",
"all": "Todos",
"back": "Back",
"backup": "Copia de seguridad",
"cancel": "Cancelar",
"categories": "Categorías",
@@ -213,10 +253,13 @@
"filter": "Filtrar",
"globally": "globalmente",
"install": "Instalar",
"imported": "Imported",
"logout": "Cerrar sesión",
"name": "Nombre",
"newUpdateAvailable": "Nueva actualización disponible",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pausa",
"preparing": "Preparando",
"remove": "Eliminar",
@@ -225,6 +268,7 @@
"resume": "Reanudar",
"retry": "Reintentar",
"save": "Guardar",
"saved": "Saved",
"search": "Buscar",
"searchFor": "Buscar por",
"searchResults": "Resultados de la búsqueda",
@@ -235,11 +279,13 @@
"sort": "Ordenar",
"submit": "Enviar",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "descargas",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascendente)",
"autoDownload": "Descarga automática",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Por fuente",
"chapterSort": "Orden de capítulo por defecto",
"desc": "(Descendente)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Deshabilitar la respuesta háptica",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Modo de visualización",
"downloadNewChapters": "Descargar nuevos capítulos",
"epub": "EPUB",
"epubLocation": "Ubicación EPUB",
"epubLocationDescription": "El lugar donde se abren y exportan sus archivos EPUB.",
"globalUpdate": "Actualización global",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Elementos por fila",
@@ -294,11 +357,21 @@
"novelSort": "Ordenar novela",
"refreshMetadata": "Actualizar automáticamente los metadatos",
"refreshMetadataDescription": "Buscar nueva portada y detalles al actualizar la biblioteca",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Actualizar biblioteca al iniciar",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Mostrar la última actualización",
"useFAB": "Usar FAB en la biblioteca"
"useFAB": "Usar FAB en la biblioteca",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "Todas las fuentes",
@@ -310,6 +383,8 @@
"clearHistorWarning": "¿Estás seguro? Todo el historial se perderá.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Historial de búsqueda"
},
"library": "Biblioteca",
@@ -346,6 +421,7 @@
}
},
"empty": "Tu biblioteca está vacía. Añade series a tu biblioteca desde el navegador.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Nombre del capítulo",
"chapterNumber": "Número del capítulo",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Introduzca un nombre de capítulo válido",
"validChapterNumber": "Introduzca un número de capítulo válido"
},
"jumpToChapter": "Saltar al capítulo",
"openChapter": "Abrir Capítulo"
"openChapter": "Abrir Capítulo",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrar",
"noSummary": "Sin resumen",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Estilo de fuente",
"fullscreen": "Pantalla completa",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Cantidad de desplazamiento (alto de la pantalla de forma predeterminada)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Tamaño del texto",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Desplazarse hasta abajo",
@@ -489,7 +598,9 @@
"finished": "Finalizado",
"nextChapter": "Next: %{name}",
"noNextChapter": "No hay próximo capítulo",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Color del texto",
"textColorModal": "Text color",
"title": "Lector",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No se han encontrado resultados"
@@ -528,10 +640,16 @@
"title": "Estadísticas",
"titlesInLibrary": "Títulos en biblioteca",
"totalChapters": "Capítulos totales",
"unreadChapters": "Capítulos no leídos"
"unreadChapters": "Capítulos no leídos",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+151 -14
View File
@@ -13,7 +13,7 @@
"cachedNovelsDeletedToast": "Romans en cache supprimés",
"chapterInsertFailed": "Insertion de chapitre échoué",
"clearCachedNovels": "Vider les romans en cache",
"clearCachedNovelsDesc": "",
"clearCachedNovelsDesc": "Delete cached novels which not in your library",
"clearDatabaseWarning": "Les progrès et les chapitres lus et téléchargés hors de la bibliothèque, seront perdus.",
"clearUpdatesMessage": "Mises à jour effacées.",
"clearUpdatesTab": "Effacer l'onglet Mises à jour",
@@ -22,16 +22,22 @@
"dataManagement": "Gestion de données",
"deleteReadChapters": "Supprimer les chapitres lus",
"deleteReadChaptersDialogTitle": "Tous les chapitres marqués comme lus seront supprimés.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Importer EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Importer un roman",
"importStaticFiles": "Importer les fichiers statiques",
"novelInsertFailed": "Insertion du roman échouée",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "Agent utilisateur",
"recreateDBIndexes": "Récréer les index de la base de donnée",
"recreateDBIndexesToast": "Index de la base de données recréés",
"recreateDBIndexesDialogTitle": "Tous les index de la base de données seront recréés.\nCela peut prendre un certain temps.",
"recreateDBIndexesDesc": "Recrée les index de la base de données. Cela peut améliorer les performances sur les appareils plus lents."
"recreateDBIndexesDesc": "Recrée les index de la base de données. Cela peut améliorer les performances sur les appareils plus lents.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Apparence",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Par défaut",
"appTheme": "Thème",
"darkTheme": "Sombre",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Masquer l'arrière-plan",
"lightTheme": "Clair",
"navbar": "Barre de navigation",
"novelInfo": "Informations sur le roman",
"pureBlackDarkMode": "Mode noir pur",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Afficher l'historique dans la navigation",
"showUpdatesInTheNav": "Afficher les mises à jour dans la navigation",
"themeMode": "Thème",
@@ -136,30 +147,58 @@
"available": "Disponible",
"deletePluginMessage": "Êtes-vous sûr de vouloir désinstaller %{name}?",
"discover": "Découvrir",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Recherche globale",
"installPlugin": "Install %{name}",
"installFailed": "L'installation a échoué: %{name}",
"installed": "Installé",
"installedPlugin": "Installé %{name}",
"installedPlugins": "Plugins installés",
"language": "Language",
"lastUsed": "Dernière utilisation",
"latest": "Dernier",
"listEmpty": "Activer les langues à partir des paramètres",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Sélectionner la source",
"selectSourceDesc": "Sélectionnez une source à partir de laquelle migrer"
"selectSourceDesc": "Sélectionnez une source à partir de laquelle migrer",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Épinglé %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Plugins épinglés",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Supprimé de la bibliothèque",
"repositories": "Repositories",
"searchbar": "Rechercher des sources",
"searchResults": "Résultats de la recherche",
"selectNovel": "Sélectionnez un roman",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Désinstallé %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Mise à jour échouée",
"updatesPending": "Updates pending",
"updatedTo": "Mis à jour %{version}",
"settings": {
"title": "Paramètres des plugins",
@@ -196,6 +235,7 @@
"about": "À propos",
"add": "Ajouter",
"all": "Tout",
"back": "Back",
"backup": "Sauvegarde",
"cancel": "Annuler",
"categories": "Catégories",
@@ -213,10 +253,13 @@
"filter": "Filtre",
"globally": "globalement",
"install": "Installer",
"imported": "Imported",
"logout": "Se déconnecter",
"name": "Nom",
"newUpdateAvailable": "Nouvelle mise à jour disponible",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Mettre en pause",
"preparing": "Preparing",
"remove": "Retirer",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Réessayer",
"save": "Sauvegarder",
"saved": "Saved",
"search": "Recherchez",
"searchFor": "Rechercher",
"searchResults": "Résultats de la recherche",
@@ -235,11 +279,13 @@
"sort": "Trier",
"submit": "Soumettre",
"loading": "Chargement ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Actualiser",
"share": "Partager",
"openInWebView": "Open in WebView",
"openInBrowser": "Ouvrir dans le navigateur",
"clearCookies": "Effacer les cookies",
"cookiesCleared": "Cookies effacés",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapitre : %{name}",
"completed": "Téléchargement terminé",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Téléchargement",
"downloadingNovel": "Téléchargement : %{name}",
"downloadsLower": "télécharger",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascendant)",
"autoDownload": "Téléchargement automatique",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Par Source",
"chapterSort": "Tri des chapitres par défaut",
"desc": "(Descendant)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Désactiver le retour haptique",
"disableHapticFeedbackDescription": "Désactiver les vibrations pour les interactions tactiles.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Mode d'affichage",
"downloadNewChapters": "Télécharger les nouveaux chapitres",
"epub": "EPUB",
"epubLocation": "Localisation EPUB",
"epubLocationDescription": "L'endroit où vous ouvrez et exportez vos fichiers EPUB.",
"globalUpdate": "Mise à jour globale",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Taille de la grille",
"gridSizeDesc": "%{num} par ligne",
"itemsPerRow": "Élément par ligne",
@@ -294,11 +357,21 @@
"novelSort": "Trier les romans",
"refreshMetadata": "Rafraîchir automatiquement les métadonnées",
"refreshMetadataDescription": "Vérifier la nouvelle couverture et les détails lors de la mise à jour de la bibliothèque",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Ordre de tri",
"updateLibrary": "Mettre à jour la bibliothèque au lancement",
"updateLibraryDesc": "Non recommandé pour les appareils ????",
"updateTime": "Afficher la date de dernière mise à jour",
"useFAB": "Utiliser FAB dans la bibliothèque"
"useFAB": "Utiliser FAB dans la bibliothèque",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "toutes les sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Êtes-vous sûr? Tout l'historique sera perdu.",
"deleted": "Historique supprimé.",
"nothingReadRecently": "Rien lu récemment",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Historique des recherches"
},
"library": "Bibliothèque",
@@ -346,6 +421,7 @@
}
},
"empty": "Votre bibliothèque est vide. Ajouter des séries à votre bibliothèque à partir de Parcourir",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Importer un EPUB",
"openRandom": "Ouvrir une série au hasard",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Signet",
"downloaded": "Téléchargé",
"unread": "Non lu"
"unread": "Non lu",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "Par nom de chapitre",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Le JS personnalisé peut ne pas être pris en charge par tous les lecteurs EPUB",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "Fin du chapitre",
"export": "Export",
"exportAll": "Exporter tous les chapitres",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Inclure le CSS personnalisé",
"includeCustomJS": "Inclure le JS personnalisé",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Veuillez saisir un numéro de chapitre valide",
"selectFolder": "Sélectionnez le dossier de destination pour le fichier EPUB",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Impossible d'exporter l'EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Couverture sauvegardée",
"coverNotSaved": "Couverture non sauvegardée",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Nom du Chapitre",
"chapterNumber": "Numéro du chapitre",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Entrez un nom de chapitre valide",
"validChapterNumber": "Entrez un numéro de chapitre valide"
},
"jumpToChapter": "Aller au chapitre",
"openChapter": "Ouvrir le chapitre"
"openChapter": "Ouvrir le chapitre",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrer",
"noSummary": "Aucun résumé",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Sélection de texte",
"autoscroll": "Défilement automatique",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Couleur",
"display": "Display",
"fontStyle": "Style de police",
"fullscreen": "Plein écran",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Hauteur de la ligne",
"navigation": "Navigation",
"padding": "Marge intérieure",
"pageReader": "Lecture paginée (Expérimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Défilement du montant (hauteur de l'écran par défaut)",
"showBatteryAndTime": "Batterie et Heure",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Progression de la lecture",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Balayer entre les chapitres",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Alignement du texte",
"textSize": "Taille du texte",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Bouton de volume pour défilé"
"volumeButtonsScroll": "Bouton de volume pour défilé",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Défiler jusqu'en bas",
@@ -489,7 +598,9 @@
"finished": "Terminé",
"nextChapter": "Suivant: %{name}",
"noNextChapter": "Il n'y a pas de prochain chapitre",
"noPreviousChapter": "Il n'y a pas de chapitre précédent"
"noPreviousChapter": "Il n'y a pas de chapitre précédent",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Couleur du texte",
"textColorModal": "Text color",
"title": "Lecteur",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Aucun résultat trouvé"
@@ -528,10 +640,16 @@
"title": "Statistiques",
"titlesInLibrary": "Titres dans la bibliothèque",
"totalChapters": "Nombre total de chapitres",
"unreadChapters": "Chapitres non lus"
"unreadChapters": "Chapitres non lus",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Suivi",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Se déconnecter de %{name} ?",
"revalidate": "Revalider",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "ניהול מידע",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "יבוא רומן",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "תצוגה",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "ערכת נושא כהה",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "ערכת נושא בהירה",
"navbar": "סרגל הניווט",
"novelInfo": "מידע על הרומן",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "הראה היסטוריה בסרגל הניווט",
"showUpdatesInTheNav": "הראה עדכונים בסרגל הניווט",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Olvasott fejezetek törlése",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Felfedezés",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Globális keresés",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Utoljára használt",
"latest": "Legújabb",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Eltávolítva a könyvtárból",
"repositories": "Repositories",
"searchbar": "Források keresése",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Hozzáadás",
"all": "Összes",
"back": "Back",
"backup": "Backup",
"cancel": "Mégse",
"categories": "Kategóriák",
@@ -213,10 +253,13 @@
"filter": "Szűrő",
"globally": "globálisan",
"install": "Telepítés",
"imported": "Imported",
"logout": "Logout",
"name": "Név",
"newUpdateAvailable": "Új frissítés érhető el",
"none": "None",
"ok": "OK",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Újrapróbálkozás",
"save": "Mentés",
"saved": "Saved",
"search": "Keresés",
"searchFor": "Keresés a következőre",
"searchResults": "Keresési eredmények",
@@ -235,11 +279,13 @@
"sort": "Rendezés",
"submit": "Küldés",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "letöltések",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Növekvő)",
"autoDownload": "Automatikus letöltés",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Forrás szerint",
"chapterSort": "Default chapter sort",
"desc": "(Csökkenő)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Haptikus visszajelzés kikapcsolása",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Megjelenítési mód",
"downloadNewChapters": "Új fejezetek letöltése",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Globális frissítés",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Soronkénti elemek",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Metaadatok automatikus frissítése",
"refreshMetadataDescription": "Új borító és leírás keresése a könyvtár frissítésekor",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Könyvtár frissítése indításkor",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Utolsó frissítés idejének mutatása",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "összes forrás",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Biztos vagy benne? Minden előzmény törlődni fog.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Keresési előzmények"
},
"library": "Könyvtár",
@@ -346,6 +421,7 @@
}
},
"empty": "A könyvtárad üres. Adj sorozatokat a könyvtáradhoz a Böngészés menüben.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Fejezet címe",
"chapterNumber": "Fejezet száma",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Adj meg egy létező fejezetcímet",
"validChapterNumber": "Adj meg egy létező fejezetszámot"
},
"jumpToChapter": "Fejezethez ugrás",
"openChapter": "Fejezet megnyitása"
"openChapter": "Fejezet megnyitása",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Áttelepítés",
"noSummary": "Nincs összefoglaló",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Szín",
"display": "Display",
"fontStyle": "Betűtípus",
"fullscreen": "Teljes képernyő",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Görgetés mértéke (alapértelmezetten a képernyő magassága)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Betűméret",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Végére ugrás",
@@ -489,7 +598,9 @@
"finished": "Befejezett",
"nextChapter": "Next: %{name}",
"noNextChapter": "Nincs következő fejezet",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Betűszín",
"textColorModal": "Text color",
"title": "Olvasó",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Nincs találat"
@@ -528,10 +640,16 @@
"title": "Statisztikák",
"titlesInLibrary": "Címek a könyvtárban",
"totalChapters": "Összes fejezetek száma",
"unreadChapters": "Olvasatlan fejezetek"
"unreadChapters": "Olvasatlan fejezetek",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
File diff suppressed because one or more lines are too long
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Elimina capitoli letti",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Scopri",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Ricerca globale",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Ultima usata",
"latest": "Ultimi",
"listEmpty": "Abilita le lingue dalle impostazioni",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Rimosso dalla libreria",
"repositories": "Repositories",
"searchbar": "Cerca fonti",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Aggiungi",
"all": "Tutte",
"back": "Back",
"backup": "Backup",
"cancel": "Annulla",
"categories": "Categorie",
@@ -213,10 +253,13 @@
"filter": "Filtra",
"globally": "globale",
"install": "Installa",
"imported": "Imported",
"logout": "Logout",
"name": "Nome",
"newUpdateAvailable": "Nuovo aggiornamento disponibile",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Riprova",
"save": "Salva",
"saved": "Saved",
"search": "Cerca",
"searchFor": "Cerca per",
"searchResults": "Risultati della ricerca",
@@ -235,11 +279,13 @@
"sort": "Ordina",
"submit": "Invia",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "Scaricamenti",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Crescente)",
"autoDownload": "Scarica automaticamente",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Per fonte",
"chapterSort": "Ordinamento predefinito dei capitoli",
"desc": "(Decrescente)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disattiva vibrazione",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Modalità di visualizzazione",
"downloadNewChapters": "Scarica nuovi capitoli",
"epub": "EPUB",
"epubLocation": "Posizione EPUB",
"epubLocationDescription": "Il posto dove apri ed esporti i tuoi file EPUB.",
"globalUpdate": "Aggiornamento globale",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Elementi per riga",
@@ -294,11 +357,21 @@
"novelSort": "Ordine novel",
"refreshMetadata": "Aggiorna automaticamente i metadati",
"refreshMetadataDescription": "Controlla nuove copertine e dettagli durante l'aggiornamento della libreria",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Aggiorna la libreria all'avvio",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Mostra l'orario dell'ultimo aggiornamento",
"useFAB": "Usa FAB nella libreria"
"useFAB": "Usa FAB nella libreria",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "Tutte le fonti",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Sei sicuro? Tutta la cronologia sarà persa.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Cerca nella cronologia"
},
"library": "Libreria",
@@ -346,6 +421,7 @@
}
},
"empty": "La tua libreria è vuota. Aggiungi delle serie alla tua libreria da Esplora.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Nome Capitolo",
"chapterNumber": "Numero del capitolo",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Inserisci un nome di capitolo valido",
"validChapterNumber": "Inserisci un numero di capitolo valido"
},
"jumpToChapter": "Vai al Capitolo",
"openChapter": "Apri Capitolo"
"openChapter": "Apri Capitolo",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migra",
"noSummary": "Nessuna trama",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Colore",
"display": "Display",
"fontStyle": "Stile del font",
"fullscreen": "Schermo Intero",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Quantità di scorrimento (altezza schermo di default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Dimensione del testo",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scorri in fondo",
@@ -489,7 +598,9 @@
"finished": "Finito",
"nextChapter": "Next: %{name}",
"noNextChapter": "Non c'è un capitolo successivo",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Colore del testo",
"textColorModal": "Text color",
"title": "Lettore",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Nessun risultato trovato"
@@ -528,10 +640,16 @@
"title": "Statistiche",
"titlesInLibrary": "Titoli nella libreria",
"totalChapters": "Totale capitoli",
"unreadChapters": "Capitoli non letti"
"unreadChapters": "Capitoli non letti",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "グローバル検索",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "設定から言語を有効にする",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "ライブラリから削除",
"repositories": "Repositories",
"searchbar": "ソースを検索",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "追加",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "キャンセル",
"categories": "カテゴリー",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "世界的に",
"install": "インストール",
"imported": "Imported",
"logout": "Logout",
"name": "名前",
"newUpdateAvailable": "新しいアップデートが見つかりました",
"none": "None",
"ok": "わかりました",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "保存",
"saved": "Saved",
"search": "検索",
"searchFor": "検索する",
"searchResults": "検索結果",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "本当によろしいですか?すべての履歴は失われます。",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "閲覧履歴"
},
"library": "ライブラリ",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "移行",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "フォントスタイル",
"fullscreen": "全画面",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "文字サイズ",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "次の章はありません",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+151 -14
View File
@@ -1,6 +1,6 @@
{
"aboutScreen": {
"website": "",
"website": "Website",
"discord": "Discord",
"github": "Github",
"helpTranslate": "Help translate",
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Verwijder gelezen hoofdstukken",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Ontdek",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Globaal zoeken",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Laatst gebruikt",
"latest": "Recentst",
"listEmpty": "Schakel talen in via instellingen",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Verwijderd uit bibliotheek",
"repositories": "Repositories",
"searchbar": "Zoek bronnen",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Voeg toe",
"all": "Alle",
"back": "Back",
"backup": "Backup",
"cancel": "Annuleren",
"categories": "Categorieën",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globaal",
"install": "Installeer",
"imported": "Imported",
"logout": "Logout",
"name": "Naam",
"newUpdateAvailable": "Nieuwe update beschikbaar",
"none": "None",
"ok": "Oké",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Opslaan",
"saved": "Saved",
"search": "Zoeken",
"searchFor": "Zoek naar",
"searchResults": "Zoekresultaten",
@@ -235,11 +279,13 @@
"sort": "Sorteer",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "alle bronnen",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Weet je het zeker? Alle geschiedenis zal verloren gaan.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Zoek geschiedenis"
},
"library": "Bibliotheek",
@@ -346,6 +421,7 @@
}
},
"empty": "Je bibliotheek is leeg. Voeg series toe aan je bibliotheek vanuit Bladeren.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migreer",
"noSummary": "Geen samenvatting",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Kleur",
"display": "Display",
"fontStyle": "Lettertype",
"fullscreen": "Volledig scherm",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Tekstgrootte",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Voltooid",
"nextChapter": "Next: %{name}",
"noNextChapter": "Er is geen volgend hoofdstuk",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Tekstkleur",
"textColorModal": "Text color",
"title": "Lezer",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Geen resultaten gevonden"
@@ -528,10 +640,16 @@
"title": "Statistieken",
"titlesInLibrary": "Titels in bibliotheek",
"totalChapters": "Total chapters",
"unreadChapters": "Ongelezen hoofdstukken"
"unreadChapters": "Ongelezen hoofdstukken",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "ଯୋଡ଼ନ୍ତୁ",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "ବର୍ଗ",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "ନାମ",
"newUpdateAvailable": "ନୂଆ ଅଦ୍ୟତନ ଉପଲବ୍ଧ",
"none": "None",
"ok": "ଠିକ୍ ଅଛି",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "ପୁନଃ ଚେଷ୍ଟା କରିବା",
"save": "ସଞ୍ଚୟ କରିବା",
"saved": "Saved",
"search": "ସନ୍ଧାନ",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+154 -17
View File
@@ -22,16 +22,22 @@
"dataManagement": "Manejo de informação",
"deleteReadChapters": "Excluir capítulos lidos",
"deleteReadChaptersDialogTitle": "Você tem certeza? Todos os capítulos marcados como lidos serão excluídos.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Importar EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Importar livro",
"importStaticFiles": "Importar arquivo estático",
"novelInsertFailed": "falha ao inserir livro",
"networking": "Networking",
"useFAB": "Utilize FAV invez do botão",
"userAgent": "Agente usuário",
"recreateDBIndexes": "Recriar índices do BD",
"recreateDBIndexesToast": "Índices do BD foi recriado",
"recreateDBIndexesDialogTitle": "Todos os índices do BD vai ser Recriado.\nIsso pode demorar um pouco.",
"recreateDBIndexesDesc": "Recrie os índices do BD. Isso pode melhorar o desempenho em dispositivos mais lentos."
"recreateDBIndexesDesc": "Recrie os índices do BD. Isso pode melhorar o desempenho em dispositivos mais lentos.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Aparência",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Padrão",
"appTheme": "Tema do aplicativo",
"darkTheme": "Tema escuro",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Esconder fundo",
"lightTheme": "Tema claro",
"navbar": "Barra de navegação",
"novelInfo": "Informação do livro",
"pureBlackDarkMode": "Modo escuridão",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Mostrar histórico no navegador",
"showUpdatesInTheNav": "Mostrar novidades no navegador",
"themeMode": "Aparência",
@@ -66,7 +77,7 @@
"teal": "Ciano",
"turquoise": "Turquesa",
"yotsuba": "Japonês",
"catppuccin": "",
"catppuccin": "Catppuccin",
"yinyang": "Yin & Yang"
}
},
@@ -104,11 +115,11 @@
"drive": {
"backup": "Salvar no Drive",
"backupInterruped": "Backup do Drive interrompido",
"googleDriveBackup": "",
"restore": "",
"googleDriveBackup": "Google Drive Backup",
"restore": "Drive Restore",
"restoreInterruped": "Restauração do Drive interrompida"
},
"googeDrive": "",
"googeDrive": "Googe Drive",
"googeDriveDesc": "Fazer backup no Google Drive",
"invalidBackupFolder": "Pasta de backup inválida",
"localBackup": "Backup local",
@@ -136,30 +147,58 @@
"available": "Disponível",
"deletePluginMessage": "Tem certeza de que deseja desinstalar o %{name}?",
"discover": "Descubra",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Pesquisa global",
"installPlugin": "Install %{name}",
"installFailed": "A instalação falhou: %{name}",
"installed": "Instalado",
"installedPlugin": "%{name} Instalado",
"installedPlugins": "Extensões Instalado",
"language": "Language",
"lastUsed": "Usado por último",
"latest": "Mais recentes",
"listEmpty": "Habilitar idiomas a partir das configurações",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrar %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel já está na biblioteca",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Selecionar fonte",
"selectSourceDesc": "Selecione uma fonte para migrar"
"selectSourceDesc": "Selecione uma fonte para migrar",
"useDestination": "Use destination source"
},
"noSource": "Sua biblioteca não tem nenhuma novel desta fonte",
"pinnedPlugin": "%{name} fixado",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Extensões fixadas",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removido da biblioteca",
"repositories": "Repositories",
"searchbar": "Pesquisar nas fontes",
"searchResults": "Resultados da pesquisa",
"selectNovel": "Selecionar novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "%{name} desinstalada",
"unpinnedPlugin": "%{name} desfixada",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Atualização falhou",
"updatesPending": "Updates pending",
"updatedTo": "Atualizado para %{version}",
"settings": {
"title": "Configurações da extensão",
@@ -196,6 +235,7 @@
"about": "Sobre",
"add": "Adicionar",
"all": "Todos",
"back": "Back",
"backup": "Salvo",
"cancel": "Cancelar",
"categories": "Categorias",
@@ -213,10 +253,13 @@
"filter": "Filtro",
"globally": "globalmente",
"install": "Instalar",
"imported": "Imported",
"logout": "Desconectar",
"name": "Nome",
"newUpdateAvailable": "Nova atualização disponível",
"none": "None",
"ok": "Confirmar",
"close": "Close",
"pause": "Pausar",
"preparing": "Preparando",
"remove": "Remover",
@@ -225,6 +268,7 @@
"resume": "Continuar",
"retry": "Tente novamente",
"save": "Salvar",
"saved": "Saved",
"search": "Pesquisar",
"searchFor": "Pesquisar por",
"searchResults": "Resultados da busca",
@@ -235,11 +279,13 @@
"sort": "Ordenar por",
"submit": "Aplicar",
"loading": "Carregando...",
"moreOptions": "More options",
"warning": "Aviso"
},
"webview": {
"refresh": "Rever",
"share": "Compartilhar",
"openInWebView": "Open in WebView",
"openInBrowser": "Abrir na internet",
"clearCookies": "Limpar dados temporários",
"cookiesCleared": "Dados temporários limpos",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Capítulo: %{name}",
"completed": "Transferência completa",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Transferindo",
"downloadingNovel": "Transferindo: %{name}",
"downloadsLower": "baixados",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Crescente)",
"autoDownload": "Download automático",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Por fonte",
"chapterSort": "Ordem padrão dos capítulos",
"desc": "(Decrescente)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "Pode aumentar o desempenho em dispositivos mais lentos",
"disableHapticFeedback": "Desativar o feedback tátil",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Modo de exibição",
"downloadNewChapters": "Baixar novos capítulos",
"epub": "EPUB",
"epubLocation": "Localização do EPUB",
"epubLocationDescription": "O local onde você abre e exporta seus arquivos EPUB.",
"globalUpdate": "Atualização global",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Itens por linha",
@@ -294,11 +357,21 @@
"novelSort": "Ordenar Novel",
"refreshMetadata": "Atualizar os metadados automaticamente",
"refreshMetadataDescription": "Verificar por novas capas e detalhes durante a atualização da biblioteca",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Ordenar",
"updateLibrary": "Atualizar biblioteca ao iniciar",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Mostrar horário da última atualização",
"useFAB": "Usar Botões flutuantes na Biblioteca"
"useFAB": "Usar Botões flutuantes na Biblioteca",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "todas as fontes",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Você tem certeza? Todo o histórico será perdido.",
"deleted": "Histórico deletado",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Histórico de pesquisa"
},
"library": "Biblioteca",
@@ -346,6 +421,7 @@
}
},
"empty": "Sua biblioteca está vazia. Adicione séries à sua biblioteca pelo Navegador.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Importar EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Favoritado",
"downloaded": "Transferido",
"unread": "Não lido"
"unread": "Não lido",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "Por nome do capítulo",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "Finalizar capítulo",
"export": "Export",
"exportAll": "Exportar todos os capítulos",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Incluir CSS personalizado",
"includeCustomJS": "Incluir JS personalizado",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Por favor, insira um número válido de capítulos",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Iniciar capítulo",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Nome do capítulo",
"chapterNumber": "Número do Capítulo",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Insira um nome de capítulo válido",
"validChapterNumber": "Insira um número de capítulo válido"
},
"jumpToChapter": "Pular para o Capítulo",
"openChapter": "Abrir Capítulo"
"openChapter": "Abrir Capítulo",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrar",
"noSummary": "Nenhum resumo",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Cor",
"display": "Display",
"fontStyle": "Estilo da fonte",
"fullscreen": "Tela cheia",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Valor da rolagem (Padrão: altura da tela)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Tamanho do texto",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Rolar para o final",
@@ -489,7 +598,9 @@
"finished": "Finalizado",
"nextChapter": "Next: %{name}",
"noNextChapter": "Não há próximo capítulo",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Cor do Texto",
"textColorModal": "Text color",
"title": "Leitor",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Nenhum resultado encontrado"
@@ -528,10 +640,16 @@
"title": "Estatísticas",
"titlesInLibrary": "Títulos na biblioteca",
"totalChapters": "Total de capítulos",
"unreadChapters": "Capítulos não lidos"
"unreadChapters": "Capítulos não lidos",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Управление данными",
"deleteReadChapters": "Удалять прочитанные главы",
"deleteReadChaptersDialogTitle": "Вы уверены? Все главы, помеченные как прочитанные, будут удалены.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Импорт EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Импорт новеллы",
"importStaticFiles": "Импорт файлов статики",
"novelInsertFailed": "ошибка вставки главы",
"networking": "Networking",
"useFAB": "Использовать плавающую кнопку действия вместо основной кнопки",
"userAgent": "User Agent",
"recreateDBIndexes": "Пересоздать индексы базы данных",
"recreateDBIndexesToast": "Индексы базы данных пересозданы",
"recreateDBIndexesDialogTitle": "Все индексы базы данных будут пересозданы.\nЭто может занять некоторое время.",
"recreateDBIndexesDesc": "Пересоздаёт индексы базы данных. Это может улучшить производительность на слабых устройствах."
"recreateDBIndexesDesc": "Пересоздаёт индексы базы данных. Это может улучшить производительность на слабых устройствах.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Внешний вид",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "По умолчанию",
"appTheme": "Тема приложения",
"darkTheme": "Тёмная тема",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Скрыть фон",
"lightTheme": "Светлая тема",
"navbar": "Панель навигации",
"novelInfo": "Информация о новелле",
"pureBlackDarkMode": "Режим с чистым чёрным",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Показать историю в навигации",
"showUpdatesInTheNav": "Показать историю в навигации",
"themeMode": "Тема оформления",
@@ -136,30 +147,58 @@
"available": "Расширения",
"deletePluginMessage": "Вы уверены, что хотите удалить %{name}?",
"discover": "Узнайте больше",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Глобальный поиск",
"installPlugin": "Install %{name}",
"installFailed": "Не удалось установить: %{name}",
"installed": "Установлено",
"installedPlugin": "Установлено %{name}",
"installedPlugins": "Установленные плагины",
"language": "Language",
"lastUsed": "Последнее использование",
"latest": "Последние",
"listEmpty": "Включите языки в настройках",
"migration": {
"cover": "Cover",
"dialogMessage": "Перенести %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Новелла уже в библиотеке",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Выберите источник",
"selectSourceDesc": "Выберите источник для переноса из"
"selectSourceDesc": "Выберите источник для переноса из",
"useDestination": "Use destination source"
},
"noSource": "В вашей библиотеке нет новелл из этого источника",
"pinnedPlugin": "Закреплён %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Закреплено",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Удалено из библиотеки",
"repositories": "Repositories",
"searchbar": "Поиск источника",
"searchResults": "Результаты поиска",
"selectNovel": "Выбрать новеллу",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Удалено %{name}",
"unpinnedPlugin": "Откреплено %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Ошибка обновления",
"updatesPending": "Updates pending",
"updatedTo": "Обновлено до %{version}",
"settings": {
"title": "Настройки плагина",
@@ -196,6 +235,7 @@
"about": "О программе",
"add": "Добавить",
"all": "Все",
"back": "Back",
"backup": "Резервная копия",
"cancel": "Отменить",
"categories": "Категории",
@@ -213,10 +253,13 @@
"filter": "Фильтр",
"globally": "глобально",
"install": "Установить",
"imported": "Imported",
"logout": "Выйти",
"name": "Название",
"newUpdateAvailable": "Доступно новое обновление",
"none": "None",
"ok": "Ок",
"close": "Close",
"pause": "Пауза",
"preparing": "Подготовка",
"remove": "Удалить",
@@ -225,6 +268,7 @@
"resume": "Продолжить",
"retry": "Повторить",
"save": "Сохранить",
"saved": "Saved",
"search": "Поиск",
"searchFor": "Искать",
"searchResults": "Результаты поиска",
@@ -235,11 +279,13 @@
"sort": "Сортировка",
"submit": "Продолжить",
"loading": "Загрузка...",
"moreOptions": "More options",
"warning": "Внимание"
},
"webview": {
"refresh": "Обновить",
"share": "Поделиться",
"openInWebView": "Open in WebView",
"openInBrowser": "Открыть в браузере",
"clearCookies": "Очистить cookies",
"cookiesCleared": "Cookies очищены",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Либо глава пуста, либо приложению не удалось ее обработать",
"chapterName": "Глава: %{name}",
"completed": "Загрузка завершена",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Загрузка",
"downloadingNovel": "Загружается %{name}",
"downloadsLower": "Загруженное",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(По возрастанию)",
"autoDownload": "Автозагрузка",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "По источникам",
"chapterSort": "Сортировка глав по умолчанию",
"desc": "(По убыванию)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "Может повысить производительность на медленных устройствах",
"disableHapticFeedback": "Отключить виброотклик",
"disableHapticFeedbackDescription": "Выключает вибрации для взаимодействия с касаниями.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Режим Отображения",
"downloadNewChapters": "Скачивать новые главы",
"epub": "EPUB",
"epubLocation": "Расположение EPUB",
"epubLocationDescription": "Место открытия и экспорта файлов EPUB.",
"globalUpdate": "Обновить всё",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Размер сетки",
"gridSizeDesc": "%{num} в ряд",
"itemsPerRow": "элемента в ряду",
@@ -294,11 +357,21 @@
"novelSort": "Сортировка тайтлов",
"refreshMetadata": "Автоматически обновлять метаданные",
"refreshMetadataDescription": "Проверять наличие новой обложки и описания при обновлении библиотеки",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Порядок сортировки",
"updateLibrary": "Обновлять библиотеку при запуске",
"updateLibraryDesc": "Не рекомендуется для слабых устройств",
"updateTime": "Показать время последнего обновления",
"useFAB": "Использовать FAB в библиотеке"
"useFAB": "Использовать FAB в библиотеке",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "все источники",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Вы уверены? Вся история будет потеряна.",
"deleted": "История удалена.",
"nothingReadRecently": "Ничего не прочитано в последнее время",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Поиск в истории"
},
"library": "Библиотека",
@@ -346,6 +421,7 @@
}
},
"empty": "Библиотека пуста. Добавьте новеллы из меню «Источники».",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Импорт EPUB",
"openRandom": "Открыть случайную серию",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "В закладках",
"downloaded": "Загружено",
"unread": "Не прочитано"
"unread": "Не прочитано",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "По названию главы",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Применить тему чтения к EPUB",
"customJSWarning": "Пользовательский JS может поддерживаться не всеми программами чтения EPUB",
"downloadedChaptersOnly": "Только загруженные главы будут включены в EPUB файл",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Экспорт всех глав",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Включить пользовательский CSS",
"includeCustomJS": "Включить пользовательские JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Пожалуйста, введите правильный номер главы",
"selectFolder": "Выберите папку для файла EPUB",
"startChapter": "Начать главу",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Не удалось экспортировать EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Успешно экспортировано %{chapters} глав в EPUB",
"noDownloadedChapters": "Загруженных глав не найдено. Пожалуйста, загрузите главы перед экспортом.",
"noNovelSelected": "Ни одна новелла не выбрана для экспорта"
"noNovelSelected": "Ни одна новелла не выбрана для экспорта",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Обложка сохранена",
"coverNotSaved": "Обложка не сохранена",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Название главы",
"chapterNumber": "Номер главы",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Введите допустимое название главы",
"validChapterNumber": "Введите действительный номер главы"
},
"jumpToChapter": "Перейти к главе",
"openChapter": "Открыть главу"
"openChapter": "Открыть главу",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Перенос",
"noSummary": "Нет сводки",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Выбор текста",
"autoscroll": "Автопрокрутка",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Бионическое чтение",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Коснитесь для прокрутки",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Цвет",
"display": "Display",
"fontStyle": "Шрифт",
"fullscreen": "Полноэкранный",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Высота строки",
"navigation": "Navigation",
"padding": "Отступ",
"pageReader": "Постраничное чтение (экспериментально)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Удалить лишние пробелы",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Смещение автопрокрутки (высота экрана по умолчанию)",
"showBatteryAndTime": "Батарея и время",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Прогресс чтения",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Свайп между главами",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Выравнивание текста",
"textSize": "Размер текста",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Вертикальная панель навигации",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Не выключать экран",
"volumeButtonsScroll": "Прокрутка кноками громкости"
"volumeButtonsScroll": "Прокрутка кноками громкости",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Прокрутить вниз",
@@ -489,7 +598,9 @@
"finished": "Завершено",
"nextChapter": "%{name}Следующий: %{name}",
"noNextChapter": "Следующей главы нет",
"noPreviousChapter": "Предыдущей главы нет"
"noPreviousChapter": "Предыдущей главы нет",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Интервал прокрутки (секунды)",
@@ -514,7 +625,8 @@
"textColor": "Цвет текста",
"textColorModal": "Цвет текста",
"title": "Чтение",
"verticalSeekbarDesc": "Использовать вертикальную панель навигации"
"verticalSeekbarDesc": "Использовать вертикальную панель навигации",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Ничего не найдено"
@@ -528,10 +640,16 @@
"title": "Статистика",
"titlesInLibrary": "Названия в библиотеке",
"totalChapters": "Всего глав",
"unreadChapters": "Непрочитанные главы"
"unreadChapters": "Непрочитанные главы",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Отслеживание",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Выйти из %{name}?",
"revalidate": "Подтвердите еще раз",
"services": "Сервисы отслеживания"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Импортирование EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Обновляем библиотеку",
"DRIVE_BACKUP": "Резервное копирование на Google Диск",
"DRIVE_RESTORE": "Восстановление Google Drive",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Локальное восстановление",
"MIGRATE_NOVEL": "Миграция новелл",
"DOWNLOAD_CHAPTER": "Загруженные главы"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Discover",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Global search",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "Last used",
"latest": "Latest",
"listEmpty": "Enable languages from settings",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Removed from library",
"repositories": "Repositories",
"searchbar": "Search sources",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "Add",
"all": "All",
"back": "Back",
"backup": "Backup",
"cancel": "Cancel",
"categories": "Categories",
@@ -213,10 +253,13 @@
"filter": "Filter",
"globally": "globally",
"install": "Install",
"imported": "Imported",
"logout": "Logout",
"name": "Name",
"newUpdateAvailable": "New update available",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "Retry",
"save": "Save",
"saved": "Saved",
"search": "Search",
"searchFor": "Search for",
"searchResults": "Search results",
@@ -235,11 +279,13 @@
"sort": "Sort",
"submit": "Submit",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "downloads",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "all sources",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Are you sure? All history will be lost.",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Search history"
},
"library": "Library",
@@ -346,6 +421,7 @@
}
},
"empty": "Your library is empty. Add series to your library from Browse.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Chapter Name",
"chapterNumber": "Chapter Number",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Enter a valid chapter name",
"validChapterNumber": "Enter a valid chapter number"
},
"jumpToChapter": "Jump to Chapter",
"openChapter": "Open Chapter"
"openChapter": "Open Chapter",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Migrate",
"noSummary": "No summary",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Color",
"display": "Display",
"fontStyle": "Font style",
"fullscreen": "Fullscreen",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Text size",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Scroll to bottom",
@@ -489,7 +598,9 @@
"finished": "Finished",
"nextChapter": "Next: %{name}",
"noNextChapter": "There's no next chapter",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Text color",
"textColorModal": "Text color",
"title": "Reader",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "No results found"
@@ -528,10 +640,16 @@
"title": "Statistics",
"titlesInLibrary": "Titles in library",
"totalChapters": "Total chapters",
"unreadChapters": "Unread chapters"
"unreadChapters": "Unread chapters",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Veri Yönetimi",
"deleteReadChapters": "Okunmuş bölümleri sil",
"deleteReadChaptersDialogTitle": "Okunmuş olarak işaretlenmiş bütün bölümler silinecek.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "EPUB içeri aktar",
"importFailed": "Error importing EPUB:",
"importNovel": "Romanları içeri aktar",
"importStaticFiles": "İstatistik Dosyalarını İçeri Aktar",
"novelInsertFailed": "roman yüklemesi başarısız",
"networking": "Networking",
"useFAB": "Buton yerine FAB kullan",
"userAgent": "Kullanıcı aracısı",
"recreateDBIndexes": "Veritabanı indekskerini yenile",
"recreateDBIndexesToast": "Veritabanı indeksleri yenilendi",
"recreateDBIndexesDialogTitle": "Tüm veritabanı indeksleri yenilenecek. Bu biraz vakit alabilir.",
"recreateDBIndexesDesc": "Veritabanı indekslerini yeniler. Yavaş cihazlarda hız artışı sağlayabilir."
"recreateDBIndexesDesc": "Veritabanı indekslerini yeniler. Yavaş cihazlarda hız artışı sağlayabilir.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Görünüş",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "Uygulama teması",
"darkTheme": "Karanlık Tema",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Arka planı gizle",
"lightTheme": "Açık Tema",
"navbar": "Menü Çubuğu",
"novelInfo": "Roman hakkında",
"pureBlackDarkMode": "Tamamen siyah karanlık tema",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Navigasyonda geçmişi göster",
"showUpdatesInTheNav": "Menüde güncellemeleri göster",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Uzantılar",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Keşfet",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Genel arama",
"installPlugin": "Install %{name}",
"installFailed": "Yükleme hatası: %{name}",
"installed": "Yüklü",
"installedPlugin": "%{name} yüklendi",
"installedPlugins": "Yüklü uzantılar",
"language": "Language",
"lastUsed": "Son kullanılan",
"latest": "En son",
"listEmpty": "Ayarlardan dilleri etkinleştirin",
"migration": {
"cover": "Cover",
"dialogMessage": "%{url} birleştirilsin mi?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Roman kütüphanede mevcut",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Kaynak Seç",
"selectSourceDesc": "Taşıyacağınız Kaynağı Seçin"
"selectSourceDesc": "Taşıyacağınız Kaynağı Seçin",
"useDestination": "Use destination source"
},
"noSource": "Kütüphanenizde bu kaynaktan roman yok",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Kitaplıktan kaldırıldı",
"repositories": "Repositories",
"searchbar": "Kaynak ara",
"searchResults": "Search results",
"selectNovel": "Roman Seç",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "%{name} kaldırıldı",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Güncelleme başarısız",
"updatesPending": "Updates pending",
"updatedTo": "%{version}' güncellendi",
"settings": {
"title": "Uzantı Ayarları",
@@ -196,6 +235,7 @@
"about": "Hakkında",
"add": "Ekle",
"all": "Tümü",
"back": "Back",
"backup": "Yedek",
"cancel": "İptal",
"categories": "Kategoriler",
@@ -213,10 +253,13 @@
"filter": "Filtre",
"globally": "global olarak",
"install": "Yükle",
"imported": "Imported",
"logout": "Çıkış yap",
"name": "Ad",
"newUpdateAvailable": "Yeni güncelleme var",
"none": "None",
"ok": "Tamam",
"close": "Close",
"pause": "Duraklat",
"preparing": "Hazırlanıyor",
"remove": "Kaldır",
@@ -225,6 +268,7 @@
"resume": "Devam et",
"retry": "Yeniden dene",
"save": "Kaydet",
"saved": "Saved",
"search": "Ara",
"searchFor": "Şunu ara",
"searchResults": "Arama sonuçları",
@@ -235,11 +279,13 @@
"sort": "Sırala",
"submit": "Gönder",
"loading": "Yükleniyor ...",
"moreOptions": "More options",
"warning": "Dikkat"
},
"webview": {
"refresh": "Yenile",
"share": "Paylaş",
"openInWebView": "Open in WebView",
"openInBrowser": "Tarayıcıda Aç",
"clearCookies": "Çerezleri temizle",
"cookiesCleared": "Çerezler temizlendi",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Bölüm boş veya uygulama veriyi alamadı",
"chapterName": "Bölüm: %{name}",
"completed": "İndirme tamamlandı",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "İndiriliyor",
"downloadingNovel": "İndiriliyor: %{name}",
"downloadsLower": "İndirilenler",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Artan)",
"autoDownload": "Otomatik İndirme",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Kaynağa göre",
"chapterSort": "Varsayılan bölüm sıralaması",
"desc": "(Azalan)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "Yavaş cihazlarda performansı arttırabilir",
"disableHapticFeedback": "Dokunmatik geri bildirimi devre dışı bırak",
"disableHapticFeedbackDescription": "Dokunma geri bildirimleri için titreşimi devre dışı bırak.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Görünüm Modu",
"downloadNewChapters": "Yeni bölümleri indir",
"epub": "EPUB",
"epubLocation": "EPUB Konumu",
"epubLocationDescription": "EPUB dosyalarınızı açıp, dışa aktardığınız yer.",
"globalUpdate": "Global güncelleme",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Izgara boyutu",
"gridSizeDesc": "Her bir satırda %{num}",
"itemsPerRow": "Satır başına düşen öge",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sıralaması",
"refreshMetadata": "Üst veriyi otomatik olarak yenile",
"refreshMetadataDescription": "Kitaplığı güncellerken yeni kapak ve ayrıntıları denetle",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sıralama Düzeni",
"updateLibrary": "Açılışta kitaplığı güncelle",
"updateLibraryDesc": "Eski cihazlarda önerilmez",
"updateTime": "Son güncelleme süresini göster",
"useFAB": "Kitaplıkta FAB kullan"
"useFAB": "Kitaplıkta FAB kullan",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "tüm kaynaklarda",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Emin misin? Tüm geçmiş kaybolacak.",
"deleted": "Geçmiş silindi.",
"nothingReadRecently": "Geçmiş yok",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Geçmişte ara"
},
"library": "Kitaplık",
@@ -346,6 +421,7 @@
}
},
"empty": "Kitaplığın boş. Göz at yerinden kitaplığına light novel ekleyebilirsin.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "EPUB içe aktar",
"openRandom": "Rastgele Girdi Aç",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "İşaretlenmiş",
"downloaded": "İndirilmiş",
"unread": "Okunmamış"
"unread": "Okunmamış",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "Bölüm ismine göre",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Kapak kaydedildi",
"coverNotSaved": "Kapak kaydedilemedi",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Bölüm Adı",
"chapterNumber": "Bölüm Sayısı",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Geçerli bir bölüm adı girin",
"validChapterNumber": "Geçerli bir bölüm sayısı girin"
},
"jumpToChapter": "Bölüme Atla",
"openChapter": "Bölümü Aç"
"openChapter": "Bölümü Aç",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Taşın",
"noSummary": "Özet bilgisi yok",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Kaydırmak için tıkla",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Renk",
"display": "Display",
"fontStyle": "Yazı tipi",
"fullscreen": "Tam ekran",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Kenar boşluğu",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Kaydırma miktarı (varsayılan olarak ekran yüksekliği)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Yazı boyutu",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Ekranı açık tut",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Alta kaydır",
@@ -489,7 +598,9 @@
"finished": "Biten",
"nextChapter": "Sonraki: %{name}",
"noNextChapter": "Başka bölüm yok",
"noPreviousChapter": "Önceki bölüm yok"
"noPreviousChapter": "Önceki bölüm yok",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Yazı rengi",
"textColorModal": "Text color",
"title": "Okuyucu",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Sonuç bulunamadı"
@@ -528,10 +640,16 @@
"title": "İstatistikler",
"titlesInLibrary": "Kitaplıktaki başlık",
"totalChapters": "Toplam bölüm",
"unreadChapters": "Okunmamış bölüm"
"unreadChapters": "Okunmamış bölüm",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "İzleme",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "%{name}'tan çıkış yap?",
"revalidate": "Yenşden doğrula",
"services": "Servisler"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Керування даними",
"deleteReadChapters": "Видалити прочитані розділи",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "імпортувати EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Імпортувати новелу",
"importStaticFiles": "Імпортувати статичні файли",
"novelInsertFailed": "не вдалося вставити новелу",
"networking": "Networking",
"useFAB": "Використовувати FAB замість кнопки",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Зовнішній вигляд",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "Тема додатка",
"darkTheme": "Темна тема",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Приховати фон",
"lightTheme": "Світла тема",
"navbar": "Панель навігації",
"novelInfo": "Інформація про новелу",
"pureBlackDarkMode": "Режим Pure Black",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Показувати історію в навігації",
"showUpdatesInTheNav": "Показувати оновлення в навігації",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Доступно",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Огляд",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Глобальний пошук",
"installPlugin": "Install %{name}",
"installFailed": "Помилка встановлення: %{name}",
"installed": "Встановлено",
"installedPlugin": "Встановлено плагін: %{name}",
"installedPlugins": "Встановлені плагіни",
"language": "Language",
"lastUsed": "Останнє використане",
"latest": "Останні",
"listEmpty": "Увімкнути мови з налаштувань",
"migration": {
"cover": "Cover",
"dialogMessage": "Перенести дані з %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Новела вже є в бібліотеці",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Виберіть джерело",
"selectSourceDesc": "Виберіть джерело для перенесення"
"selectSourceDesc": "Виберіть джерело для перенесення",
"useDestination": "Use destination source"
},
"noSource": "У вашій бібліотеці немає новел з цього джерела",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Вилучено з бібліотеки",
"repositories": "Repositories",
"searchbar": "Шукати джерела",
"searchResults": "Search results",
"selectNovel": "Виберіть новелу",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Видалено плагін: %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Помилка оновлення",
"updatesPending": "Updates pending",
"updatedTo": "Оновлено до версії %{version}",
"settings": {
"title": "Налаштування плагінів",
@@ -196,6 +235,7 @@
"about": "Про додаток",
"add": "Додати",
"all": "Всі",
"back": "Back",
"backup": "Резервна копія",
"cancel": "Скасувати",
"categories": "Категорії",
@@ -213,10 +253,13 @@
"filter": "Фільтр",
"globally": "глобально",
"install": "Встановити",
"imported": "Imported",
"logout": "Вийти",
"name": "Назва",
"newUpdateAvailable": "Доступне нове оновлення",
"none": "None",
"ok": "Ок",
"close": "Close",
"pause": "Пауза",
"preparing": "Підготовка",
"remove": "Видалити",
@@ -225,6 +268,7 @@
"resume": "Продовжити",
"retry": "Повторити",
"save": "Зберегти",
"saved": "Saved",
"search": "Пошук",
"searchFor": "Шукати",
"searchResults": "Результати пошуку",
@@ -235,11 +279,13 @@
"sort": "Сортувати",
"submit": "Надіслати",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Оновити",
"share": "Поділитися",
"openInWebView": "Open in WebView",
"openInBrowser": "Відкрити в браузері",
"clearCookies": "Очистити куки",
"cookiesCleared": "Куки очищено",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Розділ порожній або додаток не зміг його отримати",
"chapterName": "Розділ: %{name}",
"completed": "Завантаження завершено",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Завантаження",
"downloadingNovel": "Завантаження: %{name}",
"downloadsLower": "завантаження",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(За зростанням)",
"autoDownload": "Автоматичне завантаження",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "За джерелом",
"chapterSort": "Сортування розділів за замовчуванням",
"desc": "(За спаданням)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "Може покращити продуктивність на повільних пристроях",
"disableHapticFeedback": "Вимкнути тактильний зворотний зв'язок",
"disableHapticFeedbackDescription": "Вимкнути вібрацію при дотиках.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Вигляд",
"downloadNewChapters": "Завантажувати нові розділи",
"epub": "EPUB",
"epubLocation": "Розташування EPUB",
"epubLocationDescription": "Місце, де ви відкриваєте та експортуєте свої EPUB-файли.",
"globalUpdate": "Глобальне оновлення",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Розмір сітки",
"gridSizeDesc": "%{num} в рядку",
"itemsPerRow": "Елементів у рядку",
@@ -294,11 +357,21 @@
"novelSort": "Сортування новел",
"refreshMetadata": "Автоматично оновлювати метадані",
"refreshMetadataDescription": "Перевіряти нові обкладинки та деталі при оновленні бібліотеки",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Порядок сортування",
"updateLibrary": "Оновлювати бібліотеку при запуску",
"updateLibraryDesc": "Не рекомендується для слабких пристроїв",
"updateTime": "Показувати час останнього оновлення",
"useFAB": "Використовувати FAB в бібліотеці"
"useFAB": "Використовувати FAB в бібліотеці",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "всі джерела",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Ви впевнені? Вся історія буде втрачена.",
"deleted": "Історію видалено.",
"nothingReadRecently": "Нічого не читали останнім часом",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Шукати в історії"
},
"library": "Бібліотека",
@@ -346,6 +421,7 @@
}
},
"empty": "Ваша бібліотека порожня. Додайте новели з Огляду.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Імпортувати EPUB",
"openRandom": "Відкрити випадковий запис",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Закладки",
"downloaded": "Завантажено",
"unread": "Не прочитано"
"unread": "Не прочитано",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "За назвою розділу",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Назва розділу",
"chapterNumber": "Номер розділу",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Введіть дійсну назву розділу",
"validChapterNumber": "Введіть дійсний номер розділу"
},
"jumpToChapter": "Перейти до Розділу",
"openChapter": "Відкрити Розділ"
"openChapter": "Відкрити Розділ",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Перенести",
"noSummary": "Немає зведених відомостей",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Натисніть для прокрутки",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Колір",
"display": "Display",
"fontStyle": "Стиль шрифту",
"fullscreen": "Повноекранний режим",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Відступи",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Обсяг прокручування (висота екрана за умовчанням)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Розмір тексту",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Не вимикати екран",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Прокрутити донизу",
@@ -489,7 +598,9 @@
"finished": "Завершено",
"nextChapter": "Наступний: %{name}",
"noNextChapter": "Наступний розділ не знайдено",
"noPreviousChapter": "Попереднього розділу немає"
"noPreviousChapter": "Попереднього розділу немає",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Колір тексту",
"textColorModal": "Text color",
"title": "Читалка",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Нічого не знайдено"
@@ -528,10 +640,16 @@
"title": "Статистика",
"titlesInLibrary": "Назви у бібліотеці",
"totalChapters": "Всього розділів",
"unreadChapters": "Непрочитані розділи"
"unreadChapters": "Непрочитані розділи",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Відстеження",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Вийти з %{name}?",
"revalidate": "Перевірити знову",
"services": "Сервіси"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Quản lý dữ liệu",
"deleteReadChapters": "Xóa chương đã đọc",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Nhập file EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Nhập truyện",
"importStaticFiles": "Nhập tệp tin tĩnh",
"novelInsertFailed": "lỗi khi thêm truyện",
"networking": "Networking",
"useFAB": "Sử dụng nút nổi",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Giao diện",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "Chủ đề",
"darkTheme": "Màu tối",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Ẩn bóng",
"lightTheme": "Màu sáng",
"navbar": "Thanh điều hướng",
"novelInfo": "Thông tin truyện",
"pureBlackDarkMode": "Màu đen tuyền",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Hiển thị mục lịch sử",
"showUpdatesInTheNav": "Hiển thị mục cập nhật",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Sẵn có",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "Khám phá",
"editPluginSetting": "Edit %{name}",
"globalSearch": "Tìm kiếm mở rộng",
"installPlugin": "Install %{name}",
"installFailed": "Cài đặt lỗi: %{name}",
"installed": "Đã cài đặt",
"installedPlugin": "Đã cài đặt %{name}",
"installedPlugins": "Nguồn đã cài đặt",
"language": "Language",
"lastUsed": "Lần sử dụng trước",
"latest": "Mới nhất",
"listEmpty": "Chọn ngôn ngữ",
"migration": {
"cover": "Cover",
"dialogMessage": "Chuyển đổi sang %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Truyện đã có trong thư viện",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Chọn nguồn",
"selectSourceDesc": "Chọn nguồn cần chuyển đổi"
"selectSourceDesc": "Chọn nguồn cần chuyển đổi",
"useDestination": "Use destination source"
},
"noSource": "Thư viện của bạn không có truyện nào từ nguồn này",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "Đã xoá khỏi thư viện",
"repositories": "Repositories",
"searchbar": "Tìm nguồn truyện",
"searchResults": "Search results",
"selectNovel": "Chọn truyện",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Gỡ cài đặt %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Cập nhật thất bại",
"updatesPending": "Updates pending",
"updatedTo": "Đã cập nhật lên %{version}",
"settings": {
"title": "Cài đặt nguồn",
@@ -196,6 +235,7 @@
"about": "Thông tin",
"add": "Thêm",
"all": "Tất cả",
"back": "Back",
"backup": "Sao lưu",
"cancel": "Hủy",
"categories": "Danh mục",
@@ -213,10 +253,13 @@
"filter": "Bộ lọc",
"globally": "toàn cầu",
"install": "Tải về",
"imported": "Imported",
"logout": "Đăng xuất",
"name": "Tên",
"newUpdateAvailable": "Đã có bản cập nhật mới",
"none": "None",
"ok": "Ok",
"close": "Close",
"pause": "Tạm dừng",
"preparing": "Đang chuẩn bị",
"remove": "Xoá",
@@ -225,6 +268,7 @@
"resume": "Tiếp tục",
"retry": "Thử lại",
"save": "Lưu",
"saved": "Saved",
"search": "Tìm kiếm",
"searchFor": "Tìm kiếm",
"searchResults": "Kết quả tìm kiếm",
@@ -235,11 +279,13 @@
"sort": "Sắp xếp",
"submit": "Gửi",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Làm mới",
"share": "Chia sẻ",
"openInWebView": "Open in WebView",
"openInBrowser": "Mở trong trình duyệt",
"clearCookies": "Xoá cookie",
"cookiesCleared": "Cookie đã được xoá",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Chương truyện trống hoặc ứng dụng không thể cào nó",
"chapterName": "Chapter: %{name}",
"completed": "Tải xuống hoàn thành",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Đang tải xuống",
"downloadingNovel": "Đang tải: %{name}",
"downloadsLower": "tải xuống",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Tăng dần)",
"autoDownload": "Tự động tải xuống",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "Theo nguồn",
"chapterSort": "Sắp xếp mặc định",
"desc": "(Giảm dần)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "Có thể cải thiện hiệu năng ở máy yếu",
"disableHapticFeedback": "Tắt phản hồi xúc giác",
"disableHapticFeedbackDescription": "Tắt rung khi chạm.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Chế độ Hiển thị",
"downloadNewChapters": "Tải chương mới",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Cập nhật toàn bộ",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Dạng lưới",
"gridSizeDesc": "%{num} truyện mỗi hàng",
"itemsPerRow": "Truyện trên hàng",
@@ -294,11 +357,21 @@
"novelSort": "Sắp xếp truyện",
"refreshMetadata": "Tự động làm mới metadata",
"refreshMetadataDescription": "Kiểm tra ảnh bìa và mô tả khi cập nhật thự viện",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Thứ tự sắp xếp",
"updateLibrary": "Cập nhật thư viện khi khởi động",
"updateLibraryDesc": "Không khuyến khích cho thiết bị yếu",
"updateTime": "Hiện lần cập nhật trước",
"useFAB": "Sử dụng FAB trong thư viện"
"useFAB": "Sử dụng FAB trong thư viện",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "toàn bộ nguồn",
@@ -310,6 +383,8 @@
"clearHistorWarning": "Bạn có chắc không? Toàn bộ lịch sử sẽ bị xoá.",
"deleted": "Lịch sử đã được xoá.",
"nothingReadRecently": "Không đọc gì gần đây",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "Tìm trong lịch sử"
},
"library": "Thư viện",
@@ -346,6 +421,7 @@
}
},
"empty": "Không có gì cả. Hãy thêm truyện vào thư viện từ mục Tìm kiếm.",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Nhập file EPUB",
"openRandom": "Mở truyện ngẫu nhiên",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Đã đánh dấu",
"downloaded": "Đã tải xuống",
"unread": "Chưa đọc"
"unread": "Chưa đọc",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "Theo tên chương",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "Tên Chương",
"chapterNumber": "Chương Số",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "Vui lòng nhập tên chương hợp lệ",
"validChapterNumber": "Vui lòng nhập số chương hợp lệ"
},
"jumpToChapter": "Đi tới Chương",
"openChapter": "Đọc luôn"
"openChapter": "Đọc luôn",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "Đồng bộ",
"noSummary": "Không có tóm tắt",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Ấn để cuộn",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "Màu chữ",
"display": "Display",
"fontStyle": "Phông chữ",
"fullscreen": "Toàn màn hình",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Khoảng đệm",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "Cỡ chữ",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Giữ màn hình luôn sáng",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "Xuống cuối",
@@ -489,7 +598,9 @@
"finished": "Đã xong",
"nextChapter": "Tiếp: %{name}",
"noNextChapter": "Không có chương kế tiếp",
"noPreviousChapter": "Không có chương kế tiếp"
"noPreviousChapter": "Không có chương kế tiếp",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "Màu chữ",
"textColorModal": "Text color",
"title": "Trang đọc",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "Không tìm thấy kết quả"
@@ -528,10 +640,16 @@
"title": "Thống kê",
"titlesInLibrary": "Tiêu đề",
"totalChapters": "Chương",
"unreadChapters": "Chương chưa đọc"
"unreadChapters": "Chương chưa đọc",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Theo dõi",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Đăng xuất khỏi %{name}?",
"revalidate": "Xác thực lại",
"services": "Dịch vụ"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "数据",
"deleteReadChapters": "删除已读章节",
"deleteReadChaptersDialogTitle": "所有标记为已读的章节将被删除。",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "导入 EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "导入小说",
"importStaticFiles": "导入静态文件",
"novelInsertFailed": "小说插入失败",
"networking": "Networking",
"useFAB": "使用浮动操作按钮",
"userAgent": "用户代理",
"recreateDBIndexes": "重建数据库索引",
"recreateDBIndexesToast": "数据库索引已重建",
"recreateDBIndexesDialogTitle": "所有数据库索引将被重建。\n此操作可能需要一些时间。",
"recreateDBIndexesDesc": "重建数据库索引,可提升低性能设备运行效率。"
"recreateDBIndexesDesc": "重建数据库索引,可提升低性能设备运行效率。",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "外观",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "默认",
"appTheme": "主题",
"darkTheme": "深色",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "隐藏背景",
"lightTheme": "浅色",
"navbar": "导航栏",
"novelInfo": "小说信息",
"pureBlackDarkMode": "纯黑深色模式",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "在导航中显示历史",
"showUpdatesInTheNav": "在导航中显示更新",
"themeMode": "主题模式",
@@ -136,30 +147,58 @@
"available": "可用",
"deletePluginMessage": "您确定要卸载 %{name} 吗?",
"discover": "发现",
"editPluginSetting": "Edit %{name}",
"globalSearch": "全局搜索",
"installPlugin": "Install %{name}",
"installFailed": "安装失败:%{name}",
"installed": "已安装",
"installedPlugin": "已安装 %{name}",
"installedPlugins": "已安装插件",
"language": "Language",
"lastUsed": "最近使用",
"latest": "最新",
"listEmpty": "从设置中启用语言",
"migration": {
"cover": "Cover",
"dialogMessage": "迁移 %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "小说已经在书库中",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "选择源",
"selectSourceDesc": "选择要迁移的源"
"selectSourceDesc": "选择要迁移的源",
"useDestination": "Use destination source"
},
"noSource": "您的图书馆没有该源的任何小说",
"pinnedPlugin": "已置顶%{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "已置顶的插件",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "已从书库中删除",
"repositories": "Repositories",
"searchbar": "搜索资源",
"searchResults": "搜索结果",
"selectNovel": "选择小说",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "已卸载 %{name}",
"unpinnedPlugin": "已取消置顶%{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "更新失败",
"updatesPending": "Updates pending",
"updatedTo": "已更新至 %{version}",
"settings": {
"title": "插件设置",
@@ -196,6 +235,7 @@
"about": "关于",
"add": "添加",
"all": "全部",
"back": "Back",
"backup": "备份",
"cancel": "取消",
"categories": "分类",
@@ -213,10 +253,13 @@
"filter": "筛选",
"globally": "全局",
"install": "安装",
"imported": "Imported",
"logout": "登出",
"name": "名称",
"newUpdateAvailable": "新版本可用",
"none": "None",
"ok": "确定",
"close": "Close",
"pause": "暂停",
"preparing": "准备中",
"remove": "移除",
@@ -225,6 +268,7 @@
"resume": "继续",
"retry": "重试",
"save": "保存",
"saved": "Saved",
"search": "搜索",
"searchFor": "搜索",
"searchResults": "搜索结果",
@@ -235,11 +279,13 @@
"sort": "排序",
"submit": "确定",
"loading": "正在加载...",
"moreOptions": "More options",
"warning": "警告"
},
"webview": {
"refresh": "刷新",
"share": "分享",
"openInWebView": "Open in WebView",
"openInBrowser": "在浏览器中打开",
"clearCookies": "清除Cookie",
"cookiesCleared": "Cookie已清除",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "章节为空或应用无法抓取",
"chapterName": "章节:%{name}",
"completed": "下载完成",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "正在下载",
"downloadingNovel": "正在下载:%{name}",
"downloadsLower": "下载",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(递增)",
"autoDownload": "自动下载",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "按源",
"chapterSort": "默认章节排序",
"desc": "(递减)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "可能会在运行缓慢的设备上提高性能",
"disableHapticFeedback": "禁用振动反馈",
"disableHapticFeedbackDescription": "关闭交互时产生的振动。",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "显示模式",
"downloadNewChapters": "下载新章节",
"epub": "EPUB",
"epubLocation": "EPUB 位置",
"epubLocationDescription": "打开或导入您的 EPUB 文件的位置。",
"globalUpdate": "全局更新",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "网格尺寸",
"gridSizeDesc": "每行 %{num} 列",
"itemsPerRow": "每行的项目数",
@@ -294,11 +357,21 @@
"novelSort": "小说排序",
"refreshMetadata": "自动更新元数据",
"refreshMetadataDescription": "更新书库时同时更新封面和详情",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "排序",
"updateLibrary": "打开应用时更新书库",
"updateLibraryDesc": "不推荐在低端设备上启用",
"updateTime": "显示上次更新时间",
"useFAB": "在书库中使用浮动操作按钮"
"useFAB": "在书库中使用浮动操作按钮",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "所有源",
@@ -310,6 +383,8 @@
"clearHistorWarning": "您确定吗?所有历史记录都将删除。",
"deleted": "历史记录已删除。",
"nothingReadRecently": "最近没读过什么",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "搜索历史记录"
},
"library": "书库",
@@ -346,6 +421,7 @@
}
},
"empty": "您的书库是空的。请从浏览将小说添加到您的书库中。",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "导入EPUB",
"openRandom": "随机打开",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "已添加书签",
"downloaded": "已下载",
"unread": "未读"
"unread": "未读",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "按章节名",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "将阅读器主题应用到EPUB",
"customJSWarning": "不是所有EPUB阅读器都能够支持自定义JS",
"downloadedChaptersOnly": "EPUB文件中只包含已下载的章节",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "结束章节",
"export": "Export",
"exportAll": "导出所有章节",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "包含自定义CSS",
"includeCustomJS": "包括自定义JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "请输入有效的章节数字",
"selectFolder": "选择EPUB文件的目标文件夹",
"startChapter": "起始章节",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "导出EPUB失败:%{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "成功将%{chapters}个章节导出为EPUB",
"noDownloadedChapters": "未找到已下载的章节,请在导出前下载章节。",
"noNovelSelected": "没有小说被选中导出"
"noNovelSelected": "没有小说被选中导出",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "封面已保存",
"coverNotSaved": "封面未保存",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "章节名",
"chapterNumber": "章节编号",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "输入有效的章节名",
"validChapterNumber": "输入有效的章节编号"
},
"jumpToChapter": "跳转到章节",
"openChapter": "打开章节"
"openChapter": "打开章节",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "迁移",
"noSummary": "无简介",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "文本选择",
"autoscroll": "自动滚屏",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "仿真阅读",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "点击滚屏",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "颜色",
"display": "Display",
"fontStyle": "字体样式",
"fullscreen": "全屏",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "行高",
"navigation": "Navigation",
"padding": "内边距",
"pageReader": "分页阅读(实验性)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "删除额外间距",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "滚动长度 (默认屏幕高度)",
"showBatteryAndTime": "电池和时间",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "阅读进度",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "在章节之间滑动",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "文本对齐",
"textSize": "文本大小",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "滑动打开抽屉栏",
"verticalSeekbar": "垂直进度条",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "保持屏幕开启",
"volumeButtonsScroll": "音量按键滚屏"
"volumeButtonsScroll": "音量按键滚屏",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "滚动到底部",
@@ -489,7 +598,9 @@
"finished": "已完成",
"nextChapter": "下一章: %{name}",
"noNextChapter": "没有下一章节",
"noPreviousChapter": "没有上一章节"
"noPreviousChapter": "没有上一章节",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "滚屏间隔 (秒)",
@@ -514,7 +625,8 @@
"textColor": "文字颜色",
"textColorModal": "文字颜色",
"title": "阅读器",
"verticalSeekbarDesc": "使用垂直进度条"
"verticalSeekbarDesc": "使用垂直进度条",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "未找到相应结果"
@@ -528,10 +640,16 @@
"title": "统计数据",
"titlesInLibrary": "书库中的小说",
"totalChapters": "统计数据",
"unreadChapters": "未读章节"
"unreadChapters": "未读章节",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "进度记录",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "从 %{name} 注销?",
"revalidate": "重新认证",
"services": "服务"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "导入EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "正在更新书库",
"DRIVE_BACKUP": "Google云端硬盘备份",
"DRIVE_RESTORE": "Google云端硬盘恢复",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "本地恢复",
"MIGRATE_NOVEL": "正在迁移小说",
"DOWNLOAD_CHAPTER": "正在下载章节"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+150 -13
View File
@@ -22,16 +22,22 @@
"dataManagement": "Data Management",
"deleteReadChapters": "Delete read chapters",
"deleteReadChaptersDialogTitle": "All chapters marked as read will be deleted.",
"disabled": "Disabled",
"dnsOverHttps": "DNS over HTTPS (DoH)",
"dnsOverHttpsDescription": "Uses the selected resolver for app requests and downloads. WebView traffic is not affected.",
"importEpub": "Import EPUB",
"importFailed": "Error importing EPUB:",
"importNovel": "Import Novel",
"importStaticFiles": "Import Static Files",
"novelInsertFailed": "novel insert failed",
"networking": "Networking",
"useFAB": "Use FAB instead of button",
"userAgent": "User Agent",
"recreateDBIndexes": "Recreate DB indexes",
"recreateDBIndexesToast": "Recreated DB indexes",
"recreateDBIndexesDialogTitle": "All DB indexes will be recreated.\nThis may take a while.",
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices."
"recreateDBIndexesDesc": "Recreates DB indexes. This may improve performance on slower devices.",
"restartRequired": "Restart LNReader to apply this change."
},
"appearance": "Appearance",
"appearanceScreen": {
@@ -45,11 +51,16 @@
"appLanguageDefault": "Default",
"appTheme": "App theme",
"darkTheme": "Dark Theme",
"dateFormat": "Date format",
"dateFormatDefault": "Default (%{date})",
"dynamicColors": "Dynamic",
"hideBackdrop": "Hide backdrop",
"lightTheme": "Light Theme",
"navbar": "Navbar",
"novelInfo": "Novel info",
"pureBlackDarkMode": "Pure black dark mode",
"relativeTimestamps": "Relative timestamps",
"relativeTimestampsDescription": "\"Today\" instead of \"%{date}\"",
"showHistoryInTheNav": "Show history in the nav",
"showUpdatesInTheNav": "Show updates in the nav",
"themeMode": "Theme mode",
@@ -136,30 +147,58 @@
"available": "Available",
"deletePluginMessage": "Are you sure you want to uninstall %{name}?",
"discover": "发现",
"editPluginSetting": "Edit %{name}",
"globalSearch": "全局搜索",
"installPlugin": "Install %{name}",
"installFailed": "Installation failed: %{name}",
"installed": "Installed",
"installedPlugin": "Installed %{name}",
"installedPlugins": "Installed plugins",
"language": "Language",
"lastUsed": "最近使用",
"latest": "最新",
"listEmpty": "从设置中启用语言",
"migration": {
"cover": "Cover",
"dialogMessage": "Migrate %{url}?",
"keepCurrent": "Keep current",
"metadata": "Novel details",
"novelAlreadyInLibrary": "Novel already in library",
"preservedState": "Categories, matched chapter progress and bookmarks, and reader settings will be preserved.",
"redownloadChapters": "Re-download matching downloaded chapters",
"reviewDescription": "Choose what to keep when migrating to %{name}.",
"reviewTitle": "Review migration",
"selectSource": "Select Source",
"selectSourceDesc": "Select a Source To Migrate From"
"selectSourceDesc": "Select a Source To Migrate From",
"useDestination": "Use destination source"
},
"noSource": "Your library does not have any novels from this source",
"pinnedPlugin": "Pinned %{name}",
"noPlugins": "No plugins found. Add a repository or enable more languages in Browse Settings.",
"noSearchResults": "No matching results",
"noSources": "No sources installed. Install a plugin to start browsing novels.",
"openPluginDetails": "Open details for %{name}",
"openPluginSettings": "Open settings for %{name}",
"openWebsite": "Open the website for %{name}",
"pinSource": "Pin %{name}",
"pinnedPlugins": "Pinned plugins",
"pluginDetails": "Plugin details",
"pluginIcon": "plugin icon",
"pluginNotInstalled": "This plugin is not installed",
"plugins": "Plugins",
"removeFromLibrary": "從書庫中移除",
"repositories": "Repositories",
"searchbar": "搜索资源",
"searchResults": "Search results",
"selectNovel": "Select Novel",
"sources": "Sources",
"togglePluginSetting": "Toggle %{name}",
"uninstalledPlugin": "Uninstalled %{name}",
"unpinnedPlugin": "Unpinned %{name}",
"uninstall": "Uninstall",
"unpinSource": "Unpin %{name}",
"updatePlugin": "Update %{name}",
"updateAll": "Update all",
"updateFailed": "Update failed",
"updatesPending": "Updates pending",
"updatedTo": "Updated to %{version}",
"settings": {
"title": "Plugin Settings",
@@ -196,6 +235,7 @@
"about": "About",
"add": "新增",
"all": "全部",
"back": "Back",
"backup": "Backup",
"cancel": "取消",
"categories": "類別",
@@ -213,10 +253,13 @@
"filter": "筛选",
"globally": "全球",
"install": "安裝",
"imported": "Imported",
"logout": "Logout",
"name": "名稱",
"newUpdateAvailable": "有可用更新",
"none": "None",
"ok": "确定",
"close": "Close",
"pause": "Pause",
"preparing": "Preparing",
"remove": "Remove",
@@ -225,6 +268,7 @@
"resume": "Resume",
"retry": "重試",
"save": "保存",
"saved": "Saved",
"search": "搜索",
"searchFor": "搜尋",
"searchResults": "搜尋結果",
@@ -235,11 +279,13 @@
"sort": "排序",
"submit": "提交",
"loading": "Loading ...",
"moreOptions": "More options",
"warning": "Warning"
},
"webview": {
"refresh": "Refresh",
"share": "Share",
"openInWebView": "Open in WebView",
"openInBrowser": "Open in browser",
"clearCookies": "Clear cookies",
"cookiesCleared": "Cookies cleared",
@@ -260,6 +306,7 @@
"chapterEmptyOrScrapeError": "Either chapter is empty or the app couldn't scrape it",
"chapterName": "Chapter: %{name}",
"completed": "Download completed",
"storageInfo": "Downloaded chapters are stored in the app's private storage on this device.",
"downloading": "Downloading",
"downloadingNovel": "Downloading: %{name}",
"downloadsLower": "下載",
@@ -271,6 +318,14 @@
"generalSettingsScreen": {
"asc": "(Ascending)",
"autoDownload": "Auto-download",
"automaticUpdates": "Automatic updates",
"automaticUpdatesDaily": "Daily",
"automaticUpdatesDescription": "Runs when a network connection is available. Android may delay background work to save battery.",
"automaticUpdatesEvery12Hours": "Every 12 hours",
"automaticUpdatesEvery2Days": "Every 2 days",
"automaticUpdatesEvery3Days": "Every 3 days",
"automaticUpdatesOff": "Off",
"automaticUpdatesWeekly": "Weekly",
"bySource": "By source",
"chapterSort": "Default chapter sort",
"desc": "(Descending)",
@@ -278,12 +333,20 @@
"disableLoadingAnimationsDesc": "May improve performance on slower devices",
"disableHapticFeedback": "Disable haptic feedback",
"disableHapticFeedbackDescription": "Turn off vibrations for touch interactions.",
"chapterDownloadCooldown": "Chapter download cooldown",
"chapterDownloadCooldownDesc": "Wait between sequential chapter downloads (seconds). Helps avoid source rate limits during batch downloads.",
"chapterDownloadCooldownPlaceholder": "Cooldown in seconds (e.g. 1.5)",
"chapterDownloadCooldownWarning": "Very low cooldowns may trigger rate limiting or temporary IP bans from sources.",
"displayMode": "Display Mode",
"downloadNewChapters": "Download new chapters",
"epub": "EPUB",
"epubLocation": "EPUB Location",
"epubLocationDescription": "The place where you open and export your EPUB files.",
"globalUpdate": "Global update",
"globalUpdateCategories": "Categories",
"globalUpdateCategoriesDescription": "Novels in excluded categories will not be updated, even when they are also in an included category. Tap a category to cycle between included, excluded, and neither.",
"globalUpdateExclude": "Exclude: %{categories}",
"globalUpdateInclude": "Include: %{categories}",
"gridSize": "Grid size",
"gridSizeDesc": "%{num} per row",
"itemsPerRow": "Items per row",
@@ -294,11 +357,21 @@
"novelSort": "Novel Sort",
"refreshMetadata": "Automatically refresh metadata",
"refreshMetadataDescription": "Check for new cover and details when updating library",
"smartUpdate": "Smart update",
"smartUpdateSkipCompleted": "Skip entries with \"Completed\" status",
"smartUpdateSkipUnstarted": "Skip unstarted entries",
"smartUpdateSkipWithUnread": "Skip entries with unread chapter(s)",
"sortOrder": "Sort Order",
"updateLibrary": "Update library on launch",
"updateLibraryDesc": "Not recommended for low devices",
"updateTime": "Show last update time",
"useFAB": "Use FAB in Library"
"useFAB": "Use FAB in Library",
"timeTracking": "Time Tracking",
"enableTimeTracking": "Enable time tracking",
"enableTimeTrackingDesc": "Tracks time spent reading and displays it in the statistics screen",
"inactivityTimeout": "Inactivity Timeout",
"inactivityTimeoutNever": "Never",
"inactivityTimeoutDesc": "Automatically pause time tracking after a period of inactivity"
},
"globalSearch": {
"allSources": "所有源",
@@ -310,6 +383,8 @@
"clearHistorWarning": "您确定吗?所有历史记录都将删除。",
"deleted": "History deleted.",
"nothingReadRecently": "Nothing read recently",
"removeHistoryWarning": "This will remove the read date of this chapter. Are you sure?",
"resetAllChapters": "Reset all chapters for this entry",
"searchbar": "搜索历史记录"
},
"library": "书库",
@@ -346,6 +421,7 @@
}
},
"empty": "您的库是空的。从浏览添加丛书到您的库里。",
"emptyCategory": "This category is empty.",
"extraMenu": {
"importEpub": "Import EPUB",
"openRandom": "Open Random Entry",
@@ -372,7 +448,8 @@
"filters": {
"bookmarked": "Bookmarked",
"downloaded": "Downloaded",
"unread": "Unread"
"unread": "Unread",
"scanlators": "Ignore scanlators"
},
"order": {
"byChapterName": "By chapter name",
@@ -385,11 +462,16 @@
"exportEpubModal": {
"applyReaderTheme": "Apply reader theme to EPUB",
"customJSWarning": "Custom JS may not be supported by all EPUB readers",
"downloadedChaptersOnly": "Only downloaded chapters will be included in the EPUB file",
"description": "Choose a destination and which downloaded chapters to include in the EPUB.",
"directory": "Directory",
"endChapter": "End Chapter",
"export": "Export",
"exportAll": "Export All Chapters",
"fileName": "File name",
"fileNameRequired": "Enter a file name",
"includeCustomCSS": "Include Custom CSS",
"includeCustomJS": "Include Custom JS",
"includeChapterNumber": "Include chapter number in chapter titles",
"invalidRange": "Please enter valid chapter numbers",
"selectFolder": "Select destination folder for EPUB file",
"startChapter": "Start Chapter",
@@ -398,9 +480,12 @@
},
"epub": {
"exportFailed": "Failed to export EPUB: %{error}",
"exportingChapter": "%{current}/%{total} · %{chapter}",
"exportSuccess": "Successfully exported %{chapters} chapters as EPUB",
"noDownloadedChapters": "No downloaded chapters found. Please download chapters before exporting.",
"noNovelSelected": "No novel selected for export"
"noNovelSelected": "No novel selected for export",
"preparingExport": "Preparing EPUB",
"savingExport": "Saving EPUB"
},
"coverSaved": "Cover saved",
"coverNotSaved": "Cover not saved",
@@ -429,12 +514,16 @@
"jumpToChapterModal": {
"chapterName": "章節名稱",
"chapterNumber": "章節編號",
"description": "Search by chapter number or name, then choose whether to open it or scroll to it in the chapter list.",
"error": {
"validChapterName": "請輸入有效的章節名稱",
"validChapterNumber": "請輸入有效的章節編號"
},
"jumpToChapter": "跳轉至章節",
"openChapter": "打開章節"
"openChapter": "打開章節",
"openChapterDescription": "Turn off to scroll to it in the chapter list.",
"searchByName": "Search by Chapter Name",
"searchByNameDescription": "Turn off to search by chapter number."
},
"migrate": "迁移",
"noSummary": "無總結",
@@ -458,27 +547,47 @@
},
"readerScreen": {
"bottomSheet": {
"alignCenter": "Center",
"alignJustify": "Justify",
"alignLeft": "Left",
"alignRight": "Right",
"allowTextSelection": "Text selection",
"autoscroll": "Auto-scroll",
"autoscrollDescription": "Scroll the page automatically at a steady pace",
"bionicReading": "Bionic reading",
"bionicReadingDescription": "Bold the start of each word to help your eyes track faster",
"tapToScroll": "Tap to scroll",
"tapToScrollDescription": "Tap the top or bottom of the screen to scroll through the chapter",
"color": "颜色",
"display": "Display",
"fontStyle": "字体样式",
"fullscreen": "全屏",
"fullscreenDescription": "Hide the status bar and navigation bar while reading",
"lineHeight": "Line height",
"navigation": "Navigation",
"padding": "Padding",
"pageReader": "Paged reading (Experimental)",
"pageReaderDescription": "Split the chapter into pages instead of scrolling. Experimental — may break formatting in some novels",
"removeExtraSpacing": "Remove extra spacing",
"removeExtraSpacingDescription": "Collapse extra blank lines between paragraphs",
"scrollAmount": "Scroll amount (screen height by default)",
"showBatteryAndTime": "Battery & time",
"showBatteryAndTimeDescription": "Show battery level and clock over the reading screen",
"showProgressPercentage": "Reading progress",
"showProgressPercentageDescription": "Show the percentage read at the bottom of the screen",
"swipeGestures": "Swipe between chapters",
"swipeGesturesDescription": "Swipe left or right to jump to the next or previous chapter",
"textAlign": "Text alignment",
"textSize": "文本大小",
"ttsAutoPageAdvanceDescription": "Move to the next chapter automatically when narration ends",
"ttsEngineDescription": "Text-to-speech engine used to generate audio",
"ttsScrollToTopDescription": "Jump back to the top of the chapter when narration starts",
"useChapterDrawerSwipeNavigation": "Swipe to open drawer",
"verticalSeekbar": "Vertical seekbar",
"verticalSeekbarDescription": "Show a slider on the side to jump to any point in the chapter",
"keepScreenOn": "Keep screen on",
"volumeButtonsScroll": "Volume button scrolling"
"volumeButtonsScroll": "Volume button scrolling",
"volumeButtonsScrollDescription": "Use the volume buttons to scroll instead of adjusting volume"
},
"drawer": {
"scrollToBottom": "捲動至底",
@@ -489,7 +598,9 @@
"finished": "已完成",
"nextChapter": "Next: %{name}",
"noNextChapter": "没有下一章节",
"noPreviousChapter": "There's no previous chapter"
"noPreviousChapter": "There's no previous chapter",
"searchMinLength": "Enter at least %{count} characters to search",
"searchPlaceholder": "Search chapter"
},
"readerSettings": {
"autoScrollInterval": "Scroll interval (seconds)",
@@ -514,7 +625,8 @@
"textColor": "文字颜色",
"textColorModal": "Text color",
"title": "阅读器",
"verticalSeekbarDesc": "Use vertical seekbar"
"verticalSeekbarDesc": "Use vertical seekbar",
"volumeButtonOffset": "Volume button scroll offset (screen heights)"
},
"sourceScreen": {
"noResultsFound": "未找到相应结果"
@@ -528,10 +640,16 @@
"title": "統計資料",
"titlesInLibrary": "Titles in library",
"totalChapters": "總章節",
"unreadChapters": "未讀章節"
"unreadChapters": "未讀章節",
"topNovelsByTimeSpent": "Top novels by time spent",
"topCategoriesByTimeSpent": "Top categories by time spent",
"totalTimeSpent": "Total time spent",
"showNovels": "Group by novel",
"showCategories": "Group by category"
},
"tracking": "Tracking",
"trackingScreen": {
"info": "Tracking is one-way: reading progress is sent to external services. Link each novel individually using its tracking button.",
"logOutMessage": "Log out from %{name}?",
"revalidate": "Revalidate",
"services": "Services"
@@ -559,6 +677,7 @@
},
"notifications": {
"IMPORT_EPUB": "Importing EPUB",
"EXPORT_EPUB": "Exporting EPUB",
"UPDATE_LIBRARY": "Updating Library",
"DRIVE_BACKUP": "Google Drive Backup",
"DRIVE_RESTORE": "Google Drive Restore",
@@ -568,5 +687,23 @@
"LOCAL_RESTORE": "Local Restore",
"MIGRATE_NOVEL": "Migrating Novel",
"DOWNLOAD_CHAPTER": "Downloading Chapter"
},
"time": {
"seconds": {
"one": "%{count} second",
"other": "%{count} seconds"
},
"minutes": {
"one": "%{count} minute",
"other": "%{count} minutes"
},
"hours": {
"one": "%{count} hour",
"other": "%{count} hours"
},
"days": {
"one": "%{count} day",
"other": "%{count} days"
}
}
}
+37
View File
@@ -77,10 +77,18 @@ export interface StringMap {
'appearanceScreen.theme.yinyang': 'string';
'backupScreen.backupName': 'string';
'backupScreen.backupCreated': 'string';
'backupScreen.backupCreatedWithWarnings': 'string';
'backupScreen.backupRestored': 'string';
'backupScreen.backupRestoredSelectedWithWarnings': 'string';
'backupScreen.backupRestoredSummary.one': 'string';
'backupScreen.backupRestoredSummary.other': 'string';
'backupScreen.backupRestoredWithWarnings.one': 'string';
'backupScreen.backupRestoredWithWarnings.other': 'string';
'backupScreen.savingBackup': 'string';
'backupScreen.categoriesRestored': 'string';
'backupScreen.categoriesRestoredWithErrors': 'string';
'backupScreen.categoriesRestoreFailedSummary.one': 'string';
'backupScreen.categoriesRestoreFailedSummary.other': 'string';
'backupScreen.categoryRestoreFailed': 'string';
'backupScreen.categoryFileNotFound': 'string';
'backupScreen.categoryFileReadFailed': 'string';
@@ -93,14 +101,26 @@ export interface StringMap {
'backupScreen.failed': 'string';
'backupScreen.novelsRestored': 'string';
'backupScreen.novelsRestoredWithErrors': 'string';
'backupScreen.novelsBackupFailedSummary.one': 'string';
'backupScreen.novelsBackupFailedSummary.other': 'string';
'backupScreen.novelsRestoreFailedSummary.one': 'string';
'backupScreen.novelsRestoreFailedSummary.other': 'string';
'backupScreen.novelBackupFailed': 'string';
'backupScreen.novelRestoreFailed': 'string';
'backupScreen.novelDirectoryNotFound': 'string';
'backupScreen.novelDirectoryReadFailed': 'string';
'backupScreen.restoringCategories': 'string';
'backupScreen.restoringCategoriesProgress': 'string';
'backupScreen.restoringNovels': 'string';
'backupScreen.restoringNovelsProgress': 'string';
'backupScreen.restoringSettings': 'string';
'backupScreen.missingPluginsAfterRestore': 'string';
'backupScreen.sectionsRestoreFailedSummary.one': 'string';
'backupScreen.sectionsRestoreFailedSummary.other': 'string';
'backupScreen.sectionsBackupFailedSummary.one': 'string';
'backupScreen.sectionsBackupFailedSummary.other': 'string';
'backupScreen.settingsRestored': 'string';
'backupScreen.settingsRestoreFailedSummary': 'string';
'backupScreen.settingsFileNotFound': 'string';
'backupScreen.settingsRestoreFailed': 'string';
'backupScreen.settingsFileWriteFailed': 'string';
@@ -115,7 +135,19 @@ export interface StringMap {
'backupScreen.invalidBackupFolder': 'string';
'backupScreen.localBackup': 'string';
'backupScreen.noBackupFound': 'string';
'backupScreen.options.title': 'string';
'backupScreen.options.description': 'string';
'backupScreen.options.selectAll': 'string';
'backupScreen.options.library': 'string';
'backupScreen.options.libraryDescription': 'string';
'backupScreen.options.settings': 'string';
'backupScreen.options.settingsDescription': 'string';
'backupScreen.options.plugins': 'string';
'backupScreen.options.pluginsDescription': 'string';
'backupScreen.options.downloadedFiles': 'string';
'backupScreen.options.downloadedFilesDescription': 'string';
'backupScreen.preparingData': 'string';
'backupScreen.preparingSelectedFiles': 'string';
'backupScreen.remote.backup': 'string';
'backupScreen.remote.host': 'string';
'backupScreen.remote.unknownHost': 'string';
@@ -125,9 +157,11 @@ export interface StringMap {
'backupScreen.restoreLargeBackupsWarning': 'string';
'backupScreen.restorinBackup': 'string';
'backupScreen.restoringData': 'string';
'backupScreen.restoringSelectedFiles': 'string';
'backupScreen.selfHost': 'string';
'backupScreen.selfHostDesc': 'string';
'backupScreen.uploadingData': 'string';
'backupScreen.uploadingSelectedFiles': 'string';
'backupScreen.uploadingDownloadedFiles': 'string';
'browse': 'string';
'browseScreen.addedToLibrary': 'string';
@@ -588,6 +622,9 @@ export interface StringMap {
'notifications.LOCAL_RESTORE': 'string';
'notifications.MIGRATE_NOVEL': 'string';
'notifications.DOWNLOAD_CHAPTER': 'string';
'notifications.taskQueued': 'string';
'notifications.taskCompleted': 'string';
'notifications.taskFailed': 'string';
'time.seconds.one': 'string';
'time.seconds.other': 'string';
'time.minutes.one': 'string';
@@ -0,0 +1,71 @@
import NativeFile from '@modules/native-file';
import { getMMKVObject, setMMKVObject } from '@utils/mmkv/mmkv';
import {
getPlugin,
INSTALLED_PLUGINS_KEY,
reloadInstalledPlugins,
} from '../pluginManager';
import type { PluginItem } from '../types';
jest.mock('@database/queries/RepositoryQueries', () => ({
getRepositoriesFromDb: jest.fn().mockResolvedValue([]),
}));
jest.mock('@noble/ciphers/aes.js', () => ({
gcm: jest.fn(),
}));
jest.mock('@noble/ciphers/utils.js', () => ({
bytesToUtf8: jest.fn(),
utf8ToBytes: jest.fn(),
}));
jest.mock('cheerio', () => ({
load: jest.fn(),
}));
jest.mock('htmlparser2', () => ({
Parser: jest.fn(),
}));
jest.mock('@hooks/persisted/useUserAgent', () => ({
getUserAgent: () => 'LNReader test',
}));
jest.mock('@utils/mmkv/mmkv', () => ({
getMMKVObject: jest.fn(),
setMMKVObject: jest.fn(),
}));
const restoredPlugins = [
{ id: 'restored', name: 'Restored plugin' },
{ id: 'invalid', name: 'Invalid plugin' },
] as PluginItem[];
describe('reloadInstalledPlugins', () => {
it('loads restored bundles and removes registry entries that cannot load', async () => {
jest.mocked(getMMKVObject).mockReturnValueOnce(restoredPlugins);
jest.mocked(NativeFile.readFile).mockImplementation(async path => {
if (path.endsWith('/restored/index.js')) {
return `exports.default = {
id: 'restored',
name: 'Restored plugin',
version: '1.0.0',
site: 'https://example.com'
};`;
}
throw new Error('Missing plugin bundle');
});
await expect(reloadInstalledPlugins()).resolves.toEqual(['invalid']);
expect(getPlugin('restored')).toMatchObject({
id: 'restored',
version: '1.0.0',
});
expect(getPlugin('invalid')).toBeUndefined();
expect(setMMKVObject).toHaveBeenCalledWith(INSTALLED_PLUGINS_KEY, [
restoredPlugins[0],
]);
});
});
+28 -2
View File
@@ -9,10 +9,10 @@ import { encode, decode } from 'urlencode';
import { getRepositoriesFromDb } from '@database/queries/RepositoryQueries';
import { getUserAgent } from '@hooks/persisted/useUserAgent';
import { newer } from '@utils/compareVersion';
import NativeFile from '@modules/native-file'
import NativeFile from '@modules/native-file';
import { showToast } from '@utils/showToast';
import { PLUGIN_STORAGE } from '@utils/Storages';
import { getMMKVObject } from '@utils/mmkv/mmkv';
import { getMMKVObject, setMMKVObject } from '@utils/mmkv/mmkv';
import {
store,
@@ -193,12 +193,38 @@ const initializeInstalledPlugins = async () => {
await Promise.all(installedPlugins.map(plugin => loadPlugin(plugin.id)));
};
const reloadInstalledPlugins = async (): Promise<string[]> => {
const installedPlugins =
getMMKVObject<PluginItem[]>(INSTALLED_PLUGINS_KEY) || [];
Object.keys(plugins).forEach(pluginId => {
plugins[pluginId] = undefined;
});
const results = await Promise.all(
installedPlugins.map(async plugin => ({
plugin,
source: await loadPlugin(plugin.id),
})),
);
const restoredPlugins = results
.filter(result => result.source)
.map(result => result.plugin);
setMMKVObject(INSTALLED_PLUGINS_KEY, restoredPlugins);
return results
.filter(result => !result.source)
.map(result => result.plugin.id);
};
const LOCAL_PLUGIN_ID = 'local';
export {
getPlugin,
loadPlugin,
initializeInstalledPlugins,
reloadInstalledPlugins,
installPlugin,
uninstallPlugin,
updatePlugin,
+111
View File
@@ -0,0 +1,111 @@
import {
createSqliteAdapter,
classifySqlStatement,
SqliteQueryResult,
} from '@rozenite/sqlite-plugin';
import { db } from '@database/db';
/**
* Columns from op-sqlite's QueryResult may be undefined when the native layer
* returns keyed rows directly. Always derive a usable column list, falling
* back to the keys of the first row object.
*/
const deriveColumns = (
columnNames: string[] | undefined,
firstRow: Record<string, unknown> | undefined,
): string[] => {
if (columnNames && columnNames.length > 0) {
return columnNames;
}
if (firstRow) {
return Object.keys(firstRow);
}
return [];
};
/**
* op-sqlite's enhanced `execute` wrapper only reconstructs rows from rawRows
* when `result.rows` is falsy — but an empty array `[]` is truthy in JS, so
* the fallback is skipped. We handle the same reconstruction ourselves to
* stay robust across all op-sqlite versions.
*/
const reconstructRows = (
rows: unknown,
rawRows: unknown,
columnNames: string[] | undefined,
): Record<string, unknown>[] => {
if (Array.isArray(rows) && rows.length > 0) {
return rows as Record<string, unknown>[];
}
if (!Array.isArray(rawRows) || rawRows.length === 0) {
return [];
}
const cols = deriveColumns(columnNames, undefined);
if (cols.length === 0) {
// No column info at all — return raw rows as value-only arrays
return rawRows as unknown as Record<string, unknown>[];
}
return rawRows.map((rawRow: unknown) => {
const row: Record<string, unknown> = {};
const values = rawRow as unknown[];
for (let j = 0; j < cols.length; j++) {
row[cols[j]!] = values[j];
}
return row;
});
};
/**
* Rozenite SQLite adapter for @op-engineering/op-sqlite.
*
* Gated to `__DEV__`; import and pass to `useRozeniteSqlitePlugin` only
* during development builds.
*/
export const opSqliteAdapter = __DEV__
? createSqliteAdapter({
adapterId: 'op-sqlite',
adapterName: 'OP SQLite',
databases: {
main: {
name: 'lnreader.db',
executeStatements: async statements => {
const results: SqliteQueryResult[] = [];
for (const stmt of statements) {
const start = performance.now();
const result = await db.execute(
stmt.sql,
stmt.params as any[] | undefined,
);
const duration = performance.now() - start;
const rows = reconstructRows(
result.rows,
(result as any).rawRows,
result.columnNames,
);
const columns = deriveColumns(result.columnNames, rows[0]);
results.push({
rows,
columns,
metadata: {
statementType: classifySqlStatement(stmt.sql),
rowCount: rows.length,
changes: result.rowsAffected ?? null,
lastInsertRowId: result.insertId ?? null,
durationMs: duration,
},
});
}
return results;
},
},
},
})
: null;
@@ -0,0 +1,171 @@
import { useCallback } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { Checkbox } from '@components/Checkbox/Checkbox';
import { Dialog } from '@components/Dialog';
import { getString } from '@i18n/translations';
import {
areAllBackupOptionsSelected,
hasSelectedBackupOption,
type BackupOptions,
} from '@services/backup/options';
import type { ThemeColors } from '@theme/types';
type BackupOptionKey = keyof BackupOptions;
const OPTION_KEYS: BackupOptionKey[] = [
'library',
'settings',
'plugins',
'downloadedFiles',
];
const OPTION_STRINGS: Record<
BackupOptionKey,
{
description: Parameters<typeof getString>[0];
label: Parameters<typeof getString>[0];
}
> = {
library: {
label: 'backupScreen.options.library',
description: 'backupScreen.options.libraryDescription',
},
settings: {
label: 'backupScreen.options.settings',
description: 'backupScreen.options.settingsDescription',
},
plugins: {
label: 'backupScreen.options.plugins',
description: 'backupScreen.options.pluginsDescription',
},
downloadedFiles: {
label: 'backupScreen.options.downloadedFiles',
description: 'backupScreen.options.downloadedFilesDescription',
},
};
type BackupOptionsListProps = {
options: BackupOptions;
onChange: (options: BackupOptions) => void;
theme: ThemeColors;
};
export const BackupOptionsList = ({
options,
onChange,
theme,
}: BackupOptionsListProps) => {
const allSelected = areAllBackupOptionsSelected(options);
const someSelected = hasSelectedBackupOption(options);
const toggleAll = useCallback(() => {
const selected = !allSelected;
onChange({
library: selected,
settings: selected,
plugins: selected,
downloadedFiles: selected,
});
}, [allSelected, onChange]);
const toggleOption = useCallback(
(key: BackupOptionKey) => {
const selected = !options[key];
onChange({
...options,
[key]: selected,
...(key === 'library' && !selected
? { downloadedFiles: false }
: undefined),
});
},
[onChange, options],
);
return (
<Dialog.ScrollArea>
<ScrollView
contentContainerStyle={styles.scrollContent}
nestedScrollEnabled
>
<Checkbox
label={getString('backupScreen.options.selectAll')}
onPress={toggleAll}
status={allSelected ? true : someSelected ? 'indeterminate' : false}
theme={theme}
/>
<View
style={[styles.divider, { backgroundColor: theme.outlineVariant }]}
/>
{OPTION_KEYS.map(key => {
const disabled = key === 'downloadedFiles' && !options.library;
const strings = OPTION_STRINGS[key];
return (
<Checkbox
key={key}
description={getString(strings.description)}
disabled={disabled}
label={getString(strings.label)}
onPress={() => toggleOption(key)}
status={options[key]}
theme={theme}
viewStyle={styles.option}
/>
);
})}
</ScrollView>
</Dialog.ScrollArea>
);
};
type BackupOptionsDialogProps = BackupOptionsListProps & {
visible: boolean;
onCancel: () => void;
onConfirm: () => void;
};
export const BackupOptionsDialog = ({
visible,
options,
onCancel,
onChange,
onConfirm,
theme,
}: BackupOptionsDialogProps) => (
<Dialog.Root
onDismiss={onCancel}
testID="backup-options-dialog"
visible={visible}
>
<Dialog.Header>
<Dialog.Title>{getString('backupScreen.options.title')}</Dialog.Title>
<Dialog.Description>
{getString('backupScreen.options.description')}
</Dialog.Description>
</Dialog.Header>
<BackupOptionsList onChange={onChange} options={options} theme={theme} />
<Dialog.Actions>
<Dialog.Action title={getString('common.cancel')} onPress={onCancel} />
<Dialog.Action
disabled={!hasSelectedBackupOption(options)}
title={getString('common.backup')}
onPress={onConfirm}
/>
</Dialog.Actions>
</Dialog.Root>
);
const styles = StyleSheet.create({
divider: {
height: 1,
marginVertical: 4,
},
option: {
paddingVertical: 8,
},
scrollContent: {
paddingHorizontal: 8,
paddingVertical: 4,
},
});
@@ -13,6 +13,12 @@ import { DriveFile } from '@api/drive/types';
import { backgroundTasks } from '@services/backgroundTasks';
import { useAppSettings } from '@hooks/persisted';
import { formatDate } from '@utils/dateFormat';
import {
DEFAULT_BACKUP_OPTIONS,
hasSelectedBackupOption,
type BackupOptions,
} from '@services/backup/options';
import { BackupOptionsList } from './BackupOptions';
enum BackupModal {
UNAUTHORIZED,
@@ -98,6 +104,9 @@ function CreateBackup({
}) {
const [backupName, setBackupName] = useState('');
const [fetching, setFetching] = useState(false);
const [options, setOptions] = useState<BackupOptions>({
...DEFAULT_BACKUP_OPTIONS,
});
const prepare = async () => {
setFetching(true);
@@ -126,16 +135,28 @@ function CreateBackup({
placeholderTextColor={theme.onSurfaceDisabled}
disabled={fetching}
/>
<BackupOptionsList
onChange={setOptions}
options={options}
theme={theme}
/>
<View style={styles.footerContainer}>
<Button
disabled={backupName.trim().length === 0 || fetching}
disabled={
backupName.trim().length === 0 ||
fetching ||
!hasSelectedBackupOption(options)
}
title={getString('common.ok')}
onPress={() => {
prepare().then(folder => {
closeModal();
backgroundTasks.enqueue({
name: 'DRIVE_BACKUP',
data: folder,
data: {
backupFolder: folder,
options,
},
});
});
}}
@@ -9,6 +9,12 @@ import { useCallback, useEffect, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
import { TextInput } from 'react-native-paper';
import {
DEFAULT_BACKUP_OPTIONS,
hasSelectedBackupOption,
type BackupOptions,
} from '@services/backup/options';
import { BackupOptionsList } from './BackupOptions';
enum BackupModal {
SET_HOST,
@@ -35,6 +41,9 @@ function CreateBackup({
closeModal: () => void;
}) {
const [backupName, setBackupName] = useState('');
const [options, setOptions] = useState<BackupOptions>({
...DEFAULT_BACKUP_OPTIONS,
});
return (
<>
@@ -47,9 +56,16 @@ function CreateBackup({
theme={{ colors: { ...theme } }}
placeholderTextColor={theme.onSurfaceDisabled}
/>
<BackupOptionsList
onChange={setOptions}
options={options}
theme={theme}
/>
<View style={styles.footerContainer}>
<Button
disabled={backupName.trim().length === 0}
disabled={
backupName.trim().length === 0 || !hasSelectedBackupOption(options)
}
title={getString('common.ok')}
onPress={() => {
closeModal();
@@ -58,6 +74,7 @@ function CreateBackup({
data: {
host,
backupFolder: backupName + '.backup',
options,
},
});
}}
@@ -0,0 +1,99 @@
import { fireEvent, render, screen } from '@testing-library/react-native';
import type { BackupOptions } from '@services/backup/options';
import type { ThemeColors } from '@theme/types';
import { BackupOptionsList } from '../BackupOptions';
jest.mock('@i18n/translations', () => ({
getString: (key: string) => key,
}));
jest.mock('@hooks/persisted', () => ({
useTheme: () => mockTheme,
}));
const mockTheme = {
onSurface: '#111111',
onSurfaceDisabled: '#777777',
onSurfaceVariant: '#555555',
outlineVariant: '#cccccc',
primary: '#6200ee',
rippleColor: '#eeeeee',
} as ThemeColors;
const allSelected: BackupOptions = {
library: true,
settings: true,
plugins: true,
downloadedFiles: true,
};
describe('BackupOptionsList', () => {
it('supports selecting and clearing every backup section', () => {
const onChange = jest.fn();
render(
<BackupOptionsList
onChange={onChange}
options={allSelected}
theme={mockTheme}
/>,
);
fireEvent.press(
screen.getByRole('checkbox', {
name: 'backupScreen.options.selectAll',
}),
);
expect(onChange).toHaveBeenCalledWith({
library: false,
settings: false,
plugins: false,
downloadedFiles: false,
});
});
it('clears downloaded files when library data is deselected', () => {
const onChange = jest.fn();
render(
<BackupOptionsList
onChange={onChange}
options={allSelected}
theme={mockTheme}
/>,
);
fireEvent.press(
screen.getByRole('checkbox', {
name: 'backupScreen.options.library',
}),
);
expect(onChange).toHaveBeenCalledWith({
library: false,
settings: true,
plugins: true,
downloadedFiles: false,
});
});
it('disables downloaded files when library data is not selected', () => {
render(
<BackupOptionsList
onChange={() => {}}
options={{
...allSelected,
library: false,
downloadedFiles: false,
}}
theme={mockTheme}
/>,
);
expect(
screen.getByRole('checkbox', {
name: 'backupScreen.options.downloadedFiles',
}),
).toBeDisabled();
});
});
@@ -10,16 +10,36 @@ import { getString } from '@i18n/translations';
import { StyleSheet } from 'react-native';
import dayjs from 'dayjs';
import NativeFile from '@modules/native-file';
import { useState } from 'react';
import {
DEFAULT_BACKUP_OPTIONS,
type BackupOptions,
} from '@services/backup/options';
import { BackupOptionsDialog } from './Components/BackupOptions';
const BackupSettings = ({ navigation }: BackupSettingsScreenProps) => {
const theme = useTheme();
const [backupOptions, setBackupOptions] = useState<BackupOptions>({
...DEFAULT_BACKUP_OPTIONS,
});
const {
value: backupOptionsVisible,
setFalse: closeBackupOptions,
setTrue: openBackupOptions,
} = useBoolean();
const {
value: googleDriveModalVisible,
setFalse: closeGoogleDriveModal,
setTrue: openGoogleDriveModal,
} = useBoolean();
const createLocalBackup = async () => {
const createLocalBackup = () => {
setBackupOptions({ ...DEFAULT_BACKUP_OPTIONS });
openBackupOptions();
};
const chooseLocalBackupDestination = async () => {
closeBackupOptions();
try {
const filename = `lnreader_backup_${dayjs().format(
'YYYY-MM-DD_HH_mm',
@@ -30,7 +50,7 @@ const BackupSettings = ({ navigation }: BackupSettingsScreenProps) => {
);
backgroundTasks.enqueue({
name: 'LOCAL_BACKUP',
data: { destinationUri },
data: { destinationUri, options: backupOptions },
});
} catch {
// Closing Android's document picker intentionally leaves the queue unchanged.
@@ -115,6 +135,14 @@ const BackupSettings = ({ navigation }: BackupSettingsScreenProps) => {
visible={selfHostModalVisible}
closeModal={closeSelfHostModal}
/>
<BackupOptionsDialog
onCancel={closeBackupOptions}
onChange={setBackupOptions}
onConfirm={chooseLocalBackupDestination}
options={backupOptions}
theme={theme}
visible={backupOptionsVisible}
/>
</SafeAreaView>
);
};
@@ -10,17 +10,14 @@ import {
updateRepository,
} from '@database/queries/RepositoryQueries';
import { Repository } from '@database/types';
import { useBackHandler, useBoolean } from '@hooks/index';
import { useBoolean } from '@hooks/index';
import { usePluginActions, useTheme } from '@hooks/persisted';
import { getString } from '@i18n/translations';
import AddRepositoryModal from './components/AddRepositoryModal';
import RepositoryCard from './components/RepositoryCard';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import {
RespositorySettingsScreenProps,
RootStackParamList,
} from '@navigators/types';
import { RespositorySettingsScreenProps } from '@navigators/types';
import { showToast } from '@utils/showToast';
import { useLiveQuery } from '@database/manager/liveQuery';
import { repositorySchema } from '@database/schema';
@@ -73,14 +70,6 @@ const SettingsBrowseScreen = ({
}
}, [params, upsertRepository]);
useBackHandler(() => {
if (!navigation.canGoBack()) {
navigation.popTo<keyof RootStackParamList>('BottomNavigator');
return true;
}
return false;
});
return (
<SafeAreaView excludeTop>
<Appbar
@@ -89,7 +78,6 @@ const SettingsBrowseScreen = ({
if (navigation.canGoBack()) {
navigation.goBack();
}
navigation.popTo<keyof RootStackParamList>('BottomNavigator');
}}
theme={theme}
/>
@@ -16,6 +16,8 @@ import {
allowsDuplicateTask,
createBackgroundTaskMetadata,
fromNativeTaskRecord,
getBackgroundTaskQueueName,
willTaskWaitInQueue,
} from './taskDefinitions';
import { BACKGROUND_TASKS_STORE_KEY } from './constants';
@@ -23,7 +25,6 @@ const makeTemporaryId = () =>
`pending-${Date.now()}-${Math.random().toString(36).slice(2)}`;
export class BackgroundTaskQueue {
private currentTaskId?: string;
private interruptedTasks = new Map<string, 'pause' | 'cancel'>();
private notificationPermissionRequest?: Promise<boolean>;
@@ -59,7 +60,13 @@ export class BackgroundTaskQueue {
enqueue = (tasks: BackgroundTask | BackgroundTask[]) => {
for (const task of Array.isArray(tasks) ? tasks : [tasks]) {
this.enqueueOne(task).catch(() => undefined);
this.enqueueOne(task, true).catch(() => undefined);
}
};
private enqueueSilently = (tasks: BackgroundTask | BackgroundTask[]) => {
for (const task of Array.isArray(tasks) ? tasks : [tasks]) {
this.enqueueOne(task, false).catch(() => undefined);
}
};
@@ -100,7 +107,6 @@ export class BackgroundTaskQueue {
}
async run(taskId: string, task: BackgroundTask, checkpoint?: string) {
this.currentTaskId = taskId;
const queue = this.getSnapshot();
if (!queue.some(item => item.id === taskId)) {
queue.push({
@@ -115,8 +121,8 @@ export class BackgroundTaskQueue {
try {
await executeBackgroundTask(
task,
this.updateProgress.bind(this),
this.enqueue,
transformer => this.updateProgress(taskId, transformer),
this.enqueueSilently,
{
checkpoint,
updateCheckpoint: value => {
@@ -128,9 +134,20 @@ export class BackgroundTaskQueue {
},
);
this.throwIfInterrupted(taskId);
await NativeBackgroundTasks.complete(taskId);
const completedTask = this.getSnapshot().find(item => item.id === taskId);
await NativeBackgroundTasks.complete(
taskId,
completedTask?.meta.completionText ??
getString('notifications.taskCompleted'),
);
} catch (error) {
await NativeBackgroundTasks.fail(taskId, String(error), false);
await NativeBackgroundTasks.fail(
taskId,
getString('notifications.taskFailed', {
error: error instanceof Error ? error.message : String(error),
}),
false,
);
if (!this.interruptedTasks.has(taskId)) {
throw error;
}
@@ -139,7 +156,7 @@ export class BackgroundTaskQueue {
}
}
private async enqueueOne(task: BackgroundTask) {
private async enqueueOne(task: BackgroundTask, showQueuedToast: boolean) {
this.notificationPermissionRequest ??= askForPostNotificationsPermission();
await this.notificationPermissionRequest;
@@ -157,6 +174,8 @@ export class BackgroundTaskQueue {
state: 'queued',
meta: createBackgroundTaskMetadata(task, false),
};
const shouldShowQueuedToast =
showQueuedToast && willTaskWaitInQueue(task, current);
this.store([...current, pending]);
try {
@@ -166,12 +185,18 @@ export class BackgroundTaskQueue {
pending.meta.name,
pending.meta.progressText || getString('common.preparing'),
allowsDuplicateTask(task),
getBackgroundTaskQueueName(task),
);
const latest = this.getSnapshot().filter(item => item.id !== pending.id);
if (!latest.some(item => item.id === id)) {
latest.push({ ...pending, id });
}
this.store(latest);
if (shouldShowQueuedToast) {
showToast(
getString('notifications.taskQueued', { task: pending.meta.name }),
);
}
} catch (error) {
this.store(this.getSnapshot().filter(item => item.id !== pending.id));
showToast(
@@ -183,10 +208,9 @@ export class BackgroundTaskQueue {
}
private updateProgress(
taskId: string,
transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata,
) {
const taskId = this.currentTaskId;
if (!taskId) return;
this.throwIfInterrupted(taskId);
const queue = this.getSnapshot();
@@ -224,7 +248,6 @@ export class BackgroundTaskQueue {
} else {
this.store(this.getSnapshot().filter(item => item.id !== taskId));
}
this.currentTaskId = undefined;
this.interruptedTasks.delete(taskId);
}
@@ -0,0 +1,186 @@
import NativeBackgroundTasks from '@modules/native-background-tasks';
import { showToast } from '@utils/showToast';
import { BackgroundTaskQueue } from '../BackgroundTaskQueue';
import { executeBackgroundTask } from '../executeTask';
let mockStoredTasks: unknown[] = [];
jest.mock('@modules/native-background-tasks', () => ({
__esModule: true,
default: {
complete: jest.fn(),
enqueue: jest.fn().mockResolvedValue('native-task-1'),
fail: jest.fn(),
updateProgress: jest.fn().mockResolvedValue(undefined),
},
}));
jest.mock('../executeTask', () => ({
executeBackgroundTask: jest.fn(),
}));
jest.mock('@utils/askForPostNoftificationsPermission', () => ({
askForPostNotificationsPermission: jest.fn().mockResolvedValue(true),
}));
jest.mock('@utils/mmkv/mmkv', () => ({
getMMKVObject: jest.fn(() => mockStoredTasks),
setMMKVObject: jest.fn((_key: string, value: unknown[]) => {
mockStoredTasks = value;
}),
}));
jest.mock('@i18n/translations', () => ({
getString: (key: string, options?: Record<string, string>) =>
key === 'notifications.taskFailed'
? `Failed: ${options?.error}`
: key === 'notifications.taskQueued'
? `${options?.task} queued`
: key === 'notifications.LOCAL_RESTORE'
? 'Local restore'
: key === 'notifications.DOWNLOAD_CHAPTER'
? 'Download'
: key === 'common.preparing'
? 'Preparing'
: 'Completed',
}));
jest.mock('@utils/showToast', () => ({
showToast: jest.fn(),
}));
const task = {
name: 'LOCAL_RESTORE' as const,
data: { sourceUri: 'file://backup.zip' },
};
describe('BackgroundTaskQueue completion notifications', () => {
beforeEach(() => {
mockStoredTasks = [];
jest.clearAllMocks();
});
it('passes a task-provided completion summary to the native notification', async () => {
jest
.mocked(executeBackgroundTask)
.mockImplementation(async (_task, updateProgress) => {
updateProgress(meta => ({
...meta,
completionText: 'Backup restored with warnings',
}));
});
await new BackgroundTaskQueue().run('restore-1', task);
expect(NativeBackgroundTasks.complete).toHaveBeenCalledWith(
'restore-1',
'Backup restored with warnings',
);
});
it('localizes failure text before handing it to the native notification', async () => {
jest
.mocked(executeBackgroundTask)
.mockRejectedValueOnce(new Error('Invalid backup'));
await expect(
new BackgroundTaskQueue().run('restore-2', task),
).rejects.toThrow('Invalid backup');
expect(NativeBackgroundTasks.fail).toHaveBeenCalledWith(
'restore-2',
'Failed: Invalid backup',
false,
);
});
it('shows queued feedback when the task must wait in its lane', async () => {
const downloadTask = {
name: 'DOWNLOAD_CHAPTER' as const,
data: {
novelName: 'Example Novel',
novelId: 42,
pluginId: 'source-a',
chapters: [{ chapterId: 7, chapterName: 'Chapter 7' }],
},
};
mockStoredTasks = [
{
id: 'existing-task',
task: downloadTask,
state: 'running',
meta: {
name: 'Download: Example Novel',
isRunning: true,
progress: undefined,
progressText: undefined,
},
},
];
new BackgroundTaskQueue().enqueue(downloadTask);
await Promise.resolve();
await Promise.resolve();
expect(NativeBackgroundTasks.enqueue).toHaveBeenCalledWith(
downloadTask.name,
JSON.stringify(downloadTask),
'Download: Example Novel',
'Chapter 7',
true,
'lnreader-background-task:download:source-a',
);
expect(showToast).toHaveBeenCalledWith('Download: Example Novel queued');
});
it('does not show queued feedback when the task can start immediately', async () => {
new BackgroundTaskQueue().enqueue(task);
await Promise.resolve();
await Promise.resolve();
expect(NativeBackgroundTasks.enqueue).toHaveBeenCalled();
expect(showToast).not.toHaveBeenCalled();
});
it('keeps progress updates scoped to concurrently running tasks', async () => {
const firstTask = {
name: 'LOCAL_RESTORE' as const,
data: { sourceUri: 'file://first.zip' },
};
const secondTask = {
name: 'LOCAL_RESTORE' as const,
data: { sourceUri: 'file://second.zip' },
};
const resolvers: (() => void)[] = [];
jest
.mocked(executeBackgroundTask)
.mockImplementation(async (runningTask, updateProgress) => {
if (runningTask.name !== 'LOCAL_RESTORE') {
throw new Error('Unexpected task type');
}
updateProgress(meta => ({
...meta,
progressText: runningTask.data.sourceUri,
}));
await new Promise<void>(resolve => resolvers.push(resolve));
});
const firstRun = new BackgroundTaskQueue().run('first', firstTask);
const secondRun = new BackgroundTaskQueue().run('second', secondTask);
await Promise.resolve();
expect(NativeBackgroundTasks.updateProgress).toHaveBeenCalledWith(
'first',
-1,
'file://first.zip',
);
expect(NativeBackgroundTasks.updateProgress).toHaveBeenCalledWith(
'second',
-1,
'file://second.zip',
);
resolvers.forEach(resolve => resolve());
await Promise.all([firstRun, secondRun]);
});
});
@@ -4,7 +4,9 @@ import {
allowsDuplicateTask,
createBackgroundTaskMetadata,
fromNativeTaskRecord,
getBackgroundTaskQueueName,
getBackgroundTaskTitle,
willTaskWaitInQueue,
} from '../taskDefinitions';
jest.mock('@i18n/translations', () => ({
@@ -32,6 +34,7 @@ describe('background task definitions', () => {
data: {
novelName: 'Example Novel',
novelId: 42,
pluginId: 'source-a',
chapters: [{ chapterId: 42, chapterName: 'Chapter 7' }],
},
};
@@ -47,6 +50,65 @@ describe('background task definitions', () => {
});
});
it('serializes downloads from one plugin but separates other plugin lanes', () => {
const createDownload = (pluginId: string): BackgroundTask => ({
name: 'DOWNLOAD_CHAPTER',
data: {
novelName: 'Example Novel',
novelId: 42,
pluginId,
chapters: [{ chapterId: 42, chapterName: 'Chapter 7' }],
},
});
expect(getBackgroundTaskQueueName(createDownload('source-a'))).toBe(
getBackgroundTaskQueueName(createDownload('source-a')),
);
expect(getBackgroundTaskQueueName(createDownload('source-a'))).not.toBe(
getBackgroundTaskQueueName(createDownload('source-b')),
);
});
it('uses independent lanes for different non-download task types', () => {
expect(getBackgroundTaskQueueName({ name: 'UPDATE_LIBRARY' })).not.toBe(
getBackgroundTaskQueueName({
name: 'LOCAL_RESTORE',
data: { sourceUri: 'file://backup.zip' },
}),
);
});
it('only reports queueing when a lane or the global limit blocks a task', () => {
const createDownload = (pluginId: string): BackgroundTask => ({
name: 'DOWNLOAD_CHAPTER',
data: {
novelName: pluginId,
pluginId,
chapters: [{ chapterId: 42, chapterName: 'Chapter 7' }],
},
});
const queuedDownload = (pluginId: string) => ({
id: pluginId,
task: createDownload(pluginId),
state: 'queued' as const,
meta: createBackgroundTaskMetadata(createDownload(pluginId), false),
});
expect(willTaskWaitInQueue(createDownload('source-a'), [])).toBe(false);
expect(
willTaskWaitInQueue(createDownload('source-a'), [
queuedDownload('source-a'),
]),
).toBe(true);
expect(
willTaskWaitInQueue(createDownload('source-d'), [
queuedDownload('source-a'),
queuedDownload('source-b'),
queuedDownload('source-c'),
]),
).toBe(true);
});
it('derives metadata for a multi-file EPUB import', () => {
const task: BackgroundTask = {
name: 'IMPORT_EPUB',
+20 -2
View File
@@ -5,12 +5,21 @@ import type {
EpubExportChapter,
EpubExportMetadata,
} from '@modules/nitro-epub';
import type { BackupOptions } from '@services/backup/options';
export type SelfHostData = {
host: string;
backupFolder: string;
options?: BackupOptions;
};
export type DriveBackupData =
| DriveFile
| {
backupFolder: DriveFile;
options?: BackupOptions;
};
export type MigrationNovelPreference = 'current' | 'destination';
export type MigrationNovelOptions = {
@@ -55,11 +64,14 @@ export type BackgroundTask =
name: 'UPDATE_LIBRARY';
data?: { categoryId?: number; categoryName?: string };
}
| { name: 'DRIVE_BACKUP'; data: DriveFile }
| { name: 'DRIVE_BACKUP'; data: DriveBackupData }
| { name: 'DRIVE_RESTORE'; data: DriveFile }
| { name: 'SELF_HOST_BACKUP'; data: SelfHostData }
| { name: 'SELF_HOST_RESTORE'; data: SelfHostData }
| { name: 'LOCAL_BACKUP'; data: { destinationUri: string } }
| {
name: 'LOCAL_BACKUP';
data: { destinationUri: string; options?: BackupOptions };
}
| { name: 'LOCAL_RESTORE'; data: { sourceUri: string } }
| { name: 'MIGRATE_NOVEL'; data: MigrateNovelData }
| DownloadChapterTask;
@@ -68,6 +80,11 @@ export type DownloadChapterTask = {
name: 'DOWNLOAD_CHAPTER';
data: {
novelName: string;
/**
* Optional for compatibility with download tasks queued before
* per-plugin execution lanes were introduced.
*/
pluginId?: string;
/**
* Optional for compatibility with download tasks persisted before the
* per-novel queue identity was introduced.
@@ -82,6 +99,7 @@ export type BackgroundTaskMetadata = {
isRunning: boolean;
progress: number | undefined;
progressText: string | undefined;
completionText?: string;
};
export type TaskProgressUpdater = (
@@ -19,9 +19,48 @@ const MULTIPLICABLE_TASKS: BackgroundTask['name'][] = [
'MIGRATE_NOVEL',
];
const BACKGROUND_TASK_QUEUE_PREFIX = 'lnreader-background-task';
const MAX_CONCURRENT_DOWNLOADS = 3;
export const allowsDuplicateTask = (task: BackgroundTask) =>
MULTIPLICABLE_TASKS.includes(task.name);
export const getBackgroundTaskQueueName = (task: BackgroundTask) =>
task.name === 'DOWNLOAD_CHAPTER'
? `${BACKGROUND_TASK_QUEUE_PREFIX}:download:${
task.data.pluginId || 'legacy'
}`
: `${BACKGROUND_TASK_QUEUE_PREFIX}:task:${task.name}`;
export const willTaskWaitInQueue = (
task: BackgroundTask,
activeTasks: QueuedBackgroundTask[],
) => {
const queueName = getBackgroundTaskQueueName(task);
if (
activeTasks.some(
activeTask => getBackgroundTaskQueueName(activeTask.task) === queueName,
)
) {
return true;
}
if (task.name !== 'DOWNLOAD_CHAPTER') {
return false;
}
const activeDownloadLanes = new Set(
activeTasks
.filter(
activeTask =>
activeTask.task.name === 'DOWNLOAD_CHAPTER' &&
activeTask.state !== 'paused',
)
.map(activeTask => getBackgroundTaskQueueName(activeTask.task)),
);
return activeDownloadLanes.size >= MAX_CONCURRENT_DOWNLOADS;
};
export const getBackgroundTaskTitle = (task: BackgroundTask) => {
switch (task.name) {
case 'DOWNLOAD_CHAPTER':
@@ -0,0 +1,52 @@
import { getBackupCompletionText, type BackupResult } from '../backupResult';
jest.mock('@i18n/translations', () => ({
getString: (key: string, options?: Record<string, string | number>) => {
const strings: Record<string, string> = {
'backupScreen.backupCreated': 'Backup created successfully',
'backupScreen.backupCreatedWithWarnings':
'Backup created with warnings: %{warnings}',
};
const pluralStrings: Record<string, [string, string]> = {
'backupScreen.novelsBackupFailedSummary': [
'%{count} novel failed',
'%{count} novels failed',
],
'backupScreen.sectionsBackupFailedSummary': [
'%{count} backup section failed',
'%{count} backup sections failed',
],
};
const pluralString = pluralStrings[key];
const template = pluralString
? pluralString[options?.count === 1 ? 0 : 1]
: strings[key] ?? key;
return Object.entries(options ?? {}).reduce(
(text, [name, value]) => text.replace(`%{${name}}`, String(value)),
template,
);
},
}));
describe('backup result notifications', () => {
it('uses a concise success message when every section is backed up', () => {
const result: BackupResult = {
failedNovelCount: 0,
failedSectionCount: 0,
};
expect(getBackupCompletionText(result)).toBe('Backup created successfully');
});
it('aggregates partial backup failures', () => {
expect(
getBackupCompletionText({
failedNovelCount: 2,
failedSectionCount: 1,
}),
).toBe(
'Backup created with warnings: 2 novels failed; 1 backup section failed',
);
});
});
+115
View File
@@ -0,0 +1,115 @@
import NativeFile from '@modules/native-file';
import NativeZipArchive from '@modules/native-zip-archive';
import { createBackup, restoreBackup } from '../local';
import { finalizeRestoredPlugins } from '../restoreResult';
import { prepareBackupData, restoreData } from '../utils';
jest.mock('../utils', () => ({
CACHE_DIR_PATH: '/cache/BackupData',
clearBackupCache: jest.fn(),
prepareBackupData: jest.fn(),
restoreData: jest.fn(),
}));
jest.mock('../restoreResult', () => ({
finalizeRestoredPlugins: jest.fn(),
getRestoreCompletionText: jest.fn(),
}));
jest.mock('../backupResult', () => ({
getBackupCompletionText: jest.fn(() => 'Backup created'),
}));
jest.mock('@utils/Storages', () => ({
NOVEL_STORAGE: '/storage/Novels',
PLUGIN_STORAGE: '/storage/Plugins',
ROOT_STORAGE: '/storage',
}));
jest.mock('@utils/sleep', () => ({
sleep: jest.fn().mockResolvedValue(undefined),
}));
jest.mock('@i18n/translations', () => ({
getString: (key: string) => key,
}));
describe('local selective backup', () => {
it('creates archives only for selected file sections', async () => {
jest.mocked(prepareBackupData).mockResolvedValue({
failedNovelCount: 0,
failedSectionCount: 0,
});
jest.mocked(NativeZipArchive.zip).mockResolvedValue(undefined);
jest.mocked(NativeFile.copyFile).mockResolvedValue(undefined);
await createBackup({
destinationUri: 'content://backup.zip',
options: {
library: true,
settings: true,
plugins: true,
downloadedFiles: false,
},
});
expect(prepareBackupData).toHaveBeenCalledWith('/cache/BackupData', {
library: true,
settings: true,
plugins: true,
downloadedFiles: false,
});
expect(NativeZipArchive.zip).toHaveBeenCalledWith(
'/storage/Plugins',
'/cache/BackupData/plugins.zip',
);
expect(NativeZipArchive.zip).not.toHaveBeenCalledWith(
'/storage/Novels',
expect.any(String),
);
expect(NativeZipArchive.zip).toHaveBeenCalledWith(
'/cache/BackupData',
'/cache/BackupData.zip',
);
});
it('loads restored plugins after their archive is extracted', async () => {
const restoreResult = {
novelCount: 1,
failedNovelCount: 0,
categoryCount: 0,
failedCategoryCount: 0,
settingsRestored: true,
failedSectionCount: 0,
pluginIds: ['restored'],
manifest: {
appVersion: '2.1.0',
formatVersion: 2 as const,
sections: {
library: true,
settings: true,
plugins: true,
downloadedFiles: false,
},
},
};
jest.mocked(restoreData).mockResolvedValueOnce(restoreResult);
jest.mocked(NativeFile.exists).mockResolvedValue(true);
jest.mocked(NativeFile.copyFile).mockResolvedValue(undefined);
jest.mocked(NativeZipArchive.unzip).mockResolvedValue(undefined);
jest.mocked(finalizeRestoredPlugins).mockResolvedValueOnce([]);
await restoreBackup({ sourceUri: 'content://backup.zip' });
expect(NativeZipArchive.unzip).toHaveBeenCalledWith(
'/cache/BackupData/plugins.zip',
'/storage/Plugins',
);
expect(finalizeRestoredPlugins).toHaveBeenCalledWith(restoreResult);
expect(
jest.mocked(finalizeRestoredPlugins).mock.invocationCallOrder[0],
).toBeGreaterThan(
jest.mocked(NativeZipArchive.unzip).mock.invocationCallOrder[1],
);
});
});
@@ -0,0 +1,63 @@
import {
areAllBackupOptionsSelected,
DEFAULT_BACKUP_OPTIONS,
hasSelectedBackupOption,
resolveBackupOptions,
} from '../options';
import { getSelectedBackupFileSections } from '../fileSections';
import { ZipBackupName } from '../types';
jest.mock('@utils/Storages', () => ({
NOVEL_STORAGE: '/storage/Novels',
PLUGIN_STORAGE: '/storage/Plugins',
}));
describe('backup options', () => {
it('preserves the existing full-backup behavior by default', () => {
expect(resolveBackupOptions()).toEqual(DEFAULT_BACKUP_OPTIONS);
expect(areAllBackupOptionsSelected(resolveBackupOptions())).toBe(true);
});
it('prevents downloaded files from being selected without library data', () => {
expect(
resolveBackupOptions({
library: false,
settings: true,
plugins: false,
downloadedFiles: true,
}),
).toEqual({
library: false,
settings: true,
plugins: false,
downloadedFiles: false,
});
});
it('requires at least one selected section', () => {
expect(
hasSelectedBackupOption({
library: false,
settings: false,
plugins: false,
downloadedFiles: false,
}),
).toBe(false);
});
it('maps selected file sections to independent archives', () => {
expect(
getSelectedBackupFileSections({
library: true,
settings: true,
plugins: true,
downloadedFiles: false,
}),
).toEqual([
{
archiveName: ZipBackupName.PLUGINS,
storagePath: '/storage/Plugins',
},
]);
});
});
@@ -0,0 +1,167 @@
import NativeFile from '@modules/native-file';
import { reloadInstalledPlugins } from '@plugins/pluginManager';
import {
finalizeRestoredPlugins,
getMissingRestorePluginIds,
getRestoreCompletionText,
type RestoreResult,
} from '../restoreResult';
jest.mock('@i18n/translations', () => ({
getString: (key: string, options?: Record<string, string | number>) => {
const strings: Record<string, string> = {
'backupScreen.settingsRestoreFailedSummary': 'settings failed',
'backupScreen.missingPluginsAfterRestore': 'Missing plugins: %{plugins}',
};
const pluralStrings: Record<string, [string, string]> = {
'backupScreen.backupRestoredSummary': [
'Restored %{count} novel',
'Restored %{count} novels',
],
'backupScreen.backupRestoredWithWarnings': [
'Restored %{count} novel with warnings: %{warnings}',
'Restored %{count} novels with warnings: %{warnings}',
],
'backupScreen.novelsRestoreFailedSummary': [
'%{count} novel failed',
'%{count} novels failed',
],
'backupScreen.categoriesRestoreFailedSummary': [
'%{count} category failed',
'%{count} categories failed',
],
'backupScreen.sectionsRestoreFailedSummary': [
'%{count} backup section failed',
'%{count} backup sections failed',
],
};
const pluralString = pluralStrings[key];
const template = pluralString
? pluralString[options?.count === 1 ? 0 : 1]
: strings[key] ?? key;
return Object.entries(options ?? {}).reduce(
(text, [name, value]) => text.replace(`%{${name}}`, String(value)),
template,
);
},
}));
jest.mock('@plugins/pluginManager', () => ({
LOCAL_PLUGIN_ID: 'local',
reloadInstalledPlugins: jest.fn(),
}));
jest.mock('@utils/Storages', () => ({
PLUGIN_STORAGE: '/storage/Plugins',
}));
const successfulResult: RestoreResult = {
novelCount: 4,
failedNovelCount: 0,
categoryCount: 2,
failedCategoryCount: 0,
settingsRestored: true,
failedSectionCount: 0,
pluginIds: ['installed'],
manifest: {
appVersion: '2.1.0',
formatVersion: 2,
sections: {
library: true,
settings: true,
plugins: true,
downloadedFiles: true,
},
},
};
describe('restore result notifications', () => {
beforeEach(() => {
jest.mocked(reloadInstalledPlugins).mockReset();
});
it('uses a concise success message when the restore has no warnings', () => {
expect(getRestoreCompletionText(successfulResult, [])).toBe(
'Restored 4 novels',
);
});
it('aggregates restore failures and missing plugin identifiers', () => {
expect(
getRestoreCompletionText(
{
...successfulResult,
failedNovelCount: 2,
failedCategoryCount: 1,
settingsRestored: false,
failedSectionCount: 1,
},
['source.one', 'source.two'],
),
).toBe(
'Restored 4 novels with warnings: 2 novels failed; 1 category failed; settings failed; 1 backup section failed; Missing plugins: source.one, source.two',
);
});
it('checks each referenced plugin once and ignores local novels', async () => {
jest
.mocked(NativeFile.exists)
.mockImplementation(async path => path.includes('installed'));
await expect(
getMissingRestorePluginIds(['local', 'installed', 'missing', 'missing']),
).resolves.toEqual(['missing']);
expect(NativeFile.exists).toHaveBeenCalledTimes(2);
});
it('reloads restored plugin bundles and reports bundles that fail to load', async () => {
jest.mocked(reloadInstalledPlugins).mockResolvedValueOnce(['invalid']);
jest
.mocked(NativeFile.exists)
.mockImplementation(async path => path.includes('installed'));
await expect(
finalizeRestoredPlugins({
...successfulResult,
pluginIds: ['installed', 'missing'],
}),
).resolves.toEqual(['missing', 'invalid']);
});
it('does not reload plugins when that section was omitted', async () => {
await finalizeRestoredPlugins({
...successfulResult,
pluginIds: [],
manifest: {
...successfulResult.manifest,
sections: {
...successfulResult.manifest.sections,
plugins: false,
},
},
});
expect(reloadInstalledPlugins).not.toHaveBeenCalled();
});
it('does not describe an intentionally omitted library as zero novels', () => {
expect(
getRestoreCompletionText(
{
...successfulResult,
novelCount: 0,
manifest: {
...successfulResult.manifest,
sections: {
...successfulResult.manifest.sections,
library: false,
downloadedFiles: false,
},
},
},
[],
),
).toBe('backupScreen.backupRestored');
});
});
+215
View File
@@ -0,0 +1,215 @@
import {
_restoreNovelAndChapters,
getAllNovels,
} from '@database/queries/NovelQueries';
import { getNovelChapters } from '@database/queries/ChapterQueries';
import {
_restoreCategory,
getAllNovelCategories,
getCategoriesFromDb,
} from '@database/queries/CategoryQueries';
import NativeFile from '@modules/native-file';
import { MMKVStorage } from '@utils/mmkv/mmkv';
import { prepareBackupData, restoreData } from '../utils';
import type { BackupOptions } from '../options';
jest.mock('@database/queries/NovelQueries', () => ({
_restoreNovelAndChapters: jest.fn(),
getAllNovels: jest.fn(),
}));
jest.mock('@database/queries/ChapterQueries', () => ({
getNovelChapters: jest.fn(),
}));
jest.mock('@database/queries/CategoryQueries', () => ({
_restoreCategory: jest.fn(),
getAllNovelCategories: jest.fn(),
getCategoriesFromDb: jest.fn(),
}));
jest.mock('@hooks/persisted/useSelfHost', () => ({
SELF_HOST_BACKUP: 'SELF_HOST_BACKUP',
}));
jest.mock('@hooks/persisted/migrations/trackerMigration', () => ({
OLD_TRACKED_NOVEL_PREFIX: 'OLD_TRACKED_NOVEL_PREFIX',
}));
jest.mock('@hooks/persisted/useUpdates', () => ({
LAST_UPDATE_TIME: 'LAST_UPDATE_TIME',
}));
jest.mock('@utils/mmkv/mmkv', () => ({
MMKVStorage: {
getAllKeys: jest.fn(() => []),
getBoolean: jest.fn(),
getString: jest.fn(),
set: jest.fn(),
},
}));
jest.mock('@i18n/translations', () => ({
getString: (key: string) => key,
}));
jest.mock('@plugins/pluginManager', () => ({
INSTALLED_PLUGINS_KEY: 'INSTALL_PLUGINS',
}));
jest.mock('@utils/Storages', () => ({
ROOT_STORAGE: '/storage',
}));
const pluginOnlyOptions: BackupOptions = {
library: false,
settings: false,
plugins: true,
downloadedFiles: false,
};
describe('selective backup data', () => {
beforeEach(() => {
jest.mocked(NativeFile.exists).mockResolvedValue(false);
jest.mocked(NativeFile.mkdir).mockResolvedValue(undefined);
jest.mocked(NativeFile.writeFile).mockResolvedValue(undefined);
jest.mocked(getAllNovels).mockResolvedValue([]);
jest.mocked(getNovelChapters).mockResolvedValue([]);
jest.mocked(getCategoriesFromDb).mockResolvedValue([]);
jest.mocked(getAllNovelCategories).mockResolvedValue([]);
});
it('writes the selected sections to the v2 manifest', async () => {
await prepareBackupData('/cache', pluginOnlyOptions);
expect(NativeFile.writeFile).toHaveBeenCalledTimes(2);
expect(NativeFile.writeFile).toHaveBeenCalledWith(
'/cache/Version.json',
expect.stringContaining(
'"sections":{"library":false,"settings":false,"plugins":true,"downloadedFiles":false}',
),
);
expect(getAllNovels).not.toHaveBeenCalled();
expect(getCategoriesFromDb).not.toHaveBeenCalled();
expect(NativeFile.writeFile).toHaveBeenCalledWith(
'/cache/Plugins.json',
'[]',
);
});
it('does not warn about sections intentionally omitted by the manifest', async () => {
jest
.mocked(NativeFile.readFile)
.mockResolvedValueOnce(
JSON.stringify({
appVersion: '2.1.0',
formatVersion: 2,
sections: pluginOnlyOptions,
}),
)
.mockResolvedValueOnce('[]');
jest
.mocked(NativeFile.exists)
.mockImplementation(async path => path.endsWith('/Plugins.json'));
const result = await restoreData('/cache');
expect(result).toMatchObject({
failedNovelCount: 0,
failedCategoryCount: 0,
failedSectionCount: 0,
settingsRestored: true,
manifest: {
formatVersion: 2,
sections: pluginOnlyOptions,
},
});
expect(_restoreNovelAndChapters).not.toHaveBeenCalled();
expect(_restoreCategory).not.toHaveBeenCalled();
expect(MMKVStorage.set).toHaveBeenCalledWith('INSTALL_PLUGINS', '[]');
});
it('clears file-backed metadata when downloaded files are omitted', async () => {
jest.mocked(getAllNovels).mockResolvedValueOnce([
{
id: 1,
name: 'Example',
path: '/example',
pluginId: 'source',
cover: 'file:///storage/Novels/source/1/cover.png',
},
]);
jest.mocked(getNovelChapters).mockResolvedValueOnce([
{
id: 10,
novelId: 1,
path: '/chapter-1',
name: 'Chapter 1',
isDownloaded: true,
},
] as Awaited<ReturnType<typeof getNovelChapters>>);
await prepareBackupData('/cache', {
library: true,
settings: false,
plugins: false,
downloadedFiles: false,
});
const novelWrite = jest
.mocked(NativeFile.writeFile)
.mock.calls.find(([path]) => path.endsWith('/1.json'));
expect(JSON.parse(novelWrite?.[1] ?? '{}')).toMatchObject({
cover: null,
chapters: [{ id: 10, isDownloaded: false }],
});
});
it('omits the installed-plugin registry when plugin files are excluded', async () => {
jest
.mocked(MMKVStorage.getAllKeys)
.mockReturnValueOnce(['INSTALL_PLUGINS', 'OTHER_SETTING']);
jest
.mocked(MMKVStorage.getString)
.mockImplementation(key =>
key === 'INSTALL_PLUGINS'
? '[{"id":"source"}]'
: key === 'OTHER_SETTING'
? 'kept'
: undefined,
);
await prepareBackupData('/cache', {
library: false,
settings: true,
plugins: false,
downloadedFiles: false,
});
const settingsWrite = jest
.mocked(NativeFile.writeFile)
.mock.calls.find(([path]) => path.endsWith('/Setting.json'));
expect(JSON.parse(settingsWrite?.[1] ?? '{}')).toEqual({
OTHER_SETTING: 'kept',
});
});
it('treats backups without a section manifest as legacy full backups', async () => {
jest
.mocked(NativeFile.readFile)
.mockResolvedValueOnce(JSON.stringify({ version: '2.0.0' }));
const result = await restoreData('/cache');
expect(result.manifest).toMatchObject({
appVersion: '2.0.0',
formatVersion: 1,
sections: {
library: true,
settings: true,
plugins: true,
downloadedFiles: true,
},
});
});
});
+33
View File
@@ -0,0 +1,33 @@
import { getString } from '@i18n/translations';
export type BackupResult = {
failedNovelCount: number;
failedSectionCount: number;
};
export const getBackupCompletionText = (result: BackupResult) => {
const warnings: string[] = [];
if (result.failedNovelCount > 0) {
warnings.push(
getString('backupScreen.novelsBackupFailedSummary', {
count: result.failedNovelCount,
}),
);
}
if (result.failedSectionCount > 0) {
warnings.push(
getString('backupScreen.sectionsBackupFailedSummary', {
count: result.failedSectionCount,
}),
);
}
if (warnings.length === 0) {
return getString('backupScreen.backupCreated');
}
return getString('backupScreen.backupCreatedWithWarnings', {
warnings: warnings.join('; '),
});
};
+83 -35
View File
@@ -2,16 +2,51 @@ import { DriveFile } from '@api/drive/types';
import { sleep } from '@utils/sleep';
import { exists } from '@api/drive';
import { getString } from '@i18n/translations';
import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils';
import {
CACHE_DIR_PATH,
clearBackupCache,
prepareBackupData,
restoreData,
} from '../utils';
import {
finalizeRestoredPlugins,
getRestoreCompletionText,
} from '../restoreResult';
import { getBackupCompletionText } from '../backupResult';
import { download, updateMetadata, uploadMedia } from '@api/drive/request';
import { ZipBackupName } from '../types';
import { ROOT_STORAGE } from '@utils/Storages';
import type { TaskProgressUpdater } from '@services/backgroundTasks/contracts';
import type {
DriveBackupData,
TaskProgressUpdater,
} from '@services/backgroundTasks/contracts';
import { getSelectedBackupFileSections } from '../fileSections';
import { resolveBackupOptions } from '../options';
const uploadBackupSection = async (
sourcePath: string,
name: ZipBackupName,
backupFolder: DriveFile,
) => {
const file = await uploadMedia(sourcePath);
await updateMetadata(
file.id,
{
name,
mimeType: 'application/zip',
parents: [backupFolder.id],
},
file.parents[0],
);
};
export const createDriveBackup = async (
backupFolder: DriveFile,
data: DriveBackupData,
setMeta: TaskProgressUpdater,
) => {
const backupFolder = 'backupFolder' in data ? data.backupFolder : data;
const requestedOptions = 'backupFolder' in data ? data.options : undefined;
const options = resolveBackupOptions(requestedOptions);
setMeta(meta => ({
...meta,
isRunning: true,
@@ -19,7 +54,7 @@ export const createDriveBackup = async (
progressText: getString('backupScreen.preparingData'),
}));
await prepareBackupData(CACHE_DIR_PATH);
const backupResult = await prepareBackupData(CACHE_DIR_PATH, options);
setMeta(meta => ({
...meta,
@@ -29,39 +64,29 @@ export const createDriveBackup = async (
await sleep(500);
const file = await uploadMedia(CACHE_DIR_PATH);
await updateMetadata(
file.id,
{
name: ZipBackupName.DATA,
mimeType: 'application/zip',
parents: [backupFolder.id],
},
file.parents[0],
);
await uploadBackupSection(CACHE_DIR_PATH, ZipBackupName.DATA, backupFolder);
setMeta(meta => ({
...meta,
progress: 2 / 3,
progressText: getString('backupScreen.uploadingDownloadedFiles'),
progressText: getString('backupScreen.uploadingSelectedFiles'),
}));
const file2 = await uploadMedia(ROOT_STORAGE);
await updateMetadata(
file2.id,
{
name: ZipBackupName.DOWNLOAD,
mimeType: 'application/zip',
parents: [backupFolder.id],
},
file2.parents[0],
);
for (const section of getSelectedBackupFileSections(options)) {
await uploadBackupSection(
section.storagePath,
section.archiveName,
backupFolder,
);
}
const completionText = getBackupCompletionText(backupResult);
setMeta(meta => ({
...meta,
progress: 3 / 3,
isRunning: false,
progressText: completionText,
completionText,
}));
};
@@ -77,15 +102,11 @@ export const driveRestore = async (
}));
const zipDataFile = await exists(ZipBackupName.DATA, false, backupFolder.id);
const zipDownloadFile = await exists(
ZipBackupName.DOWNLOAD,
false,
backupFolder.id,
);
if (!zipDataFile || !zipDownloadFile) {
if (!zipDataFile) {
throw new Error(getString('backupScreen.invalidBackupFolder'));
}
await clearBackupCache();
await download(zipDataFile, CACHE_DIR_PATH);
await sleep(500);
@@ -95,20 +116,47 @@ export const driveRestore = async (
progressText: getString('backupScreen.restoringData'),
}));
await restoreData(CACHE_DIR_PATH);
const restoreResult = await restoreData(CACHE_DIR_PATH, setMeta);
await sleep(500);
setMeta(meta => ({
...meta,
progress: 2 / 3,
progressText: getString('backupScreen.downloadingDownloadedFiles'),
progressText: getString('backupScreen.restoringSelectedFiles'),
}));
await download(zipDownloadFile, ROOT_STORAGE);
if (restoreResult.manifest.formatVersion === 1) {
const legacyFile = await exists(
ZipBackupName.DOWNLOAD,
false,
backupFolder.id,
);
if (!legacyFile) {
throw new Error(getString('backupScreen.invalidBackupFolder'));
}
await download(legacyFile, ROOT_STORAGE);
} else {
for (const section of getSelectedBackupFileSections(
restoreResult.manifest.sections,
)) {
const file = await exists(section.archiveName, false, backupFolder.id);
if (!file) {
throw new Error(getString('backupScreen.invalidBackupFolder'));
}
await download(file, section.storagePath);
}
}
const missingPluginIds = await finalizeRestoredPlugins(restoreResult);
const completionText = getRestoreCompletionText(
restoreResult,
missingPluginIds,
);
setMeta(meta => ({
...meta,
progress: 3 / 3,
isRunning: false,
progressText: completionText,
completionText,
}));
};
+29
View File
@@ -0,0 +1,29 @@
import { NOVEL_STORAGE, PLUGIN_STORAGE } from '@utils/Storages';
import type { BackupOptions } from './options';
import { ZipBackupName } from './types';
export type BackupFileSection = {
archiveName: ZipBackupName;
storagePath: string;
};
export const getSelectedBackupFileSections = (
options: BackupOptions,
): BackupFileSection[] => {
const sections: BackupFileSection[] = [];
if (options.plugins) {
sections.push({
archiveName: ZipBackupName.PLUGINS,
storagePath: PLUGIN_STORAGE,
});
}
if (options.downloadedFiles) {
sections.push({
archiveName: ZipBackupName.NOVEL_FILES,
storagePath: NOVEL_STORAGE,
});
}
return sections;
};
+57 -26
View File
@@ -1,18 +1,33 @@
import { showToast } from '@utils/showToast';
import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils';
import NativeZipArchive from '@modules/native-zip-archive'
import {
CACHE_DIR_PATH,
clearBackupCache,
prepareBackupData,
restoreData,
} from '../utils';
import {
finalizeRestoredPlugins,
getRestoreCompletionText,
} from '../restoreResult';
import { getBackupCompletionText } from '../backupResult';
import NativeZipArchive from '@modules/native-zip-archive';
import { ROOT_STORAGE } from '@utils/Storages';
import { ZipBackupName } from '../types';
import NativeFile from '@modules/native-file'
import NativeFile from '@modules/native-file';
import { getString } from '@i18n/translations';
import type { TaskProgressUpdater } from '@services/backgroundTasks/contracts';
import { sleep } from '@utils/sleep';
import { getSelectedBackupFileSections } from '../fileSections';
import { resolveBackupOptions, type BackupOptions } from '../options';
export const createBackup = async (
{ destinationUri }: { destinationUri: string },
{
destinationUri,
options: requestedOptions,
}: { destinationUri: string; options?: BackupOptions },
setMeta?: TaskProgressUpdater,
) => {
try {
const options = resolveBackupOptions(requestedOptions);
setMeta?.(meta => ({
...meta,
isRunning: true,
@@ -20,20 +35,22 @@ export const createBackup = async (
progressText: getString('backupScreen.preparingData'),
}));
await prepareBackupData(CACHE_DIR_PATH);
const backupResult = await prepareBackupData(CACHE_DIR_PATH, options);
setMeta?.(meta => ({
...meta,
progress: 1 / 4,
progressText: getString('backupScreen.uploadingDownloadedFiles'),
progressText: getString('backupScreen.preparingSelectedFiles'),
}));
await sleep(200);
await NativeZipArchive.zip(
ROOT_STORAGE,
CACHE_DIR_PATH + '/' + ZipBackupName.DOWNLOAD,
);
for (const section of getSelectedBackupFileSections(options)) {
await NativeZipArchive.zip(
section.storagePath,
`${CACHE_DIR_PATH}/${section.archiveName}`,
);
}
setMeta?.(meta => ({
...meta,
@@ -53,19 +70,19 @@ export const createBackup = async (
await NativeFile.copyFile(CACHE_DIR_PATH + '.zip', destinationUri);
const completionText = getBackupCompletionText(backupResult);
setMeta?.(meta => ({
...meta,
progress: 4 / 4,
isRunning: false,
progressText: completionText,
completionText,
}));
showToast(getString('backupScreen.backupCreated'));
} catch (error: any) {
setMeta?.(meta => ({
...meta,
isRunning: false,
}));
showToast(error.message);
throw error;
}
};
@@ -82,9 +99,7 @@ export const restoreBackup = async (
progressText: getString('backupScreen.downloadingData'),
}));
if (await NativeFile.exists(CACHE_DIR_PATH)) {
await NativeFile.unlink(CACHE_DIR_PATH);
}
await clearBackupCache();
const localPath = CACHE_DIR_PATH + '-source.zip';
await NativeFile.copyFile(sourceUri, localPath);
@@ -106,35 +121,51 @@ export const restoreBackup = async (
await sleep(200);
await restoreData(CACHE_DIR_PATH);
const restoreResult = await restoreData(CACHE_DIR_PATH, setMeta);
setMeta?.(meta => ({
...meta,
progress: 3 / 4,
progressText: getString('backupScreen.downloadingDownloadedFiles'),
progressText: getString('backupScreen.restoringSelectedFiles'),
}));
await sleep(200);
// TODO: unlink here too?
await NativeZipArchive.unzip(
CACHE_DIR_PATH + '/' + ZipBackupName.DOWNLOAD,
ROOT_STORAGE,
if (restoreResult.manifest.formatVersion === 1) {
const legacyArchive = CACHE_DIR_PATH + '/' + ZipBackupName.DOWNLOAD;
if (!(await NativeFile.exists(legacyArchive))) {
throw new Error(getString('backupScreen.invalidBackupFolder'));
}
await NativeZipArchive.unzip(legacyArchive, ROOT_STORAGE);
} else {
for (const section of getSelectedBackupFileSections(
restoreResult.manifest.sections,
)) {
const archivePath = `${CACHE_DIR_PATH}/${section.archiveName}`;
if (!(await NativeFile.exists(archivePath))) {
throw new Error(getString('backupScreen.invalidBackupFolder'));
}
await NativeZipArchive.unzip(archivePath, section.storagePath);
}
}
const missingPluginIds = await finalizeRestoredPlugins(restoreResult);
const completionText = getRestoreCompletionText(
restoreResult,
missingPluginIds,
);
setMeta?.(meta => ({
...meta,
progress: 4 / 4,
isRunning: false,
progressText: completionText,
completionText,
}));
showToast(getString('backupScreen.backupRestored'));
} catch (error: any) {
setMeta?.(meta => ({
...meta,
isRunning: false,
}));
showToast(error.message);
throw error;
}
};
+33
View File
@@ -0,0 +1,33 @@
export type BackupOptions = {
library: boolean;
settings: boolean;
plugins: boolean;
downloadedFiles: boolean;
};
export const DEFAULT_BACKUP_OPTIONS: BackupOptions = {
library: true,
settings: true,
plugins: true,
downloadedFiles: true,
};
export const resolveBackupOptions = (
options?: BackupOptions,
): BackupOptions => {
const resolved = {
...DEFAULT_BACKUP_OPTIONS,
...options,
};
return {
...resolved,
downloadedFiles: resolved.library && resolved.downloadedFiles,
};
};
export const hasSelectedBackupOption = (options: BackupOptions) =>
Object.values(options).some(Boolean);
export const areAllBackupOptionsSelected = (options: BackupOptions) =>
Object.values(options).every(Boolean);
+107
View File
@@ -0,0 +1,107 @@
import { getString } from '@i18n/translations';
import NativeFile from '@modules/native-file';
import {
LOCAL_PLUGIN_ID,
reloadInstalledPlugins,
} from '@plugins/pluginManager';
import { PLUGIN_STORAGE } from '@utils/Storages';
import type { ResolvedBackupManifest } from './types';
export type RestoreResult = {
novelCount: number;
failedNovelCount: number;
categoryCount: number;
failedCategoryCount: number;
settingsRestored: boolean;
failedSectionCount: number;
pluginIds: string[];
manifest: ResolvedBackupManifest;
};
export const getMissingRestorePluginIds = async (pluginIds: string[]) => {
const uniquePluginIds = [...new Set(pluginIds)].filter(
pluginId => pluginId !== LOCAL_PLUGIN_ID,
);
const pluginAvailability = await Promise.all(
uniquePluginIds.map(async pluginId => ({
pluginId,
exists: await NativeFile.exists(`${PLUGIN_STORAGE}/${pluginId}/index.js`),
})),
);
return pluginAvailability
.filter(plugin => !plugin.exists)
.map(plugin => plugin.pluginId);
};
export const finalizeRestoredPlugins = async (result: RestoreResult) => {
const failedPluginIds = result.manifest.sections.plugins
? await reloadInstalledPlugins()
: [];
return [
...new Set([
...(await getMissingRestorePluginIds(result.pluginIds)),
...failedPluginIds,
]),
];
};
export const getRestoreCompletionText = (
result: RestoreResult,
missingPluginIds: string[],
) => {
const warnings: string[] = [];
if (result.failedNovelCount > 0) {
warnings.push(
getString('backupScreen.novelsRestoreFailedSummary', {
count: result.failedNovelCount,
}),
);
}
if (result.failedCategoryCount > 0) {
warnings.push(
getString('backupScreen.categoriesRestoreFailedSummary', {
count: result.failedCategoryCount,
}),
);
}
if (!result.settingsRestored) {
warnings.push(getString('backupScreen.settingsRestoreFailedSummary'));
}
if (result.failedSectionCount > 0) {
warnings.push(
getString('backupScreen.sectionsRestoreFailedSummary', {
count: result.failedSectionCount,
}),
);
}
if (missingPluginIds.length > 0) {
warnings.push(
getString('backupScreen.missingPluginsAfterRestore', {
plugins: missingPluginIds.join(', '),
}),
);
}
if (warnings.length === 0) {
if (!result.manifest.sections.library) {
return getString('backupScreen.backupRestored');
}
return getString('backupScreen.backupRestoredSummary', {
count: result.novelCount,
});
}
if (!result.manifest.sections.library) {
return getString('backupScreen.backupRestoredSelectedWithWarnings', {
warnings: warnings.join('; '),
});
}
return getString('backupScreen.backupRestoredWithWarnings', {
count: result.novelCount,
warnings: warnings.join('; '),
});
};
+47 -8
View File
@@ -1,18 +1,31 @@
import { sleep } from '@utils/sleep';
import { download, upload } from '@api/remote';
import { getString } from '@i18n/translations';
import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils';
import {
CACHE_DIR_PATH,
clearBackupCache,
prepareBackupData,
restoreData,
} from '../utils';
import {
finalizeRestoredPlugins,
getRestoreCompletionText,
} from '../restoreResult';
import { getBackupCompletionText } from '../backupResult';
import { ZipBackupName } from '../types';
import { ROOT_STORAGE } from '@utils/Storages';
import type {
SelfHostData,
TaskProgressUpdater,
} from '@services/backgroundTasks/contracts';
import { getSelectedBackupFileSections } from '../fileSections';
import { resolveBackupOptions } from '../options';
export const createSelfHostBackup = async (
{ host, backupFolder }: SelfHostData,
{ host, backupFolder, options: requestedOptions }: SelfHostData,
setMeta: TaskProgressUpdater,
) => {
const options = resolveBackupOptions(requestedOptions);
setMeta(meta => ({
...meta,
isRunning: true,
@@ -20,7 +33,7 @@ export const createSelfHostBackup = async (
progressText: getString('backupScreen.preparingData'),
}));
await prepareBackupData(CACHE_DIR_PATH);
const backupResult = await prepareBackupData(CACHE_DIR_PATH, options);
setMeta(meta => ({
...meta,
@@ -35,17 +48,22 @@ export const createSelfHostBackup = async (
setMeta(meta => ({
...meta,
progress: 2 / 3,
progressText: getString('backupScreen.uploadingDownloadedFiles'),
progressText: getString('backupScreen.uploadingSelectedFiles'),
}));
await sleep(200);
await upload(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE);
for (const section of getSelectedBackupFileSections(options)) {
await upload(host, backupFolder, section.archiveName, section.storagePath);
}
const completionText = getBackupCompletionText(backupResult);
setMeta(meta => ({
...meta,
progress: 3 / 3,
isRunning: false,
progressText: completionText,
completionText,
}));
};
@@ -60,6 +78,7 @@ export const selfHostRestore = async (
progressText: getString('backupScreen.downloadingData'),
}));
await clearBackupCache();
await download(host, backupFolder, ZipBackupName.DATA, CACHE_DIR_PATH);
setMeta(meta => ({
@@ -70,21 +89,41 @@ export const selfHostRestore = async (
await sleep(200);
await restoreData(CACHE_DIR_PATH);
const restoreResult = await restoreData(CACHE_DIR_PATH, setMeta);
setMeta(meta => ({
...meta,
progress: 2 / 3,
progressText: getString('backupScreen.downloadingDownloadedFiles'),
progressText: getString('backupScreen.restoringSelectedFiles'),
}));
await sleep(200);
await download(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE);
if (restoreResult.manifest.formatVersion === 1) {
await download(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE);
} else {
for (const section of getSelectedBackupFileSections(
restoreResult.manifest.sections,
)) {
await download(
host,
backupFolder,
section.archiveName,
section.storagePath,
);
}
}
const missingPluginIds = await finalizeRestoredPlugins(restoreResult);
const completionText = getRestoreCompletionText(
restoreResult,
missingPluginIds,
);
setMeta(meta => ({
...meta,
progress: 3 / 3,
isRunning: false,
progressText: completionText,
completionText,
}));
};
+19
View File
@@ -1,11 +1,30 @@
import type { BackupOptions } from './options';
export enum ZipBackupName {
DATA = 'data.zip',
DOWNLOAD = 'download.zip',
NOVEL_FILES = 'novel-files.zip',
PLUGINS = 'plugins.zip',
}
export enum BackupEntryName {
VERSION = 'Version.json',
CATEGORY = 'Category.json',
SETTING = 'Setting.json',
PLUGIN_METADATA = 'Plugins.json',
NOVEL_AND_CHAPTERS = 'NovelAndChapters',
}
export type BackupManifest = {
appVersion: string;
formatVersion: 2;
sections: BackupOptions;
};
export type ResolvedBackupManifest =
| BackupManifest
| {
appVersion?: string;
formatVersion: 1;
sections: BackupOptions;
};
+252 -158
View File
@@ -14,24 +14,43 @@ import {
getCategoriesFromDb,
} from '@database/queries/CategoryQueries';
import { BackupCategory, BackupNovel } from '@database/types';
import { BackupEntryName } from './types';
import {
BackupEntryName,
type BackupManifest,
type ResolvedBackupManifest,
} from './types';
import { ROOT_STORAGE } from '@utils/Storages';
import { BACKGROUND_TASKS_STORE_KEY } from '@services/backgroundTasks/constants';
import type { TaskProgressUpdater } from '@services/backgroundTasks/contracts';
import NativeFile from '@modules/native-file';
import { showToast } from '@utils/showToast';
import { getString } from '@i18n/translations';
import type { RestoreResult } from './restoreResult';
import type { BackupResult } from './backupResult';
import {
DEFAULT_BACKUP_OPTIONS,
resolveBackupOptions,
type BackupOptions,
} from './options';
import { INSTALLED_PLUGINS_KEY } from '@plugins/pluginManager';
const APP_STORAGE_URI = 'file://' + ROOT_STORAGE;
export const CACHE_DIR_PATH =
NativeFile.ExternalCachesDirectoryPath + '/BackupData';
const backupMMKVData = () => {
export const clearBackupCache = async (cacheDirPath = CACHE_DIR_PATH) => {
if (await NativeFile.exists(cacheDirPath)) {
await NativeFile.unlink(cacheDirPath);
}
};
const backupMMKVData = (options: BackupOptions) => {
const excludeKeys = [
BACKGROUND_TASKS_STORE_KEY,
OLD_TRACKED_NOVEL_PREFIX,
SELF_HOST_BACKUP,
LAST_UPDATE_TIME,
...(options.plugins ? [] : [INSTALLED_PLUGINS_KEY]),
];
const keys = MMKVStorage.getAllKeys().filter(
key => !excludeKeys.includes(key),
@@ -56,221 +75,296 @@ const restoreMMKVData = (data: any) => {
}
};
export const prepareBackupData = async (cacheDirPath: string) => {
export const prepareBackupData = async (
cacheDirPath: string,
requestedOptions?: BackupOptions,
): Promise<BackupResult> => {
const options = resolveBackupOptions(requestedOptions);
const novelDirPath = cacheDirPath + '/' + BackupEntryName.NOVEL_AND_CHAPTERS;
if (await NativeFile.exists(novelDirPath)) {
await NativeFile.unlink(novelDirPath);
}
let failedNovelCount = 0;
let failedSectionCount = 0;
await NativeFile.mkdir(novelDirPath); // this also creates cacheDirPath
await clearBackupCache(cacheDirPath);
await NativeFile.mkdir(cacheDirPath);
// version
try {
await NativeFile.writeFile(
cacheDirPath + '/' + BackupEntryName.VERSION,
JSON.stringify({ version: version }),
);
} catch (error: any) {
showToast(
getString('backupScreen.versionFileWriteFailed', {
error: error?.message || String(error),
}),
);
throw error;
}
const manifest: BackupManifest = {
appVersion: version,
formatVersion: 2,
sections: options,
};
await NativeFile.writeFile(
cacheDirPath + '/' + BackupEntryName.VERSION,
JSON.stringify(manifest),
);
// novels
await getAllNovels().then(async novels => {
for (const novel of novels) {
try {
const chapters = await getNovelChapters(novel.id);
await NativeFile.writeFile(
novelDirPath + '/' + novel.id + '.json',
JSON.stringify({
chapters: chapters,
...novel,
cover: novel.cover?.replace(APP_STORAGE_URI, ''),
}),
);
} catch (error: any) {
showToast(
getString('backupScreen.novelBackupFailed', {
novelName: novel.name,
error: error?.message,
}),
);
if (options.library) {
await NativeFile.mkdir(novelDirPath);
await getAllNovels().then(async novels => {
for (const novel of novels) {
try {
const chapters = await getNovelChapters(novel.id);
const backedUpChapters = options.downloadedFiles
? chapters
: chapters.map(chapter => ({
...chapter,
isDownloaded: false,
}));
const isStoredCover = novel.cover?.startsWith(APP_STORAGE_URI);
await NativeFile.writeFile(
novelDirPath + '/' + novel.id + '.json',
JSON.stringify({
chapters: backedUpChapters,
...novel,
cover:
!options.downloadedFiles && isStoredCover
? null
: novel.cover?.replace(APP_STORAGE_URI, ''),
}),
);
} catch {
failedNovelCount++;
}
}
}
});
});
// categories
try {
const categories = await getCategoriesFromDb();
const novelCategories = await getAllNovelCategories();
await NativeFile.writeFile(
cacheDirPath + '/' + BackupEntryName.CATEGORY,
JSON.stringify(
categories.map(category => {
return {
...category,
novelIds: novelCategories
.filter(nc => nc.categoryId === category.id)
.map(nc => nc.novelId),
};
}),
),
);
} catch (error: any) {
showToast(
getString('backupScreen.categoryFileWriteFailed', {
error: error?.message || String(error),
}),
);
// categories
try {
const categories = await getCategoriesFromDb();
const novelCategories = await getAllNovelCategories();
await NativeFile.writeFile(
cacheDirPath + '/' + BackupEntryName.CATEGORY,
JSON.stringify(
categories.map(category => {
return {
...category,
novelIds: novelCategories
.filter(nc => nc.categoryId === category.id)
.map(nc => nc.novelId),
};
}),
),
);
} catch {
failedSectionCount++;
}
}
// settings
if (options.settings) {
try {
await NativeFile.writeFile(
cacheDirPath + '/' + BackupEntryName.SETTING,
JSON.stringify(backupMMKVData(options)),
);
} catch {
failedSectionCount++;
}
}
// installed plugin registry
if (options.plugins) {
try {
await NativeFile.writeFile(
cacheDirPath + '/' + BackupEntryName.PLUGIN_METADATA,
MMKVStorage.getString(INSTALLED_PLUGINS_KEY) ?? '[]',
);
} catch {
failedSectionCount++;
}
}
return {
failedNovelCount,
failedSectionCount,
};
};
const getBackupManifest = async (
cacheDirPath: string,
): Promise<ResolvedBackupManifest> => {
try {
await NativeFile.writeFile(
cacheDirPath + '/' + BackupEntryName.SETTING,
JSON.stringify(backupMMKVData()),
);
} catch (error: any) {
showToast(
getString('backupScreen.settingsFileWriteFailed', {
error: error?.message || String(error),
}),
const fileContent = await NativeFile.readFile(
cacheDirPath + '/' + BackupEntryName.VERSION,
);
const data = JSON.parse(fileContent) as Partial<BackupManifest> & {
version?: string;
};
if (data.formatVersion === 2 && data.sections) {
return {
appVersion: data.appVersion ?? data.version ?? '',
formatVersion: 2,
sections: resolveBackupOptions(data.sections),
};
}
return {
appVersion: data.version,
formatVersion: 1,
sections: DEFAULT_BACKUP_OPTIONS,
};
} catch {
return {
formatVersion: 1,
sections: DEFAULT_BACKUP_OPTIONS,
};
}
};
export const restoreData = async (cacheDirPath: string) => {
const updateRestoreProgress = (
setMeta: TaskProgressUpdater | undefined,
progressText: string,
) => {
setMeta?.(meta => ({
...meta,
progressText,
}));
};
export const restoreData = async (
cacheDirPath: string,
setMeta?: TaskProgressUpdater,
): Promise<RestoreResult> => {
const manifest = await getBackupManifest(cacheDirPath);
const novelDirPath = cacheDirPath + '/' + BackupEntryName.NOVEL_AND_CHAPTERS;
const pluginIds = new Set<string>();
// version
// nothing to do
// novels
showToast(getString('backupScreen.restoringNovels'));
if (manifest.sections.library) {
updateRestoreProgress(setMeta, getString('backupScreen.restoringNovels'));
}
let novelCount = 0;
let failedCount = 0;
let failedSectionCount = 0;
if (!(await NativeFile.exists(novelDirPath))) {
showToast(getString('backupScreen.novelDirectoryNotFound'));
if (!manifest.sections.library) {
// Intentionally omitted from this backup.
} else if (!(await NativeFile.exists(novelDirPath))) {
failedSectionCount++;
} else {
try {
const items = await NativeFile.readDir(novelDirPath);
for (const item of items) {
if (!item.isDirectory) {
try {
const fileContent = await NativeFile.readFile(item.path);
const backupNovel = JSON.parse(fileContent) as BackupNovel;
const items = (await NativeFile.readDir(novelDirPath)).filter(
item => !item.isDirectory,
);
for (const [index, item] of items.entries()) {
updateRestoreProgress(
setMeta,
getString('backupScreen.restoringNovelsProgress', {
current: index + 1,
total: items.length,
}),
);
try {
const fileContent = await NativeFile.readFile(item.path);
const backupNovel = JSON.parse(fileContent) as BackupNovel;
pluginIds.add(backupNovel.pluginId);
if (!backupNovel.cover?.startsWith('http')) {
backupNovel.cover = APP_STORAGE_URI + backupNovel.cover;
}
await _restoreNovelAndChapters(backupNovel);
novelCount++;
} catch (error: any) {
failedCount++;
const novelName =
item.path.split('/').pop()?.replace('.json', '') || 'Unknown';
showToast(
getString('backupScreen.novelRestoreFailed', {
novelName: novelName,
error: error?.message || String(error),
}),
);
if (!backupNovel.cover?.startsWith('http')) {
backupNovel.cover = APP_STORAGE_URI + backupNovel.cover;
}
await _restoreNovelAndChapters(backupNovel);
novelCount++;
} catch {
failedCount++;
}
}
} catch (error: any) {
showToast(
getString('backupScreen.novelDirectoryReadFailed', {
error: error?.message || String(error),
}),
);
} catch {
failedSectionCount++;
}
}
if (failedCount > 0) {
showToast(
getString('backupScreen.novelsRestoredWithErrors', {
count: novelCount,
failedCount: failedCount,
}),
);
} else {
showToast(getString('backupScreen.novelsRestored', { count: novelCount }));
}
// categories
showToast(getString('backupScreen.restoringCategories'));
if (manifest.sections.library) {
updateRestoreProgress(
setMeta,
getString('backupScreen.restoringCategories'),
);
}
const categoryFilePath = cacheDirPath + '/' + BackupEntryName.CATEGORY;
let categoryCount = 0;
let failedCategoryCount = 0;
if (!(await NativeFile.exists(categoryFilePath))) {
showToast(getString('backupScreen.categoryFileNotFound'));
if (!manifest.sections.library) {
// Intentionally omitted from this backup.
} else if (!(await NativeFile.exists(categoryFilePath))) {
failedSectionCount++;
} else {
try {
const fileContent = await NativeFile.readFile(categoryFilePath);
const categories: BackupCategory[] = JSON.parse(fileContent);
for (const category of categories) {
for (const [index, category] of categories.entries()) {
updateRestoreProgress(
setMeta,
getString('backupScreen.restoringCategoriesProgress', {
current: index + 1,
total: categories.length,
}),
);
try {
_restoreCategory(category);
await _restoreCategory(category);
categoryCount++;
} catch (error: any) {
} catch {
failedCategoryCount++;
showToast(
getString('backupScreen.categoryRestoreFailed', {
categoryName: category.name || category.id.toString(),
error: error?.message || String(error),
}),
);
}
}
} catch (error: any) {
showToast(
getString('backupScreen.categoryFileReadFailed', {
error: error?.message || String(error),
}),
);
} catch {
failedSectionCount++;
}
}
if (failedCategoryCount > 0) {
showToast(
getString('backupScreen.categoriesRestoredWithErrors', {
count: categoryCount,
failedCount: failedCategoryCount,
}),
);
} else {
showToast(
getString('backupScreen.categoriesRestored', {
count: categoryCount,
}),
);
}
// settings
showToast(getString('backupScreen.restoringSettings'));
if (manifest.sections.settings) {
updateRestoreProgress(setMeta, getString('backupScreen.restoringSettings'));
}
const settingsFilePath = cacheDirPath + '/' + BackupEntryName.SETTING;
let settingsRestored = !manifest.sections.settings;
if (!(await NativeFile.exists(settingsFilePath))) {
showToast(getString('backupScreen.settingsFileNotFound'));
if (!manifest.sections.settings) {
// Intentionally omitted from this backup.
} else if (!(await NativeFile.exists(settingsFilePath))) {
// Reported as a settings warning in the completion summary.
} else {
try {
const fileContent = await NativeFile.readFile(settingsFilePath);
const settingsData = JSON.parse(fileContent);
restoreMMKVData(settingsData);
showToast(getString('backupScreen.settingsRestored'));
} catch (error: any) {
showToast(
getString('backupScreen.settingsRestoreFailed', {
error: error?.message || String(error),
}),
);
settingsRestored = true;
} catch {
// Included in the completion warning below.
}
}
// installed plugin registry
if (manifest.formatVersion === 2 && manifest.sections.plugins) {
const pluginMetadataPath =
cacheDirPath + '/' + BackupEntryName.PLUGIN_METADATA;
if (!(await NativeFile.exists(pluginMetadataPath))) {
failedSectionCount++;
} else {
try {
const installedPlugins = await NativeFile.readFile(pluginMetadataPath);
JSON.parse(installedPlugins);
MMKVStorage.set(INSTALLED_PLUGINS_KEY, installedPlugins);
} catch {
failedSectionCount++;
}
}
}
return {
novelCount,
failedNovelCount: failedCount,
categoryCount,
failedCategoryCount,
settingsRestored,
failedSectionCount,
pluginIds: [...pluginIds],
manifest,
};
};
+5 -3
View File
@@ -83,13 +83,15 @@ export const exportEpub = async (
{ replaceIfDestinationExists: true },
);
const completionText = getString('novelScreen.epub.exportSuccess', {
chapters: result.chapterCount.toString(),
});
updateProgress(meta => ({
...meta,
isRunning: false,
progress: 1,
progressText: getString('novelScreen.epub.exportSuccess', {
chapters: result.chapterCount.toString(),
}),
progressText: completionText,
completionText,
}));
} catch (error) {
updateProgress(meta => ({
+1
View File
@@ -210,6 +210,7 @@ export const migrateNovel = async (
data: {
novelName: toNovel.name,
novelId: toNovel.id,
pluginId: toNovel.pluginId,
chapters: chaptersToDownload,
},
});
@@ -78,6 +78,7 @@ const updateNovelTotalPages = async (novelId: number, totalPages: number) => {
* Distinguishes between new chapters (triggers download) and existing chapters (updates metadata).
*/
const updateNovelChapters = async (
pluginId: string,
novelName: string,
novelId: number,
chapters: ChapterItem[],
@@ -144,6 +145,7 @@ const updateNovelChapters = async (
data: {
novelName,
novelId,
pluginId,
chapters: insertedNewChapters.map(insertedChapter => ({
chapterId: insertedChapter.id,
chapterName:
@@ -196,6 +198,7 @@ const updateNovel = async (
await updateNovelTotalPages(novelId, novel.totalPages);
}
await updateNovelChapters(
pluginId,
novel.name,
novelId,
novel.chapters || [],
@@ -217,6 +220,7 @@ const updateNovel = async (
String(oldTotalPages),
);
await updateNovelChapters(
pluginId,
novel.name,
novelId,
sourcePage.chapters || [],
@@ -232,6 +236,7 @@ const updateNovel = async (
try {
const sourcePage = await fetchPage(pluginId, novelPath, String(page));
await updateNovelChapters(
pluginId,
novel.name,
novelId,
sourcePage.chapters || [],
@@ -260,6 +265,7 @@ const updateNovelPage = async (
const sourcePage = await fetchPage(pluginId, novelPath, page);
await updateNovelChapters(
pluginId,
novelName,
novelId,
sourcePage.chapters || [],