Explorar o código

feat(i18n): standardize English and Simplified Chinese localization across UI

feat(i18n): standardize English and Simplified Chinese UI strings

- Move hardcoded user-facing text from Compose screens and dialogs into string resources
- Add and refine English and zh-rCN translations for settings, theme, about, backup, TOC, and book info flows
- Keep internal logs, exceptions, parser text, and comments unchanged unless user-facing
- Preserve existing behavior while improving localization consistency
dat-bi hai 5 meses
pai
achega
7107b35728
Modificáronse 34 ficheiros con 777 adicións e 381 borrados
  1. 1 0
      .gitignore
  2. 6 3
      app/src/main/java/io/legado/app/ui/about/AboutScreen.kt
  3. 5 5
      app/src/main/java/io/legado/app/ui/about/AboutSheets.kt
  4. 9 9
      app/src/main/java/io/legado/app/ui/about/AboutViewModel.kt
  5. 9 5
      app/src/main/java/io/legado/app/ui/about/MiuixAboutScreen.kt
  6. 4 4
      app/src/main/java/io/legado/app/ui/about/UpdateDialog.kt
  7. 29 20
      app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt
  8. 14 9
      app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditViewModel.kt
  9. 1 1
      app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt
  10. 60 30
      app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt
  11. 28 13
      app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt
  12. 6 2
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt
  13. 4 3
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleViewModel.kt
  14. 28 17
      app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt
  15. 3 2
      app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigViewModel.kt
  16. 10 6
      app/src/main/java/io/legado/app/ui/config/coverConfig/CoverConfigScreen.kt
  17. 1 1
      app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt
  18. 8 8
      app/src/main/java/io/legado/app/ui/config/customTheme/CustomThemeScreen.kt
  19. 3 3
      app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt
  20. 1 1
      app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfigScreen.kt
  21. 1 1
      app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt
  22. 4 2
      app/src/main/java/io/legado/app/ui/config/themeConfig/LabelColorManageSheet.kt
  23. 16 9
      app/src/main/java/io/legado/app/ui/config/themeConfig/NavIconManageSheet.kt
  24. 27 23
      app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt
  25. 1 1
      app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigViewModel.kt
  26. 40 39
      app/src/main/java/io/legado/app/ui/config/themeManage/EditThemeSheet.kt
  27. 28 28
      app/src/main/java/io/legado/app/ui/config/themeManage/ThemeManageScreen.kt
  28. 3 1
      app/src/main/java/io/legado/app/ui/widget/components/AppFloatingActionButton.kt
  29. 6 4
      app/src/main/java/io/legado/app/ui/widget/components/AppPullToRefresh.kt
  30. 8 2
      app/src/main/java/io/legado/app/ui/widget/components/CollapsibleHeader.kt
  31. 2 2
      app/src/main/java/io/legado/app/ui/widget/components/dialog/ColorPickerSheet.kt
  32. 142 0
      app/src/main/res/values-zh-rCN/strings.xml
  33. 8 8
      app/src/main/res/values/arrays.xml
  34. 261 119
      app/src/main/res/values/strings.xml

+ 1 - 0
.gitignore

@@ -17,3 +17,4 @@ package-lock.json
 # Kotlin 2.0
 .kotlin/
 /.vscode/settings.json
+/.codex/skills

+ 6 - 3
app/src/main/java/io/legado/app/ui/about/AboutScreen.kt

@@ -85,6 +85,9 @@ private fun MaterialAboutScreen(
     versionName: String,
 ) {
     val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
+    val privacyPolicyTitle = stringResource(R.string.about_privacy_policy_title)
+    val licenseTitle = stringResource(R.string.about_license_title)
+    val disclaimerTitle = stringResource(R.string.about_disclaimer_title)
 
     AppScaffold(
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
@@ -178,7 +181,7 @@ private fun MaterialAboutScreen(
                         onClick = {
                             onIntent(
                                 AboutIntent.ShowMdFile(
-                                    "隐私政策",
+                                    privacyPolicyTitle,
                                     "privacyPolicy.md"
                                 )
                             )
@@ -188,13 +191,13 @@ private fun MaterialAboutScreen(
                 SettingItemWithDivider {
                     SettingItem(
                         title = stringResource(R.string.license),
-                        onClick = { onIntent(AboutIntent.ShowMdFile("许可证", "LICENSE.md")) }
+                        onClick = { onIntent(AboutIntent.ShowMdFile(licenseTitle, "LICENSE.md")) }
                     )
                 }
                 SettingItemWithDivider {
                     SettingItem(
                         title = stringResource(R.string.disclaimer),
-                        onClick = { onIntent(AboutIntent.ShowMdFile("免责声明", "disclaimer.md")) }
+                        onClick = { onIntent(AboutIntent.ShowMdFile(disclaimerTitle, "disclaimer.md")) }
                     )
                 }
                 SettingItemWithDivider {

+ 5 - 5
app/src/main/java/io/legado/app/ui/about/AboutSheets.kt

@@ -80,7 +80,7 @@ fun UpdateSheet(
 ) {
     val title = when (mode) {
         UpdateMode.UPDATE -> stringResource(R.string.check_update)
-        UpdateMode.VIEW_LOG -> "已经更新至"
+        UpdateMode.VIEW_LOG -> stringResource(R.string.about_installed_version_title)
     }
 
     AppModalBottomSheet(
@@ -99,7 +99,7 @@ fun UpdateSheet(
                     verticalAlignment = Alignment.CenterVertically
                 ) {
                     AppText(
-                        text = "当前版本",
+                        text = stringResource(R.string.about_current_version),
                         style = LegadoTheme.typography.bodyMedium,
                     )
                     Spacer(modifier = Modifier.width(8.dp))
@@ -114,7 +114,7 @@ fun UpdateSheet(
                     verticalAlignment = Alignment.CenterVertically
                 ) {
                     AppText(
-                        text = "新版本",
+                        text = stringResource(R.string.about_new_version),
                         style = LegadoTheme.typography.bodyMedium,
                     )
                     Spacer(modifier = Modifier.width(8.dp))
@@ -144,7 +144,7 @@ fun UpdateSheet(
                     verticalAlignment = Alignment.CenterVertically
                 ) {
                     AppText(
-                        text = "渠道",
+                        text = stringResource(R.string.about_update_channel),
                         style = LegadoTheme.typography.bodyMedium,
                     )
                     Spacer(modifier = Modifier.width(8.dp))
@@ -182,7 +182,7 @@ fun UpdateSheet(
                 Spacer(modifier = Modifier.height(16.dp))
                 PrimaryButton(
                     onClick = onStartDownload,
-                    text = "更新",
+                    text = stringResource(R.string.about_update_action),
                     modifier = Modifier.fillMaxWidth()
                 )
             }

+ 9 - 9
app/src/main/java/io/legado/app/ui/about/AboutViewModel.kt

@@ -152,17 +152,17 @@ class AboutViewModel(application: Application) : BaseViewModel(application) {
     private fun saveLog() {
         execute {
             val backupPath = AppConfig.backupPath ?: run {
-                _effects.tryEmit(AboutEffect.ShowToast("未设置备份目录"))
+                _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_backup_dir_not_set)))
                 return@execute
             }
             if (!AppConfig.recordLog) {
-                _effects.tryEmit(AboutEffect.ShowToast("未开启日志记录,请去其他设置里打开记录日志"))
+                _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_log_recording_disabled)))
                 delay(3000)
             }
             val doc = FileDoc.fromUri(backupPath.toUri(), true)
             copyLogs(doc)
             copyHeapDump(doc)
-            _effects.tryEmit(AboutEffect.ShowToast("已保存至备份目录"))
+            _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_saved_to_backup_dir)))
         }.onError {
             AppLog.put("保存日志出错\n${it.localizedMessage}", it, true)
         }
@@ -171,21 +171,21 @@ class AboutViewModel(application: Application) : BaseViewModel(application) {
     private fun createHeapDump() {
         execute {
             val backupPath = AppConfig.backupPath ?: run {
-                _effects.tryEmit(AboutEffect.ShowToast("未设置备份目录"))
+                _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_backup_dir_not_set)))
                 return@execute
             }
             if (!AppConfig.recordHeapDump) {
-                _effects.tryEmit(AboutEffect.ShowToast("未开启堆转储记录,请去其他设置里打开记录堆转储"))
+                _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_heap_dump_recording_disabled)))
                 delay(3000)
             }
-            _effects.tryEmit(AboutEffect.ShowToast("开始创建堆转储"))
+            _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_heap_dump_creating)))
             System.gc()
             CrashHandler.doHeapDump(true)
             val doc = FileDoc.fromUri(backupPath.toUri(), true)
             if (!copyHeapDump(doc)) {
-                _effects.tryEmit(AboutEffect.ShowToast("未找到堆转储文件"))
+                _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_heap_dump_not_found)))
             } else {
-                _effects.tryEmit(AboutEffect.ShowToast("已保存至备份目录"))
+                _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_saved_to_backup_dir)))
             }
         }.onError {
             AppLog.put("保存堆转储失败\n${it.localizedMessage}", it)
@@ -197,7 +197,7 @@ class AboutViewModel(application: Application) : BaseViewModel(application) {
         if (sheet is AboutSheet.Update) {
             val info = sheet.updateInfo
             if (info.downloadUrl.isBlank() || info.fileName.isBlank()) {
-                _effects.tryEmit(AboutEffect.ShowToast("下载信息不完整"))
+                _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_download_info_incomplete)))
             } else {
                 _effects.tryEmit(AboutEffect.StartDownload(info.downloadUrl, info.fileName))
             }

+ 9 - 5
app/src/main/java/io/legado/app/ui/about/MiuixAboutScreen.kt

@@ -89,6 +89,7 @@ fun MiuixAboutScreen(
     val topAppBarScrollBehavior = MiuixScrollBehavior()
     val lazyListState = rememberLazyListState()
     val isWideScreen = shouldShowSplitPane()
+    val licenseTitle = stringResource(R.string.about_license_title)
 
     val scrollProgress by remember {
         derivedStateOf {
@@ -119,7 +120,7 @@ fun MiuixAboutScreen(
         if (scrollProgress == 1f) MiuixTheme.colorScheme.surface else Color.Transparent
     }
 
-    val navigator = remember {
+    val navigator = remember(licenseTitle) {
         object : MiuixNavigator {
             override fun pop() {
                 onBack()
@@ -127,7 +128,7 @@ fun MiuixAboutScreen(
 
             override fun push(route: Any) {
                 if (route == "License") {
-                    onIntent(AboutIntent.ShowMdFile("许可证", "LICENSE.md"))
+                    onIntent(AboutIntent.ShowMdFile(licenseTitle, "LICENSE.md"))
                 }
             }
         }
@@ -180,6 +181,9 @@ private fun AboutContent(
 ) {
     val appState = LocalAppState.current
     val isWideScreen = LocalIsWideScreen.current
+    val privacyPolicyTitle = stringResource(R.string.about_privacy_policy_title)
+    val licenseTitle = stringResource(R.string.about_license_title)
+    val disclaimerTitle = stringResource(R.string.about_disclaimer_title)
 
     val backdrop = rememberBlurBackdrop()
     var blurRadius by remember { mutableFloatStateOf(60f) }
@@ -427,7 +431,7 @@ private fun AboutContent(
                             onClick = {
                                 onIntent(
                                     AboutIntent.ShowMdFile(
-                                        "隐私政策",
+                                        privacyPolicyTitle,
                                         "privacyPolicy.md"
                                     )
                                 )
@@ -435,14 +439,14 @@ private fun AboutContent(
                         )
                         ArrowPreference(
                             title = stringResource(R.string.license),
-                            onClick = { onIntent(AboutIntent.ShowMdFile("许可证", "LICENSE.md")) },
+                            onClick = { onIntent(AboutIntent.ShowMdFile(licenseTitle, "LICENSE.md")) },
                         )
                         ArrowPreference(
                             title = stringResource(R.string.disclaimer),
                             onClick = {
                                 onIntent(
                                     AboutIntent.ShowMdFile(
-                                        "免责声明",
+                                        disclaimerTitle,
                                         "disclaimer.md"
                                     )
                                 )

+ 4 - 4
app/src/main/java/io/legado/app/ui/about/UpdateDialog.kt

@@ -55,7 +55,7 @@ class UpdateDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_update) {
 
         val updateBody = arguments?.getString("updateBody")
         if (updateBody.isNullOrBlank()) {
-            toastOnUi("没有数据")
+            toastOnUi(R.string.about_no_data)
             dismiss()
             return
         }
@@ -77,16 +77,16 @@ class UpdateDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_update) {
                 binding.tvUrl.text = url
                 binding.btnUpdate.setOnClickListener {
                     if (url.isNullOrBlank() || fileName.isNullOrBlank()) {
-                        toastOnUi("下载信息不完整")
+                        toastOnUi(R.string.about_download_info_incomplete)
                         return@setOnClickListener
                     }
                     Download.start(requireContext(), url, fileName)
-                    toastOnUi("开始下载: $fileName")
+                    toastOnUi(getString(R.string.about_start_download, fileName))
                 }
             }
 
             Mode.VIEW_LOG -> {
-                binding.bottomSheetTitle.text = "已经更新至"
+                binding.bottomSheetTitle.setText(R.string.about_installed_version_title)
                 binding.tvVersion.text = BuildConfig.VERSION_NAME
                 binding.btnUpdate.gone()
                 binding.llCurrent.gone()

+ 29 - 20
app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt

@@ -178,7 +178,11 @@ fun BookInfoEditContent(
                 }
                 Spacer(modifier = Modifier.height(4.dp))
                 BookTypeDropdown(
-                    bookTypes = uiState.bookTypes,
+                    bookTypes = listOf(
+                        BookInfoEditType.TEXT to stringResource(R.string.book_type_text),
+                        BookInfoEditType.AUDIO to stringResource(R.string.book_type_audio),
+                        BookInfoEditType.IMAGE to stringResource(R.string.book_type_image)
+                    ),
                     selectedType = uiState.selectedType,
                     onTypeSelected = { viewModel.onBookTypeChange(it) }
                 )
@@ -186,8 +190,8 @@ fun BookInfoEditContent(
         }
         Spacer(modifier = Modifier.height(16.dp))
         SwitchSettingItem(
-            title = "固定书籍类型",
-            description = "书籍更新后不覆盖书籍类型",
+            title = stringResource(R.string.fixed_book_type),
+            description = stringResource(R.string.fixed_book_type_summary),
             checked = uiState.fixedType,
             onCheckedChange = { viewModel.onFixedTypeChange(it) }
         )
@@ -201,7 +205,7 @@ fun BookInfoEditContent(
         AppTextField(
             value = uiState.name,
             onValueChange = { viewModel.onNameChange(it) },
-            label = "书名",
+            label = stringResource(R.string.book_name),
             backgroundColor = inputBackgroundColor,
             modifier = Modifier.fillMaxWidth()
         )
@@ -209,7 +213,7 @@ fun BookInfoEditContent(
         AppTextField(
             value = uiState.author,
             onValueChange = { viewModel.onAuthorChange(it) },
-            label = "作者",
+            label = stringResource(R.string.author),
             backgroundColor = inputBackgroundColor,
             modifier = Modifier.fillMaxWidth()
         )
@@ -217,7 +221,7 @@ fun BookInfoEditContent(
         AppTextField(
             value = uiState.coverUrl ?: "",
             onValueChange = { viewModel.onCoverUrlChange(it) },
-            label = "封面链接",
+            label = stringResource(R.string.cover_url),
             backgroundColor = inputBackgroundColor,
             modifier = Modifier.fillMaxWidth()
         )
@@ -232,7 +236,7 @@ fun BookInfoEditContent(
         AppTextField(
             value = uiState.intro ?: "",
             onValueChange = { viewModel.onIntroChange(it) },
-            label = "简介",
+            label = stringResource(R.string.book_intro),
             backgroundColor = inputBackgroundColor,
             modifier = Modifier.fillMaxWidth()
         )
@@ -240,7 +244,7 @@ fun BookInfoEditContent(
         AppTextField(
             value = uiState.remark ?: "",
             onValueChange = { viewModel.onRemarkChange(it) },
-            label = "备注",
+            label = stringResource(R.string.book_remark),
             backgroundColor = inputBackgroundColor,
             modifier = Modifier.fillMaxWidth()
         )
@@ -250,18 +254,19 @@ fun BookInfoEditContent(
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun BookTypeDropdown(
-    bookTypes: List<String>,
-    selectedType: String,
-    onTypeSelected: (String) -> Unit
+    bookTypes: List<Pair<BookInfoEditType, String>>,
+    selectedType: BookInfoEditType,
+    onTypeSelected: (BookInfoEditType) -> Unit
 ) {
     var expanded by remember { mutableStateOf(false) }
+    val selectedTypeLabel = bookTypes.firstOrNull { it.first == selectedType }?.second.orEmpty()
 
     val textFieldState = rememberTextFieldState(
-        initialText = selectedType
+        initialText = selectedTypeLabel
     )
 
-    LaunchedEffect(selectedType) {
-        textFieldState.setTextAndPlaceCursorAtEnd(selectedType)
+    LaunchedEffect(selectedTypeLabel) {
+        textFieldState.setTextAndPlaceCursorAtEnd(selectedTypeLabel)
     }
 
     val bookInfoInputColor = ThemeConfig.bookInfoInputColor
@@ -280,7 +285,7 @@ fun BookTypeDropdown(
             state = textFieldState,
             readOnly = true,
             lineLimits = TextFieldLineLimits.SingleLine,
-            label = "书籍类型",
+            label = stringResource(R.string.book_type_label),
             backgroundColor = inputBackgroundColor,
             trailingIcon = {
                 ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
@@ -297,11 +302,11 @@ fun BookTypeDropdown(
             expanded = expanded,
             onDismissRequest = { expanded = false }
         ) {
-            bookTypes.forEach { option ->
+            bookTypes.forEach { (type, label) ->
                 RoundDropdownMenuItem(
-                    text = option,
+                    text = label,
                     onClick = {
-                        onTypeSelected(option)
+                        onTypeSelected(type)
                         expanded = false
                     }
                 )
@@ -387,12 +392,16 @@ fun KindEditor(
         AppAlertDialog(
             show = true,
             onDismissRequest = { editingIndex = null },
-            title = if (isAdding) "添加标签" else "编辑标签",
+            title = if (isAdding) {
+                stringResource(R.string.add_tag)
+            } else {
+                stringResource(R.string.edit_tag)
+            },
             content = {
                 AppTextField(
                     value = editText,
                     onValueChange = { editText = it },
-                    label = "标签",
+                    label = stringResource(R.string.tag),
                     backgroundColor = backgroundColor,
                     modifier = Modifier.fillMaxWidth()
                 )

+ 14 - 9
app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditViewModel.kt

@@ -25,6 +25,12 @@ import kotlinx.coroutines.flow.asStateFlow
 import java.io.File
 import java.io.FileOutputStream
 
+enum class BookInfoEditType {
+    TEXT,
+    AUDIO,
+    IMAGE
+}
+
 data class BookInfoEditUiState(
     val name: String = "",
     val author: String = "",
@@ -33,8 +39,7 @@ data class BookInfoEditUiState(
     val remark: String? = null,
     val kindList: List<String> = emptyList(),
     val originalKindList: List<String> = emptyList(),
-    val selectedType: String = "文本",
-    val bookTypes: List<String> = listOf("文本", "音频", "图片"),
+    val selectedType: BookInfoEditType = BookInfoEditType.TEXT,
     val fixedType: Boolean = false,
     val book: Book? = null,
 )
@@ -49,9 +54,9 @@ class BookInfoEditViewModel(application: Application) : BaseViewModel(applicatio
             book = appDb.bookDao.getBook(bookUrl)
             book?.let {
                 val selectedTypeIndex = when {
-                    it.isImage -> 2
-                    it.isAudio -> 1
-                    else -> 0
+                    it.isImage -> BookInfoEditType.IMAGE
+                    it.isAudio -> BookInfoEditType.AUDIO
+                    else -> BookInfoEditType.TEXT
                 }
                 val kinds =
                     it.kind?.split(",", "\n")?.filter { kind -> kind.isNotBlank() }.orEmpty()
@@ -63,7 +68,7 @@ class BookInfoEditViewModel(application: Application) : BaseViewModel(applicatio
                     remark = it.remark,
                     kindList = kinds,
                     originalKindList = kinds,
-                    selectedType = _uiState.value.bookTypes[selectedTypeIndex],
+                    selectedType = selectedTypeIndex,
                     fixedType = it.config.fixedType,
                     book = it
                 )
@@ -99,7 +104,7 @@ class BookInfoEditViewModel(application: Application) : BaseViewModel(applicatio
         _uiState.value = _uiState.value.copy(kindList = kindList)
     }
 
-    fun onBookTypeChange(bookType: String) {
+    fun onBookTypeChange(bookType: BookInfoEditType) {
         _uiState.value = _uiState.value.copy(selectedType = bookType)
     }
 
@@ -121,8 +126,8 @@ class BookInfoEditViewModel(application: Application) : BaseViewModel(applicatio
                 book.remark = currentState.remark
                 val local = if (book.isLocal) BookType.local else 0
                 val bookType = when (currentState.selectedType) {
-                    currentState.bookTypes[2] -> BookType.image or local
-                    currentState.bookTypes[1] -> BookType.audio or local
+                    BookInfoEditType.IMAGE -> BookType.image or local
+                    BookInfoEditType.AUDIO -> BookType.audio or local
                     else -> BookType.text or local
                 }
                 book.removeType(BookType.local, BookType.image, BookType.audio, BookType.text)

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt

@@ -40,4 +40,4 @@ class TocActivity : BaseComposeActivity() {
             }
         )
     }
-}
+}

+ 60 - 30
app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt

@@ -71,12 +71,14 @@ import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.zIndex
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import io.legado.app.R
 import io.legado.app.data.entities.Bookmark
 import io.legado.app.help.book.isLocal
 import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity
@@ -151,6 +153,15 @@ fun TocScreen(
 
     val useReplace = viewModel.useReplace
     val showWordCount = viewModel.showWordCount
+    val bookmarkManagementTitle = stringResource(R.string.bookmark_management)
+    val locateCurrentReadingText = stringResource(R.string.locate_current_reading)
+    val moveToTopText = stringResource(R.string.move_to_top)
+    val moveToBottomText = stringResource(R.string.move_to_bottom)
+    val downloadAllText = stringResource(R.string.download_all)
+    val invertSelectionText = stringResource(R.string.invert_selection)
+    val selectFollowingText = stringResource(R.string.select_following)
+    val addBookmarkText = stringResource(R.string.bookmark_add)
+    val bookmarkDefaultFileName = stringResource(R.string.bookmark)
 
     val topBarTitle = remember(
         pagerState.currentPage,
@@ -162,7 +173,7 @@ fun TocScreen(
                 book?.durChapterTitle?.takeIf { it.isNotBlank() } ?: (book?.name ?: "")
             }
 
-            1 -> "书签管理"
+            1 -> bookmarkManagementTitle
             else -> book?.name ?: ""
         }
     }
@@ -206,9 +217,15 @@ fun TocScreen(
         }
     }
 
-    val fabItems = remember(state.items) {
+    val fabItems = remember(
+        state.items,
+        locateCurrentReadingText,
+        moveToTopText,
+        moveToBottomText,
+        downloadAllText
+    ) {
         listOf(
-            FabMenuItem(Icons.Default.LocationOn, "定位至当前阅读") {
+            FabMenuItem(Icons.Default.LocationOn, locateCurrentReadingText) {
                 scope.launch {
                     val target = state.items.indexOfFirst { it.isDur }
                     if (target != -1) {
@@ -219,32 +236,37 @@ fun TocScreen(
                     }
                 }
             },
-            FabMenuItem(Icons.Default.VerticalAlignTop, "移至顶部") {
+            FabMenuItem(Icons.Default.VerticalAlignTop, moveToTopText) {
                 scope.launch { listState.animateScrollToItem(0) }
             },
-            FabMenuItem(Icons.Default.VerticalAlignBottom, "移至底部") {
+            FabMenuItem(Icons.Default.VerticalAlignBottom, moveToBottomText) {
                 scope.launch { listState.animateScrollToItem(state.items.size) }
             },
-            FabMenuItem(Icons.Default.DownloadForOffline, "下载全部") {
+            FabMenuItem(Icons.Default.DownloadForOffline, downloadAllText) {
                 viewModel.downloadAll()
             }
         )
     }
 
-    val selectionSecondaryActions = remember(state.selectedIds) {
+    val selectionSecondaryActions = remember(
+        state.selectedIds,
+        invertSelectionText,
+        selectFollowingText,
+        addBookmarkText
+    ) {
         listOf(
             ActionItem(
-                text = "反选",
+                text = invertSelectionText,
                 icon = Icons.Default.Refresh,
                 onClick = { viewModel.invertSelection() }
             ),
             ActionItem(
-                text = "选择后续",
+                text = selectFollowingText,
                 icon = Icons.Default.ExpandMore,
                 onClick = { viewModel.selectFromLast() }
             ),
             ActionItem(
-                text = "添加书签",
+                text = addBookmarkText,
                 icon = Icons.Default.BookmarkAdd,
                 onClick = { viewModel.addBookmarksForSelected() }
             )
@@ -332,35 +354,35 @@ fun TocScreen(
                 onBackClick = onBackClick,
                 onSearchToggle = { viewModel.setSearchMode(it) },
                 onSearchQueryChange = { viewModel.setSearchKey(it) },
-                searchPlaceholder = "搜索章节...",
+                searchPlaceholder = stringResource(R.string.search_chapters),
                 onClearSelection = { viewModel.clearSelection() },
                 dropDownMenuContent = { dismiss ->
                     when (pagerState.currentPage) {
                         0 -> {
                             RoundDropdownMenuItem(
-                                text = "使用替换规则",
+                                text = stringResource(R.string.use_replace_rule),
                                 isSelected = useReplace,
                                 onClick = { viewModel.toggleUseReplace() }
                             )
                             RoundDropdownMenuItem(
-                                text = "显示字数",
+                                text = stringResource(R.string.show_word_count),
                                 isSelected = showWordCount,
                                 onClick = { viewModel.toggleShowWordCount() }
                             )
                             RoundDropdownMenuItem(
-                                text = "反转目录",
+                                text = stringResource(R.string.reverse_toc),
                                 onClick = { viewModel.reverseToc() }
                             )
                             PillDivider()
                             RoundDropdownMenuItem(
-                                text = "替换规则",
+                                text = stringResource(R.string.replace_rule_title),
                                 onClick = {
                                     onOpenReplaceRule(null)
                                     dismiss()
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = "新建替换规则",
+                                text = stringResource(R.string.add_replace_rule),
                                 onClick = {
                                     val scopes = mutableListOf<String>()
                                     book?.name?.let { scopes.add(it) }
@@ -378,9 +400,9 @@ fun TocScreen(
                                 }
                             )
                             if (book?.isLocal == true) {
-                                PillHeaderDivider(title = "本地书籍选项")
+                                PillHeaderDivider(title = stringResource(R.string.local_book_options))
                                 RoundDropdownMenuItem(
-                                    text = "本地书籍目录规则",
+                                    text = stringResource(R.string.local_book_toc_rule),
                                     onClick = {
                                         val intent =
                                             Intent(context, TxtTocRuleActivity::class.java).apply {
@@ -391,7 +413,7 @@ fun TocScreen(
                                     }
                                 )
                                 RoundDropdownMenuItem(
-                                    text = "拆分超长章节",
+                                    text = stringResource(R.string.split_long_chapters),
                                     isSelected = viewModel.isSplitLongChapter,
                                     onClick = {
                                         viewModel.toggleSplitLongChapter()
@@ -403,25 +425,27 @@ fun TocScreen(
 
                         else -> {
                             RoundDropdownMenuItem(
-                                text = "导出书签为JSON",
+                                text = stringResource(R.string.export_bookmarks_json),
                                 onClick = {
                                     val dateFormat = SimpleDateFormat(
                                         "yyyyMMdd_HHmm",
                                         Locale.getDefault()
                                     ).format(Date())
-                                    val initialName = "${book?.name ?: "书签"}_$dateFormat.json"
+                                    val initialName =
+                                        "${book?.name ?: bookmarkDefaultFileName}_$dateFormat.json"
                                     exportLauncher.launch(initialName)
                                     dismiss()
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = "导出书签为MarkDown",
+                                text = stringResource(R.string.export_bookmarks_markdown),
                                 onClick = {
                                     val dateFormat = SimpleDateFormat(
                                         "yyyyMMdd_HHmm",
                                         Locale.getDefault()
                                     ).format(Date())
-                                    val initialName = "${book?.name ?: "书签"}_$dateFormat.md"
+                                    val initialName =
+                                        "${book?.name ?: bookmarkDefaultFileName}_$dateFormat.md"
                                     exportLauncher.launch(initialName)
                                     dismiss()
                                 }
@@ -437,7 +461,10 @@ fun TocScreen(
                         verticalAlignment = Alignment.CenterVertically
                     ) {
                         AppTabRow(
-                            tabTitles = listOf("目录", "书签"),
+                            tabTitles = listOf(
+                                stringResource(R.string.chapter_list),
+                                stringResource(R.string.bookmark)
+                            ),
                             selectedTabIndex = pagerState.currentPage,
                             onTabSelected = { index ->
                                 scope.launch {
@@ -453,20 +480,20 @@ fun TocScreen(
                                     checked = showVolumeMenu,
                                     onCheckedChange = { showVolumeMenu = it },
                                     imageVector = Icons.AutoMirrored.Filled.FormatListBulleted,
-                                    contentDescription = "卷管理"
+                                    contentDescription = stringResource(R.string.volume_management)
                                 )
                                 RoundDropdownMenu(
                                     expanded = showVolumeMenu,
                                     onDismissRequest = { showVolumeMenu = false }
                                 ) {
                                     RoundDropdownMenuItem(
-                                        text = "展开所有卷",
+                                        text = stringResource(R.string.expand_volume),
                                         onClick = {
                                             viewModel.expandAllVolumes(); showVolumeMenu = false
                                         }
                                     )
                                     RoundDropdownMenuItem(
-                                        text = "收起所有卷",
+                                        text = stringResource(R.string.coll_volume),
                                         onClick = {
                                             viewModel.collapseAllVolumes(); showVolumeMenu = false
                                         }
@@ -475,7 +502,7 @@ fun TocScreen(
                                     val volumeItems =
                                         remember(state.items) { state.items.filter { it.isVolume } }
                                     if (volumeItems.isNotEmpty()) {
-                                        PillHeaderDivider(title = "快速跳转")
+                                        PillHeaderDivider(title = stringResource(R.string.quick_jump))
                                         volumeItems.forEach { uiItem ->
                                             RoundDropdownMenuItem(
                                                 text = uiItem.title,
@@ -528,7 +555,10 @@ fun TocScreen(
                     onSelectAll = { viewModel.selectAll() },
                     onSelectInvert = { viewModel.invertSelection() },
                     primaryAction = ActionItem(
-                        text = "下载已选 (${state.selectedIds.size})",
+                        text = stringResource(
+                            R.string.download_selected_count,
+                            state.selectedIds.size
+                        ),
                         icon = Icons.Default.Download,
                         onClick = { viewModel.downloadSelected() }
                     ),
@@ -818,7 +848,7 @@ fun BookmarkListContent(
             contentAlignment = Alignment.Center
         ) {
             EmptyMessage(
-                message = "暂无书签"
+                message = stringResource(R.string.no_bookmark)
             )
         }
     } else {

+ 28 - 13
app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt

@@ -7,6 +7,7 @@ import androidx.compose.runtime.snapshotFlow
 
 import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.viewModelScope
+import io.legado.app.R
 import io.legado.app.base.BaseRuleViewModel
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
@@ -424,9 +425,13 @@ class TocViewModel(
         val book = bookState.value ?: return
         book.tocUrl = newRegex
         upBookTocRule(book) { error ->
-            if (error != null) context.toastOnUi("更新目录规则失败: ${error.localizedMessage}")
+            if (error != null) {
+                context.toastOnUi(
+                    context.getString(R.string.toc_rule_update_failed, error.localizedMessage)
+                )
+            }
             else {
-                context.toastOnUi("目录规则已更新")
+                context.toastOnUi(R.string.toc_rule_updated)
                 if (ReadBook.book?.bookUrl == book.bookUrl) ReadBook.upMsg(null)
             }
         }
@@ -437,8 +442,14 @@ class TocViewModel(
         val newState = !isSplitLongChapter
         book.setSplitLongChapter(newState)
         upBookTocRule(book) { error ->
-            if (error != null) context.toastOnUi("设置失败: ${error.localizedMessage}")
-            else context.toastOnUi(if (newState) "已开启长章节拆分" else "已关闭长章节拆分")
+            if (error != null) {
+                context.toastOnUi(context.getString(R.string.setting_failed, error.localizedMessage))
+            } else {
+                context.toastOnUi(
+                    if (newState) R.string.split_long_chapters_enabled
+                    else R.string.split_long_chapters_disabled
+                )
+            }
         }
     }
 
@@ -467,16 +478,16 @@ class TocViewModel(
             val book = bookState.value ?: return@launch
             val bookmarks = appDb.bookmarkDao.getByBook(book.name, book.author)
             if (bookmarks.isEmpty()) {
-                context.toastOnUi("没有可导出的书签")
+                context.toastOnUi(R.string.no_bookmarks_to_export)
                 return@launch
             }
             BookmarkExporter.exportToUri(
                 context = getApplication(), fileUri = fileUri, bookmarks = bookmarks,
                 isMd = isMd, bookName = book.name, author = book.author
             )
-            context.toastOnUi("保存成功")
+            context.toastOnUi(R.string.save_success)
         } catch (e: Exception) {
-            context.toastOnUi("保存失败: ${e.message}")
+            context.toastOnUi(context.getString(R.string.save_failed_with_error, e.message))
         }
     }
 
@@ -495,7 +506,7 @@ class TocViewModel(
             .toList()
 
         if (selectedItems.isEmpty()) {
-            context.toastOnUi("请选择章节")
+            context.toastOnUi(R.string.select_chapters)
             return@launch
         }
 
@@ -512,7 +523,7 @@ class TocViewModel(
         }
 
         appDb.bookmarkDao.insert(*bookmarks.toTypedArray())
-        context.toastOnUi("已添加 ${bookmarks.size} 个书签")
+        context.toastOnUi(context.getString(R.string.bookmarks_added_count, bookmarks.size))
         withContext(Dispatchers.Main) {
             clearSelection()
         }
@@ -525,7 +536,9 @@ class TocViewModel(
         execute {
             cacheBookChaptersUseCase.execute(book.bookUrl, indices)
         }.onSuccess { count ->
-            getApplication<Application>().toastOnUi("开始下载 $count 个章节")
+            getApplication<Application>().toastOnUi(
+                context.getString(R.string.start_downloading_chapters, count)
+            )
             clearSelection()
         }
     }
@@ -535,7 +548,7 @@ class TocViewModel(
         execute {
             cacheBookChaptersUseCase.execute(book.bookUrl, listOf(index))
         }.onSuccess {
-            getApplication<Application>().toastOnUi("开始下载章节")
+            getApplication<Application>().toastOnUi(R.string.start_downloading_chapter)
         }
     }
 
@@ -546,14 +559,16 @@ class TocViewModel(
             .map { it.id }
 
         if (targetIndices.isEmpty()) {
-            getApplication<Application>().toastOnUi("所有章节已缓存")
+            getApplication<Application>().toastOnUi(R.string.all_chapters_cached)
             return
         }
 
         execute {
             cacheBookChaptersUseCase.execute(book.bookUrl, targetIndices)
         }.onSuccess { count ->
-            getApplication<Application>().toastOnUi("开始下载剩余 $count 个章节")
+            getApplication<Application>().toastOnUi(
+                context.getString(R.string.start_downloading_remaining_chapters, count)
+            )
         }
     }
 

+ 6 - 2
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt

@@ -186,7 +186,7 @@ fun TxtRuleScreen(
     )
 
     BatchImportDialog(
-        title = "导入词典规则",
+        title = stringResource(R.string.import_txt_toc_rule),
         importState = importState,
         onDismissRequest = { viewModel.cancelImport() },
         onToggleItem = { viewModel.toggleImportSelection(it) },
@@ -262,7 +262,11 @@ fun TxtRuleScreen(
     )
 
     RuleListScaffold(
-        title = if (isPickMode) "选择目录规则" else "目录规则",
+        title = if (isPickMode) {
+            stringResource(R.string.select_toc_rule)
+        } else {
+            stringResource(R.string.txt_toc_rule)
+        },
         state = uiState,
         onBackClick = { onBackClick() },
         onSearchToggle = { active ->

+ 4 - 3
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleViewModel.kt

@@ -3,6 +3,7 @@ package io.legado.app.ui.book.toc.rule
 import android.app.Application
 import androidx.compose.runtime.Immutable
 import androidx.lifecycle.viewModelScope
+import io.legado.app.R
 import io.legado.app.base.BaseRuleViewModel
 import io.legado.app.data.entities.TxtTocRule
 import io.legado.app.data.repository.TxtTocRuleRepository
@@ -147,14 +148,14 @@ class TxtTocRuleViewModel(
     fun pasteRule(): TxtTocRule? {
         val text = context.getClipText()
         if (text.isNullOrBlank()) {
-            context.toastOnUi("剪贴板没有内容")
+            context.toastOnUi(R.string.clipboard_empty)
             return null
         }
         return try {
             GSON.fromJsonObject<TxtTocRule>(text).getOrThrow()
         } catch (e: Exception) {
-            context.toastOnUi("格式不对")
+            context.toastOnUi(R.string.invalid_format)
             null
         }
     }
-}
+}

+ 28 - 17
app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt

@@ -168,15 +168,17 @@ fun BackupConfigScreen(
     ) { uri ->
         uri?.let {
             showLoadingDialog = true
-            loadingText = "恢复中…"
+            loadingText = context.getString(R.string.on_restore)
             scope.launch {
                 try {
                     Restore.restore(context, uri)
                     showLoadingDialog = false
-                    snackbarHostState.showSnackbar("恢复成功")
+                    snackbarHostState.showSnackbar(context.getString(R.string.restore_success))
                 } catch (e: Exception) {
                     showLoadingDialog = false
-                    snackbarHostState.showSnackbar("恢复出错: ${e.localizedMessage}")
+                    snackbarHostState.showSnackbar(
+                        context.getString(R.string.restore_fail_with_error, e.localizedMessage)
+                    )
                 }
             }
         }
@@ -360,20 +362,24 @@ fun BackupConfigScreen(
                     value = tempAccount,
                     onValueChange = { tempAccount = it },
                     backgroundColor = LegadoTheme.colorScheme.surface,
-                    label = "账号"
+                    label = stringResource(R.string.web_dav_account)
                 )
                 Spacer(modifier = Modifier.height(12.dp))
                 AppTextField(
                     value = tempPassword,
                     onValueChange = { tempPassword = it },
                     backgroundColor = MiuixTheme.colorScheme.surface,
-                    label = "密码",
+                    label = stringResource(R.string.web_dav_pw),
                     visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
                     keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
                     trailingIcon = {
                         val image =
                             if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff
-                        val description = if (passwordVisible) "隐藏密码" else "显示密码"
+                        val description = if (passwordVisible) {
+                            stringResource(R.string.hide_password)
+                        } else {
+                            stringResource(R.string.show_password)
+                        }
                         IconButton(onClick = { passwordVisible = !passwordVisible }) {
                             Icon(imageVector = image, contentDescription = description)
                         }
@@ -387,13 +393,13 @@ fun BackupConfigScreen(
             showWebDavAuthDialog = false
             scope.launch {
                 showLoadingDialog = true
-                loadingText = "测试中…"
+                loadingText = context.getString(R.string.test_sync_loading_text)
                 val success = viewModel.testWebDav()
                 showLoadingDialog = false
                 if (success) {
-                    snackbarHostState.showSnackbar("WebDav 配置正确")
+                    snackbarHostState.showSnackbar(context.getString(R.string.test_sync_status_success))
                 } else {
-                    snackbarHostState.showSnackbar("WebDav 配置错误")
+                    snackbarHostState.showSnackbar(context.getString(R.string.test_sync_status_fail))
                 }
             }
         },
@@ -483,7 +489,7 @@ fun BackupConfigScreen(
     ) {
         OptionCard(
             icon = Icons.Default.PhoneAndroid,
-            text = "本地恢复",
+            text = stringResource(R.string.restore_from_local),
             onClick = {
                 showRestoreOptionSheet = false
                 restoreFileLauncher.launch(arrayOf("application/zip"))
@@ -491,20 +497,23 @@ fun BackupConfigScreen(
         )
         OptionCard(
             icon = Icons.Default.Cloud,
-            text = "网络恢复",
+            text = stringResource(R.string.restore_from_network),
             onClick = {
                 showRestoreOptionSheet = false
                 scope.launch {
                     showLoadingDialog = true
-                    loadingText = "加载中"
+                    loadingText = context.getString(R.string.loading)
                     try {
                         val names = viewModel.getBackupNames()
                         backupNames = names
                         showRestoreSheet = true
                     } catch (e: Exception) {
-                        confirmDialogTitle = "恢复"
+                        confirmDialogTitle = context.getString(R.string.restore)
                         confirmDialogText =
-                            "WebDavError\n${e.localizedMessage}\n将从本地备份恢复。"
+                            context.getString(
+                                R.string.webdav_restore_fallback_message,
+                                e.localizedMessage
+                            )
                         onConfirmAction = {
                             restoreFileLauncher.launch(arrayOf("application/zip"))
                         }
@@ -535,19 +544,21 @@ fun BackupConfigScreen(
                     onToggleSelection = {
                         showRestoreSheet = false
                         showLoadingDialog = true
-                        loadingText = "恢复中…"
+                        loadingText = context.getString(R.string.on_restore)
                         viewModel.restoreWebDav(
                             it,
                             {
                                 showLoadingDialog = false
                                 scope.launch {
-                                    snackbarHostState.showSnackbar("恢复成功")
+                                    snackbarHostState.showSnackbar(context.getString(R.string.restore_success))
                                 }
                             },
                             { error ->
                                 showLoadingDialog = false
                                 scope.launch {
-                                    snackbarHostState.showSnackbar("WebDav恢复出错\n$error")
+                                    snackbarHostState.showSnackbar(
+                                        context.getString(R.string.webdav_restore_fail, error)
+                                    )
                                 }
                             }
                         )

+ 3 - 2
app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigViewModel.kt

@@ -2,6 +2,7 @@ package io.legado.app.ui.config.backupConfig
 
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
+import io.legado.app.R
 import io.legado.app.domain.usecase.WebDavBackupUseCase
 import io.legado.app.help.storage.Backup
 import kotlinx.coroutines.Dispatchers
@@ -55,7 +56,7 @@ class BackupConfigViewModel(
                 }
             } catch (e: Exception) {
                 withContext(Dispatchers.Main) {
-                    onError(e.localizedMessage ?: "备份出错")
+                    onError(e.localizedMessage ?: appCtx.getString(R.string.backup_error))
                 }
             }
         }
@@ -76,7 +77,7 @@ class BackupConfigViewModel(
                 }
             } catch (e: Exception) {
                 withContext(Dispatchers.Main) {
-                    onError(e.localizedMessage ?: "恢复出错")
+                    onError(e.localizedMessage ?: appCtx.getString(R.string.restore_error))
                 }
             }
         }

+ 10 - 6
app/src/main/java/io/legado/app/ui/config/coverConfig/CoverConfigScreen.kt

@@ -134,9 +134,11 @@ fun CoverConfigScreen(
                 val coverCount = CoverConfig.defaultCover.split(",").filter { it.isNotBlank() }.size
                 ClickableSettingItem(
                     title = stringResource(R.string.default_cover),
-                    description = if (coverCount > 0) "已选择 $coverCount 张图片" else stringResource(
-                        R.string.select_image
-                    ),
+                    description = if (coverCount > 0) {
+                        stringResource(R.string.cover_selected_images_count, coverCount)
+                    } else {
+                        stringResource(R.string.select_image)
+                    },
                     onClick = { manageKey = PreferKey.defaultCover }
                 )
 
@@ -191,9 +193,11 @@ fun CoverConfigScreen(
                     CoverConfig.defaultCoverDark.split(",").filter { it.isNotBlank() }.size
                 ClickableSettingItem(
                     title = stringResource(R.string.default_cover),
-                    description = if (coverCount > 0) "已选择 $coverCount 张图片" else stringResource(
-                        R.string.select_image
-                    ),
+                    description = if (coverCount > 0) {
+                        stringResource(R.string.cover_selected_images_count, coverCount)
+                    } else {
+                        stringResource(R.string.select_image)
+                    },
                     onClick = { manageKey = PreferKey.defaultCoverDark }
                 )
 

+ 1 - 1
app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt

@@ -76,7 +76,7 @@ fun CoverRuleConfigSheet(
                 imageVector = Icons.Default.Save,
                 onClick = {
                     if (searchUrl.isBlank() || coverRule.isBlank()) {
-                        appCtx.toastOnUi("搜索url和cover规则不能为空")
+                        appCtx.toastOnUi(R.string.cover_rule_fields_required)
                     } else {
                         val newConfig = ruleState?.copy(
                             enable = enable,

+ 8 - 8
app/src/main/java/io/legado/app/ui/config/customTheme/CustomThemeScreen.kt

@@ -72,7 +72,7 @@ fun CustomThemeScreen(
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
         topBar = {
             GlassMediumFlexibleTopAppBar(
-                title = "自定义颜色",
+                title = stringResource(R.string.custom_theme_colors),
                 scrollBehavior = scrollBehavior,
                 navigationIcon = {
                     TopBarNavigationButton(onClick = onBackClick)
@@ -91,7 +91,7 @@ fun CustomThemeScreen(
             item {
                 SplicedColumnGroup {
                     SwitchSettingItem(
-                        title = "使用色板生成颜色",
+                        title = stringResource(R.string.theme_manage_use_palette_colors),
                         checked = !enableDeepPersonalization,
                         onCheckedChange = { ThemeConfig.enableDeepPersonalization = !it }
                     )
@@ -104,7 +104,7 @@ fun CustomThemeScreen(
                     SplicedColumnGroup(title = stringResource(R.string.color_setting)) {
                     // Primary colors
                     ClickableSettingItem(
-                        title = "主题色",
+                        title = stringResource(R.string.theme_manage_primary_color),
                         option = if (themeColor != 0) "#${Integer.toHexString(themeColor).uppercase()}" else stringResource(R.string.click_to_select),
                         onClick = {
                             currentColorKey = "themeColor"
@@ -128,7 +128,7 @@ fun CustomThemeScreen(
                     )
 
                     ClickableSettingItem(
-                        title = "次要主题色",
+                        title = stringResource(R.string.theme_manage_secondary_color),
                         option = if (secondaryThemeColor != 0) "#${Integer.toHexString(secondaryThemeColor).uppercase()}" else stringResource(R.string.click_to_select),
                         onClick = {
                             currentColorKey = "secondaryThemeColor"
@@ -152,7 +152,7 @@ fun CustomThemeScreen(
                     )
 
                     ClickableSettingItem(
-                        title = "主要字体色",
+                        title = stringResource(R.string.theme_manage_primary_text_color),
                         option = if (primaryTextColor != 0) "#${Integer.toHexString(primaryTextColor).uppercase()}" else stringResource(R.string.click_to_select),
                         onClick = {
                             currentColorKey = "primaryTextColor"
@@ -176,7 +176,7 @@ fun CustomThemeScreen(
                     )
 
                     ClickableSettingItem(
-                        title = "次要字体色",
+                        title = stringResource(R.string.theme_manage_secondary_text_color),
                         option = if (secondaryTextColor != 0) "#${Integer.toHexString(secondaryTextColor).uppercase()}" else stringResource(R.string.click_to_select),
                         onClick = {
                             currentColorKey = "secondaryTextColor"
@@ -200,7 +200,7 @@ fun CustomThemeScreen(
                     )
 
                     ClickableSettingItem(
-                        title = "背景色",
+                        title = stringResource(R.string.theme_manage_background_color),
                         option = if (themeBackgroundColor != 0) "#${Integer.toHexString(themeBackgroundColor).uppercase()}" else stringResource(R.string.click_to_select),
                         onClick = {
                             currentColorKey = "themeBackgroundColor"
@@ -224,7 +224,7 @@ fun CustomThemeScreen(
                     )
 
                     ClickableSettingItem(
-                        title = "标签容器色",
+                        title = stringResource(R.string.theme_manage_label_container_color),
                         option = if (labelContainerColor != 0) "#${Integer.toHexString(labelContainerColor).uppercase()}" else stringResource(R.string.click_to_select),
                         onClick = {
                             currentColorKey = "labelContainerColor"

+ 3 - 3
app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt

@@ -88,7 +88,7 @@ fun DirectLinkUploadBottomSheet(
                     onDismissRequest = { showMenu = false }
                 ) {
                     RoundDropdownMenuItem(
-                        text = "导入默认",
+                        text = stringResource(R.string.import_default_rule),
                         leadingIcon = { Icon(Icons.Default.Download, null) },
                         onClick = {
                             showMenu = false
@@ -124,7 +124,7 @@ fun DirectLinkUploadBottomSheet(
                                     viewModel.upView(rule)
                                 }
                             }.onFailure {
-                                context.toastOnUi("剪贴板为空或格式不对")
+                                context.toastOnUi(R.string.clipboard_empty_or_invalid)
                             }
                         }
                     )
@@ -186,7 +186,7 @@ fun DirectLinkUploadBottomSheet(
                     if (viewModel.saveDirectLinkRule()) {
                         onDismiss()
                     } else {
-                        context.toastOnUi("请填写完整信息")
+                        context.toastOnUi(R.string.complete_required_information)
                     }
                 },
                 dismissText = stringResource(R.string.cancel),

+ 1 - 1
app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfigScreen.kt

@@ -147,7 +147,7 @@ fun OtherConfigScreen(
                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                             notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
                         } else {
-                            Toast.makeText(context, "无需申请", Toast.LENGTH_SHORT).show()
+                            Toast.makeText(context, R.string.permission_not_required, Toast.LENGTH_SHORT).show()
                         }
                     }
                 )

+ 1 - 1
app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt

@@ -97,7 +97,7 @@ fun BackgroundImageManageSheet(
                     ) {
                         AppIcon(
                             imageVector = Icons.Default.Add,
-                            contentDescription = "Add",
+                            contentDescription = stringResource(R.string.add),
                             modifier = Modifier.size(48.dp),
                             tint = LegadoTheme.colorScheme.primary
                         )

+ 4 - 2
app/src/main/java/io/legado/app/ui/config/themeConfig/LabelColorManageSheet.kt

@@ -24,8 +24,10 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.core.graphics.ColorUtils
+import io.legado.app.R
 import io.legado.app.help.config.TagColorGenerator
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.button.MediumOutlinedIconButton
@@ -53,7 +55,7 @@ fun LabelColorManageSheet(
     AppModalBottomSheet(
         show = show,
         onDismissRequest = onDismissRequest,
-        title = "管理标签颜色",
+        title = stringResource(R.string.theme_config_manage_label_colors),
         startAction = {
             MediumOutlinedIconButton(
                 onClick = {
@@ -84,7 +86,7 @@ fun LabelColorManageSheet(
         ) {
             items(tagColors.size) { index ->
                 val colorPair = tagColors[index]
-                val label = "标签 ${index + 1}"
+                val label = stringResource(R.string.theme_config_label_color_name, index + 1)
                 NormalCard(
                     modifier = Modifier.fillMaxWidth(),
                     containerColor = LegadoTheme.colorScheme.onSheetContent

+ 16 - 9
app/src/main/java/io/legado/app/ui/config/themeConfig/NavIconManageSheet.kt

@@ -2,6 +2,7 @@ package io.legado.app.ui.config.themeConfig
 
 import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.StringRes
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -24,8 +25,10 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import coil.compose.AsyncImage
+import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.button.SmallTonalIconButton
 import io.legado.app.ui.widget.components.card.NormalCard
@@ -36,7 +39,7 @@ import java.io.File
 
 private data class NavIconDestination(
     val key: String,
-    val label: String,
+    @param:StringRes val labelRes: Int,
     val path: String,
     val onSetPath: (String) -> Unit,
 )
@@ -51,10 +54,10 @@ fun NavIconManageSheet(
     var activeDest by remember { mutableStateOf<String?>(null) }
 
     val destinations = listOf(
-        NavIconDestination("bookshelf", "书架", ThemeConfig.navIconBookshelf) { ThemeConfig.navIconBookshelf = it },
-        NavIconDestination("explore", "发现", ThemeConfig.navIconExplore) { ThemeConfig.navIconExplore = it },
-        NavIconDestination("rss", "订阅", ThemeConfig.navIconRss) { ThemeConfig.navIconRss = it },
-        NavIconDestination("my", "我的", ThemeConfig.navIconMy) { ThemeConfig.navIconMy = it },
+        NavIconDestination("bookshelf", R.string.bookshelf, ThemeConfig.navIconBookshelf) { ThemeConfig.navIconBookshelf = it },
+        NavIconDestination("explore", R.string.discovery, ThemeConfig.navIconExplore) { ThemeConfig.navIconExplore = it },
+        NavIconDestination("rss", R.string.rss, ThemeConfig.navIconRss) { ThemeConfig.navIconRss = it },
+        NavIconDestination("my", R.string.my, ThemeConfig.navIconMy) { ThemeConfig.navIconMy = it },
     )
 
     val selectImage = rememberLauncherForActivityResult(
@@ -79,7 +82,7 @@ fun NavIconManageSheet(
     AppModalBottomSheet(
         show = show,
         onDismissRequest = onDismissRequest,
-        title = "导航栏图标",
+        title = stringResource(R.string.theme_config_nav_icons),
     ) {
         Column(modifier = Modifier.fillMaxWidth().padding(bottom = 24.dp)) {
             Row(
@@ -89,6 +92,7 @@ fun NavIconManageSheet(
                 horizontalArrangement = Arrangement.spacedBy(12.dp)
             ) {
                 destinations.forEach { dest ->
+                    val label = stringResource(dest.labelRes)
                     Column(
                         modifier = Modifier.weight(1f),
                         horizontalAlignment = Alignment.CenterHorizontally
@@ -108,7 +112,7 @@ fun NavIconManageSheet(
                                 Box(modifier = Modifier.fillMaxSize()) {
                                     AsyncImage(
                                         model = dest.path,
-                                        contentDescription = dest.label,
+                                        contentDescription = label,
                                         modifier = Modifier.fillMaxSize(),
                                         contentScale = ContentScale.Fit
                                     )
@@ -128,7 +132,10 @@ fun NavIconManageSheet(
                                 ) {
                                     AppIcon(
                                         imageVector = Icons.Default.Add,
-                                        contentDescription = "添加${dest.label}图标",
+                                        contentDescription = stringResource(
+                                            R.string.theme_config_add_nav_icon,
+                                            label
+                                        ),
                                         modifier = Modifier.size(32.dp),
                                         tint = LegadoTheme.colorScheme.primary
                                     )
@@ -136,7 +143,7 @@ fun NavIconManageSheet(
                             }
                         }
                         AppText(
-                            text = dest.label,
+                            text = label,
                             modifier = Modifier.padding(top = 8.dp),
                         )
                     }

+ 27 - 23
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt

@@ -227,7 +227,7 @@ fun ThemeConfigScreen(
                             contentColor = MiuixTheme.colorScheme.onPrimary
                         )
                     ) {
-                        MiuixText("Miuix 目前为测试主题,且不对基于View的界面生效!")
+                        MiuixText(stringResource(R.string.theme_config_miuix_experimental_warning))
                     }
                 }
 
@@ -420,8 +420,8 @@ fun ThemeConfigScreen(
                                 onCheckedChange = { ThemeConfig.useFloatingBottomBarLiquidGlass = it }
                             )
                             SliderSettingItem(
-                                title = "底栏模糊强度",
-                                description = "控制液态玻璃的扭曲程度",
+                                title = stringResource(R.string.theme_config_bottom_bar_lens_radius),
+                                description = stringResource(R.string.theme_config_bottom_bar_lens_radius_summary),
                                 value = ThemeConfig.bottomBarLensRadius,
                                 defaultValue = 24f,
                                 valueRange = 0f..50f,
@@ -483,30 +483,30 @@ fun ThemeConfigScreen(
                     }
                     if (ThemeConfig.enableBlur) {
                         SliderSettingItem(
-                            title = "顶栏模糊半径",
-                            description = "模糊半径越大,系统运行越卡顿",
+                            title = stringResource(R.string.theme_manage_top_bar_blur_radius),
+                            description = stringResource(R.string.theme_config_blur_radius_performance_summary),
                             value = ThemeConfig.topBarBlurRadius.toFloat(),
                             defaultValue = 24f,
                             valueRange = 0f..30f,
                             onValueChange = { ThemeConfig.topBarBlurRadius = it.toInt() }
                         )
                         SliderSettingItem(
-                            title = "底栏模糊半径",
-                            description = "模糊半径越大,系统运行越卡顿",
+                            title = stringResource(R.string.theme_manage_bottom_bar_blur_radius),
+                            description = stringResource(R.string.theme_config_blur_radius_performance_summary),
                             value = ThemeConfig.bottomBarBlurRadius.toFloat(),
                             defaultValue = 8f,
                             valueRange = 0f..10f,
                             onValueChange = { ThemeConfig.bottomBarBlurRadius = it.toInt() }
                         )
                         SliderSettingItem(
-                            title = "顶栏模糊透明度",
+                            title = stringResource(R.string.theme_manage_top_bar_blur_opacity),
                             value = ThemeConfig.topBarBlurAlpha.toFloat(),
                             defaultValue = 73f,
                             valueRange = 0f..100f,
                             onValueChange = { ThemeConfig.topBarBlurAlpha = it.toInt() }
                         )
                         SliderSettingItem(
-                            title = "底栏模糊透明度",
+                            title = stringResource(R.string.theme_manage_bottom_bar_blur_opacity),
                             value = ThemeConfig.bottomBarBlurAlpha.toFloat(),
                             defaultValue = 40f,
                             valueRange = 0f..100f,
@@ -610,15 +610,15 @@ fun ThemeConfigScreen(
 
             // Container settings
             item {
-                SplicedColumnGroup(title = "容器设置") {
+                SplicedColumnGroup(title = stringResource(R.string.theme_manage_section_container)) {
                     SwitchSettingItem(
-                        title = "显示分割线",
+                        title = stringResource(R.string.show_divider_line),
                         checked = ThemeConfig.enableItemDivider,
                         onCheckedChange = { ThemeConfig.enableItemDivider = it }
                     )
                     if (ThemeConfig.enableItemDivider) {
                         SliderSettingItem(
-                            title = "分割线粗细",
+                            title = stringResource(R.string.theme_config_divider_width),
                             description = "${ThemeConfig.itemDividerWidth}dp",
                             value = ThemeConfig.itemDividerWidth,
                             defaultValue = 1f,
@@ -627,7 +627,7 @@ fun ThemeConfigScreen(
                             onValueChange = { ThemeConfig.itemDividerWidth = it }
                         )
                         SliderSettingItem(
-                            title = "分割线长度",
+                            title = stringResource(R.string.theme_config_divider_length),
                             description = "${ThemeConfig.itemDividerLength.toInt()}%",
                             value = ThemeConfig.itemDividerLength,
                             defaultValue = 80f,
@@ -636,7 +636,7 @@ fun ThemeConfigScreen(
                             onValueChange = { ThemeConfig.itemDividerLength = it }
                         )
                         ClickableSettingItem(
-                            title = "分割线颜色",
+                            title = stringResource(R.string.tip_divider_color),
                             option = if (ThemeConfig.itemDividerColor != 0) "#${Integer.toHexString(ThemeConfig.itemDividerColor).uppercase()}" else stringResource(R.string.click_to_select),
                             onClick = {
                                 showBorderColorPicker = true
@@ -659,7 +659,7 @@ fun ThemeConfigScreen(
 
             // Nav icon settings
             item {
-                SplicedColumnGroup(title = "导航栏图标设置") {
+                SplicedColumnGroup(title = stringResource(R.string.theme_config_nav_icon_settings)) {
                     val customCount = listOf(
                         ThemeConfig.navIconBookshelf,
                         ThemeConfig.navIconExplore,
@@ -667,8 +667,12 @@ fun ThemeConfigScreen(
                         ThemeConfig.navIconMy
                     ).count { it.isNotEmpty() }
                     ClickableSettingItem(
-                        title = "导航栏图标",
-                        description = if (customCount > 0) "已设置 $customCount 个自定义图标" else "使用默认图标",
+                        title = stringResource(R.string.theme_config_nav_icons),
+                        description = if (customCount > 0) {
+                            stringResource(R.string.theme_config_nav_icons_custom_count, customCount)
+                        } else {
+                            stringResource(R.string.theme_config_nav_icons_default)
+                        },
                         onClick = { showNavIconSheet = true }
                     )
                 }
@@ -676,10 +680,10 @@ fun ThemeConfigScreen(
 
             // Theme management
             item {
-                SplicedColumnGroup(title = "主题管理") {
+                SplicedColumnGroup(title = stringResource(R.string.theme_pack)) {
                     ClickableSettingItem(
-                        title = "主题管理",
-                        description = "保存、导入、导出主题配置",
+                        title = stringResource(R.string.theme_pack),
+                        description = stringResource(R.string.theme_pack_s),
                         onClick = onNavigateToThemeManage
                     )
                 }
@@ -750,7 +754,7 @@ fun ThemeConfigScreen(
         startAction = {
             SmallIconButton(
                 imageVector = Icons.Default.Delete,
-                contentDescription = "清除",
+                contentDescription = stringResource(R.string.clear),
                 onClick = {
                     ThemeConfig.appFontPath = null
                     showFontSheet = false
@@ -760,7 +764,7 @@ fun ThemeConfigScreen(
         endAction = {
             SmallIconButton(
                 imageVector = Icons.Default.Add,
-                contentDescription = "选择文件夹",
+                contentDescription = stringResource(R.string.select_folder),
                 onClick = { fontFolderLauncher.launch(null) }
             )
         },
@@ -771,7 +775,7 @@ fun ThemeConfigScreen(
                     contentAlignment = Alignment.Center
                 ) {
                     Text(
-                        text = "没有字体文件",
+                        text = stringResource(R.string.theme_config_no_font_files),
                         style = MaterialTheme.typography.bodyLarge
                     )
                 }

+ 1 - 1
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigViewModel.kt

@@ -79,4 +79,4 @@ class ThemeConfigViewModel : ViewModel() {
             ThemeConfig.bgImageLight = newPath
         }
     }
-}
+}

+ 40 - 39
app/src/main/java/io/legado/app/ui/config/themeManage/EditThemeSheet.kt

@@ -26,6 +26,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringArrayResource
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.help.config.ThemeExportData
@@ -58,7 +59,7 @@ fun EditThemeSheet(
     AppModalBottomSheet(
         show = show,
         onDismissRequest = onDismissRequest,
-        title = "编辑主题",
+        title = stringResource(R.string.theme_manage_edit_theme),
         endAction = {
             MediumIconButton(
                 onClick = {
@@ -92,30 +93,30 @@ fun EditThemeSheet(
             Spacer(modifier = Modifier.height(8.dp))
 
             // Basic settings
-            SectionTitle("基础设置")
+            SectionTitle(stringResource(R.string.theme_manage_section_basic))
             CompactDropdownSettingItem(
-                title = "主题模式",
+                title = stringResource(R.string.theme_mode),
                 selectedValue = data.themeMode,
                 displayEntries = stringArrayResource(R.array.theme_mode),
                 entryValues = stringArrayResource(R.array.theme_mode_v),
                 onValueChange = { data = data.copy(themeMode = it) }
             )
             CompactDropdownSettingItem(
-                title = "调色板风格",
+                title = stringResource(R.string.palette_style),
                 selectedValue = data.paletteStyle,
                 displayEntries = stringArrayResource(R.array.paletteStyle),
                 entryValues = stringArrayResource(R.array.paletteStyle_value),
                 onValueChange = { data = data.copy(paletteStyle = it) }
             )
             CompactDropdownSettingItem(
-                title = "Material 版本",
+                title = stringResource(R.string.material_version),
                 selectedValue = data.materialVersion,
                 displayEntries = stringArrayResource(R.array.materialVersion),
                 entryValues = stringArrayResource(R.array.materialVersion_value),
                 onValueChange = { data = data.copy(materialVersion = it) }
             )
             CompactDropdownSettingItem(
-                title = "对比度偏好",
+                title = stringResource(R.string.preferred_contrast),
                 selectedValue = data.customContrast,
                 displayEntries = stringArrayResource(R.array.customContrast),
                 entryValues = stringArrayResource(R.array.customContrast_value),
@@ -125,36 +126,36 @@ fun EditThemeSheet(
             Spacer(modifier = Modifier.height(8.dp))
 
             // Colors
-            SectionTitle("颜色设置")
+            SectionTitle(stringResource(R.string.theme_manage_section_colors))
             CompactSwitchSettingItem(
-                title = "使用色板生成颜色",
+                title = stringResource(R.string.theme_manage_use_palette_colors),
                 checked = !data.enableDeepPersonalization,
                 onCheckedChange = { data = data.copy(enableDeepPersonalization = !it) }
             )
             if (data.enableDeepPersonalization) {
-                ColorItem("主题色", data.themeColor) {
+                ColorItem(stringResource(R.string.theme_manage_primary_color), data.themeColor) {
                     currentColorKey = "themeColor"; showColorPicker = true
                 }
-                ColorItem("次要主题色", data.secondaryThemeColor) {
+                ColorItem(stringResource(R.string.theme_manage_secondary_color), data.secondaryThemeColor) {
                     currentColorKey = "secondaryThemeColor"; showColorPicker = true
                 }
-                ColorItem("主要字体色", data.primaryTextColor) {
+                ColorItem(stringResource(R.string.theme_manage_primary_text_color), data.primaryTextColor) {
                     currentColorKey = "primaryTextColor"; showColorPicker = true
                 }
-                ColorItem("次要字体色", data.secondaryTextColor) {
+                ColorItem(stringResource(R.string.theme_manage_secondary_text_color), data.secondaryTextColor) {
                     currentColorKey = "secondaryTextColor"; showColorPicker = true
                 }
-                ColorItem("背景色", data.themeBackgroundColor) {
+                ColorItem(stringResource(R.string.theme_manage_background_color), data.themeBackgroundColor) {
                     currentColorKey = "themeBackgroundColor"; showColorPicker = true
                 }
-                ColorItem("标签容器色", data.labelContainerColor) {
+                ColorItem(stringResource(R.string.theme_manage_label_container_color), data.labelContainerColor) {
                     currentColorKey = "labelContainerColor"; showColorPicker = true
                 }
             } else {
-                ColorItem("日间种子色", data.cPrimary) {
+                ColorItem(stringResource(R.string.theme_manage_day_seed_color), data.cPrimary) {
                     currentColorKey = "cPrimary"; showColorPicker = true
                 }
-                ColorItem("夜间种子色", data.cNPrimary) {
+                ColorItem(stringResource(R.string.theme_manage_night_seed_color), data.cNPrimary) {
                     currentColorKey = "cNPrimary"; showColorPicker = true
                 }
             }
@@ -162,9 +163,9 @@ fun EditThemeSheet(
             Spacer(modifier = Modifier.height(8.dp))
 
             // Interface layout
-            SectionTitle("界面布局")
+            SectionTitle(stringResource(R.string.theme_manage_section_layout))
             CompactSwitchSettingItem(
-                title = "发现",
+                title = stringResource(R.string.theme_manage_show_discovery),
                 checked = data.showDiscovery,
                 onCheckedChange = { data = data.copy(showDiscovery = it) }
             )
@@ -174,41 +175,41 @@ fun EditThemeSheet(
                 onCheckedChange = { data = data.copy(showRss = it) }
             )
             CompactSwitchSettingItem(
-                title = "显示底栏",
+                title = stringResource(R.string.show_bottom_nav),
                 checked = data.showBottomView,
                 onCheckedChange = { data = data.copy(showBottomView = it) }
             )
             CompactSwitchSettingItem(
-                title = "浮动底栏",
+                title = stringResource(R.string.floating_bottom_bar),
                 checked = data.useFloatingBottomBar,
                 onCheckedChange = { data = data.copy(useFloatingBottomBar = it) }
             )
             CompactSwitchSettingItem(
-                title = "状态栏",
+                title = stringResource(R.string.theme_manage_status_bar),
                 checked = data.showStatusBar,
                 onCheckedChange = { data = data.copy(showStatusBar = it) }
             )
             CompactSwitchSettingItem(
-                title = "翻页动画",
+                title = stringResource(R.string.theme_manage_page_turn_animation),
                 checked = data.swipeAnimation,
                 onCheckedChange = { data = data.copy(swipeAnimation = it) }
             )
             CompactDropdownSettingItem(
-                title = "平板模式",
+                title = stringResource(R.string.tabletInterface),
                 selectedValue = data.tabletInterface,
                 displayEntries = stringArrayResource(R.array.tabletInterface),
                 entryValues = stringArrayResource(R.array.tabletInterface_value),
                 onValueChange = { data = data.copy(tabletInterface = it) }
             )
             CompactDropdownSettingItem(
-                title = "标签显示",
+                title = stringResource(R.string.theme_manage_label_visibility),
                 selectedValue = data.labelVisibilityMode,
                 displayEntries = stringArrayResource(R.array.label_vis_mode),
                 entryValues = stringArrayResource(R.array.label_vis_mode_value),
                 onValueChange = { data = data.copy(labelVisibilityMode = it) }
             )
             CompactDropdownSettingItem(
-                title = "默认主页",
+                title = stringResource(R.string.default_home_page),
                 selectedValue = data.defaultHomePage,
                 displayEntries = stringArrayResource(R.array.default_home_page),
                 entryValues = stringArrayResource(R.array.default_home_page_value),
@@ -218,33 +219,33 @@ fun EditThemeSheet(
             Spacer(modifier = Modifier.height(8.dp))
 
             // Blur
-            SectionTitle("模糊效果")
+            SectionTitle(stringResource(R.string.theme_manage_section_blur))
             CompactSwitchSettingItem(
-                title = "启用模糊",
+                title = stringResource(R.string.is_blur_enable),
                 checked = data.enableBlur,
                 onCheckedChange = { data = data.copy(enableBlur = it) }
             )
             if (data.enableBlur) {
                 CompactSliderSettingItem(
-                    title = "顶栏模糊半径",
+                    title = stringResource(R.string.theme_manage_top_bar_blur_radius),
                     value = data.topBarBlurRadius.toFloat(),
                     valueRange = 1f..60f,
                     onValueChange = { data = data.copy(topBarBlurRadius = it.toInt()) }
                 )
                 CompactSliderSettingItem(
-                    title = "底栏模糊半径",
+                    title = stringResource(R.string.theme_manage_bottom_bar_blur_radius),
                     value = data.bottomBarBlurRadius.toFloat(),
                     valueRange = 1f..60f,
                     onValueChange = { data = data.copy(bottomBarBlurRadius = it.toInt()) }
                 )
                 CompactSliderSettingItem(
-                    title = "顶栏透明度",
+                    title = stringResource(R.string.theme_manage_top_bar_blur_opacity),
                     value = data.topBarBlurAlpha.toFloat(),
                     valueRange = 0f..255f,
                     onValueChange = { data = data.copy(topBarBlurAlpha = it.toInt()) }
                 )
                 CompactSliderSettingItem(
-                    title = "底栏透明度",
+                    title = stringResource(R.string.theme_manage_bottom_bar_blur_opacity),
                     value = data.bottomBarBlurAlpha.toFloat(),
                     valueRange = 0f..255f,
                     onValueChange = { data = data.copy(bottomBarBlurAlpha = it.toInt()) }
@@ -254,15 +255,15 @@ fun EditThemeSheet(
             Spacer(modifier = Modifier.height(8.dp))
 
             // Opacity
-            SectionTitle("透明度")
+            SectionTitle(stringResource(R.string.theme_manage_section_opacity))
             CompactSliderSettingItem(
-                title = "顶栏透明度",
+                title = stringResource(R.string.top_bar_opacity),
                 value = data.topBarOpacity.toFloat(),
                 valueRange = 0f..100f,
                 onValueChange = { data = data.copy(topBarOpacity = it.toInt()) }
             )
             CompactSliderSettingItem(
-                title = "底栏透明度",
+                title = stringResource(R.string.bottom_bar_opacity),
                 value = data.bottomBarOpacity.toFloat(),
                 valueRange = 0f..100f,
                 onValueChange = { data = data.copy(bottomBarOpacity = it.toInt()) }
@@ -271,9 +272,9 @@ fun EditThemeSheet(
             Spacer(modifier = Modifier.height(8.dp))
 
             // Container
-            SectionTitle("容器设置")
+            SectionTitle(stringResource(R.string.theme_manage_section_container))
             CompactSliderSettingItem(
-                title = "容器不透明度",
+                title = stringResource(R.string.container_opacity),
                 value = data.containerOpacity.toFloat(),
                 valueRange = 0f..100f,
                 onValueChange = { data = data.copy(containerOpacity = it.toInt()) }
@@ -282,14 +283,14 @@ fun EditThemeSheet(
             Spacer(modifier = Modifier.height(8.dp))
 
             // Other
-            SectionTitle("其他")
+            SectionTitle(stringResource(R.string.other))
             CompactSwitchSettingItem(
-                title = "纯黑模式",
+                title = stringResource(R.string.pure_black),
                 checked = data.isPureBlack,
                 onCheckedChange = { data = data.copy(isPureBlack = it) }
             )
             CompactSwitchSettingItem(
-                title = "弹性顶栏",
+                title = stringResource(R.string.use_flexible_top_bar),
                 checked = data.useFlexibleTopAppBar,
                 onCheckedChange = { data = data.copy(useFlexibleTopAppBar = it) }
             )

+ 28 - 28
app/src/main/java/io/legado/app/ui/config/themeManage/ThemeManageScreen.kt

@@ -83,14 +83,14 @@ fun ThemeManageScreen(
             if (target != null) {
                 exportTarget = null
                 if (ThemeImportExport.exportSavedThemeToFile(context, target, it)) {
-                    context.toastOnUi("主题导出成功")
+                    context.toastOnUi(R.string.theme_manage_export_success)
                 } else {
-                    context.toastOnUi("主题导出失败")
+                    context.toastOnUi(R.string.theme_manage_export_failed)
                 }
             } else if (ThemeImportExport.exportToFile(context, it)) {
-                context.toastOnUi("主题导出成功")
+                context.toastOnUi(R.string.theme_manage_export_success)
             } else {
-                context.toastOnUi("主题导出失败")
+                context.toastOnUi(R.string.theme_manage_export_failed)
             }
         }
     }
@@ -100,10 +100,10 @@ fun ThemeManageScreen(
     ) { uri ->
         uri?.let {
             if (ThemeImportExport.importFromUri(context, it)) {
-                context.toastOnUi("主题导入成功,部分设置需要重启生效")
+                context.toastOnUi(R.string.theme_manage_import_success)
                 showRestartDialog = true
             } else {
-                context.toastOnUi("主题导入失败")
+                context.toastOnUi(R.string.theme_manage_import_failed)
             }
         }
     }
@@ -112,7 +112,7 @@ fun ThemeManageScreen(
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
         topBar = {
             GlassMediumFlexibleTopAppBar(
-                title = "主题管理",
+                title = stringResource(R.string.theme_pack),
                 scrollBehavior = scrollBehavior,
                 navigationIcon = {
                     TopBarNavigationButton(onClick = onBackClick)
@@ -133,24 +133,24 @@ fun ThemeManageScreen(
             item(span = { GridItemSpan(maxLineSpan) }) {
                 SplicedColumnGroup {
                     ClickableSettingItem(
-                        title = "保存当前设置",
-                        description = "保存当前主题配置为新主题",
+                        title = stringResource(R.string.theme_manage_save_current),
+                        description = stringResource(R.string.theme_manage_save_current_summary),
                         onClick = {
                             newThemeName = ""
                             showSaveDialog = true
                         }
                     )
                     ClickableSettingItem(
-                        title = "导出当前主题",
-                        description = "将当前主题配置导出为JSON文件",
+                        title = stringResource(R.string.theme_manage_export_current),
+                        description = stringResource(R.string.theme_manage_export_current_summary),
                         onClick = {
                             exportTarget = null
                             exportLauncher.launch("legado_theme_${System.currentTimeMillis()}.json")
                         }
                     )
                     ClickableSettingItem(
-                        title = "导入主题配置",
-                        description = "从JSON文件导入主题配置",
+                        title = stringResource(R.string.theme_manage_import_config),
+                        description = stringResource(R.string.theme_manage_import_config_summary),
                         onClick = {
                             importLauncher.launch(arrayOf("application/json"))
                         }
@@ -161,7 +161,7 @@ fun ThemeManageScreen(
             if (savedThemes.isNotEmpty()) {
                 item(span = { GridItemSpan(maxLineSpan) }) {
                     AppText(
-                        text = "已保存的主题",
+                        text = stringResource(R.string.theme_manage_saved_themes),
                         style = MaterialTheme.typography.titleSmall,
                         color = MaterialTheme.colorScheme.onSurfaceVariant,
                         modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
@@ -207,8 +207,8 @@ fun ThemeManageScreen(
     AppAlertDialog(
         show = showSaveDialog,
         onDismissRequest = { showSaveDialog = false },
-        title = "保存主题",
-        confirmText = "保存",
+        title = stringResource(R.string.theme_manage_save_theme),
+        confirmText = stringResource(R.string.theme_manage_save),
         onConfirm = {
             if (newThemeName.isNotBlank()) {
                 ThemeImportExport.saveCurrentAsTheme(newThemeName)
@@ -216,13 +216,13 @@ fun ThemeManageScreen(
                 showSaveDialog = false
             }
         },
-        dismissText = "取消",
+        dismissText = stringResource(R.string.cancel),
         onDismiss = { showSaveDialog = false },
         content = {
             AppTextField(
                 value = newThemeName,
                 onValueChange = { newThemeName = it },
-                placeholder = { AppText(text = "请输入主题名称") },
+                placeholder = { AppText(text = stringResource(R.string.theme_manage_name_hint)) },
                 singleLine = true,
                 modifier = Modifier.fillMaxWidth()
             )
@@ -233,8 +233,8 @@ fun ThemeManageScreen(
     AppAlertDialog(
         show = applyTarget != null,
         onDismissRequest = { applyTarget = null },
-        title = "应用主题",
-        confirmText = "应用",
+        title = stringResource(R.string.theme_manage_apply_theme),
+        confirmText = stringResource(R.string.theme_manage_apply),
         onConfirm = {
             applyTarget?.let { theme ->
                 ThemeImportExport.applySavedTheme(theme)
@@ -242,17 +242,17 @@ fun ThemeManageScreen(
             }
             applyTarget = null
         },
-        dismissText = "取消",
+        dismissText = stringResource(R.string.cancel),
         onDismiss = { applyTarget = null },
-        text = "确定应用主题「${applyTarget?.name}」?应用后需要重启才能完全生效。"
+        text = stringResource(R.string.theme_manage_apply_message, applyTarget?.name.orEmpty())
     )
 
     // Delete theme dialog
     AppAlertDialog(
         show = deleteTarget != null,
         onDismissRequest = { deleteTarget = null },
-        title = "删除主题",
-        confirmText = "删除",
+        title = stringResource(R.string.theme_manage_delete_theme),
+        confirmText = stringResource(R.string.delete),
         onConfirm = {
             deleteTarget?.let { theme ->
                 ThemeImportExport.deleteSavedTheme(theme)
@@ -260,9 +260,9 @@ fun ThemeManageScreen(
             }
             deleteTarget = null
         },
-        dismissText = "取消",
+        dismissText = stringResource(R.string.cancel),
         onDismiss = { deleteTarget = null },
-        text = "确定删除主题「${deleteTarget?.name}」?此操作不可恢复。"
+        text = stringResource(R.string.theme_manage_delete_message, deleteTarget?.name.orEmpty())
     )
 
     // Edit theme sheet
@@ -323,7 +323,7 @@ private fun SavedThemeItem(
                         .background(lightBg)
                 ) {
                     AppText(
-                        text = "日间",
+                        text = stringResource(R.string.theme_manage_preview_day),
                         style = MaterialTheme.typography.labelMediumEmphasized,
                         color = if (theme.data.primaryTextColor != 0) Color(theme.data.primaryTextColor).copy(alpha = 0.6f)
                         else Color.Black.copy(alpha = 0.5f),
@@ -355,7 +355,7 @@ private fun SavedThemeItem(
                         .background(darkBg)
                 ) {
                     AppText(
-                        text = "夜间",
+                        text = stringResource(R.string.theme_manage_preview_night),
                         style = MaterialTheme.typography.labelMediumEmphasized,
                         color = Color.White.copy(alpha = 0.5f),
                         modifier = Modifier

+ 3 - 1
app/src/main/java/io/legado/app/ui/widget/components/AppFloatingActionButton.kt

@@ -41,7 +41,9 @@ import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.widget.components.text.AppText
@@ -193,7 +195,7 @@ fun AppFloatingActionButtonMenu(
                             tint = Color.White
                         )
                         Spacer(modifier = Modifier.width(8.dp))
-                        Text("菜单", color = Color.White)
+                        Text(stringResource(R.string.menu), color = Color.White)
                     }
                 }
             }

+ 6 - 4
app/src/main/java/io/legado/app/ui/widget/components/AppPullToRefresh.kt

@@ -15,8 +15,10 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme.composeEngine
 import io.legado.app.ui.theme.ThemeResolver
 import kotlinx.coroutines.delay
@@ -57,10 +59,10 @@ fun AppPullToRefresh(
             contentPadding = PaddingValues(top = topPadding + 16.dp),
             pullToRefreshState = miuixRememberPullToRefreshState(),
             refreshTexts = listOf(
-                "下拉刷新",
-                "松开刷新",
-                "正在刷新",
-                "正在刷新"
+                stringResource(R.string.pull_to_refresh),
+                stringResource(R.string.release_to_refresh),
+                stringResource(R.string.refreshing),
+                stringResource(R.string.refreshing)
             )
         ) {
             content()

+ 8 - 2
app/src/main/java/io/legado/app/ui/widget/components/CollapsibleHeader.kt

@@ -15,9 +15,11 @@ import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.rotate
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
+import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.adaptiveHorizontalPadding
 import io.legado.app.ui.widget.components.card.GlassCard
@@ -83,11 +85,15 @@ fun CollapsibleHeader(
             if (showIcon) {
                 Icon(
                     imageVector = Icons.Default.ExpandMore,
-                    contentDescription = if (isCollapsed) "展开" else "折叠",
+                    contentDescription = if (isCollapsed) {
+                        stringResource(R.string.expand)
+                    } else {
+                        stringResource(R.string.collapse)
+                    },
                     modifier = Modifier.rotate(rotation),
                     tint = LegadoTheme.colorScheme.primary
                 )
             }
         }
     }
-}
+}

+ 2 - 2
app/src/main/java/io/legado/app/ui/widget/components/dialog/ColorPickerSheet.kt

@@ -70,7 +70,7 @@ fun ColorPickerSheet(
     AppModalBottomSheet(
         show = show,
         onDismissRequest = onDismissRequest,
-        title = "选择颜色",
+        title = stringResource(R.string.select_color),
     ) {
         Column(
             modifier = Modifier
@@ -123,7 +123,7 @@ fun ColorPickerSheet(
                         }
                     },
                     modifier = Modifier.fillMaxWidth(),
-                    label = "色值",
+                    label = stringResource(R.string.color_value),
                     singleLine = true,
                     isError = isHexInputError,
                     backgroundColor = LegadoTheme.colorScheme.surface,

+ 142 - 0
app/src/main/res/values-zh-rCN/strings.xml

@@ -37,6 +37,7 @@
     <string name="predictive_back_summary">启用系统预见性返回手势</string>
     <string name="restart_to_apply">重启以应用</string>
     <string name="custom_theme">自定义主题</string>
+    <string name="custom_theme_colors">自定义颜色</string>
     <string name="appearance_setting">外观设置</string>
     <string name="click_to_select">点击选择</string>
     <string name="compose_related">Compose 相关</string>
@@ -106,6 +107,16 @@
     <string name="origin_format">来源:%s</string>
     <string name="read_dur_progress">最近:%s</string>
     <string name="book_name">书名</string>
+    <string name="cover_url">封面链接</string>
+    <string name="fixed_book_type">固定书籍类型</string>
+    <string name="fixed_book_type_summary">书籍更新后不覆盖书籍类型</string>
+    <string name="book_type_label">书籍类型</string>
+    <string name="book_type_text">文本</string>
+    <string name="book_type_audio">音频</string>
+    <string name="book_type_image">图片</string>
+    <string name="add_tag">添加标签</string>
+    <string name="edit_tag">编辑标签</string>
+    <string name="tag">标签</string>
     <string name="lasted_show">最新 · %s</string>
     <string name="check_add_bookshelf">是否将《%s》放入书架?</string>
     <string name="import_books_count">共 %s 个文本文件</string>
@@ -130,6 +141,8 @@
     <string name="pt_auto_download">自动下载最新章节</string>
     <string name="ps_auto_download">更新书籍时自动下载最新章节</string>
     <string name="backup_restore">备份与恢复</string>
+    <string name="backup_error">备份出错</string>
+    <string name="restore_error">恢复出错</string>
     <string name="web_dav_set">WebDav 设置</string>
     <string name="web_dav_set_import_old">WebDav 设置 • 导入旧版本数据</string>
     <string name="backup">备份</string>
@@ -147,6 +160,11 @@
     <string name="on_restore">正在恢复</string>
     <string name="restore_success">恢复成功</string>
     <string name="restore_fail">恢复失败</string>
+    <string name="restore_fail_with_error">恢复出错:%1$s</string>
+    <string name="restore_from_local">本地恢复</string>
+    <string name="restore_from_network">网络恢复</string>
+    <string name="webdav_restore_fallback_message">WebDAV 出错\n%1$s\n将从本地备份恢复。</string>
+    <string name="webdav_restore_fail">WebDAV 恢复出错\n%1$s</string>
     <string name="screen_direction">屏幕方向</string>
     <string name="screen_sensor">跟随传感器</string>
     <string name="screen_landscape">横向</string>
@@ -170,6 +188,13 @@
     <string name="audio_pause">播放暂停</string>
     <string name="text_return">返回</string>
     <string name="refresh">刷新</string>
+    <string name="pull_to_refresh">下拉刷新</string>
+    <string name="release_to_refresh">松开刷新</string>
+    <string name="refreshing">正在刷新</string>
+    <string name="expand">展开</string>
+    <string name="collapse">折叠</string>
+    <string name="select_color">选择颜色</string>
+    <string name="color_value">色值</string>
     <string name="start">开始</string>
     <string name="stop">停止</string>
     <string name="pause">暂停</string>
@@ -645,6 +670,8 @@
     <string name="tts">TTS</string>
     <string name="web_dav_pw">WebDav 密码</string>
     <string name="web_dav_pw_s">输入你的 WebDav 授权密码</string>
+    <string name="show_password">显示密码</string>
+    <string name="hide_password">隐藏密码</string>
     <string name="web_dav_url_s">输入你的WebDav服务器地址</string>
     <string name="web_dav_url">WebDav 服务器地址</string>
     <string name="web_dav_account">WebDav 账号</string>
@@ -945,6 +972,10 @@
     <string name="pref_anti_alias_summary">绘制图片时抗锯齿</string>
     <string name="upload_url">上传 URL</string>
     <string name="download_url_rule">下载 URL 规则</string>
+    <string name="clipboard_empty">剪贴板没有内容</string>
+    <string name="clipboard_empty_or_invalid">剪贴板为空或格式不正确</string>
+    <string name="invalid_format">格式不正确</string>
+    <string name="complete_required_information">请填写完整信息</string>
     <string name="sort_by_respondTime">响应时间排序</string>
     <string name="respondTime">响应时间:%1$d ms</string>
     <string name="export_success">导出成功</string>
@@ -1040,6 +1071,8 @@
     <string name="search_via_url">搜索URL</string>
 	<string name="cover_rule_edit">封面规则</string>
     <string name="cover_rule_summary">进入详情页时使用封面规则重新获取封面</string>
+    <string name="cover_rule_fields_required">搜索 URL 和封面规则不能为空</string>
+    <string name="cover_selected_images_count">已选择 %1$d 张图片</string>
     <string name="scroll_to_dur_source">定位到当前书源</string>
     <string name="sys_tts_config">系统 TTS 设置</string>
     <string name="sys_tts_config_summary">打开系统 TTS 设置界面</string>
@@ -1137,6 +1170,7 @@
     <string name="same_title_removed">移除重复标题</string>
     <string name="update_book_fail">更新失败</string>
     <string name="notification_permission_rationale">阅读需要发送通知来显示朗读控制和下载进度。</string>
+    <string name="permission_not_required">无需申请</string>
     <string name="webdav_after_local_restore_confirm">WebDav 书源比本地新,是否恢复</string>
     <string name="c_whitelist">白名单(contentWhitelist)</string>
     <string name="c_blacklist">黑名单(contentBlacklist)</string>
@@ -1223,6 +1257,23 @@
     <string name="key_page_on_long_press">按键长按翻页</string>
     <string name="save_log">保存日志</string>
     <string name="create_heap_dump">创建堆转储</string>
+    <string name="about_privacy_policy_title">隐私政策</string>
+    <string name="about_license_title">许可证</string>
+    <string name="about_disclaimer_title">免责声明</string>
+    <string name="about_installed_version_title">当前版本</string>
+    <string name="about_current_version">当前版本</string>
+    <string name="about_new_version">新版本</string>
+    <string name="about_update_channel">渠道</string>
+    <string name="about_update_action">更新</string>
+    <string name="about_backup_dir_not_set">未设置备份目录</string>
+    <string name="about_log_recording_disabled">未开启日志记录,请去其他设置里打开记录日志</string>
+    <string name="about_saved_to_backup_dir">已保存至备份目录</string>
+    <string name="about_heap_dump_recording_disabled">未开启堆转储记录,请去其他设置里打开记录堆转储</string>
+    <string name="about_heap_dump_creating">开始创建堆转储</string>
+    <string name="about_heap_dump_not_found">未找到堆转储文件</string>
+    <string name="about_download_info_incomplete">下载信息不完整</string>
+    <string name="about_no_data">没有数据</string>
+    <string name="about_start_download">开始下载: %s</string>
     <string name="crash_report">崩溃报告</string>
     <string name="crash_report_message">应用发生崩溃。报告问题时,请复制以下错误信息。</string>
     <string name="crash_report_empty">未找到崩溃信息。</string>
@@ -1284,6 +1335,65 @@
     <string name="ungroup">未分组</string>
     <string name="dynamic_colors">动态取色</string>
     <string name="dynamic_colors_summary">使用基于壁纸的主题颜色</string>
+    <string name="theme_manage_export_success">主题导出成功</string>
+    <string name="theme_manage_export_failed">主题导出失败</string>
+    <string name="theme_manage_import_success">主题导入成功,部分设置需要重启生效</string>
+    <string name="theme_manage_import_failed">主题导入失败</string>
+    <string name="theme_manage_save_current">保存当前设置</string>
+    <string name="theme_manage_save_current_summary">保存当前主题配置为新主题</string>
+    <string name="theme_manage_export_current">导出当前主题</string>
+    <string name="theme_manage_export_current_summary">将当前主题配置导出为 JSON 文件</string>
+    <string name="theme_manage_import_config">导入主题配置</string>
+    <string name="theme_manage_import_config_summary">从 JSON 文件导入主题配置</string>
+    <string name="theme_manage_saved_themes">已保存的主题</string>
+    <string name="theme_manage_save_theme">保存主题</string>
+    <string name="theme_manage_save">保存</string>
+    <string name="theme_manage_name_hint">请输入主题名称</string>
+    <string name="theme_manage_apply_theme">应用主题</string>
+    <string name="theme_manage_apply">应用</string>
+    <string name="theme_manage_apply_message">确定应用主题「%s」?应用后需要重启才能完全生效。</string>
+    <string name="theme_manage_delete_theme">删除主题</string>
+    <string name="theme_manage_delete_message">确定删除主题「%s」?此操作不可恢复。</string>
+    <string name="theme_manage_preview_day">日间</string>
+    <string name="theme_manage_preview_night">夜间</string>
+    <string name="theme_manage_edit_theme">编辑主题</string>
+    <string name="theme_manage_section_basic">基础设置</string>
+    <string name="theme_manage_section_colors">颜色设置</string>
+    <string name="theme_manage_use_palette_colors">使用色板生成颜色</string>
+    <string name="theme_manage_primary_color">主题色</string>
+    <string name="theme_manage_secondary_color">次要主题色</string>
+    <string name="theme_manage_primary_text_color">主要字体色</string>
+    <string name="theme_manage_secondary_text_color">次要字体色</string>
+    <string name="theme_manage_background_color">背景色</string>
+    <string name="theme_manage_label_container_color">标签容器色</string>
+    <string name="theme_manage_day_seed_color">日间种子色</string>
+    <string name="theme_manage_night_seed_color">夜间种子色</string>
+    <string name="theme_manage_section_layout">界面布局</string>
+    <string name="theme_manage_show_discovery">发现</string>
+    <string name="theme_manage_status_bar">状态栏</string>
+    <string name="theme_manage_page_turn_animation">翻页动画</string>
+    <string name="theme_manage_label_visibility">标签显示</string>
+    <string name="theme_manage_section_blur">模糊效果</string>
+    <string name="theme_manage_top_bar_blur_radius">顶栏模糊半径</string>
+    <string name="theme_manage_bottom_bar_blur_radius">底栏模糊半径</string>
+    <string name="theme_manage_top_bar_blur_opacity">顶栏模糊不透明度</string>
+    <string name="theme_manage_bottom_bar_blur_opacity">底栏模糊不透明度</string>
+    <string name="theme_manage_section_opacity">透明度</string>
+    <string name="theme_manage_section_container">容器设置</string>
+    <string name="theme_config_manage_label_colors">管理标签颜色</string>
+    <string name="theme_config_label_color_name">标签 %1$d</string>
+    <string name="theme_config_miuix_experimental_warning">Miuix 目前为测试主题,且不对基于 View 的界面生效。</string>
+    <string name="theme_config_bottom_bar_lens_radius">底栏模糊强度</string>
+    <string name="theme_config_bottom_bar_lens_radius_summary">控制液态玻璃的扭曲程度</string>
+    <string name="theme_config_blur_radius_performance_summary">模糊半径越大,系统运行越卡顿</string>
+    <string name="theme_config_divider_width">分割线粗细</string>
+    <string name="theme_config_divider_length">分割线长度</string>
+    <string name="theme_config_nav_icon_settings">导航栏图标设置</string>
+    <string name="theme_config_nav_icons">导航栏图标</string>
+    <string name="theme_config_nav_icons_custom_count">已设置 %1$d 个自定义图标</string>
+    <string name="theme_config_nav_icons_default">使用默认图标</string>
+    <string name="theme_config_add_nav_icon">添加%1$s图标</string>
+    <string name="theme_config_no_font_files">没有字体文件</string>
     <string name="input_value_range">输入数值(%1$d-%2$d)</string>
     <string name="change_icon_success">图标已修改</string>
     <string name="replace_rule_title_setting">配置替换规则</string>
@@ -1331,6 +1441,38 @@
     <string name="abi_arm64_v8a">ABI: arm64-v8a</string>
     <string name="download_link">下载链接</string>
     <string name="no_bookmark">暂无书签</string>
+    <string name="bookmark_management">书签管理</string>
+    <string name="locate_current_reading">定位至当前阅读</string>
+    <string name="move_to_top">移至顶部</string>
+    <string name="move_to_bottom">移至底部</string>
+    <string name="invert_selection">反选</string>
+    <string name="select_following">选择后续</string>
+    <string name="search_chapters">搜索章节…</string>
+    <string name="use_replace_rule">使用替换规则</string>
+    <string name="show_word_count">显示字数</string>
+    <string name="local_book_options">本地书籍选项</string>
+    <string name="local_book_toc_rule">本地书籍目录规则</string>
+    <string name="select_toc_rule">选择目录规则</string>
+    <string name="split_long_chapters">拆分超长章节</string>
+    <string name="export_bookmarks_json">导出书签为 JSON</string>
+    <string name="export_bookmarks_markdown">导出书签为 Markdown</string>
+    <string name="volume_management">卷管理</string>
+    <string name="quick_jump">快速跳转</string>
+    <string name="download_selected_count">下载已选(%1$d)</string>
+    <string name="toc_rule_update_failed">更新目录规则失败:%1$s</string>
+    <string name="toc_rule_updated">目录规则已更新</string>
+    <string name="setting_failed">设置失败:%1$s</string>
+    <string name="split_long_chapters_enabled">已开启长章节拆分</string>
+    <string name="split_long_chapters_disabled">已关闭长章节拆分</string>
+    <string name="no_bookmarks_to_export">没有可导出的书签</string>
+    <string name="save_success">保存成功</string>
+    <string name="save_failed_with_error">保存失败:%1$s</string>
+    <string name="select_chapters">请选择章节</string>
+    <string name="bookmarks_added_count">已添加 %1$d 个书签</string>
+    <string name="start_downloading_chapters">开始下载 %1$d 个章节</string>
+    <string name="start_downloading_chapter">开始下载章节</string>
+    <string name="all_chapters_cached">所有章节已缓存</string>
+    <string name="start_downloading_remaining_chapters">开始下载剩余 %1$d 个章节</string>
     <string name="read_end">已读完</string>
     <string name="system_engine">系统引擎</string>
     <string name="copy_from">剪切板导入</string>

+ 8 - 8
app/src/main/res/values/arrays.xml

@@ -13,17 +13,17 @@
 	</string-array>
 
 	<string-array name="layout_type">
-		<item>列表</item>
-		<item>单列</item>
-		<item>双列</item>
-		<item>瀑布</item>
-		<item>三列</item>
+		<item>List</item>
+		<item>Single Column</item>
+		<item>Two Columns</item>
+		<item>Waterfall</item>
+		<item>Three Columns</item>
 	</string-array>
 
 	<string-array name="rss_type">
-		<item>网页</item>
-		<item>图片</item>
-		<item>视频</item>
+		<item>Web Page</item>
+		<item>Image</item>
+		<item>Video</item>
 	</string-array>
 
 	<string-array name="group_style">

+ 261 - 119
app/src/main/res/values/strings.xml

@@ -38,7 +38,8 @@
     <string name="predictive_back_summary">Enable system predictive back gestures</string>
     <string name="restart_to_apply">Restart to apply</string>
     <string name="custom_theme">Custom Theme</string>
-    <string name="appearance_setting">外观设置</string>
+    <string name="custom_theme_colors">Custom Colors</string>
+    <string name="appearance_setting">Appearance Settings</string>
     <string name="click_to_select">Click to select</string>
     <string name="compose_related">Compose Related</string>
     <string name="use_flexible_top_bar">Use Flexible App Top Bar</string>
@@ -92,25 +93,25 @@
     <string name="setting">Settings</string>
     <string name="theme_setting">Theme Settings</string>
     <string name="theme_setting_s">Some settings related to interface or color</string>
-    <string name="personalization_setting">深度个性化设置</string>
-    <string name="personalization_setting_s">字体等深度个性化设置</string>
-    <string name="theme_pack">主题管理</string>
-    <string name="theme_pack_s">管理、导入导出完整主题包</string>
-    <string name="font_setting">字体设置</string>
-    <string name="font_setting_s">选择应用字体</string>
-    <string name="color_setting">颜色设置</string>
-    <string name="reset_color">重置颜色</string>
-    <string name="top_bar_color">应用栏颜色</string>
-    <string name="nav_bar_color">导航栏颜色</string>
-    <string name="font_color">字体颜色</string>
-    <string name="background_color">背景颜色</string>
-    <string name="border_setting">边框设置</string>
-    <string name="show_container_border">显示容器边框</string>
-    <string name="border_width">边框粗细</string>
-    <string name="border_style">边框样式</string>
-    <string name="solid_border">实线</string>
-    <string name="dashed_border">虚线</string>
-    <string name="dash_gap">虚线间隔</string>
+    <string name="personalization_setting">Advanced Personalization</string>
+    <string name="personalization_setting_s">Fine-tune fonts and other appearance details</string>
+    <string name="theme_pack">Theme Management</string>
+    <string name="theme_pack_s">Manage, import, and export complete theme packs</string>
+    <string name="font_setting">Font Settings</string>
+    <string name="font_setting_s">Choose the app font</string>
+    <string name="color_setting">Color Settings</string>
+    <string name="reset_color">Reset Colors</string>
+    <string name="top_bar_color">App Bar Color</string>
+    <string name="nav_bar_color">Navigation Bar Color</string>
+    <string name="font_color">Font Color</string>
+    <string name="background_color">Background Color</string>
+    <string name="border_setting">Border Settings</string>
+    <string name="show_container_border">Show Container Border</string>
+    <string name="border_width">Border Width</string>
+    <string name="border_style">Border Style</string>
+    <string name="solid_border">Solid</string>
+    <string name="dashed_border">Dashed</string>
+    <string name="dash_gap">Dash Gap</string>
     <string name="other_setting">Other Settings</string>
     <string name="other_setting_s">Language • Other Settings</string>
     <string name="about">About</string>
@@ -125,6 +126,16 @@
     <string name="origin_format">Origin: %s</string>
     <string name="read_dur_progress">Origin: %s</string>
     <string name="book_name">Title</string>
+    <string name="cover_url">Cover URL</string>
+    <string name="fixed_book_type">Lock Book Type</string>
+    <string name="fixed_book_type_summary">Do not overwrite the book type when the book updates</string>
+    <string name="book_type_label">Book Type</string>
+    <string name="book_type_text">Text</string>
+    <string name="book_type_audio">Audio</string>
+    <string name="book_type_image">Image</string>
+    <string name="add_tag">Add Tag</string>
+    <string name="edit_tag">Edit Tag</string>
+    <string name="tag">Tag</string>
     <string name="lasted_show">Latest: %s</string>
     <string name="check_add_bookshelf">Would you like to add %s to your Bookshelf?</string>
     <string name="import_books_count">%s text file(s) in total</string>
@@ -151,6 +162,8 @@
     <string name="pt_auto_download">Auto-download</string>
     <string name="ps_auto_download">Download the latest chapters automatically  when updating books</string>
     <string name="backup_restore">Backup and Restore</string>
+    <string name="backup_error">Backup failed</string>
+    <string name="restore_error">Restore failed</string>
     <string name="web_dav_set">WebDAV settings</string>
     <string name="web_dav_set_import_old">WebDAV settings • Import legacy data</string>
     <string name="backup">Backup</string>
@@ -168,6 +181,11 @@
     <string name="on_restore">Restoring</string>
     <string name="restore_success">Restore succeed</string>
     <string name="restore_fail">Backup failed</string>
+    <string name="restore_fail_with_error">Restore failed: %1$s</string>
+    <string name="restore_from_local">Restore from Local</string>
+    <string name="restore_from_network">Restore from Network</string>
+    <string name="webdav_restore_fallback_message">WebDAV error\n%1$s\nRestore from a local backup instead.</string>
+    <string name="webdav_restore_fail">WebDAV restore failed\n%1$s</string>
     <string name="screen_direction">Screen Orientation</string>
     <string name="screen_sensor">Auto(sensor)</string>
     <string name="screen_landscape">Landscape</string>
@@ -191,6 +209,13 @@
     <string name="audio_pause">Pause</string>
     <string name="text_return">Back</string>
     <string name="refresh">Refresh</string>
+    <string name="pull_to_refresh">Pull to refresh</string>
+    <string name="release_to_refresh">Release to refresh</string>
+    <string name="refreshing">Refreshing</string>
+    <string name="expand">Expand</string>
+    <string name="collapse">Collapse</string>
+    <string name="select_color">Select Color</string>
+    <string name="color_value">Color Value</string>
     <string name="start">Start</string>
     <string name="stop">Stop</string>
     <string name="pause">Pause</string>
@@ -643,12 +668,12 @@
     <string name="bg_image">Background Images</string>
     <string name="bg_color">Background color</string>
     <string name="text_color">Text Color</string>
-    <string name="header_color">页眉</string>
-    <string name="footer_color">页脚</string>
-    <string name="header_font">设置字体</string>
-    <string name="footer_font">页脚字体</string>
-    <string name="header_font_size">设置字号</string>
-    <string name="footer_font_size">页脚字号</string>
+    <string name="header_color">Header</string>
+    <string name="footer_color">Footer</string>
+    <string name="header_font">Header Font</string>
+    <string name="footer_font">Footer Font</string>
+    <string name="header_font_size">Header Font Size</string>
+    <string name="footer_font_size">Footer Font Size</string>
     <string name="select_image">Select a picture</string>
     <string name="group_manage">Group Management</string>
     <string name="group_select">Group selection</string>
@@ -671,6 +696,8 @@
     <string name="tts">TTS</string>
     <string name="web_dav_pw">WebDAV password</string>
     <string name="web_dav_pw_s">Input you WebDAV authorized password</string>
+    <string name="show_password">Show password</string>
+    <string name="hide_password">Hide password</string>
     <string name="web_dav_url">WebDAV Server Address</string>
     <string name="web_dav_url_s">Input your WebDAV server address</string>
     <string name="web_dav_account">WebDAV Account</string>
@@ -770,11 +797,11 @@
     <string name="contact">Contact</string>
     <string name="license">License</string>
     <string name="other">More Settings</string>
-    <string name="official_account">开源阅读</string>
+    <string name="official_account">Open Source Reading</string>
     <string name="follow_official_account">Follow WeChat Official Accounts</string>
     <string name="wechat">WeChat</string>
     <string name="thanks">Supporting me will be appreciated</string>
-    <string name="about_official_account">Official Accounts[开源阅读]</string>
+    <string name="about_official_account">Official Account [Open Source Reading]</string>
     <string name="source_auto_changing">Changing source</string>
     <string name="click_to_apply">Tap to join</string>
     <string name="middle">Middle</string>
@@ -818,7 +845,7 @@
     <string name="starting_download">Starting download</string>
     <string name="already_in_download">This book is already in Download list</string>
     <string name="click_to_open">Click to open</string>
-    <string name="follow_public_account_summary">Follow [开源阅读] to support me by clicking on ads</string>
+    <string name="follow_public_account_summary">Follow [Open Source Reading] to support me by clicking on ads</string>
     <string name="weChat_appreciation_code">WeChat Tipping Code</string>
     <string name="alipay">AliPay</string>
     <string name="alipay_red_envelope_search_code">AliPay red envelope search code</string>
@@ -849,7 +876,7 @@
     <string name="language">Language</string>
     <string name="import_rss_source">Import rss source</string>
     <string name="donate_summary">Your donation makes this app better</string>
-    <string name="about_summary">Wechat official account [开源阅读软件]</string>
+    <string name="about_summary">WeChat official account [Open Source Reading]</string>
     <string name="read_record">Read Record</string>
     <string name="del_read_record">Delete Read record</string>
     <string name="read_record_summary">Read record summary</string>
@@ -976,6 +1003,10 @@
     <string name="pref_anti_alias_summary">Anti-Aliasing when draw picture</string>
     <string name="upload_url">upload URL</string>
     <string name="download_url_rule">downloadUrlRule(downloadUrls)</string>
+    <string name="clipboard_empty">Clipboard is empty</string>
+    <string name="clipboard_empty_or_invalid">Clipboard is empty or has an invalid format</string>
+    <string name="invalid_format">Invalid format</string>
+    <string name="complete_required_information">Fill in all required information</string>
     <string name="export_success">export success</string>
     <string name="path">path</string>
     <string name="direct_link_upload_rule">Direct Link Upload Rules</string>
@@ -1042,7 +1073,7 @@
     <string name="scope_title">Act on the title</string>
     <string name="scope_content">Act on the content</string>
     <string name="join_qq_channel">Join QQ channel</string>
-    <string name="qq_channel_summary">点击加入阅读QQ频道</string>
+    <string name="qq_channel_summary">Tap to join the Reading QQ channel</string>
     <string name="menu_refresh_dur">Refresh the current chapter</string>
     <string name="menu_refresh_after">Refresh the chapter after</string>
     <string name="menu_refresh_all">Refresh all chapters</string>
@@ -1057,7 +1088,7 @@
     <string name="welcome_style">Launch interface style</string>
     <string name="welcome_style_summary">Startup screen image and whether to display text, etc.</string>
     <string name="show_welcome_text">Display text</string>
-    <string name="welcome_text">阅读|享受美好时光</string>
+    <string name="welcome_text">Read | Enjoy the Moment</string>
     <string name="custom_welcome">Customize the welcome page</string>
     <string name="custom_welcome_summary">Whether to use a customized welcome page</string>
     <string name="show_icon">Show icon</string>
@@ -1071,6 +1102,8 @@
     <string name="search_via_url">Search via URL</string>
 	<string name="cover_rule_edit">Cover Rules</string>
     <string name="cover_rule_summary">Use the cover rule to retrieve the cover when entering the details page</string>
+    <string name="cover_rule_fields_required">Search URL and cover rule cannot be empty</string>
+    <string name="cover_selected_images_count">%1$d images selected</string>
     <string name="scroll_to_dur_source">Navigate to the current book source</string>
     <string name="sys_tts_config">System tts Settings</string>
     <string name="sys_tts_config_summary">Open the system tts settings interface</string>
@@ -1103,7 +1136,7 @@
     <!-- string end -->
     <string name="error_decode_bitmap">Fail to decode bitmap</string>
     <string name="error_image_url_empty">Image URL is empty, check replacement rules</string>
-    <string name="variable_comment">变量说明(variableComment)</string>
+    <string name="variable_comment">Variable Description (variableComment)</string>
     <string name="remote_book">Remote book</string>
     <string name="reading_time_sort">Sort by reading duration</string>
     <string name="last_read_time_sort">Sort by read time</string>
@@ -1116,8 +1149,8 @@
     <string name="show_add_to_shelf_alert_title">Prompt to Add to Bookshelf When Return</string>
     <string name="show_add_to_shelf_alert_summary">Prompt to add to bookshelf when return from reading books that are not on the bookshelf</string>
     <string name="review">Review</string>
-    <string name="rule_avatar">段评发布者头像(avatarRule)</string>
-    <string name="rule_review_url">段评URL(reviewUrl)</string>
+    <string name="rule_avatar">Paragraph Review Author Avatar (avatarRule)</string>
+    <string name="rule_review_url">Paragraph Review URL (reviewUrl)</string>
     <string name="rule_review_content">Paragraph review content(contentRule)</string>
     <string name="rule_post_time">Release time of paragraph comments(postTimeRule)</string>
     <string name="rule_review_quote">Paragraph comment replied to URL(reviewQuoteUrl)</string>
@@ -1167,28 +1200,29 @@
     <string name="same_title_removed">Remove Duplicate Title</string>
     <string name="update_book_fail">Update failed</string>
     <string name="notification_permission_rationale">Reading requires sending notifications to show reading control and download progress</string>
+    <string name="permission_not_required">Permission not required</string>
     <string name="webdav_after_local_restore_confirm">The WebDAV book source is newer than the local one. Do you want to restore it?</string>
-    <string name="c_whitelist">白名单(contentWhitelist)</string>
-    <string name="c_blacklist">黑名单(contentBlacklist)</string>
+    <string name="c_whitelist">Whitelist (contentWhitelist)</string>
+    <string name="c_blacklist">Blacklist (contentBlacklist)</string>
     <string name="confirm">Confirm</string>
     <string name="jump_to_another_app">Jump to another app</string>
     <string name="clear_webview_data">Clear WebView Data</string>
     <string name="clear_webview_data_summary">Clear all data in WebView</string>
     <string name="source_tab_list">List</string>
-    <string name="source_tab_start">启动</string>
-    <string name="r_startHtml">启动页html文本</string>
-    <string name="r_startStyle">启动页style文本</string>
-    <string name="r_startJs">启动页Js(startJs)</string>
-    <string name="r_preloadJs">预注入Js(preloadJs)</string>
-    <string name="enable_preload">预加载</string>
-    <string name="load_with_web_log">显示WebView日志</string>
-    <string name="cache_first">优先缓存</string>
-    <string name="layout_type">布局</string>
+    <string name="source_tab_start">Startup</string>
+    <string name="r_startHtml">Startup page HTML text</string>
+    <string name="r_startStyle">Startup page style text</string>
+    <string name="r_startJs">Startup page JS (startJs)</string>
+    <string name="r_preloadJs">Pre-injected JS (preloadJs)</string>
+    <string name="enable_preload">Preload</string>
+    <string name="load_with_web_log">Show WebView Logs</string>
+    <string name="cache_first">Cache First</string>
+    <string name="layout_type">Layout</string>
     <string name="dict_rule">Dictionary Rules</string>
     <string name="config_dict_rule">Config dict rule</string>
     <string name="create">Create</string>
-    <string name="url_rule">URL规则(urlRule)</string>
-    <string name="show_rule">显示规则(showRule)</string>
+    <string name="url_rule">URL Rule (urlRule)</string>
+    <string name="show_rule">Display Rule (showRule)</string>
     <string name="config_txt_toc_rule">Configure txt catalog rules</string>
     <string name="import_dict_rule">Import dict rule</string>
     <string name="keep_group">Keep group</string>
@@ -1217,7 +1251,7 @@
     <string name="respondTime">respondTime: %1$d ms</string>
     <string name="load_word_count">Load word count</string>
     <string name="replace_exclude_scope">Exclude range, optional title or source URL</string>
-    <string name="format_js_rule">格式化规则(formatJs)</string>
+    <string name="format_js_rule">Format Rule (formatJs)</string>
     <string name="adjust_pos">Adjustment position</string>
     <string name="error_scope_input">Please enter the correct range</string>
     <string name="custom_export">Custom Export</string>
@@ -1255,6 +1289,23 @@
     <string name="key_page_on_long_press">Press and Hold the Key to Turn Page</string>
     <string name="save_log">Save logs</string>
     <string name="create_heap_dump">Create Heap Dump</string>
+    <string name="about_privacy_policy_title">Privacy Policy</string>
+    <string name="about_license_title">License</string>
+    <string name="about_disclaimer_title">Disclaimer</string>
+    <string name="about_installed_version_title">Installed Version</string>
+    <string name="about_current_version">Installed Version</string>
+    <string name="about_new_version">Latest Version</string>
+    <string name="about_update_channel">Release Channel</string>
+    <string name="about_update_action">Download Update</string>
+    <string name="about_backup_dir_not_set">Set a backup directory first</string>
+    <string name="about_log_recording_disabled">Log recording is off. Turn it on in Other Settings first.</string>
+    <string name="about_saved_to_backup_dir">Saved to backup folder</string>
+    <string name="about_heap_dump_recording_disabled">Heap dump recording is off. Turn it on in Other Settings first.</string>
+    <string name="about_heap_dump_creating">Creating heap dump…</string>
+    <string name="about_heap_dump_not_found">Heap dump file not found</string>
+    <string name="about_download_info_incomplete">Update download information is incomplete</string>
+    <string name="about_no_data">No update log available</string>
+    <string name="about_start_download">Downloading %s</string>
     <string name="crash_report">Crash report</string>
     <string name="crash_report_message">The app crashed. Copy the error information below when reporting the issue.</string>
     <string name="crash_report_empty">No crash information found.</string>
@@ -1283,7 +1334,7 @@
     <string name="read_aloud_read_phone_state_permission_rationale">Reading requires phone state access to pause reading aloud during calls</string>
     <string name="read_aloud_by_media_button_title">Start Read Aloud via Headset Buttons</string>
     <string name="read_aloud_by_media_button_summary">Use headset buttons to start reading aloud</string>
-    <string name="group_sources_by_domain">按域名分组显示</string>
+    <string name="group_sources_by_domain">Group Sources by Domain</string>
     <string name="theme_config">Theme Configuration</string>
     <string name="show_manga_ui">Manga Viewer</string>
     <string name="disable_manga_scale">Disable Manga Zoom</string>
@@ -1316,6 +1367,65 @@
     <string name="ungroup">Ungrouped</string>
     <string name="dynamic_colors">Dynamic Colors</string>
     <string name="dynamic_colors_summary">Use wallpaper-based theme colors</string>
+    <string name="theme_manage_export_success">Theme exported</string>
+    <string name="theme_manage_export_failed">Could not export theme</string>
+    <string name="theme_manage_import_success">Theme imported. Some changes require a restart.</string>
+    <string name="theme_manage_import_failed">Could not import theme</string>
+    <string name="theme_manage_save_current">Save Current Theme</string>
+    <string name="theme_manage_save_current_summary">Save the current appearance settings as a theme</string>
+    <string name="theme_manage_export_current">Export Current Theme</string>
+    <string name="theme_manage_export_current_summary">Export the current theme settings as a JSON file</string>
+    <string name="theme_manage_import_config">Import Theme File</string>
+    <string name="theme_manage_import_config_summary">Import theme settings from a JSON file</string>
+    <string name="theme_manage_saved_themes">Saved Themes</string>
+    <string name="theme_manage_save_theme">Save Theme</string>
+    <string name="theme_manage_save">Save</string>
+    <string name="theme_manage_name_hint">Enter a theme name</string>
+    <string name="theme_manage_apply_theme">Apply Theme</string>
+    <string name="theme_manage_apply">Apply</string>
+    <string name="theme_manage_apply_message">Apply theme \"%s\"? A restart is required for all changes to take effect.</string>
+    <string name="theme_manage_delete_theme">Delete Theme</string>
+    <string name="theme_manage_delete_message">Delete theme \"%s\"? This cannot be undone.</string>
+    <string name="theme_manage_preview_day">Day</string>
+    <string name="theme_manage_preview_night">Night</string>
+    <string name="theme_manage_edit_theme">Edit Theme</string>
+    <string name="theme_manage_section_basic">Basic Settings</string>
+    <string name="theme_manage_section_colors">Colors</string>
+    <string name="theme_manage_use_palette_colors">Generate Colors from Palette</string>
+    <string name="theme_manage_primary_color">Primary Color</string>
+    <string name="theme_manage_secondary_color">Secondary Color</string>
+    <string name="theme_manage_primary_text_color">Primary Text Color</string>
+    <string name="theme_manage_secondary_text_color">Secondary Text Color</string>
+    <string name="theme_manage_background_color">Background Color</string>
+    <string name="theme_manage_label_container_color">Label Container Color</string>
+    <string name="theme_manage_day_seed_color">Day Seed Color</string>
+    <string name="theme_manage_night_seed_color">Night Seed Color</string>
+    <string name="theme_manage_section_layout">Interface Layout</string>
+    <string name="theme_manage_show_discovery">Show Discovery</string>
+    <string name="theme_manage_status_bar">Status Bar</string>
+    <string name="theme_manage_page_turn_animation">Page Turn Animation</string>
+    <string name="theme_manage_label_visibility">Label Visibility</string>
+    <string name="theme_manage_section_blur">Blur Effects</string>
+    <string name="theme_manage_top_bar_blur_radius">Top Bar Blur Radius</string>
+    <string name="theme_manage_bottom_bar_blur_radius">Bottom Bar Blur Radius</string>
+    <string name="theme_manage_top_bar_blur_opacity">Top Bar Blur Opacity</string>
+    <string name="theme_manage_bottom_bar_blur_opacity">Bottom Bar Blur Opacity</string>
+    <string name="theme_manage_section_opacity">Opacity</string>
+    <string name="theme_manage_section_container">Container</string>
+    <string name="theme_config_manage_label_colors">Manage Label Colors</string>
+    <string name="theme_config_label_color_name">Label %1$d</string>
+    <string name="theme_config_miuix_experimental_warning">Miuix is currently experimental and does not apply to View-based screens.</string>
+    <string name="theme_config_bottom_bar_lens_radius">Bottom Bar Blur Strength</string>
+    <string name="theme_config_bottom_bar_lens_radius_summary">Controls the liquid glass distortion strength</string>
+    <string name="theme_config_blur_radius_performance_summary">Higher blur radius may reduce performance</string>
+    <string name="theme_config_divider_width">Divider Width</string>
+    <string name="theme_config_divider_length">Divider Length</string>
+    <string name="theme_config_nav_icon_settings">Navigation Bar Icon Settings</string>
+    <string name="theme_config_nav_icons">Navigation Bar Icons</string>
+    <string name="theme_config_nav_icons_custom_count">%1$d custom icons set</string>
+    <string name="theme_config_nav_icons_default">Using default icons</string>
+    <string name="theme_config_add_nav_icon">Add %1$s icon</string>
+    <string name="theme_config_no_font_files">No font files</string>
     <string name="input_value_range">Enter value (%1$d-%2$d)</string>
     <string name="change_icon_success">Icon Updated</string>
     <string name="replace_rule_title_setting">Configure Replace Rules</string>
@@ -1363,6 +1473,38 @@
     <string name="abi_arm64_v8a">ABI: arm64-v8a</string>
     <string name="download_link">Download Link</string>
     <string name="no_bookmark">No Bookmarks</string>
+    <string name="bookmark_management">Bookmark Management</string>
+    <string name="locate_current_reading">Go to Current Reading</string>
+    <string name="move_to_top">Move to Top</string>
+    <string name="move_to_bottom">Move to Bottom</string>
+    <string name="invert_selection">Invert Selection</string>
+    <string name="select_following">Select Following</string>
+    <string name="search_chapters">Search chapters…</string>
+    <string name="use_replace_rule">Use Replacement Rules</string>
+    <string name="show_word_count">Show Word Count</string>
+    <string name="local_book_options">Local Book Options</string>
+    <string name="local_book_toc_rule">Local Book Chapter Rule</string>
+    <string name="select_toc_rule">Select Chapter Rule</string>
+    <string name="split_long_chapters">Split Long Chapters</string>
+    <string name="export_bookmarks_json">Export Bookmarks as JSON</string>
+    <string name="export_bookmarks_markdown">Export Bookmarks as Markdown</string>
+    <string name="volume_management">Volume Management</string>
+    <string name="quick_jump">Quick Jump</string>
+    <string name="download_selected_count">Download Selected (%1$d)</string>
+    <string name="toc_rule_update_failed">Failed to update chapter rule: %1$s</string>
+    <string name="toc_rule_updated">Chapter rule updated</string>
+    <string name="setting_failed">Setting failed: %1$s</string>
+    <string name="split_long_chapters_enabled">Long chapter splitting enabled</string>
+    <string name="split_long_chapters_disabled">Long chapter splitting disabled</string>
+    <string name="no_bookmarks_to_export">No bookmarks to export</string>
+    <string name="save_success">Saved</string>
+    <string name="save_failed_with_error">Save failed: %1$s</string>
+    <string name="select_chapters">Select chapters</string>
+    <string name="bookmarks_added_count">%1$d bookmarks added</string>
+    <string name="start_downloading_chapters">Downloading %1$d chapters</string>
+    <string name="start_downloading_chapter">Downloading chapter</string>
+    <string name="all_chapters_cached">All chapters are cached</string>
+    <string name="start_downloading_remaining_chapters">Downloading remaining %1$d chapters</string>
     <string name="read_end">Finished Reading</string>
     <string name="system_engine">System Engine</string>
     <string name="copy_from">Import from Clipboard</string>
@@ -1394,61 +1536,61 @@
     <string name="author_read">%1$s • %2$d Not read</string>
     <string name="menu_alpha_sum">Current %d%%</string>
     <string name="menu_alpha">Menu Transparency</string>
-    <string name="change_source_option_title">请选择操作</string>
-    <string name="replace_current_book">替换本书</string>
-    <string name="add_as_new_book">新增书籍</string>
-    <string name="quick_add_to_shelf">快速添加到书架</string>
-    <string name="book_added_to_shelf">书籍已添加到书架</string>
-    <string name="seed_photo_summary">使用图片生成主题颜色</string>
-    <string name="seed_photo_alart">若需使用种子颜色,请取消种子图片选择</string>
+    <string name="change_source_option_title">Choose an Action</string>
+    <string name="replace_current_book">Replace This Book</string>
+    <string name="add_as_new_book">Add as New Book</string>
+    <string name="quick_add_to_shelf">Quick Add to Bookshelf</string>
+    <string name="book_added_to_shelf">Book added to bookshelf</string>
+    <string name="seed_photo_summary">Generate theme colors from an image</string>
+    <string name="seed_photo_alart">To use a seed color, clear the selected seed image first</string>
     <string name="click_to_delete">Click to delete or change</string>
-    <string name="tip_custom_mode">由于主题引擎依赖Monet,对于低于Android12的设备,动态取色与自定义主题不会生效。</string>
-    <string name="restart_required_message">修改该选项需要重启</string>
-    <string name="restart_later_message">稍后重启</string>
-    <string name="search_select_group">选择书源分组</string>
-    <string name="day_night_switch">深浅色模式开关</string>
-    <string name="manga_long_click">长按保存图片</string>
-    <string name="read_type_long">长按设置为全局默认模式</string>
-    <string name="read_slider_mode">滑动条显示样式</string>
-    <string name="bookshelf_update_limit">书架更新数量限制</string>
-    <string name="text_shadow_x">X轴距离</string>
-    <string name="text_shadow_y">Y轴距离</string>
-    <string name="text_shadow_radius">阴影半径</string>
-    <string name="text_shadow_color">阴影</string>
-    <string name="global_read_setting">阅读显示 • 漫画相关</string>
-    <string name="screen_settings">屏幕显示与亮度</string>
+    <string name="tip_custom_mode">The theme engine depends on Monet. Dynamic colors and custom themes do not take effect on devices below Android 12.</string>
+    <string name="restart_required_message">Changing this option requires a restart</string>
+    <string name="restart_later_message">Restart Later</string>
+    <string name="search_select_group">Select Book Source Group</string>
+    <string name="day_night_switch">Light/Dark Mode Switch</string>
+    <string name="manga_long_click">Long press to save image</string>
+    <string name="read_type_long">Long press to set as the global default mode</string>
+    <string name="read_slider_mode">Slider Display Style</string>
+    <string name="bookshelf_update_limit">Bookshelf Update Limit</string>
+    <string name="text_shadow_x">X Offset</string>
+    <string name="text_shadow_y">Y Offset</string>
+    <string name="text_shadow_radius">Shadow Radius</string>
+    <string name="text_shadow_color">Shadow</string>
+    <string name="global_read_setting">Reading Display • Manga</string>
+    <string name="screen_settings">Display and Brightness</string>
     <string name="refresh_limit_unlimited">Unlimited</string>
     <string name="refresh_limit_books">%1$d Books</string>
 
-    <string name="reading_behavior">阅读行为</string>
-    <string name="page_control">翻页控制</string>
-    <string name="redirect_policy">网页重定向策略</string>
-    <string name="redirect_allow_all">允许所有重定向</string>
-    <string name="redirect_ask_always">总是询问</string>
-    <string name="redirect_ask_cross_origin">域名不同时询问</string>
-    <string name="redirect_block_cross_origin">阻止跨域名重定向</string>
-    <string name="redirect_block_all">阻止所有重定向</string>
-    <string name="redirect_ask_same_domain_block_cross">域内询问且域外阻止</string>
+    <string name="reading_behavior">Reading Behavior</string>
+    <string name="page_control">Page Turn Controls</string>
+    <string name="redirect_policy">Web Redirect Policy</string>
+    <string name="redirect_allow_all">Allow All Redirects</string>
+    <string name="redirect_ask_always">Always Ask</string>
+    <string name="redirect_ask_cross_origin">Ask When Domain Changes</string>
+    <string name="redirect_block_cross_origin">Block Cross-Domain Redirects</string>
+    <string name="redirect_block_all">Block All Redirects</string>
+    <string name="redirect_ask_same_domain_block_cross">Ask Within Domain, Block Cross-Domain</string>
     <string name="show_status">Show Status Bar</string>
-    <string name="sort_old_to_new">从旧到新</string>
-    <string name="sort_new_to_old">从新到旧</string>
-    <string name="sort_name_asc">名称升序</string>
-    <string name="sort_name_desc">名称降序</string>
-    <string name="text_dottedline">下划虚线</string>
-    <string name="jump_to_volume">跳转到卷</string>
-    <string name="cancel_select">取消选择</string>
-    <string name="coll_volume">折叠所有卷</string>
-    <string name="expand_volume">展开所有卷</string>
-    <string name="custom">自定义</string>
-    <string name="global_setting">全局设置</string>
-    <string name="text_typeface">字体样式</string>
-    <string name="shadow_typeface">阴影样式</string>
+    <string name="sort_old_to_new">Oldest First</string>
+    <string name="sort_new_to_old">Newest First</string>
+    <string name="sort_name_asc">Name Ascending</string>
+    <string name="sort_name_desc">Name Descending</string>
+    <string name="text_dottedline">Dotted Underline</string>
+    <string name="jump_to_volume">Jump to Volume</string>
+    <string name="cancel_select">Cancel Selection</string>
+    <string name="coll_volume">Collapse All Volumes</string>
+    <string name="expand_volume">Expand All Volumes</string>
+    <string name="custom">Custom</string>
+    <string name="global_setting">Global Settings</string>
+    <string name="text_typeface">Font Style</string>
+    <string name="shadow_typeface">Shadow Style</string>
     <string name="long_click_to_custom">Long Tap to Customize</string>
-    <string name="disable_manga_scroll_animation">禁用翻页动画</string>
-    <string name="manga_cross_fade">禁用加载淡入动画</string>
-    <string name="text_shadow_set">阴影设置</string>
-    <string name="paragraph_typeface">段落样式</string>
-    <string name="same_name_book">已有相同书籍</string>
+    <string name="disable_manga_scroll_animation">Disable Page Turn Animation</string>
+    <string name="manga_cross_fade">Disable Loading Fade-In Animation</string>
+    <string name="text_shadow_set">Shadow Settings</string>
+    <string name="paragraph_typeface">Paragraph Style</string>
+    <string name="same_name_book">A book with the same name already exists</string>
     <string name="underline_color">Underline Color</string>
     <string name="dotted_line_black">Dash Length</string>
     <string name="dotted_line_while">Gap Length</string>
@@ -1457,7 +1599,7 @@
     <string name="sure_cache_book">Cache the Book?</string>
     <string name="auto_check_new_backup_t">Auto-Check for New Backups</string>
     <string name="auto_check_new_backup_s">Check for new backups when opening the app, if new backups are found, prompt the user to update.</string>
-    <string name="replace_purify_badge">计数替换净化</string>
+    <string name="replace_purify_badge">Count Replacements and Purify Rules</string>
     <string name="page_anim_fade">Fade</string>
     <string name="show_bookshelf_tab_menu">Show More Bookshelf Tab</string>
     <string name="show_unread_new">Show New Chapter Flag</string>
@@ -1480,32 +1622,32 @@
     <string name="select_operation">Select an operation</string>
     <string name="text_accent_color">Accent Color</string>
     <string name="read_color">Color Style</string>
-    <string name="reverse_volume_key_page">反转音量键翻页方向</string>
-    <string name="palette_style">调色板样式</string>
-    <string name="material_version">颜色规格</string>
-    <string name="tip_compose_set">这里的选项仅可以控制使用Compose重写的界面。对于禁用的选项,会在日后实现时开放,现在仅作占位。</string>
+    <string name="reverse_volume_key_page">Reverse Volume-Key Page Turn Direction</string>
+    <string name="palette_style">Palette Style</string>
+    <string name="material_version">Color Specification</string>
+    <string name="tip_compose_set">These options only affect screens rewritten in Compose. Disabled options are placeholders and will be enabled after implementation.</string>
     <string name="is_blur_enable">Enable Element Blur</string>
-    <string name="use_underline">总是使用下划线表示强调文本</string>
-    <string name="adapt_special_style">适配特殊样式</string>
+    <string name="use_underline">Always Use Underline for Emphasized Text</string>
+    <string name="adapt_special_style">Adapt Special Styles</string>
     <string name="compose_engine">Theme Style</string>
     <string name="container_opacity">Container Opacity</string>
     <string name="container_opacity_summary">%d</string>
-    <string name="top_bar_opacity">顶栏不透明度</string>
+    <string name="top_bar_opacity">Top Bar Opacity</string>
     <string name="top_bar_opacity_summary">%d</string>
-    <string name="bottom_bar_opacity">底栏不透明度</string>
+    <string name="bottom_bar_opacity">Bottom Bar Opacity</string>
     <string name="bottom_bar_opacity_summary">%d</string>
-    <string name="web_service_auto_start">启动应用时打开Web服务</string>
-    <string name="read_aloud_preload">听书预加载数量</string>
+    <string name="web_service_auto_start">Start Web Service When App Opens</string>
+    <string name="read_aloud_preload">Read-Aloud Preload Count</string>
     <string name="read_aloud_preload_summary">%d</string>
-    <string name="audio_cache_clean_time">音频缓存保留时间(分钟)</string>
-    <string name="audio_cache_clean_time_summary">当前%d, 输入 0 代表退出即刻清理。</string>
-    <string name="show_swipe_animation">启用滑动时动画</string>
-    <string name="click_image_way">点击图片行为</string>
-    <string name="show_web_log">输出日志</string>
-    <string name="is_blur_progressive_enable">启用渐变模糊</string>
-    <string name="enable_select_vibrator">选择文本时震动</string>
-    <string name="re_sync_webdav">重新同步远程书籍</string>
-    <string name="underline_padding">离字间距</string>
+    <string name="audio_cache_clean_time">Audio Cache Retention Time (minutes)</string>
+    <string name="audio_cache_clean_time_summary">Current: %d. Enter 0 to clear immediately on exit.</string>
+    <string name="show_swipe_animation">Enable Animation While Swiping</string>
+    <string name="click_image_way">Image Tap Action</string>
+    <string name="show_web_log">Output Logs</string>
+    <string name="is_blur_progressive_enable">Enable Progressive Blur</string>
+    <string name="enable_select_vibrator">Vibrate When Selecting Text</string>
+    <string name="re_sync_webdav">Resync Remote Books</string>
+    <string name="underline_padding">Underline Gap</string>
 
     <!-- Missing strings -->
     <string name="cover_info_orientation">Cover orientation</string>