Compare commits

...

243 Commits

Author SHA1 Message Date
Huang Xin cc85e5c304 release: version 0.9.49 (#1247) 2025-05-27 14:47:09 +02:00
Huang Xin b0cc0b6f1a layout: better organized panels in settings dialog (#1246) 2025-05-27 14:37:32 +02:00
Huang Xin 6b033f7509 translator: post-process translated text for punctuation spacing (#1245) 2025-05-27 14:32:25 +02:00
Huang Xin 5c0670031a fix: use updated library when dropfile to import on web browser (#1244) 2025-05-26 16:33:18 +02:00
Huang Xin 35735cd931 fix: skip speech when the text is empty at the end of some chapters, closes #1231 (#1243) 2025-05-26 16:03:43 +02:00
Huang Xin 699a01c78f feat: support full book translation to bilingual books, closes #398 (#1240) 2025-05-26 12:23:33 +02:00
Huang Xin 033e161455 layout: overlay the footer of the translator popup and less sensitive page flip with mouse wheel (#1238)
* layout: overlay the footer of the translator popup

* fix: less sensitive page flip with mouse wheel, addresses #1165
2025-05-25 09:32:00 +02:00
Huang Xin e6428656c2 fix: select filtered books when activating select all (#1237) 2025-05-25 08:02:34 +02:00
Huang Xin 68c3c45da3 feat: add an option to opt-out telemetry (#1236) 2025-05-25 07:48:57 +02:00
Huang Xin 36e87d4eb6 layout: display full publisher field in book details, closes #1224 (#1234) 2025-05-24 07:14:22 +02:00
Huang Xin 7f5059a8ea tts: always infer language code from script in bilingual TTS (#1233) 2025-05-24 07:01:41 +02:00
Huang Xin 8dc04f4095 feat: add bilingual TTS support for bilingual books, closes #1226 (#1230) 2025-05-23 18:18:24 +02:00
Huang Xin 6a48f3feb4 fix: use external browsers to open links in epub file, closes #1211 (#1228) 2025-05-23 08:15:57 +02:00
Huang Xin facf5fba75 fix: reorder icons in the ico file for Windows, closes #1175 (#1227) 2025-05-22 16:47:21 +02:00
Huang Xin 9700942e24 fix: transient navigation bar should work now for Android 8-15, closes #1199 (#1225) 2025-05-22 16:12:59 +02:00
Huang Xin e238b49fbf feat: add an option to invert image color in dark mode (#1223) 2025-05-22 10:38:09 +02:00
Huang Xin 32dfadf182 doc: update screenshots (#1222) 2025-05-22 09:34:16 +02:00
Huang Xin 66744108e2 css: unset link color in footnotes in dark mode for safari (#1221) 2025-05-22 09:09:02 +02:00
Huang Xin 401f28847c fix: add location only when toc item is at the same level as the section (#1220) 2025-05-22 08:08:52 +02:00
Huang Xin 9674b45cf0 layout: align text end for select labels on safari (#1219) 2025-05-22 07:03:10 +02:00
Huang Xin a4bc88d305 feat: add azure and google translator providers, closes #1135 and closes #1215 (#1218) 2025-05-21 17:25:00 +02:00
Huang Xin b6b8b17760 fix: normalize input text by removing newlines to prevent translation glitches (#1210) 2025-05-21 05:26:20 +02:00
Huang Xin 14810d6737 feat: add select all button in select mode, closes #1202 (#1209) 2025-05-20 16:58:04 +02:00
Huang Xin ed6c92a65a css: don't override body background, closes #1206 (#1208)
Revisit #1151.
2025-05-20 15:11:36 +02:00
Huang Xin 20d7a1185e layout: grouping modal now should stay on top of the header bar (#1207) 2025-05-20 14:12:17 +02:00
Huang Xin 32bcfda9ad fix: reimporting books will update the import time, being consistent with update time, closes #1200 (#1203) 2025-05-20 04:40:41 +02:00
Huang Xin def157cacb release: version 0.9.43 (#1196) 2025-05-19 16:51:34 +02:00
Huang Xin 7c464b9a8d feat: add prev/next section buttons in footer bar, closes #1125 (#1195) 2025-05-19 16:40:11 +02:00
Huang Xin 90f3512ae2 feat: add scrolling overlap in pixels option to paginate in scrolled mode (#1194) 2025-05-19 15:21:06 +02:00
Huang Xin 12d93ba749 fix: adjust scroll offset to account for header/footer bars when paginating in scroll mode, closes #1181 (#1193) 2025-05-19 10:34:05 +02:00
Huang Xin 554d786105 fix: update library in store when deleting book in bookshelf, closes #1178 (#1191) 2025-05-19 08:31:25 +02:00
Huang Xin eb71c59524 chore: config wrangler before deployment in Cloudflare (#1189) 2025-05-19 07:55:11 +02:00
Huang Xin 93e81ee05b release: remove deprecated MSI installer, closes #1174 (#1187) 2025-05-19 03:29:44 +02:00
Huang Xin b83972e366 refactor: use translator hook to support more translator providers (#1186) 2025-05-18 18:35:37 +02:00
Huang Xin 967536aa23 refactor: update wrangler config (#1185) 2025-05-18 17:52:50 +02:00
Huang Xin 4523437e34 refactor: add kv cache for translation backend (#1184) 2025-05-18 17:16:44 +02:00
Huang Xin e49dd4accb feat: show cloud backup status for each book on mobile and desktop, closes #1167 (#1173) 2025-05-17 17:02:42 +02:00
Huang Xin b7c1f5703c fix: less sensitive magic trackpad and mouse when flipping page, closes #1165 (#1172) 2025-05-17 08:56:22 +02:00
Huang Xin 5373b67aae css: get rid of most overriding for background color, closes #1162 (#1164) 2025-05-16 08:23:34 +02:00
Huang Xin ddc9673260 css: compat for background colors in chapter headers of Feedbooks eBooks (#1163) 2025-05-16 03:29:07 +02:00
Huang Xin 16b0821eab i18n: add translations of Nederlands (#1161) 2025-05-15 18:32:38 +02:00
Huang Xin 93876a73cc layout: responsive translator popup, also closes #1157 (#1160) 2025-05-15 18:19:32 +02:00
Huang Xin 048867e595 settings: disable spell check in color input (#1159) 2025-05-15 05:33:35 +02:00
Huang Xin 602be376a1 css: include additional weight variants for built-in fonts (#1158) 2025-05-15 05:04:16 +02:00
Huang Xin c52ae43cd9 settings: avoid horizontal margin default to zero, fixes #1151 (#1155) 2025-05-14 15:25:32 +02:00
Huang Xin 348d743e82 css: styling adjustments for better compatibility with Gutenberg eBooks (#1154)
This also closes #1153, #1148, #1147 and the css problem in #1151.
2025-05-14 14:55:07 +02:00
Huang Xin c7e495c379 settings: keep screen awake is disabled by default, closes #1145 (#1149)
* layout: hint info now takes up full width in scrolled mode

* settings: keep screen awake is disabled by default, closes #1145
2025-05-14 11:11:32 +02:00
Huang Xin e368076cac chore: update bundleVersion for macOS AppStore build (#1146) 2025-05-14 03:45:00 +02:00
Huang Xin 382e0ca3c6 release: version 0.9.41 (#1143) 2025-05-13 14:17:01 +02:00
Huang Xin d96fb32bcf fix: apply layout styles to div tag, closes #1130 (#1142) 2025-05-13 13:48:21 +02:00
Huang Xin 4779e2638f fix: disable updater for non-appimage app on linux, closes #1129 (#1141) 2025-05-13 12:50:40 +02:00
Huang Xin d094ba9916 chore: cache avatar image for offline usage (#1140) 2025-05-13 10:42:00 +02:00
Huang Xin e4ba0c302e fix: segment txt with paragraph count if no chapter titles are parsed, closes #1134 (#1139) 2025-05-13 10:21:47 +02:00
Huang Xin 0150177a9d chore: bump tauri to version 2.5.1 (#1138) 2025-05-13 09:30:34 +02:00
Huang Xin 74e32d47b0 release: add linux arch armhf (#1133) 2025-05-13 14:57:27 +08:00
Huang Xin 2dd7cf54e6 refactor: use text selector hook in annotator, closes #1020 (#1131) 2025-05-12 18:00:45 +02:00
Huang Xin a5cbfe97db fix: replace background when attribute changed, closes #1124 (#1126) 2025-05-11 17:07:17 +02:00
Huang Xin da49526f4c fix: now auto orientation follows system settings, closes #1093 and closes #1098 (#1122) 2025-05-11 14:57:05 +02:00
Huang Xin 2e46bc1627 css: get rid of hardcoded font color only when overriding font, closes #1095 (#1120) 2025-05-11 14:34:51 +02:00
Huang Xin 7e542e8694 fix: reorganize TTS control view hierarchy, closes #1080 (#1119) 2025-05-11 12:06:15 +02:00
Huang Xin 2a624ef57a css: only override background color in dark mode, closes #1066 (#1117) 2025-05-11 09:33:03 +02:00
Huang Xin 60ae0c9ac3 feat: show book file size in book details (#1114) 2025-05-10 18:28:08 +02:00
Huang Xin 110b4b37cd ui: various UI fixes and enhancements on mobile platforms (#1113) 2025-05-10 18:18:15 +02:00
Huang Xin f0edbf14a9 fix: padding top of the drag handler with the cutouts, closes #1101 (#1110) 2025-05-10 08:27:40 +02:00
Huang Xin 20669a9cb3 fix: show updating status in the about window, closes #1079 (#1109) 2025-05-10 08:00:25 +02:00
Huang Xin 3756bec66c css: special case for align center and zero text indent (#1105) 2025-05-09 14:40:55 +02:00
Huang Xin 3e1d03dc17 css: override text align and fix hardcoded text intent (#1104) 2025-05-09 14:20:38 +02:00
Huang Xin 4aebbf679f fix: various on styles of UI and footnote visibility (#1099)
* fix: footnote now should be hidden in srcdoc iframes

* fix: eliminate white flash on startup when using dark mode

* feat: rescale UI size with pixel density
2025-05-08 18:21:31 +02:00
Huang Xin 0a562d90ac fix: maintain library state across reader/library navigation, closes #1072 (#1096) 2025-05-08 09:26:58 +02:00
Huang Xin e91a39e8b6 release: version 0.9.40 (#1092) 2025-05-07 20:03:58 +02:00
Huang Xin 2006baac4b revert: unbreak style overriding, closes #1058 and closes #1075 (#1091) 2025-05-07 19:56:14 +02:00
Huang Xin 0d4f61f08a compat: fix insane img dimension might cause layout miscalculation, closes #1073 (#1090) 2025-05-07 18:33:13 +02:00
Huang Xin 0b5f0ede3b fix: close action bar when the footbar is dismissed, closes #1064 (#1086) 2025-05-07 14:44:17 +02:00
Huang Xin 99a146525f fix: transient navigation bar in Android 9, closes #1059 (#1085) 2025-05-07 14:11:46 +02:00
Huang Xin 6080f9e0f3 fix: revert to unlock screen orientation for library page, closes #1063 (#1084) 2025-05-07 13:50:40 +02:00
Huang Xin 6b3d41a4b8 fix: styling links and also the content of links, closes #1057 (#1083) 2025-05-07 12:46:28 +02:00
Huang Xin e403c009c2 fix: avoid glitches when expanding toc sub items, closes #1033 (#1081) 2025-05-07 12:22:42 +02:00
Huang Xin 14bf2bcf8f fix: segment separator now should be more than 8 dash, closes #1056 (#1077) 2025-05-07 09:58:30 +02:00
Huang Xin 7f66af71c8 fix: dialog drag handler layout, closes #1036 (#1076) 2025-05-07 09:27:20 +02:00
Huang Xin cbab201138 doc: format issue templates (#1071) 2025-05-07 06:04:20 +02:00
Huang Xin 0f47d6d393 release: version 0.9.39 (#1055) 2025-05-06 18:58:43 +02:00
Huang Xin 38101cb76d fix: temporarily disable font name with style for windows (#1054) 2025-05-06 18:29:27 +02:00
Huang Xin 8f0982ae33 fix: replace bg color only for non-default theme color, closes #1049 (#1053) 2025-05-06 18:16:00 +02:00
Huang Xin 1c015f5973 feat: add option to open last book on start, closes #328 (#1052) 2025-05-06 17:00:57 +02:00
Huang Xin 4c1af671bb fix: use separate compact margin and gap when header and footer are dismissed, closes #734 (#1047) 2025-05-06 12:38:04 +02:00
Huang Xin b4cda00a91 fix: don't scroll when selection is in current page in TTS, closes #863 (#1046) 2025-05-06 11:09:50 +02:00
Huang Xin c326ad402d fix: calculate vw/vh size from iframe viewport, closes #1027 (#1045) 2025-05-06 09:32:01 +02:00
Huang Xin e03636e0a1 fix: expanding clickable region of TOC expander icons, closes #1033 (#1044) 2025-05-06 06:45:51 +02:00
Huang Xin 9669f1272d fix: more robust txt parser, closes #1042 (#1043)
* refactor: reuse BookCover component in BookDetail dialog

* fix: more robust txt parser, closes #1042
2025-05-06 06:28:25 +02:00
Huang Xin 4094fd86ff fix: padding drag handler on Android, closes #1036 (#1039)
* refactor: load default icon when avatar image fails to load

* fix: padding drag handler on Android, closes #1036
2025-05-05 17:42:57 +02:00
Huang Xin 27df89e261 fix: override text-indent when overriding book layout, closes #1026 (#1038) 2025-05-05 11:58:49 +02:00
Huang Xin ddcee6b3df fix: TTS timeout options should popup now in iOS (#1037) 2025-05-05 11:43:10 +02:00
Huang Xin 5e04f6ae03 feat: support locking screen orientation, closes #860 (#1034) 2025-05-05 10:55:50 +02:00
Huang Xin 6299ea09b7 fix: escape special characters when converting from txt to epub, closes #1018 (#1024) 2025-05-04 13:46:08 +02:00
Huang Xin 4f6f45fe8a fix: don't preview font style for system fonts on Linux, closes #1015 (#1023) 2025-05-04 12:23:01 +02:00
Huang Xin 6b290f09f5 refactor: unify hooks for pagination and continuous scroll (#1021) 2025-05-04 10:58:44 +02:00
Huang Xin c9e08f2c01 fix: more robust continuous scroll, closes #993 (#1017) 2025-05-03 12:07:32 +02:00
Huang Xin 8167f27156 feat: add location info for each entry in table of contents (#1016) 2025-05-02 15:08:20 +02:00
Huang Xin 77c7176526 fix: volume retained when app is in background for iOS (#1014) 2025-05-02 11:15:42 +02:00
Huang Xin a8d657414d fix: more robust txt parser (#1005) 2025-04-30 18:01:47 +02:00
Huang Xin ed368e1205 fix: skip rt elements in annotation and search for Japanese books, closes #904 (#1004) 2025-04-30 11:36:52 +02:00
Huang Xin 7de1bdf824 fix: more robust method to detect language for TTS (#1003) 2025-04-30 09:36:51 +02:00
Huang Xin 141c502c76 fix: handle full CJK Unified Ideographs in search terms, closes #989 (#1002) 2025-04-30 08:52:19 +02:00
Huang Xin b1ea729a54 fix: also overriding anchor color when overriding font in dark mode, closes #995 (#1000) 2025-04-30 06:19:32 +02:00
Huang Xin d3eff19ab9 fix: filter out some non-free fonts on Android and Linux, closes #994 (#999) 2025-04-29 20:02:54 +02:00
Huang Xin 59f0cede39 fix: import system fonts list on Android (#998)
Note that the system fonts might not be available in Android webview
2025-04-29 19:14:03 +02:00
Huang Xin 946d17882b fix: reactivating volume keys interception when resumed from background on iOS (#997) 2025-04-29 18:39:36 +02:00
Huang Xin f9adf789d7 release: version 0.9.38 (#986) 2025-04-28 18:24:29 +02:00
Huang Xin efb8f70d01 fix: inherit book fonts for popup footnotes (#985) 2025-04-28 18:16:56 +02:00
Huang Xin e235cb98d2 fix: intercept the Back key to dismiss popup dialogs on Android, closes #636 (#983) 2025-04-28 16:57:15 +02:00
Huang Xin 4275508ccd feat: add volume keys for page turning, closes #471 (#982) 2025-04-28 12:36:24 +02:00
Huang Xin a424ae8b15 feat: retrieve system fonts on iOS and Android and show font weight variants, closes #949 and closes #557 (#976) 2025-04-26 17:37:04 +02:00
Huang Xin 9303ec8c5f feat: add primary color input in theme editor, closes #963 (#970) 2025-04-26 09:58:20 +02:00
Huang Xin 98faed320a feat: add more English voices for all en locales, closes #966 (#969) 2025-04-26 09:00:09 +02:00
Huang Xin 5bbdc70420 fix: preserve selection anchor when spanning paginated content, closes #873 (#968) 2025-04-26 07:45:51 +02:00
Huang Xin de21182240 fix: don't panic on plugin log initialization error, closes #953 (#961) 2025-04-25 14:42:57 +02:00
Huang Xin 4ae35992a1 compat: auto hide navigation bar also for Android version below 11 (#960) 2025-04-25 10:56:36 +02:00
Huang Xin b91d7b7a61 release: version 0.9.37 (#957) 2025-04-24 21:14:57 +02:00
Huang Xin 7c0d9bf375 fix: popup footnotes for anchors without epub namespace, closes #948 (#956) 2025-04-24 21:03:05 +02:00
Huang Xin ea8ab91ed3 feat: add list view for the bookshelf, closes #542 (#955) 2025-04-24 19:53:09 +02:00
Huang Xin efd415ece2 fix: dismiss status bar when resumed from background on Android (#952) 2025-04-24 11:12:10 +02:00
Huang Xin ec86d68b80 fix: check xdg-mime before registering deeplink in Linux, closes #475 (#951) 2025-04-24 16:19:14 +08:00
Huang Xin 172ab382bc fix: query status bar height on Android, closes #943 (#947) 2025-04-23 14:01:37 +02:00
Huang Xin ad551a04a1 feat: add description from metadata in book details, closes #913 (#946) 2025-04-22 19:01:43 +02:00
Huang Xin 4dc943d23e fix: tts now fallbacks to metadata language of the book, closes #916 (#945) 2025-04-22 18:12:14 +02:00
Huang Xin ee93885c04 fix: sync custom themes from global settings to localstorage, closes #917 (#944) 2025-04-22 14:52:46 +02:00
Huang Xin 14c032aaff feat: add global keyboard shortcut to toggle fullscreen with F11, closes #933 (#942) 2025-04-22 14:13:52 +02:00
Huang Xin 2f619ca745 fix: open book even the library is empty, closes #937 (#941) 2025-04-22 13:56:07 +02:00
Huang Xin 8979827cf5 fix: compat language code for api versions (#940) 2025-04-22 12:38:07 +02:00
Huang Xin ae16eeffa7 fix: prevent scrollToAnchor on focus outside content container, closes #906 (#928) 2025-04-21 14:57:01 +02:00
Huang Xin 8e96ef3e5d fix: hide navigation bar on reader page on Android 11, closes #921 (#927) 2025-04-21 14:49:16 +02:00
Huang Xin 38e24da968 fix: support v1 and v2 of translate API (#919) 2025-04-20 12:30:25 +08:00
Huang Xin 7371efe1d1 release: version 0.9.36 (#915) 2025-04-19 18:07:45 +02:00
Huang Xin 3cb37bca5d feat: add GuanKiapTsingKhai-T in the CJK fonts list, closes #884 (#914) 2025-04-19 18:00:35 +02:00
Huang Xin b9fa204eda fix: show system navigation bar by swiping up from bottom on Android, closes #592 (#912) 2025-04-19 17:02:17 +02:00
Huang Xin cf66665096 feat: immersive UI in reader page on iOS and Android, closes #886 and closes #864 (#911) 2025-04-19 16:43:39 +02:00
Huang Xin 759779a98d fix: links in about window is now clickable in apps (#902) 2025-04-17 11:17:07 +02:00
Huang Xin 68409db8a0 compat: more accessible iframes to support LingKuma and Immersive Translate extension, closes #889 (#901) 2025-04-17 10:36:04 +02:00
Huang Xin 46f0e8e7f6 feat: open files with readest from file manager on iOS (#898) 2025-04-16 17:36:11 +02:00
davimed 6260168caa Fix filename with quotes (#897)
* Escape double quotes in filenames

* Format code
2025-04-16 17:34:15 +02:00
Huang Xin a8430a7be1 feat: open files with readest from file manager on Android, closes #865 (#895) 2025-04-16 07:04:30 +02:00
Huang Xin 7bfaa38746 feat: add download/upload buttons in book detail dialogs (#891) 2025-04-15 06:19:29 +02:00
Huang Xin c5b2feda48 release: version 0.9.35 (#888) 2025-04-14 17:50:39 +02:00
Huang Xin 59f875f590 feat: add books sorting by title and author in the library page, closes #540 (#887) 2025-04-14 17:38:41 +02:00
Huang Xin 8efad90932 feat: in-app updater for Android (#885) 2025-04-14 14:21:58 +02:00
Huang Xin 5c23642ac0 chore: generate Android package signature and download url in update file 2025-04-13 23:59:56 +08:00
Huang Xin ee6500cecb fix: books without cover images now can be synced across devices (#878) 2025-04-13 10:47:50 +02:00
Huang Xin f004d7b3ea release: version 0.9.33 (#875) 2025-04-13 02:31:13 +08:00
Huang Xin f7d53508a6 refactor: new updater dialog (#874) 2025-04-13 02:06:14 +08:00
Huang Xin 53e7f2526c fix: window maximized and fullscreen now don't conflict with each other (#872) 2025-04-12 13:42:36 +02:00
Huang Xin 8f0e2d1642 chore: show webview version info in the about window (#869) 2025-04-12 16:35:54 +08:00
Huang Xin c807f0ccc8 refactor: OAuth flow is now handled via ASWebAuthenticationSession on macOS (#866) 2025-04-12 12:49:41 +08:00
Huang Xin 6ba15319a8 fix: reverse punctuation transform when highlighting (#862) 2025-04-11 06:30:08 +02:00
Huang Xin 3ea3a50baa fix: handle file basename from content provider uri (#861) 2025-04-11 05:18:12 +02:00
Huang Xin ccd467ebcf feat: support separate header/footer visibility for paginated and scrolled modes (#859) 2025-04-11 03:40:26 +02:00
Huang Xin fe25c3e995 feat: support native sign-in-with-apple on macOS (#856) 2025-04-10 14:49:11 +02:00
Huang Xin d4af2d3ef5 feat: also add sans-serif CJK fonts (#853) 2025-04-09 16:03:24 +02:00
Huang Xin 6ff1edb67c fix: no padding top for bookmark ribbon (#851) 2025-04-09 13:03:23 +02:00
Huang Xin 3d2847e427 fix: RTL layout for the bottom configuration panel, closes #849 (#850) 2025-04-09 10:47:15 +02:00
Huang Xin ff4d8af054 release: version 0.9.32 (#848) 2025-04-09 06:01:55 +02:00
Huang Xin 612302f42f feat: add LXGW WenKai TC in CJK fonts list (#847) 2025-04-09 05:57:06 +02:00
Huang Xin a26607a154 fix: don't inline the font menu if more options are available (#846) 2025-04-09 05:32:24 +02:00
Huang Xin 939c7049ed fix: position the underline and squiggly highlight decoration at the middle between text lines (#845) 2025-04-09 05:26:22 +02:00
Huang Xin 0ead07e704 fix: don't reset all font-size and font color when override book style (#842) 2025-04-09 01:55:36 +02:00
Huang Xin f4908c4536 release: version 0.9.31 (#839) 2025-04-08 15:54:34 +02:00
Huang Xin 37951c2ebf feat: add book metadata search in the bookshelf, closes #100 (#838) 2025-04-08 15:37:18 +02:00
Huang Xin 915f222e08 doc: add a troubleshooting section in README (#837) 2025-04-08 18:05:22 +08:00
Huang Xin 9446df1b9c feat: add an option to configure default CJK fonts in CJK environments, closes #780 (#836) 2025-04-08 11:27:59 +02:00
Huang Xin bfcf89b093 fix: toggle the menus on the whole screen when Click-To-Flip is disabled, closes #831 (#834) 2025-04-08 08:09:46 +02:00
Huang Xin b0cf087d78 fix: support opening content URIs by copying to cache when direct access fails, closes #829 (#833) 2025-04-08 07:45:33 +02:00
Huang Xin 5863586766 feat: vertical layout for highlighting tools, closes #787 (#830) 2025-04-07 17:24:14 +02:00
Huang Xin 99182a1f9e feat: add configurable timeout to stop TTS playback automatically, closes #747 (#826) 2025-04-07 12:40:14 +02:00
Huang Xin 2599f40dfb feat: add option to keep window on top, closes #820 (#825) 2025-04-07 08:23:46 +02:00
Huang Xin aa66ba54c5 fix: set proper default theme mode, closes #823 (#824) 2025-04-07 07:16:31 +02:00
Huang Xin 4f0ef01a17 fix: tts now works in background in iOS, closes #547 (#822)
To enable background playback in Android, go to Settings > Apps & Notifications > Readest > Battery > Battery Optimization, and disable battery optimization for Readest.
2025-04-06 18:42:58 +02:00
Huang Xin 267e1656db release: version 0.9.30 (#817) 2025-04-05 11:39:37 +02:00
Huang Xin dcecda2984 fix: cache NativeFile with LRU, closes #797 (#816) 2025-04-05 11:27:07 +02:00
Huang Xin ce88f0229a fix: rounded reader widget only when sidebar is invisible , closes #809 (#815) 2025-04-05 10:08:39 +02:00
Huang Xin 316c82ea52 fix: override link color only when overriding font, closes #812 (#814) 2025-04-05 09:34:29 +02:00
Huang Xin 25be60ce2a chore: bump tauri to 2.4.1 (#813) 2025-04-05 07:40:33 +02:00
Huang Xin 4703eec045 fix: set correct filename in openFile API, closes #803 (#810) 2025-04-05 12:41:26 +08:00
Huang Xin ab22a83332 fix: open default file chooser on Android, closes #798 and closes #799 (#807) 2025-04-04 05:01:12 +02:00
Huang Xin a3d6ebe44d feat: add fixed storage quota for self-hosted instance, closes #805 (#806) 2025-04-04 03:48:43 +02:00
Huang Xin f8804b5d3c fix: revert font family, size and color when overrideFont is set, closes #792 (#804) 2025-04-03 17:15:19 +02:00
Huang Xin ee3785ad51 fix: hack to decode incorrectly encoded url in href (#796) 2025-04-02 07:35:00 +02:00
Huang Xin 5ba40cd2d6 release: version 0.9.29 (#790) 2025-04-01 18:27:15 +02:00
Huang Xin 6131180a31 fix: initiate OAuth using Custom Tabs on Android, closes #361 (#788) 2025-04-01 17:04:17 +02:00
Huang Xin 7ccf3d0632 refactor: read local file with fs plugin for Tauri App, closes #553 and closes #489 (#785) 2025-04-01 14:26:26 +02:00
Huang Xin 39fb7f759e fix: handle preflight on tauri apps (#776) 2025-03-31 03:38:47 +02:00
Huang Xin d89834a53d chore: balance translator load between auth and guest users (#771) 2025-03-30 16:35:13 +02:00
Huang Xin 3c4dd2a143 fix: fix translation api with retry (#770) 2025-03-30 15:59:57 +02:00
Huang Xin 847efb7082 fix: head bar may hide login buttons in auth page (#769) 2025-03-30 11:32:57 +02:00
Huang Xin 01db8f90fa fix: store the filepath for transient import, closes #760 (#768) 2025-03-30 10:27:45 +02:00
Huang Xin e70c46ff02 fix: only replace quote punctuations for vertical layout, closes #761 (#767) 2025-03-30 09:49:22 +02:00
Huang Xin 7cb1998ebb fix: soft keyboard in custom css textarea, closes #762 and closes #763 (#766) 2025-03-30 09:33:27 +02:00
Huang Xin f5fd37f40d release: version 0.9.28 (hotfix) (#759) 2025-03-29 19:46:09 +01:00
Huang Xin 2daee62b80 fix: dismiss highlighting tools for annotation taking (#758) 2025-03-29 19:40:37 +01:00
Huang Xin ffd179bb06 fix: TXT file correctly read and imported on desktop apps, closes #756 (#757) 2025-03-29 19:11:41 +01:00
Huang Xin 4acc0d8e12 feat: replace punctuation marks in vertical reading mode (#754) 2025-03-29 12:42:23 +01:00
Huang Xin a66786444f release: version 0.9.27 (#751) 2025-03-29 08:03:38 +01:00
Huang Xin 01ad18ca57 feat: global fulltext search shortcut with ctrl/cmd+f (#750) 2025-03-29 07:45:41 +01:00
Huang Xin a72c8f2391 refactor: rename plugin safari-auth to native-bridge (#749)
* fix: layout tweaks for mobile

* refactor: rename plugin safari-auth to native-bridge
2025-03-29 07:27:18 +01:00
Huang Xin f90177713a tts: avoid pauses after abbreviations, faster warming up, preferred voice ids (#742) 2025-03-28 13:25:08 +01:00
Huang Xin 041eb1b921 feat: support more languages in deepl translator (#740) 2025-03-28 07:31:34 +01:00
Huang Xin 105302d241 fix: don't crash with some malformed toc data for PDF, closes #518 (#739) 2025-03-28 06:54:32 +01:00
Huang Xin bd9c7b459c fix: avoid stale values in relocate handler, closes #736 (#738) 2025-03-28 06:33:51 +01:00
Huang Xin 1c122752bb fix: update downloadedAt field properly (#737) 2025-03-28 06:03:18 +01:00
Huang Xin 203ef8b986 feat: add reset password page, closes #700 (#731) 2025-03-28 03:52:04 +08:00
Huang Xin ceda806f77 fix: override book writing-mode (#730) 2025-03-27 15:53:35 +01:00
Huang Xin baac9dfa25 fix: unhardcode background color in non-default themes (#729) 2025-03-27 13:05:03 +01:00
Huang Xin 10aace0026 fix: avoid view glitch when initialize view settings, closes #713 (#728) 2025-03-27 12:40:41 +01:00
Huang Xin 3e38896078 feat: add option to swap click-to-flip area, closes #719 (#727) 2025-03-27 11:35:57 +01:00
Huang Xin 0fa53c5f15 fix: should listen single-instance event on Windows to open-with files, closes #724 (#726) 2025-03-27 09:11:03 +01:00
Xi Gou 2b6320b880 refactor: support both r2 and s3 storage (#718) 2025-03-27 07:00:27 +01:00
Huang Xin 7ba938ea25 fix: header layout for auth and user pages (#725) 2025-03-27 05:14:38 +01:00
Huang Xin 6d4ad34e32 release: version 0.9.26 (#723) 2025-03-26 17:44:26 +01:00
Huang Xin 3239ff0a80 feat: add LXGW WenKai to serif fonts list (#722) 2025-03-26 17:28:14 +01:00
Huang Xin 8b7e3c6f79 fix: textarea hidden behind keyboard in fullscreen Android app. closes #653 (#720) 2025-03-26 15:57:35 +01:00
Huang Xin ace4b78420 fix: uncrash scroll prev at start and scroll next at end (#712) 2025-03-26 12:42:39 +01:00
Huang Xin 39ac89fead fix: don't override bg color for img, closes #656 (#711) 2025-03-26 12:23:46 +01:00
Huang Xin 490fe7e70c ui: fix library header padding (#710) 2025-03-26 09:07:38 +01:00
Huang Xin 06a7a8ea82 feat: add option for transient import from file managers, closes #456 (#709) 2025-03-26 07:11:47 +01:00
Huang Xin b7f4a3503f doc: add TXT to supported formats list (#708)
* fix: set max width for annotation toolbar

* doc: add TXT to supported formats list
2025-03-26 11:45:56 +08:00
Huang Xin 835ed443a5 fix: style tweaks for Windows (#707) 2025-03-25 18:58:23 +01:00
Huang Xin 3f3c6983ba fix: also export newly created annotations (#704) 2025-03-25 06:26:11 +01:00
Huang Xin f22ce1c225 fix: access runtime variables in function invoke (#703) 2025-03-25 05:29:22 +01:00
Huang Xin 9990de112f chore: bump updates of next.js and tauri (#702) 2025-03-25 04:46:07 +01:00
Huang Xin ede37757db chore: support deploy in cloudflare with opennext and fix android build 2025-03-25 03:52:04 +08:00
Huang Xin 0c65d44bc9 chore: add android release in github actions (#695) 2025-03-25 01:57:05 +08:00
Huang Xin 07c9813ec1 fix: no background color for section and page info in vertical scroll mode (#694) 2025-03-24 07:13:38 +01:00
Huang Xin 03f0ee1f7d release: version 0.9.25 (#690) 2025-03-23 19:39:39 +01:00
Huang Xin ebc23c4ce3 feat: export annotations in markdown format, closes #152 (#689) 2025-03-23 19:33:05 +01:00
Huang Xin d098487cd1 fix: add metadata parsed from cbz zip comment, closes #639 (#688) 2025-03-23 18:18:08 +01:00
Huang Xin 14f2db730a fix: add option to override justify style in books, closes #671 (#687) 2025-03-23 16:59:45 +01:00
Huang Xin a79f301165 feat: add language preference options in settings (#686) 2025-03-23 16:39:58 +01:00
Huang Xin 05d086352f chore: add f-droid metadata (#682) 2025-03-23 08:59:34 +01:00
Huang Xin ef864a32c2 feat: action bar at the bottom on mobile devices (#681) 2025-03-23 08:56:57 +01:00
Huang Xin 0d93a13407 fix: format footer in popup window (#665) 2025-03-21 05:52:56 +01:00
Huang Xin ab0208b7ab fix: now img in paragraph won't get indented and default to override book layout (#657) 2025-03-20 17:10:26 +01:00
Huang Xin c52f3c0430 feat: support importing TXT files, closes #308 (#655) 2025-03-20 15:00:30 +01:00
Huang Xin accf6fb5c7 feat: snap dialog to preview effects of book settings adjustments (#646) 2025-03-19 14:23:52 +01:00
Huang Xin 18c2ce1274 fix: various fixes on layout settings and styles, closes #637 (#644) 2025-03-19 10:14:02 +01:00
Huang Xin 9a104b918c fix: dismiss popup footnotes on window resize, also closes #634 and closes #635 (#638) 2025-03-19 04:22:15 +01:00
301 changed files with 19907 additions and 5303 deletions
+8 -4
View File
@@ -7,13 +7,17 @@ assignees: ''
---
**Does your feature request involve difficulty for you to complete a task? Please describe.**
A clear and concise description of what the problem is. Ex. I think it takes too many steps to [...]
> A clear and concise description of what the problem is. Ex. I think it takes too many steps to [...]
**Describe the solution you'd like**
A clear and concise description of what you'd like to happen.
> A clear and concise description of what you'd like to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
> A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any additional context or screenshots about the feature request here.
> Add any additional context or screenshots about the feature request here.
+10 -19
View File
@@ -11,22 +11,16 @@ assignees: ''
A clear and concise description of what the current behavior is.
Please also add **screenshots** of the existing application.
**Example:**
```
In DarkMode, when scrollbar are displayed (for example on Companies page, with enough companies in the list), we see a blank square in the bottom right corner
[screenshot]
```
> **Example:**
> In DarkMode, when scrollbar are displayed (for example on Companies page, with enough companies in the list), we see a blank square in the bottom right corner
> [screenshot]
## Expected behavior
A clear and concise description of what the expected behavior is.
**Example:**
```
The blank square should be transparent (invisible)
```
> **Example:**
> The blank square should be transparent (invisible)
## Technical inputs
@@ -34,11 +28,8 @@ Operating System:
Readest Version:
**Example:**
```
Operating System: macOS 14.3.1
Readest Version: 0.9.0
We are displaying custom scrollbars that disappear when the user is not scrolling. See ScrollWrapper.
Probably fixable with CSS
```
> **Example:**
> Operating System: Android 14 (WebView 135.0)
> Readest Version: 0.9.0
> We are displaying custom scrollbars that disappear when the user is not scrolling. See ScrollWrapper.
> Probably fixable with CSS
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
run: cargo fmt --check
- name: Clippy Check
working-directory: apps/readest-app/src-tauri
run: cargo clippy -- -D warnings
run: cargo clippy -p Readest --no-deps -- -D warnings
build_web_app:
runs-on: ubuntu-latest
+121 -10
View File
@@ -56,27 +56,40 @@ jobs:
fail-fast: false
matrix:
config:
- os: ubuntu-latest
release: android
rust_target: aarch64-linux-android,armv7-linux-androideabi,i686-linux-android,x86_64-linux-android
- os: ubuntu-22.04
release: linux
arch: x86_64
rust_target: x86_64-unknown-linux-gnu
- os: ubuntu-22.04-arm
release: linux
arch: aarch64
rust_target: aarch64-unknown-linux-gnu
- os: ubuntu-22.04-arm
release: linux
arch: armhf
rust_target: arm-unknown-linux-gnueabihf
args: '--target arm-unknown-linux-gnueabihf --bundles deb'
- os: macos-latest
release: macos
arch: aarch64
rust_target: x86_64-apple-darwin,aarch64-apple-darwin
args: '--target universal-apple-darwin'
- os: windows-latest
release: windows
arch: x86_64
rust_target: x86_64-pc-windows-msvc
args: '--target x86_64-pc-windows-msvc'
args: '--target x86_64-pc-windows-msvc --bundles nsis'
- os: windows-latest
release: windows
arch: aarch64
rust_target: aarch64-pc-windows-msvc
args: '--target aarch64-pc-windows-msvc --bundles nsis'
runs-on: ${{ matrix.config.os }}
timeout-minutes: 30
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
@@ -94,6 +107,21 @@ jobs:
node-version: 22
cache: pnpm
- name: setup Java (for Android build only)
if: matrix.config.release == 'android'
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: setup Android SDK (for Android build only)
if: matrix.config.release == 'android'
uses: android-actions/setup-android@v3
- name: install NDK (for Android build only)
if: matrix.config.release == 'android'
run: sdkmanager "ndk;27.0.11902837"
- name: install dependencies
run: pnpm install
@@ -107,7 +135,24 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.config.os }}-cargo-${{ hashFiles('Cargo.lock') }}
key: ${{ matrix.config.os }}-${{ matrix.config.release }}-${{ matrix.config.arch }}-cargo
- name: install dependencies (ubuntu only)
if: contains(matrix.config.os, 'ubuntu') && matrix.config.release != 'android' && matrix.config.arch != 'armhf'
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libfontconfig-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils
- name: install dependencies (ubuntu only - armhf specific)
if: contains(matrix.config.os, 'ubuntu') && matrix.config.arch == 'armhf'
run: |
sudo dpkg --add-architecture armhf
sudo apt-get update
sudo apt-get install -y pkg-config libfontconfig-dev:armhf libgtk-3-dev:armhf libwebkit2gtk-4.1-dev:armhf libappindicator3-dev:armhf librsvg2-dev:armhf gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
echo 'PKG_CONFIG_ALLOW_CROSS=1' >> $GITHUB_ENV
echo 'PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig' >> $GITHUB_ENV
echo 'PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf' >> $GITHUB_ENV
echo 'CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc' >> $GITHUB_ENV
- name: create .env.local file for Next.js
run: |
@@ -116,17 +161,76 @@ jobs:
echo "NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}" >> .env.local
echo "NEXT_PUBLIC_SUPABASE_ANON_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}" >> .env.local
echo "NEXT_PUBLIC_APP_PLATFORM=tauri" >> .env.local
cp .env.local apps/readest-app/.env.local
- name: copy .env.local to apps/readest-app
run: cp .env.local apps/readest-app/.env.local
- name: install dependencies (ubuntu only)
if: contains(matrix.config.os, 'ubuntu')
- name: build and upload Android apks
if: matrix.config.release == 'android'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/27.0.11902837
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libfontconfig-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils
cd apps/readest-app/
rm -rf src-tauri/gen/android
pnpm tauri android init
pnpm tauri icon ../../data/icons/readest-book.png
git checkout .
pushd src-tauri/gen/android
echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" > keystore.properties
echo "password=${{ secrets.ANDROID_KEY_PASSWORD }}" >> keystore.properties
base64 -d <<< "${{ secrets.ANDROID_KEY_BASE64 }}" > $RUNNER_TEMP/keystore.jks
echo "storeFile=$RUNNER_TEMP/keystore.jks" >> keystore.properties
popd
version=${{ needs.get-release.outputs.release_version }}
apk_path=src-tauri/gen/android/app/build/outputs/apk/universal/release
universial_apk=Readest_${version}_universal.apk
arm64_apk=Readest_${version}_arm64.apk
pnpm tauri android build
cp ${apk_path}/app-universal-release.apk $universial_apk
pnpm tauri android build -t aarch64
cp ${apk_path}/app-universal-release.apk $arm64_apk
echo "Uploading $universial_apk to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} $universial_apk --clobber
echo "Uploading $arm64_apk to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} $arm64_apk --clobber
echo "Uploading signatures to GitHub release"
pnpm tauri signer sign $universial_apk
pnpm tauri signer sign $arm64_apk
gh release upload ${{ needs.get-release.outputs.release_tag }} $universial_apk.sig --clobber
gh release upload ${{ needs.get-release.outputs.release_tag }} $arm64_apk.sig --clobber
- name: download and update latest.json for Android release
if: matrix.config.release == 'android'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cd apps/readest-app/
curl -sL https://github.com/readest/readest/releases/latest/download/latest.json -o latest.json
version=${{ needs.get-release.outputs.release_version }}
universial_apk_url="https://github.com/readest/readest/releases/download/${{ needs.get-release.outputs.release_tag }}/Readest_${version}_universal.apk"
arm64_apk_url="https://github.com/readest/readest/releases/download/${{ needs.get-release.outputs.release_tag }}/Readest_${version}_arm64.apk"
universial_sig=$(cat Readest_${version}_universal.apk.sig)
arm64_sig=$(cat Readest_${version}_arm64.apk.sig)
jq --arg url "$universial_apk_url" \
--arg sig "$universial_sig" \
'.platforms["android-universal"] = {signature: $sig, url: $url}' latest.json > tmp.$$.json && mv tmp.$$.json latest.json
jq --arg url "$arm64_apk_url" \
--arg sig "$arm64_sig" \
'.platforms["android-arm64"] = {signature: $sig, url: $url}' latest.json > tmp.$$.json && mv tmp.$$.json latest.json
echo "Uploading updated latest.json to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} latest.json --clobber
- uses: tauri-apps/tauri-action@v0
if: matrix.config.release != 'android'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
@@ -144,6 +248,13 @@ jobs:
releaseBody: ${{ needs.get-release.outputs.release_note }}
args: ${{ matrix.config.args || '' }}
- name: upload release notes to GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Uploading release notes to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} apps/readest-app/release-notes.json --clobber
- name: upload portable binaries (Windows only)
if: matrix.config.os == 'windows-latest'
env:
+1 -1
View File
@@ -3,4 +3,4 @@
url = https://github.com/readest/foliate-js.git
[submodule "packages/tauri"]
path = packages/tauri
url = https://github.com/tauri-apps/tauri.git
url = https://github.com/readest/tauri.git
Generated
+1451 -1373
View File
File diff suppressed because it is too large Load Diff
+16 -1
View File
@@ -1,3 +1,18 @@
[workspace]
members = ["apps/readest-app/src-tauri"]
members = [
"apps/readest-app/src-tauri",
"packages/tauri/crates/tauri",
]
resolver = "2"
[workspace.package]
authors = ["Bilingify LLC"]
homepage = "https://readest.com"
license = "AGPL-3.0"
repository = "https://github.com/readest/readest"
categories = []
edition = "2021"
rust-version = "1.77.2"
[patch.crates-io]
tauri = { path = "packages/tauri/crates/tauri" }
+40 -6
View File
@@ -26,7 +26,7 @@
<a href="#screenshots">Screenshots</a> •
<a href="#downloads">Downloads</a> •
<a href="#getting-started">Getting Started</a> •
<a href="#contributors">Contributors</a> •
<a href="#troubleshooting">Troubleshooting</a> •
<a href="#license">License</a>
</p>
@@ -42,7 +42,7 @@
| **Feature** | **Description** | **Status** |
| --------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------- |
| **Multi-Format Support** | Support EPUB, MOBI, KF8 (AZW3), FB2, CBZ, PDF (experimental) | ✅ |
| **Multi-Format Support** | Support EPUB, MOBI, KF8 (AZW3), FB2, CBZ, TXT, PDF (experimental) | ✅ |
| **Scroll/Page View Modes** | Switch between scrolling or paginated reading modes. | ✅ |
| **Full-Text Search** | Search across the entire book to find relevant sections. | ✅ |
| **Annotations and Highlighting** | Add highlights, bookmarks, and notes to enhance your reading experience. | ✅ |
@@ -54,6 +54,7 @@
| **File Association and Open With** | Quickly open files in Readest in your file browser with one-click. | ✅ |
| **Sync across Platforms** | Synchronize book files, reading progress, notes, and bookmarks across all supported platforms. | ✅ |
| **Text-to-Speech (TTS) Support** | Enable text-to-speech functionality for a more accessible reading experience. | ✅ |
| **Library Management** | Organize, sort, and manage your entire ebook library. | ✅ |
## Planned Features
@@ -62,7 +63,6 @@
| **Feature** | **Description** | **Priority** |
| ------------------------------- | ------------------------------------------------------------------------------------------ | ------------ |
| **Library Management** | Organize, sort, and manage your entire ebook library. | 🛠 |
| **AI-Powered Summarization** | Generate summaries of books or chapters using AI for quick insights. | 🛠 |
| **Sync with Koreader** | Synchronize reading progress, notes, and bookmarks with [Koreader][link-koreader] devices. | 🔄 |
| **Keyboard Navigation** | Implement vimium-style keybindings for book navigation. | 🔄 |
@@ -78,10 +78,10 @@ Stay tuned for continuous improvements and updates! Contributions and suggestion
![Annotations](./data/screenshots/annotations.png)
![DeepL](./data/screenshots/deepl.png)
![TTS](./data/screenshots/tts_speak_aloud.png)
![DeepL](./data/screenshots/deepl.png)
![Footnote](./data/screenshots/footnote_popover.png)
![Wikipedia](./data/screenshots/wikipedia_vertical.png)
@@ -199,6 +199,39 @@ nix develop ./ops#android # enter a dev shell for the android app
Please check the [wiki][link-gh-wiki] of this project for more information on development.
## Troubleshooting
### 1. Readest Wont Launch on Windows (Missing Edge WebView2 Runtime)
**Symptom**
- When you double-click readest.exe, nothing happens. No window appears, and Task Manager does not show the process.
- This can affect both the standard installer and the portable version.
**Cause**
- Microsoft Edge WebView2 Runtime is either missing, outdated, or improperly installed on your system. Readest depends on WebView2 to render the interface on Windows.
**How to Fix**
1. Check if WebView2 is installed
- Open “Add or Remove Programs” (a.k.a. Apps & features) on Windows. Look for “Microsoft Edge WebView2 Runtime.”
2. Install or Update WebView2
- Download the WebView2 Runtime directly from Microsoft: [link](https://developer.microsoft.com/en-us/microsoft-edge/webview2?form=MA13LH).
- If you prefer an offline installer, download the offline package and run it as an Administrator.
3. Re-run Readest
- After installing/updating WebView2, launch readest.exe again.
- If you still encounter problems, reboot your PC and try again.
**Additional Tips**
- If reinstalling once doesnt work, uninstall Edge WebView2 completely, then reinstall it with Administrator privileges.
- Verify your Windows installation has the latest updates from Microsoft.
**Still Stuck?**
- See Issue [readest/readest#358](https://github.com/readest/readest/issues/358) for further details, or head over to our [Discord][link-discord] server and open a support discussion with detailed logs of your environment and the steps youve taken.
## Contributors
Readest is open-source, and contributions are welcome! Feel free to open issues, suggest features, or submit pull requests. Please **review our [contributing guidelines](CONTRIBUTING.md) before you start**. We also welcome you to join our [Discord][link-discord] community for either support or contributing guidance.
@@ -219,9 +252,10 @@ The following libraries and frameworks are used in this software:
- [zip.js](https://github.com/gildas-lormeau/zip.js), which is licensed under the BSD-3-Clause license.
- [fflate](https://github.com/101arrowz/fflate), which is MIT licensed.
- [PDF.js](https://github.com/mozilla/pdf.js), which is licensed under Apache License 2.0.
- [daisyUI](https://github.com/saadeghi/daisyui), which is MIT licensed.
- [next.js](https://github.com/vercel/next.js), which is MIT licensed.
- [react](https://github.com/facebook/react), which is MIT licensed.
- [react-icons](https://github.com/react-icons/react-icons), which has various open-source licenses.
- [react](https://github.com/facebook/react), which is MIT licensed.
- [tauri](https://github.com/tauri-apps/tauri), which is MIT licensed.
The following fonts are utilized in this software, either bundled within the application or provided through web fonts:
+11
View File
@@ -4,14 +4,25 @@ NEXT_PUBLIC_POSTHOG_HOST=YOUR_POSTHOG_HOST
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
NEXT_PUBLIC_STORAGE_FIXED_QUOTA=1073741824
SUPABASE_ADMIN_KEY=YOUR_SUPABASE_ADMIN_KEY
DEEPL_PRO_API_KEYS=YOUR_DEEPL_PRO_API_KEYS
DEEPL_FREE_API_KEYS=YOUR_DEEPL_FREE_API_KEYS
# r2, s3
NEXT_PUBLIC_OBJECT_STORAGE_TYPE=r2
R2_TOKEN_VALUE=YOUR_R2_TOKEN_VALUE
R2_ACCESS_KEY_ID=YOUR_R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY=YOUR_R2_SECRET_ACCESS_KEY
R2_BUCKET_NAME=YOUR_R2_BUCKET_NAME
R2_ACCOUNT_ID=YOUR_R2_ACCOUNT_ID
R2_REGION=YOUR_R2_REGION
S3_ENDPOINT=PLACE_HOLDER
S3_ACCESS_KEY_ID=PLACE_HOLDER
S3_SECRET_ACCESS_KEY=PLACE_HOLDER
S3_BUCKET_NAME=PLACE_HOLDER
S3_REGION=PLACE_HOLDER
+1 -1
View File
@@ -32,7 +32,7 @@ yarn-error.log*
/private_keys
# plists
*.plist
Entitlements*.plist
# local confs
tauri.*.conf.json
@@ -17,6 +17,7 @@ module.exports = {
'el',
'ko',
'uk',
'nl',
'pl',
'pt',
'ru',
+22 -16
View File
@@ -1,6 +1,6 @@
{
"name": "@readest/readest-app",
"version": "0.9.23",
"version": "0.9.49",
"private": true,
"scripts": {
"dev": "dotenv -e .env.tauri -- next dev",
@@ -32,6 +32,7 @@
"release-ios-appstore": "dotenv -e .env.ios-appstore.local -- bash scripts/release-ios-appstore.sh",
"preview": "NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare && wrangler dev",
"deploy": "NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare && wrangler deploy",
"config-wrangler": "sed -i \"s/\\${TRANSLATIONS_KV_ID}/$TRANSLATIONS_KV_ID/g\" wrangler.toml",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
},
"dependencies": {
@@ -42,20 +43,21 @@
"@supabase/auth-ui-react": "^0.4.7",
"@supabase/auth-ui-shared": "^0.1.8",
"@supabase/supabase-js": "^2.47.7",
"@tauri-apps/api": "2.3.0",
"@tauri-apps/api": "2.5.0",
"@tauri-apps/plugin-cli": "^2.2.0",
"@tauri-apps/plugin-deep-link": "^2.2.0",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-haptics": "^2.2.3",
"@tauri-apps/plugin-http": "^2.3.0",
"@tauri-apps/plugin-log": "^2.2.3",
"@tauri-apps/plugin-deep-link": "^2.2.1",
"@tauri-apps/plugin-dialog": "^2.2.1",
"@tauri-apps/plugin-fs": "^2.2.1",
"@tauri-apps/plugin-haptics": "^2.2.4",
"@tauri-apps/plugin-http": "^2.4.3",
"@tauri-apps/plugin-log": "^2.4.0",
"@tauri-apps/plugin-opener": "^2.2.6",
"@tauri-apps/plugin-os": "^2.2.0",
"@tauri-apps/plugin-process": "^2.2.0",
"@tauri-apps/plugin-shell": "~2.2.0",
"@tauri-apps/plugin-updater": "^2.5.1",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.1",
"@tauri-apps/plugin-shell": "~2.2.1",
"@tauri-apps/plugin-updater": "^2.7.1",
"@zip.js/zip.js": "^2.7.53",
"aws4fetch": "^1.0.20",
"clsx": "^2.1.1",
"cors": "^2.8.5",
"cssbeautify": "^0.3.1",
@@ -65,25 +67,28 @@
"i18next-http-backend": "^3.0.1",
"js-md5": "^0.8.3",
"jwt-decode": "^4.0.0",
"next": "15.2.2",
"posthog-js": "^1.205.0",
"next": "15.2.4",
"posthog-js": "^1.246.0",
"react": "19.0.0",
"react-color": "^2.19.3",
"react-dom": "19.0.0",
"react-i18next": "^15.2.0",
"react-icons": "^5.4.0",
"react-responsive": "^10.0.0",
"semver": "^7.7.1",
"tinycolor2": "^1.6.0",
"zustand": "5.0.1"
},
"devDependencies": {
"@tauri-apps/cli": "2.3.1",
"@opennextjs/cloudflare": "^0.5.12",
"@tauri-apps/cli": "2.5.0",
"@types/cors": "^2.8.17",
"@types/cssbeautify": "^0.3.5",
"@types/node": "^22.10.1",
"@types/react": "18.3.12",
"@types/react-color": "^3.0.13",
"@types/react-dom": "18.3.1",
"@types/semver": "^7.7.0",
"@types/tinycolor2": "^1.4.6",
"autoprefixer": "^10.4.20",
"cpx2": "^8.0.0",
@@ -99,6 +104,7 @@
"postcss-nested": "^7.0.2",
"raw-loader": "^4.0.2",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.2"
"typescript": "^5.7.2",
"wrangler": "^4.4.0"
}
}
@@ -5,7 +5,6 @@
"Animation": "الرسوم المتحركة",
"Annotate": "إضافة ملاحظة",
"Apply": "تطبيق",
"Are you sure to delete the selected books?": "هل أنت متأكد من رغبتك في حذف الكتب المحددة؟",
"Auto Mode": "الوضع التلقائي",
"Behavior": "السلوك",
"Book": "الكتاب",
@@ -27,7 +26,6 @@
"Delete": "حذف",
"Delete Highlight": "حذف التمييز",
"Dictionary": "القاموس",
"Disable Click-to-Flip": "تعطيل التقليب بالنقر",
"Download Readest": "تحميل ريديست",
"Edit": "تحرير",
"Enter your custom CSS here...": "أدخل الـ CSS المخصص هنا...",
@@ -43,15 +41,12 @@
"Global Settings": "الإعدادات العامة",
"Go Back": "العودة",
"Go Forward": "التقدم",
"Go Left": "الذهاب لليسار",
"Go Right": "الذهاب لليمين",
"Grass": "عشبي",
"Gray": "رمادي",
"Gruvbox": "چروفبوكس",
"Highlight": "تمييز",
"Horizontal Direction": "الاتجاه الأفقي",
"Hyphenation": "الواصلة",
"Identifier:": "المعرف:",
"Import Books": "استيراد الكتب",
"Language:": "اللغة:",
"Layout": "التخطيط",
@@ -64,7 +59,6 @@
"Match Whole Words": "مطابقة الكلمات الكاملة",
"Maximum Number of Columns": "العدد الأقصى للأعمدة",
"Minimum Font Size": "الحد الأدنى لحجم الخط",
"Misc": "أخرى",
"Monospace Font": "خط أحادي المسافة (Monospace)",
"More Info": "مزيد من المعلومات",
"Nord": "نورد",
@@ -112,7 +106,6 @@
"Unknown": "غير معروف",
"Untitled": "بدون عنوان",
"Updated:": "تم التحديث:",
"User avatar": "صورة المستخدم",
"Version {{version}}": "الإصدار {{version}}",
"Vertical Direction": "الاتجاه العمودي",
"Welcome to your library. You can import your books here and read them anytime.": "مرحبًا بكم في مكتبتكم. يمكنكم استيراد كتبكم هنا وقراءتها في أي وقت.",
@@ -126,8 +119,6 @@
"Apply to All Books": "تطبيق على جميع الكتب",
"Apply to This Book": "تطبيق على هذا الكتاب",
"Unable to fetch the translation. Try again later.": "تعذر جلب الترجمة. حاول مرة أخرى لاحقًا.",
"Update Now!": "قم بالتحديث الآن!",
"Update": "تحديث",
"Check Update": "التحقق من التحديث",
"Already the latest version": "الإصدار الحالي هو الإصدار الأحدث بالفعل",
"Book Details": "تفاصيل الكتاب",
@@ -152,7 +143,6 @@
"Text Indent": "مسافة بادئة للنص",
"Paragraph Margin": "هامش الفقرة",
"Override Book Layout": "تجاوز تخطيط الكتاب",
"Add to Group": "إضافة إلى مجموعة",
"Untitled Group": "مجموعة بدون عنوان",
"Group Books": "تجميع الكتب",
"Remove From Group": "إزالة من المجموعة",
@@ -247,5 +237,102 @@
"Border Color": "لون الحدود",
"Border Frame": "إطار الحدود",
"Show Header": "عرض الرأس",
"Show Footer": "عرض التذييل"
"Show Footer": "عرض التذييل",
"Small": "صغير",
"Large": "كبير",
"Auto": "تلقائي",
"Language": "اللغة",
"No annotations to export": "لا توجد تعليقات للتصدير",
"Author": "المؤلف",
"Exported from Readest": "تم التصدير من ريديست",
"Highlights & Annotations": "التمييزات والتعليقات",
"Note": "ملاحظة",
"Copied to clipboard": "تم النسخ إلى الحافظة",
"Export Annotations": "تصدير التعليقات",
"Auto Import on File Open": "استيراد تلقائي عند فتح الملف",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "لم يتم التعرف على فصول.",
"Failed to parse the EPUB file.": "فشل في تحليل ملف EPUB.",
"This book format is not supported.": "هذا تنسيق الكتاب غير مدعوم.",
"Unable to fetch the translation. Please log in first and try again.": "تعذر جلب الترجمة. يرجى تسجيل الدخول أولاً ثم المحاولة مرة أخرى.",
"Group": "تجميع",
"Always on Top": "دائمًا في المقدمة",
"No Timeout": "لا مهلة",
"{{value}} minute": "{{value}} دقيقة",
"{{value}} minutes": "{{value}} دقائق",
"{{value}} hour": "{{value}} ساعة",
"{{value}} hours": "{{value}} ساعات",
"CJK Font": "خط CJK",
"Clear Search": "مسح البحث",
"Header & Footer": "الرأس والتذييل",
"Apply also in Scrolled Mode": "تطبيق أيضًا في وضع التمرير",
"A new version of Readest is Available!": "إصدار جديد من ريديست متاح!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "إصدار ريديست {{newVersion}} متاح (الإصدار المثبت {{currentVersion}}).",
"Download and install now?": "هل ترغب في تنزيل وتثبيت الآن؟",
"Downloading {{downloaded}} of {{contentLength}}": "جارٍ تنزيل {{downloaded}} من {{contentLength}}",
"Download finished": "اكتمل التنزيل",
"DOWNLOAD & INSTALL": "تنزيل وتثبيت",
"Changelog:": "سجل التغييرات:",
"Software Update": "تحديث البرنامج",
"Title": "العنوان",
"Date Read": "تاريخ القراءة",
"Date Added": "تاريخ الإضافة",
"Format": "التنسيق",
"Ascending": "تصاعدي",
"Descending": "تنازلي",
"Sort by...": "ترتيب حسب...",
"Added:": "تاريخ الإضافة:",
"Format:": "التنسيق:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "الوصف:",
"No description available": "لا يوجد وصف متاح",
"List": "قائمة",
"Grid": "شبكة",
"(from 'As You Like It', Act II)": "(من 'كما تحب'، الفصل الثاني)",
"Link Color": "لون الرابط",
"Volume Keys for Page Flip": "مفاتيح الصوت لتقليب الصفحات",
"Clicks for Page Flip": "نقرات لتقليب الصفحات",
"Swap Clicks Area": "تبديل منطقة النقرات",
"Screen": "الشاشة",
"Orientation": "الاتجاه",
"Portrait": "العمودي",
"Landscape": "الأفقي",
"Open Last Book on Start": "فتح آخر كتاب عند البدء",
"Checking for updates...": "جارٍ التحقق من التحديثات...",
"Error checking for updates": "خطأ في التحقق من التحديثات",
"Details": "التفاصيل",
"File Size:": "حجم الملف:",
"Auto Detect": "الكشف التلقائي",
"Next Section": "القسم التالي",
"Previous Section": "القسم السابق",
"Next Page": "الصفحة التالية",
"Previous Page": "الصفحة السابقة",
"Are you sure to delete {{count}} selected book(s)?_zero": "هل أنت متأكد من حذف الكتب المحددة؟",
"Are you sure to delete {{count}} selected book(s)?_one": "هل أنت متأكد من حذف الكتاب المحدد؟",
"Are you sure to delete {{count}} selected book(s)?_two": "هل أنت متأكد من حذف الكتابين المحددين؟",
"Are you sure to delete {{count}} selected book(s)?_few": "هل أنت متأكد من حذف {{count}} كتب محددة؟",
"Are you sure to delete {{count}} selected book(s)?_many": "هل أنت متأكد من حذف {{count}} كتابًا محددًا؟",
"Are you sure to delete {{count}} selected book(s)?_other": "هل أنت متأكد من حذف {{count}} من الكتب المحددة؟",
"Are you sure to delete the selected book?": "هل أنت متأكد من حذف الكتاب المحدد؟",
"Deselect": "إلغاء",
"Select All": "الكل",
"No translation available.": "لا توجد ترجمة متاحة.",
"Translated by {{provider}}.": "ترجم بواسطة {{provider}}.",
"DeepL": "ديب إل",
"Google Translate": "ترجمة جوجل",
"Azure Translator": "مترجم أزور",
"Invert Image In Dark Mode": "عكس الصورة في الوضع المظلم",
"Help improve Readest": "ساعد في تحسين ريديست",
"Sharing anonymized statistics": "مشاركة إحصائيات مجهولة الهوية",
"Interface Language": "لغة الواجهة",
"Translation": "الترجمة",
"Enable Translation": "تمكين الترجمة",
"Translation Service": "خدمة الترجمة",
"Translate To": "ترجمة إلى",
"Disable Translation": "تعطيل الترجمة",
"Scroll": "تمرير",
"Overlap Pixels": "تداخل البكسلات",
"Click": "نقر",
"System Language": "لغة النظام"
}
@@ -5,7 +5,6 @@
"Animation": "Animation",
"Annotate": "Annotieren",
"Apply": "Anwenden",
"Are you sure to delete the selected books?": "Möchten Sie die ausgewählten Bücher wirklich löschen?",
"Auto Mode": "Automatischer Modus",
"Behavior": "Verhalten",
"Book": "Buch",
@@ -27,7 +26,6 @@
"Delete": "Löschen",
"Delete Highlight": "Hervorhebung löschen",
"Dictionary": "Wörterbuch",
"Disable Click-to-Flip": "Klick zum Blättern deaktivieren",
"Download Readest": "Readest herunterladen",
"Edit": "Bearbeiten",
"Enter your custom CSS here...": "Geben Sie hier Ihr benutzerdefiniertes CSS ein...",
@@ -43,15 +41,12 @@
"Global Settings": "Globale Einstellungen",
"Go Back": "Zurück",
"Go Forward": "Vorwärts",
"Go Left": "Nach links",
"Go Right": "Nach rechts",
"Grass": "Grasgrün",
"Gray": "Grau",
"Gruvbox": "Gruvbox",
"Highlight": "Hervorheben",
"Horizontal Direction": "Horizontale Richtung",
"Hyphenation": "Silbentrennung",
"Identifier:": "Kennung:",
"Import Books": "Bücher importieren",
"Language:": "Sprache:",
"Layout": "Layout",
@@ -64,7 +59,6 @@
"Match Whole Words": "Ganze Wörter",
"Maximum Number of Columns": "Maximale Spaltenanzahl",
"Minimum Font Size": "Minimale Schriftgröße",
"Misc": "Sonstiges",
"Monospace Font": "Monospace-Schriftart",
"More Info": "Weitere Informationen",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Unbekannt",
"Untitled": "Ohne Titel",
"Updated:": "Aktualisiert:",
"User avatar": "Benutzer-Avatar",
"Version {{version}}": "Version {{version}}",
"Vertical Direction": "Vertikale Richtung",
"Welcome to your library. You can import your books here and read them anytime.": "Willkommen in Ihrer Bibliothek. Sie können hier Ihre Bücher importieren und jederzeit lesen.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Auf alle Bücher anwenden",
"Apply to This Book": "Auf dieses Buch anwenden",
"Unable to fetch the translation. Try again later.": "Übersetzung konnte nicht abgerufen werden. Versuchen Sie es später erneut.",
"Update Now!": "Jetzt aktualisieren!",
"Update": "Aktualisieren",
"Check Update": "Aktualisierung prüfen",
"Already the latest version": "Bereits die neueste Version",
"Book Details": "Buchdetails",
@@ -138,7 +129,7 @@
"Book downloaded: {{title}}": "Buch heruntergeladen: {{title}}",
"Failed to download book: {{title}}": "Fehler beim Herunterladen des Buches: {{title}}",
"Upload Book": "Buch hochladen",
"Auto Upload Books to Cloud": "Bücher automatisch in die Cloud hochladen",
"Auto Upload Books to Cloud": "Bücher automatisch hochladen",
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% des Cloud-Speichers verwendet.",
"Storage": "Speicher",
"Book deleted: {{title}}": "Buch gelöscht: {{title}}",
@@ -152,7 +143,6 @@
"Text Indent": "Texteinzug",
"Paragraph Margin": "Absatzrand",
"Override Book Layout": "Buch-Layout überschreiben",
"Add to Group": "Zur Gruppe hinzufügen",
"Untitled Group": "Unbenannte Gruppe",
"Group Books": "Bücher gruppieren",
"Remove From Group": "Aus Gruppe entfernen",
@@ -247,5 +237,98 @@
"Border Color": "Randfarbe",
"Border Frame": "Rahmenrand",
"Show Header": "Kopfzeile anzeigen",
"Show Footer": "Fußzeile anzeigen"
"Show Footer": "Fußzeile anzeigen",
"Small": "Klein",
"Large": "Groß",
"Auto": "Automatisch",
"Language": "Sprache",
"No annotations to export": "Keine Anmerkungen zum Exportieren",
"Author": "Autor",
"Exported from Readest": "Exportiert von Readest",
"Highlights & Annotations": "Hervorhebungen & Anmerkungen",
"Note": "Notiz",
"Copied to clipboard": "In die Zwischenablage kopiert",
"Export Annotations": "Anmerkungen exportieren",
"Auto Import on File Open": "Automatischer Import beim Öffnen einer Datei",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Keine Kapitel erkannt.",
"Failed to parse the EPUB file.": "Fehler beim Parsen der EPUB-Datei.",
"This book format is not supported.": "Dieses Buchformat wird nicht unterstützt.",
"Unable to fetch the translation. Please log in first and try again.": "Übersetzung konnte nicht abgerufen werden. Bitte zuerst anmelden und erneut versuchen.",
"Group": "Gruppieren",
"Always on Top": "Immer im Vordergrund",
"No Timeout": "Kein Timeout",
"{{value}} minute": "{{value}} Minute",
"{{value}} minutes": "{{value}} Minuten",
"{{value}} hour": "{{value}} Stunde",
"{{value}} hours": "{{value}} Stunden",
"CJK Font": "CJK-Schriftart",
"Clear Search": "Suche löschen",
"Header & Footer": "Kopf- & Fußzeile",
"Apply also in Scrolled Mode": "Auch im Scroll-Modus anwenden",
"A new version of Readest is Available!": "Eine neue Version von Readest ist verfügbar!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} ist verfügbar (installierte Version {{currentVersion}}).",
"Download and install now?": "Jetzt herunterladen und installieren?",
"Downloading {{downloaded}} of {{contentLength}}": "Herunterladen {{downloaded}} von {{contentLength}}",
"Download finished": "Download abgeschlossen",
"DOWNLOAD & INSTALL": "HERUNTERLADEN & INSTALLIEREN",
"Changelog:": "Änderungsprotokoll:",
"Software Update": "Software-Update",
"Title": "Titel",
"Date Read": "Datum gelesen",
"Date Added": "Datum hinzugefügt",
"Format": "Format",
"Ascending": "Aufsteigend",
"Descending": "Absteigend",
"Sort by...": "Sortieren nach...",
"Added:": "Hinzugefügt:",
"Format:": "Format:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Beschreibung:",
"No description available": "Keine Beschreibung verfügbar",
"List": "Liste",
"Grid": "Raster",
"(from 'As You Like It', Act II)": "(aus 'Wie es euch gefällt', Akt II)",
"Link Color": "Linkfarbe",
"Volume Keys for Page Flip": "Volumen-Tasten für Seitenwechsel",
"Clicks for Page Flip": "Tippen zum Blättern",
"Swap Clicks Area": "Tippbereiche tauschen",
"Screen": "Bildschirm",
"Orientation": "Orientierung",
"Portrait": "Hochformat",
"Landscape": "Querformat",
"Open Last Book on Start": "Letztes Buch beim Start öffnen",
"Checking for updates...": "Auf Updates prüfen...",
"Error checking for updates": "Fehler beim Überprüfen auf Updates",
"Details": "Details",
"File Size:": "Dateigröße:",
"Auto Detect": "Automatisch erkennen",
"Next Section": "Nächster Abschnitt",
"Previous Section": "Vorheriger Abschnitt",
"Next Page": "Nächste Seite",
"Previous Page": "Vorherige Seite",
"Are you sure to delete {{count}} selected book(s)?_one": "Möchtest du das ausgewählte Buch wirklich löschen?",
"Are you sure to delete {{count}} selected book(s)?_other": "Möchtest du die {{count}} ausgewählten Bücher wirklich löschen?",
"Are you sure to delete the selected book?": "Möchtest du das ausgewählte Buch wirklich löschen?",
"Deselect": "Aufheben",
"Select All": "Alle",
"No translation available.": "Keine Übersetzung verfügbar.",
"Translated by {{provider}}.": "Übersetzt von {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Übersetzer",
"Azure Translator": "Azure Übersetzer",
"Invert Image In Dark Mode": "Invertiere Bild im Dunkeln",
"Help improve Readest": "Readest verbessern helfen",
"Sharing anonymized statistics": "Anonymisierte Statistiken teilen",
"Interface Language": "Benutzeroberflächensprache",
"Translation": "Übersetzung",
"Enable Translation": "Übersetzung aktivieren",
"Translation Service": "Übersetzungsdienst",
"Translate To": "Übersetzen nach",
"Disable Translation": "Übersetzung deaktivieren",
"Scroll": "Scrollen",
"Overlap Pixels": "Überlappungspixel",
"Click": "Tippen",
"System Language": "Systemsprache"
}
@@ -5,7 +5,6 @@
"Animation": "Κινούμενα σχέδια",
"Annotate": "Σχολιασμός",
"Apply": "Εφαρμογή",
"Are you sure to delete the selected books?": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τα επιλεγμένα βιβλία;",
"Auto Mode": "Αυτόματη λειτουργία",
"Behavior": "Συμπεριφορά",
"Book": "Βιβλίο",
@@ -27,7 +26,6 @@
"Delete": "Διαγραφή",
"Delete Highlight": "Διαγραφή επισήμανσης",
"Dictionary": "Λεξικό",
"Disable Click-to-Flip": "Απενεργοποίηση κλικ για γύρισμα σελίδας",
"Download Readest": "Λήψη Readest",
"Edit": "Επεξεργασία",
"Enter your custom CSS here...": "Εισάγετε το προσαρμοσμένο CSS εδώ...",
@@ -43,15 +41,12 @@
"Global Settings": "Γενικές ρυθμίσεις",
"Go Back": "Πίσω",
"Go Forward": "Μπροστά",
"Go Left": "Αριστερά",
"Go Right": "Δεξιά",
"Grass": "Γρασίδι",
"Gray": "Γκρι",
"Gruvbox": "Gruvbox",
"Highlight": "Επισήμανση",
"Horizontal Direction": "Οριζόντια κατεύθυνση",
"Hyphenation": "Συλλαβισμός",
"Identifier:": "Αναγνωριστικό:",
"Import Books": "Εισαγωγή βιβλίων",
"Language:": "Γλώσσα:",
"Layout": "Διάταξη",
@@ -64,7 +59,6 @@
"Match Whole Words": "Ταίριασμα ολόκληρων λέξεων",
"Maximum Number of Columns": "Μέγιστος αριθμός στηλών",
"Minimum Font Size": "Ελάχιστο μέγεθος γραμματοσειράς",
"Misc": "Διάφορα",
"Monospace Font": "Γραμματοσειρά σταθερού πλάτους",
"More Info": "Περισσότερες πληροφορίες",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Άγνωστο",
"Untitled": "Χωρίς τίτλο",
"Updated:": "Ενημερώθηκε:",
"User avatar": "Εικόνα χρήστη",
"Version {{version}}": "Έκδοση {{version}}",
"Vertical Direction": "Κάθετη κατεύθυνση",
"Welcome to your library. You can import your books here and read them anytime.": "Καλώς ήρθατε στη βιβλιοθήκη σας. Μπορείτε να εισάγετε τα βιβλία σας εδώ και να τα διαβάσετε οποιαδήποτε στιγμή.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Εφαρμογή σε όλα τα βιβλία",
"Apply to This Book": "Εφαρμογή σε αυτό το βιβλίο",
"Unable to fetch the translation. Try again later.": "Αδυναμία λήψης της μετάφρασης. Δοκιμάστε ξανά αργότερα.",
"Update Now!": "Ενημέρωση τώρα!",
"Update": "Ενημέρωση",
"Check Update": "Έλεγχος ενημέρωσης",
"Already the latest version": "Ήδη η τελευταία έκδοση",
"Book Details": "Λεπτομέρειες βιβλίου",
@@ -138,12 +129,12 @@
"Book downloaded: {{title}}": "Το βιβλίο με τίτλο {{title}} κατέβηκε",
"Failed to download book: {{title}}": "Αποτυχία λήψης βιβλίου: {{title}}",
"Upload Book": "Ανέβασμα βιβλίου",
"Auto Upload Books to Cloud": "Αυτόματο ανέβασμα βιβλίων στο cloud",
"Auto Upload Books to Cloud": "Αυτόματη αποθήκευση στο cloud",
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% της αποθήκευσης στο cloud χρησιμοποιήθηκε.",
"Storage": "Αποθήκευση",
"Book deleted: {{title}}": "Το βιβλίο με τίτλο {{title}} διαγράφηκε",
"Failed to delete book: {{title}}": "Αποτυχία διαγραφής βιβλίου: {{title}}",
"Check Updates on Start": "Έλεγχος ενημερώσεων κατά την εκκίνηση",
"Check Updates on Start": "Αυτόματος έλεγχος ενημερώσεων",
"Insufficient storage quota": "Ανεπαρκές όριο αποθήκευσης",
"Font Weight": "Βάρος γραμματοσειράς",
"Line Spacing": "Διάκενο γραμμών",
@@ -152,7 +143,6 @@
"Text Indent": "Εσοχή κειμένου",
"Paragraph Margin": "Περιθώριο παραγράφου",
"Override Book Layout": "Παράκαμψη διάταξης βιβλίου",
"Add to Group": "Προσθήκη στην ομάδα",
"Untitled Group": "Ομάδα χωρίς τίτλο",
"Group Books": "Ομαδοποίηση βιβλίων",
"Remove From Group": "Αφαίρεση από την ομάδα",
@@ -247,5 +237,98 @@
"Border Color": "Χρώμα περιγράμματος",
"Border Frame": "Πλαίσιο περιγράμματος",
"Show Header": "Εμφάνιση κεφαλίδας",
"Show Footer": "Εμφάνιση υποσέλιδου"
"Show Footer": "Εμφάνιση υποσέλιδου",
"Small": "Μικρό",
"Large": "Μεγάλο",
"Auto": "Αυτόματο",
"Language": "Γλώσσα",
"No annotations to export": "Δεν υπάρχουν σχολιασμοί για εξαγωγή",
"Author": "Συγγραφέας",
"Exported from Readest": "Εξήχθη από το Readest",
"Highlights & Annotations": "Επισημάνσεις & Σχολιασμοί",
"Note": "Σημείωση",
"Copied to clipboard": "Αντιγράφηκε στο πρόχειρο",
"Export Annotations": "Εξαγωγή σχολιασμών",
"Auto Import on File Open": "Αυτόματη εισαγωγή κατά το άνοιγμα αρχείου",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Δεν εντοπίστηκαν κεφάλαια.",
"Failed to parse the EPUB file.": "Αποτυχία ανάλυσης του αρχείου EPUB.",
"This book format is not supported.": "Αυτή η μορφή βιβλίου δεν υποστηρίζεται.",
"Unable to fetch the translation. Please log in first and try again.": "Αδυναμία λήψης της μετάφρασης. Παρακαλώ συνδεθείτε πρώτα και δοκιμάστε ξανά.",
"Group": "Ομάδα",
"Always on Top": "Πάντα στην κορυφή",
"No Timeout": "Χωρίς χρονικό όριο",
"{{value}} minute": "{{value}} λεπτό",
"{{value}} minutes": "{{value}} λεπτά",
"{{value}} hour": "{{value}} ώρα",
"{{value}} hours": "{{value}} ώρες",
"CJK Font": "Γραμματοσειρά CJK",
"Clear Search": "Καθαρισμός αναζήτησης",
"Header & Footer": "Κεφαλίδα & Υποσέλιδο",
"Apply also in Scrolled Mode": "Εφαρμογή και σε λειτουργία κύλισης",
"A new version of Readest is Available!": "Μια νέα έκδοση του Readest είναι διαθέσιμη!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Η έκδοση Readest {{newVersion}} είναι διαθέσιμη (έκδοση που είναι εγκατεστημένη {{currentVersion}}).",
"Download and install now?": "Θέλετε να την κατεβάσετε και να την εγκαταστήσετε τώρα;",
"Downloading {{downloaded}} of {{contentLength}}": "Κατεβάζω {{downloaded}} από {{contentLength}}",
"Download finished": "Η λήψη ολοκληρώθηκε",
"DOWNLOAD & INSTALL": "ΛΗΨΗ & ΕΓΚΑΤΑΣΤΑΣΗ",
"Changelog:": "Αλλαγές:",
"Software Update": "Ενημέρωση λογισμικού",
"Title": "Τίτλος",
"Date Read": "Ημερομηνία ανάγνωσης",
"Date Added": "Ημερομηνία προσθήκης",
"Format": "Μορφή",
"Ascending": "Αύξουσα",
"Descending": "Φθίνουσα",
"Sort by...": "Ταξινόμηση κατά...",
"Added:": "Προστέθηκε:",
"Format:": "Μορφή:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Περιγραφή:",
"No description available": "Δεν είναι διαθέσιμη καμία περιγραφή",
"List": "Λίστα",
"Grid": "Πλέγμα",
"(from 'As You Like It', Act II)": "('Όπως σας αρέσει', Πράξη II)",
"Link Color": "Χρώμα συνδέσμου",
"Volume Keys for Page Flip": "Ανατροπή με πλήκτρα έντασης",
"Clicks for Page Flip": "Κλικ για ανατροπή σελίδας",
"Swap Clicks Area": "Ανταλλαγή περιοχής κλικ",
"Screen": "Οθόνη",
"Orientation": "Προσανατολισμός",
"Portrait": "Πορτραίτο",
"Landscape": "Τοπίο",
"Open Last Book on Start": "Άνοιγμα τελευταίου βιβλίου",
"Checking for updates...": "Έλεγχος για ενημερώσεις...",
"Error checking for updates": "Σφάλμα κατά τον έλεγχο για ενημερώσεις",
"Details": "Πληρ.",
"File Size:": "Μέγεθος αρχείου:",
"Auto Detect": "Αυτόματη ανίχνευση",
"Next Section": "Επόμενη ενότητα",
"Previous Section": "Προηγούμενη ενότητα",
"Next Page": "Επόμενη σελίδα",
"Previous Page": "Προηγούμενη σελίδα",
"Are you sure to delete {{count}} selected book(s)?_one": "Είστε σίγουροι ότι θέλετε να διαγράψετε το επιλεγμένο βιβλίο;",
"Are you sure to delete {{count}} selected book(s)?_other": "Είστε σίγουροι ότι θέλετε να διαγράψετε τα {{count}} επιλεγμένα βιβλία;",
"Are you sure to delete the selected book?": "Είστε σίγουροι ότι θέλετε να διαγράψετε το επιλεγμένο βιβλίο;",
"Deselect": "Άρση",
"Select All": "Όλα",
"No translation available.": "Δεν είναι διαθέσιμη καμία μετάφραση.",
"Translated by {{provider}}.": "Μεταφρασμένο από {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Μετάφραση Google",
"Azure Translator": "Μεταφραστής Azure",
"Invert Image In Dark Mode": "Αντιστροφή σε σκούρο",
"Help improve Readest": "Βοηθήστε στη βελτίωση του Readest",
"Sharing anonymized statistics": "Μοιράζομαι ανώνυμες στατιστικές",
"Interface Language": "Γλώσσα διεπαφής",
"Translation": "Μετάφραση",
"Enable Translation": "Ενεργοποίηση μετάφρασης",
"Translation Service": "Υπηρεσία μετάφρασης",
"Translate To": "Μετάφραση σε",
"Disable Translation": "Απενεργοποίηση μετάφρασης",
"Scroll": "Κύλιση",
"Overlap Pixels": "Επικάλυψη εικονοστοιχείων",
"Click": "Κλικ",
"System Language": "Γλώσσα συστήματος"
}
@@ -1 +1,4 @@
{}
{
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC"
}
@@ -5,7 +5,6 @@
"Animation": "Animación",
"Annotate": "Anotar",
"Apply": "Aplicar",
"Are you sure to delete the selected books?": "¿Estás seguro de eliminar los libros seleccionados?",
"Auto Mode": "Modo automático",
"Behavior": "Comportamiento",
"Book": "Libro",
@@ -27,7 +26,6 @@
"Delete": "Eliminar",
"Delete Highlight": "Eliminar resaltado",
"Dictionary": "Diccionario",
"Disable Click-to-Flip": "Desactivar clic para voltear",
"Download Readest": "Descargar Readest",
"Edit": "Editar",
"Enter your custom CSS here...": "Introduce tu CSS personalizado aquí...",
@@ -43,15 +41,12 @@
"Global Settings": "Configuración global",
"Go Back": "Volver",
"Go Forward": "Avanzar",
"Go Left": "Ir a la izquierda",
"Go Right": "Ir a la derecha",
"Grass": "Hierba",
"Gray": "Gris",
"Gruvbox": "Gruvbox",
"Highlight": "Resaltar",
"Horizontal Direction": "Dirección horizontal",
"Hyphenation": "Guionización",
"Identifier:": "Identificador:",
"Import Books": "Importar libros",
"Language:": "Idioma:",
"Layout": "Diseño",
@@ -64,7 +59,6 @@
"Match Whole Words": "Coincidir palabras completas",
"Maximum Number of Columns": "Número máximo de columnas",
"Minimum Font Size": "Tamaño de fuente mínimo",
"Misc": "Varios",
"Monospace Font": "Fuente monoespaciada",
"More Info": "Más información",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Desconocido",
"Untitled": "Sin título",
"Updated:": "Actualizado:",
"User avatar": "Avatar del usuario",
"Version {{version}}": "Versión {{version}}",
"Vertical Direction": "Dirección vertical",
"Welcome to your library. You can import your books here and read them anytime.": "Bienvenido a tu biblioteca. Puedes importar tus libros aquí y leerlos en cualquier momento.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Aplicar a todos los libros",
"Apply to This Book": "Aplicar a este libro",
"Unable to fetch the translation. Try again later.": "No se puede obtener la traducción. Inténtalo de nuevo más tarde.",
"Update Now!": "¡Actualizar ahora!",
"Update": "Actualizar",
"Check Update": "Comprobar actualización",
"Already the latest version": "Ya es la última versión",
"Book Details": "Detalles del libro",
@@ -152,7 +143,6 @@
"Text Indent": "Sangría de texto",
"Paragraph Margin": "Margen de párrafo",
"Override Book Layout": "Sobrescribir diseño del libro",
"Add to Group": "Añadir a grupo",
"Untitled Group": "Grupo sin título",
"Group Books": "Agrupar libros",
"Remove From Group": "Eliminar del grupo",
@@ -247,5 +237,99 @@
"Border Color": "Color del borde",
"Border Frame": "Marco de borde",
"Show Header": "Mostrar encabezado",
"Show Footer": "Mostrar pie de página"
"Show Footer": "Mostrar pie de página",
"Small": "Pequeño",
"Large": "Grande",
"Auto": "Automático",
"Language": "Idioma",
"No annotations to export": "No hay anotaciones para exportar",
"Author": "Autor",
"Exported from Readest": "Exportado desde Readest",
"Highlights & Annotations": "Resaltados y anotaciones",
"Note": "Nota",
"Copied to clipboard": "Copiado al portapapeles",
"Export Annotations": "Exportar anotaciones",
"Auto Import on File Open": "Importación automática al abrir archivo",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "No se detectaron capítulos.",
"Failed to parse the EPUB file.": "Error al analizar el archivo EPUB.",
"This book format is not supported.": "Este formato de libro no es compatible.",
"Unable to fetch the translation. Please log in first and try again.": "No se puede obtener la traducción. Por favor, inicia sesión primero e inténtalo de nuevo.",
"Group": "Grupo",
"Always on Top": "Siempre en la parte superior",
"No Timeout": "Sin tiempo de espera",
"{{value}} minute": "{{value}} minuto",
"{{value}} minutes": "{{value}} minutos",
"{{value}} hour": "{{value}} hora",
"{{value}} hours": "{{value}} horas",
"CJK Font": "Fuente CJK",
"Clear Search": "Limpiar búsqueda",
"Header & Footer": "Encabezado y pie de página",
"Apply also in Scrolled Mode": "Aplicar también en modo desplazamiento",
"A new version of Readest is Available!": "¡Una nueva versión de Readest está disponible!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponible (versión instalada {{currentVersion}}).",
"Download and install now?": "Descargar e instalar ahora?",
"Downloading {{downloaded}} of {{contentLength}}": "Descargando {{downloaded}} de {{contentLength}}",
"Download finished": "Descarga finalizada",
"DOWNLOAD & INSTALL": "DESCARGAR E INSTALAR",
"Changelog:": "Notas de la versión:",
"Software Update": "Actualización de software",
"Title": "Título",
"Date Read": "Fecha de lectura",
"Date Added": "Fecha añadida",
"Format": "Formato",
"Ascending": "Ascendente",
"Descending": "Descendente",
"Sort by...": "Ordenar por...",
"Added:": "Agregado:",
"Format:": "Formato:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Descripción:",
"No description available": "No hay descripción disponible",
"List": "Lista",
"Grid": "Cuadrícula",
"(from 'As You Like It', Act II)": "(de 'Como gustéis', Acto II)",
"Link Color": "Color del enlace",
"Volume Keys for Page Flip": "Botones de volumen para pasar página",
"Clicks for Page Flip": "Toques para pasar página",
"Swap Clicks Area": "Invertir zonas de toque",
"Screen": "Pantalla",
"Orientation": "Orientación",
"Portrait": "Vertical",
"Landscape": "Horizontal",
"Open Last Book on Start": "Abrir último libro al iniciar",
"Checking for updates...": "Comprobando actualizaciones...",
"Error checking for updates": "Error al comprobar actualizaciones",
"Details": "Detalles",
"File Size:": "Peso del archivo:",
"Auto Detect": "Detección automática",
"Next Section": "Siguiente sección",
"Previous Section": "Sección anterior",
"Next Page": "Página siguiente",
"Previous Page": "Página anterior",
"Are you sure to delete {{count}} selected book(s)?_one": "¿Seguro que quieres eliminar el libro seleccionado?",
"Are you sure to delete {{count}} selected book(s)?_many": "¿Seguro que quieres eliminar los {{count}} libros seleccionados?",
"Are you sure to delete {{count}} selected book(s)?_other": "¿Seguro que quieres eliminar los {{count}} libros seleccionados?",
"Are you sure to delete the selected book?": "¿Seguro que quieres eliminar el libro seleccionado?",
"Deselect": "Quitar",
"Select All": "Todo",
"No translation available.": "No hay traducción disponible.",
"Translated by {{provider}}.": "Traducido por {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Translate",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "Invertir imagen en oscuro",
"Help improve Readest": "Ayuda a mejorar Readest",
"Sharing anonymized statistics": "Compartiendo estadísticas anónimas",
"Interface Language": "Idioma de la interfaz",
"Translation": "Traducción",
"Enable Translation": "Habilitar traducción",
"Translation Service": "Servicio de traducción",
"Translate To": "Traducir a",
"Disable Translation": "Deshabilitar traducción",
"Scroll": "Desplazar",
"Overlap Pixels": "Superponer píxeles",
"Click": "Click",
"System Language": "Idioma del sistema"
}
@@ -5,7 +5,6 @@
"Animation": "Animation",
"Annotate": "Annoter",
"Apply": "Appliquer",
"Are you sure to delete the selected books?": "Êtes-vous sûr de vouloir supprimer les livres sélectionnés ?",
"Auto Mode": "Mode automatique",
"Behavior": "Comportement",
"Book": "Livre",
@@ -27,7 +26,6 @@
"Delete": "Supprimer",
"Delete Highlight": "Supprimer le surlignage",
"Dictionary": "Dictionnaire",
"Disable Click-to-Flip": "Désactiver le clic pour tourner",
"Download Readest": "Télécharger Readest",
"Edit": "Modifier",
"Enter your custom CSS here...": "Saisissez votre CSS personnalisé ici...",
@@ -43,15 +41,12 @@
"Global Settings": "Paramètres globaux",
"Go Back": "Retour",
"Go Forward": "Avancer",
"Go Left": "Aller à gauche",
"Go Right": "Aller à droite",
"Grass": "Herbe",
"Gray": "Gris",
"Gruvbox": "Gruvbox",
"Highlight": "Surligner",
"Horizontal Direction": "Direction horizontale",
"Hyphenation": "Césure",
"Identifier:": "Identifiant :",
"Import Books": "Importer des livres",
"Language:": "Langue :",
"Layout": "Mise en page",
@@ -64,7 +59,6 @@
"Match Whole Words": "Mots entiers",
"Maximum Number of Columns": "Nombre maximal de colonnes",
"Minimum Font Size": "Taille minimale de police",
"Misc": "Divers",
"Monospace Font": "Police monospace",
"More Info": "Plus d'informations",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Inconnu",
"Untitled": "Sans titre",
"Updated:": "Mis à jour :",
"User avatar": "Avatar de l'utilisateur",
"Version {{version}}": "Version {{version}}",
"Vertical Direction": "Direction verticale",
"Welcome to your library. You can import your books here and read them anytime.": "Bienvenue dans votre bibliothèque. Vous pouvez importer vos livres ici et les lire à tout moment.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Appliquer à tous les livres",
"Apply to This Book": "Appliquer à ce livre",
"Unable to fetch the translation. Try again later.": "Impossible de récupérer la traduction. Réessayez plus tard.",
"Update Now!": "Mettre à jour maintenant !",
"Update": "Mettre à jour",
"Check Update": "Vérifier la mise à jour",
"Already the latest version": "Déjà la dernière version",
"Book Details": "Détails du livre",
@@ -152,7 +143,6 @@
"Text Indent": "Retrait du texte",
"Paragraph Margin": "Marge de paragraphe",
"Override Book Layout": "Remplacer la mise en page du livre",
"Add to Group": "Ajouter au groupe",
"Untitled Group": "Groupe sans titre",
"Group Books": "Regrouper les livres",
"Remove From Group": "Retirer du groupe",
@@ -247,5 +237,99 @@
"Border Color": "Couleur de la bordure",
"Border Frame": "Cadre de bordure",
"Show Header": "Afficher l'en-tête",
"Show Footer": "Afficher le pied de page"
"Show Footer": "Afficher le pied de page",
"Small": "Petit",
"Large": "Grand",
"Auto": "Automatique",
"Language": "Langue",
"No annotations to export": "Aucune annotation à exporter",
"Author": "Auteur",
"Exported from Readest": "Exporté depuis Readest",
"Highlights & Annotations": "Surlignages et annotations",
"Note": "Note",
"Copied to clipboard": "Copié dans le presse-papiers",
"Export Annotations": "Exporter les annotations",
"Auto Import on File Open": "Importation automatique à l'ouverture du fichier",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Aucun chapitre détecté.",
"Failed to parse the EPUB file.": "Échec de l'analyse du fichier EPUB.",
"This book format is not supported.": "Ce format de livre n'est pas pris en charge.",
"Unable to fetch the translation. Please log in first and try again.": "Impossible de récupérer la traduction. Veuillez d'abord vous connecter et réessayer.",
"Group": "Groupe",
"Always on Top": "Toujours au-dessus",
"No Timeout": "Aucun délai d'attente",
"{{value}} minute": "{{value}} minute",
"{{value}} minutes": "{{value}} minutes",
"{{value}} hour": "{{value}} heure",
"{{value}} hours": "{{value}} heures",
"CJK Font": "Police CJK",
"Clear Search": "Effacer la recherche",
"Header & Footer": "En-tête et pied de page",
"Apply also in Scrolled Mode": "Appliquer également en mode défilement",
"A new version of Readest is Available!": "Avis de mise à jour",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} est disponible (version installée {{currentVersion}}).",
"Download and install now?": "Télécharger et installer maintenant ?",
"Downloading {{downloaded}} of {{contentLength}}": "Téléchargement {{downloaded}} sur {{contentLength}}",
"Download finished": "Téléchargement terminé",
"DOWNLOAD & INSTALL": "TÉLÉCHARGER ET INSTALLER",
"Changelog:": "Journal des modifications :",
"Software Update": "Mise à jour logicielle",
"Title": "Titre",
"Date Read": "Date de lecture",
"Date Added": "Date ajoutée",
"Format": "Format",
"Ascending": "Ascendant",
"Descending": "Descendant",
"Sort by...": "Trier par...",
"Added:": "Ajouté :",
"Format:": "Format :",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Description :",
"No description available": "Aucune description disponible",
"List": "Liste",
"Grid": "Grille",
"(from 'As You Like It', Act II)": "(de 'Comme il vous plaira', Acte II)",
"Link Color": "Couleur du lien",
"Volume Keys for Page Flip": "Boutons volume pour tourner la page",
"Clicks for Page Flip": "Taps pour tourner la page",
"Swap Clicks Area": "Inverser zones de tap",
"Screen": "Écran",
"Orientation": "Orientation",
"Portrait": "Portrait",
"Landscape": "Paysage",
"Open Last Book on Start": "Ouvrir le dernier livre au démarrage",
"Checking for updates...": "Vérification des mises à jour...",
"Error checking for updates": "Erreur lors de la vérification des mises à jour",
"Details": "Détails",
"File Size:": "Taille du fichier :",
"Auto Detect": "Détection automatique",
"Next Section": "Section suivante",
"Previous Section": "Section précédente",
"Next Page": "Page suivante",
"Previous Page": "Page précédente",
"Are you sure to delete {{count}} selected book(s)?_one": "Supprimer le livre sélectionné ?",
"Are you sure to delete {{count}} selected book(s)?_many": "Supprimer les {{count}} livres sélectionnés ?",
"Are you sure to delete {{count}} selected book(s)?_other": "Supprimer les {{count}} livres sélectionnés ?",
"Are you sure to delete the selected book?": "Supprimer le livre sélectionné ?",
"Deselect": "Annuler",
"Select All": "Tous",
"No translation available.": "Aucune traduction disponible.",
"Translated by {{provider}}.": "Traduit par {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Traduction",
"Azure Translator": "Traducteur Azure",
"Invert Image In Dark Mode": "Inverser l'image en mode sombre",
"Help improve Readest": "Contribuer à améliorer Readest",
"Sharing anonymized statistics": "Partage de statistiques anonymisées",
"Interface Language": "Langue de l'interface",
"Translation": "Traduction",
"Enable Translation": "Activer la traduction",
"Translation Service": "Service de traduction",
"Translate To": "Traduire vers",
"Disable Translation": " désactiver la traduction",
"Scroll": "Défilement",
"Overlap Pixels": "Pixels de chevauchement",
"Click": "Cliquer",
"System Language": "Langue du système"
}
@@ -5,7 +5,6 @@
"Animation": "एनीमेशन",
"Annotate": "टिप्पणी",
"Apply": "लागू करें",
"Are you sure to delete the selected books?": "क्या आप चयनित पुस्तकों को हटाना चाहते हैं?",
"Auto Mode": "स्वचालित मोड",
"Behavior": "व्यवहार",
"Book": "पुस्तक",
@@ -27,7 +26,6 @@
"Delete": "हटाएं",
"Delete Highlight": "हाइलाइट हटाएं",
"Dictionary": "शब्दकोश",
"Disable Click-to-Flip": "क्लिक-टू-फ्लिप अक्षम करें",
"Download Readest": "Readest डाउनलोड करें",
"Edit": "संपादित करें",
"Enter your custom CSS here...": "अपना कस्टम CSS यहां दर्ज करें...",
@@ -43,15 +41,12 @@
"Global Settings": "वैश्विक सेटिंग्स",
"Go Back": "वापस जाएं",
"Go Forward": "आगे जाएं",
"Go Left": "बाएं जाएं",
"Go Right": "दाएं जाएं",
"Grass": "घास",
"Gray": "ग्रे",
"Gruvbox": "ग्रूवबॉक्स",
"Highlight": "हाइलाइट",
"Horizontal Direction": "क्षैतिज दिशा",
"Hyphenation": "हाइफ़नेशन",
"Identifier:": "पहचानकर्ता:",
"Import Books": "पुस्तकें आयात करें",
"Language:": "भाषा:",
"Layout": "लेआउट",
@@ -64,7 +59,6 @@
"Match Whole Words": "पूरे शब्द मैच करें",
"Maximum Number of Columns": "कॉलम की अधिकतम संख्या",
"Minimum Font Size": "न्यूनतम फ़ॉन्ट साइज़",
"Misc": "विविध",
"Monospace Font": "मोनोस्पेस फ़ॉन्ट",
"More Info": "अधिक जानकारी",
"Nord": "नॉर्ड",
@@ -112,7 +106,6 @@
"Unknown": "अज्ञात",
"Untitled": "शीर्षकहीन",
"Updated:": "अपडेट किया गया:",
"User avatar": "उपयोगकर्ता अवतार",
"Version {{version}}": "संस्करण {{version}}",
"Vertical Direction": "ऊर्ध्वाधर दिशा",
"Welcome to your library. You can import your books here and read them anytime.": "आपकी लाइब्रेरी में आपका स्वागत है। आप यहां अपनी पुस्तकें आयात कर सकते हैं और कभी भी पढ़ सकते हैं।",
@@ -126,8 +119,6 @@
"Apply to All Books": "सभी पुस्तकों पर लागू करें",
"Apply to This Book": "इस पुस्तक पर लागू करें",
"Unable to fetch the translation. Try again later.": "अनुवाद प्राप्त करने में असमर्थ। बाद में पुनः प्रयास करें।",
"Update Now!": "अभी अपडेट करें!",
"Update": "अपडेट करें",
"Check Update": "अपडेट जांचें",
"Already the latest version": "पहले से ही नवीनतम संस्करण",
"Book Details": "पुस्तक विवरण",
@@ -152,7 +143,6 @@
"Text Indent": "टेक्स्ट इंडेंट",
"Paragraph Margin": "पैराग्राफ मार्जिन",
"Override Book Layout": "पुस्तक लेआउट ओवरराइड करें",
"Add to Group": "समूह में जोड़ें",
"Untitled Group": "अभिनामित समूह",
"Group Books": "समूह पुस्तकें",
"Remove From Group": "समूह से हटाएं",
@@ -247,5 +237,98 @@
"Border Color": "बॉर्डर रंग",
"Border Frame": "बॉर्डर फ्रेम",
"Show Header": "हेडर दिखाएं",
"Show Footer": "फ़ुटर दिखाएं"
"Show Footer": "फ़ुटर दिखाएं",
"Small": "छोटा",
"Large": "बड़ा",
"Auto": "स्वचालित",
"Language": "भाषा",
"No annotations to export": "निर्यात करने के लिए कोई टिप्पणियाँ नहीं हैं",
"Author": "लेखक",
"Exported from Readest": "Readest से निर्यात किया गया",
"Highlights & Annotations": "हाइलाइट और टिप्पणियाँ",
"Note": "टिप्पणी",
"Copied to clipboard": "क्लिपबोर्ड पर कॉपी किया गया",
"Export Annotations": "टिप्पणियाँ निर्यात करें",
"Auto Import on File Open": "फ़ाइल खोलने पर स्वचालित आयात",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "कोई अध्याय नहीं मिला।",
"Failed to parse the EPUB file.": "EPUB फ़ाइल को पार्स करने में विफल।",
"This book format is not supported.": "यह पुस्तक प्रारूप समर्थित नहीं है।",
"Unable to fetch the translation. Please log in first and try again.": "अनुवाद प्राप्त करने में असमर्थ। कृपया पहले लॉग इन करें और पुनः प्रयास करें।",
"Group": "समूह",
"Always on Top": "सर्वोच्च पर हमेशा",
"No Timeout": "कोई समय समाप्त नहीं",
"{{value}} minute": "{{value}} मिनट",
"{{value}} minutes": "{{value}} मिनट",
"{{value}} hour": "{{value}} घंटा",
"{{value}} hours": "{{value}} घंटे",
"CJK Font": "CJK फ़ॉन्ट",
"Clear Search": "खोज साफ करें",
"Header & Footer": "हेडर और फ़ुटर",
"Apply also in Scrolled Mode": "स्क्रॉल मोड में भी लागू करें",
"A new version of Readest is Available!": "Readest का एक नया संस्करण उपलब्ध है!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} उपलब्ध है (स्थापित संस्करण {{currentVersion}})।",
"Download and install now?": "अब डाउनलोड और इंस्टॉल करें?",
"Downloading {{downloaded}} of {{contentLength}}": "डाउनलोड कर रहा है {{downloaded}} का {{contentLength}}",
"Download finished": "डाउनलोड पूरा हुआ",
"DOWNLOAD & INSTALL": "डाउनलोड और इंस्टॉल करें",
"Changelog:": "चेंजलॉग:",
"Software Update": "सॉफ़्टवेयर अपडेट",
"Title": "शीर्षक",
"Date Read": "पढ़ने की तारीख",
"Date Added": "जोड़ा गया दिनांक",
"Format": "फॉर्मेट",
"Ascending": "आरोही",
"Descending": "अवरोही",
"Sort by...": "द्वारा क्रमबद्ध करें...",
"Added:": "जोड़ा गया:",
"Format:": "फॉर्मेट:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "विवरण:",
"No description available": "कोई विवरण उपलब्ध नहीं है",
"List": "सूची",
"Grid": "ग्रिड",
"(from 'As You Like It', Act II)": "('जैसा आप इसे पसंद करते हैं', अधिनियम II)",
"Link Color": "लिंक रंग",
"Volume Keys for Page Flip": "पृष्ठ फ़्लिप के लिए वॉल्यूम कुंजियाँ",
"Clicks for Page Flip": "पृष्ठ फ़्लिप के लिए क्लिक करें",
"Swap Clicks Area": "क्लिक क्षेत्र स्वैप करें",
"Screen": "स्क्रीन",
"Orientation": "अवयव",
"Portrait": "पोर्ट्रेट",
"Landscape": "लैंडस्केप",
"Open Last Book on Start": "शुरू में अंतिम पुस्तक खोलें",
"Checking for updates...": "अपडेट की जांच कर रहा है...",
"Error checking for updates": "अपडेट की जांच करते समय त्रुटि",
"Details": "विवरण",
"File Size:": "फ़ाइल आकार:",
"Auto Detect": "स्वचालित पहचान",
"Next Section": "अगला अनुभाग",
"Previous Section": "पिछला अनुभाग",
"Next Page": "अगला पृष्ठ",
"Previous Page": "पिछला पृष्ठ",
"Are you sure to delete {{count}} selected book(s)?_one": "क्या आप चयनित पुस्तक हटाना चाहते हैं?",
"Are you sure to delete {{count}} selected book(s)?_other": "क्या आप चयनित {{count}} पुस्तकें हटाना चाहते हैं?",
"Are you sure to delete the selected book?": "क्या आप चयनित पुस्तक हटाना चाहते हैं?",
"Deselect": "रद्द करें",
"Select All": "सब चुनें",
"No translation available.": "कोई अनुवाद उपलब्ध नहीं है।",
"Translated by {{provider}}.": "{{provider}} द्वारा अनुवादित।",
"DeepL": "डीपएल",
"Google Translate": "गूगल अनुवाद",
"Azure Translator": "एज़्योर अनुवादक",
"Invert Image In Dark Mode": "अंधेरे में छवि को उलटें",
"Help improve Readest": "Readest में सुधार करने में मदद करें",
"Sharing anonymized statistics": "सांख्यिकी साझा करना",
"Interface Language": "इंटरफ़ेस भाषा",
"Translation": "अनुवाद",
"Enable Translation": "अनुवाद सक्षम करें",
"Translation Service": "अनुवाद सेवा",
"Translate To": "अनुवाद करें",
"Disable Translation": "अनुवाद अक्षम करें",
"Scroll": "स्क्रॉल",
"Overlap Pixels": "ओवरलैप पिक्सेल",
"Click": "क्लिक करें",
"System Language": "सिस्टम भाषा"
}
@@ -5,7 +5,6 @@
"Animation": "Animasi",
"Annotate": "Anotasi",
"Apply": "Terapkan",
"Are you sure to delete the selected books?": "Apakah Anda yakin ingin menghapus buku yang dipilih?",
"Auto Mode": "Mode Otomatis",
"Behavior": "Perilaku",
"Book": "Buku",
@@ -27,7 +26,6 @@
"Delete": "Hapus",
"Delete Highlight": "Hapus Sorotan",
"Dictionary": "Kamus",
"Disable Click-to-Flip": "Nonaktifkan Klik untuk Membalik",
"Download Readest": "Unduh Readest",
"Edit": "Edit",
"Enter your custom CSS here...": "Masukkan CSS kustom Anda di sini...",
@@ -43,15 +41,12 @@
"Global Settings": "Pengaturan Global",
"Go Back": "Kembali",
"Go Forward": "Maju",
"Go Left": "Ke Kiri",
"Go Right": "Ke Kanan",
"Grass": "Rumput",
"Gray": "Abu-abu",
"Gruvbox": "Gruvbox",
"Highlight": "Sorot",
"Horizontal Direction": "Arah Horizontal",
"Hyphenation": "Pemenggalan Kata",
"Identifier:": "Pengenal:",
"Import Books": "Impor Buku",
"Language:": "Bahasa:",
"Layout": "Tata Letak",
@@ -64,7 +59,6 @@
"Match Whole Words": "Cocokkan Kata Utuh",
"Maximum Number of Columns": "Jumlah Kolom Maksimum",
"Minimum Font Size": "Ukuran Font Minimum",
"Misc": "Lain-lain",
"Monospace Font": "Font Monospace",
"More Info": "Info Lebih Lanjut",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Tidak Diketahui",
"Untitled": "Tanpa Judul",
"Updated:": "Diperbarui:",
"User avatar": "Avatar Pengguna",
"Version {{version}}": "Versi {{version}}",
"Vertical Direction": "Arah Vertikal",
"Welcome to your library. You can import your books here and read them anytime.": "Selamat datang di perpustakaan Anda. Anda dapat mengimpor buku-buku Anda di sini dan membacanya kapan saja.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Terapkan ke semua buku",
"Apply to This Book": "Terapkan ke buku ini",
"Unable to fetch the translation. Try again later.": "Tidak dapat mengambil terjemahan. Coba lagi nanti.",
"Update Now!": "Perbarui Sekarang!",
"Update": "Perbarui",
"Check Update": "Periksa pembaruan",
"Already the latest version": "Sudah versi terbaru",
"Book Details": "Detail Buku",
@@ -152,7 +143,6 @@
"Text Indent": "Indentasi Teks",
"Paragraph Margin": "Margin Paragraf",
"Override Book Layout": "Ganti Tata Letak Buku",
"Add to Group": "Tambahkan ke Grup",
"Untitled Group": "Grup Tanpa Judul",
"Group Books": "Grupkan Buku",
"Remove From Group": "Hapus dari Grup",
@@ -247,5 +237,97 @@
"Border Color": "Warna Garis",
"Border Frame": "Bingkai Garis",
"Show Header": "Tampilkan Header",
"Show Footer": "Tampilkan Footer"
"Show Footer": "Tampilkan Footer",
"Small": "Kecil",
"Large": "Besar",
"Auto": "Otomatis",
"Language": "Bahasa",
"No annotations to export": "Tidak ada anotasi yang diekspor",
"Author": "Penulis",
"Exported from Readest": "Diekspor dari Readest",
"Highlights & Annotations": "Sorotan & Anotasi",
"Note": "Catatan",
"Copied to clipboard": "Disalin ke papan klip",
"Export Annotations": "Ekspor Anotasi",
"Auto Import on File Open": "Impor Otomatis saat File Dibuka",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Tidak ada bab yang terdeteksi.",
"Failed to parse the EPUB file.": "Gagal mengurai file EPUB.",
"This book format is not supported.": "Format buku ini tidak didukung.",
"Unable to fetch the translation. Please log in first and try again.": "Tidak dapat mengambil terjemahan. Silakan masuk terlebih dahulu dan coba lagi.",
"Group": "Grup",
"Always on Top": "Selalu di Atas",
"No Timeout": "Tidak Ada Waktu Habis",
"{{value}} minute": "{{value}} menit",
"{{value}} minutes": "{{value}} menit",
"{{value}} hour": "{{value}} jam",
"{{value}} hours": "{{value}} jam",
"CJK Font": "Font CJK",
"Clear Search": "Hapus Pencarian",
"Header & Footer": "Header & Footer",
"Apply also in Scrolled Mode": "Terapkan juga di Mode Gulir",
"A new version of Readest is Available!": "Versi baru Readest Tersedia!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} tersedia (versi terinstal {{currentVersion}}).",
"Download and install now?": "Unduh dan instal sekarang?",
"Downloading {{downloaded}} of {{contentLength}}": "Mengunduh {{downloaded}} dari {{contentLength}}",
"Download finished": "Unduhan selesai",
"DOWNLOAD & INSTALL": "UNDUH & INSTAL",
"Changelog:": "Catatan Perubahan:",
"Software Update": "Pembaruan Perangkat Lunak",
"Title": "Judul",
"Date Read": "Tanggal Dibaca",
"Date Added": "Tanggal Ditambahkan",
"Format": "Format",
"Ascending": "Naik",
"Descending": "Turun",
"Sort by...": "Urutkan berdasarkan...",
"Added:": "Ditambahkan:",
"Format:": "Format:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Deskripsi:",
"No description available": "Tidak ada deskripsi yang tersedia",
"List": "Daftar",
"Grid": "Jala",
"(from 'As You Like It', Act II)": "(dari 'Seperti yang Anda Inginkan', Bab II)",
"Link Color": "Warna Tautan",
"Volume Keys for Page Flip": "Volume Keys untuk Pembalikan Halaman",
"Clicks for Page Flip": "Klik untuk Pembalikan Halaman",
"Swap Clicks Area": "Area Klik Tukar",
"Screen": "Screen",
"Orientation": "Orientasi",
"Portrait": "Potret",
"Landscape": "Lanskap",
"Open Last Book on Start": "Buka Buku Terakhir saat Memulai",
"Checking for updates...": "Memeriksa pembaruan...",
"Error checking for updates": "Kesalahan saat memeriksa pembaruan",
"Details": "Detail",
"File Size:": "Ukuran File:",
"Auto Detect": "Deteksi Otomatis",
"Next Section": "Bagian Selanjutnya",
"Previous Section": "Bagian Sebelumnya",
"Next Page": "Halaman Berikutnya",
"Previous Page": "Halaman Sebelumnya",
"Are you sure to delete {{count}} selected book(s)?_other": "Apakah Anda yakin ingin menghapus {{count}} buku yang dipilih?",
"Are you sure to delete the selected book?": "Apakah Anda yakin ingin menghapus buku yang dipilih?",
"Deselect": "Batal",
"Select All": "Pilih Semua",
"No translation available.": "Tidak ada terjemahan yang tersedia.",
"Translated by {{provider}}.": "Terjemahan oleh {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Translate",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "Inversi Gambar di Gelap",
"Help improve Readest": "Bantu tingkatkan Readest",
"Sharing anonymized statistics": "Berbagi statistik anonim",
"Interface Language": "Bahasa Antarmuka",
"Translation": "Terjemahan",
"Enable Translation": "Aktifkan Terjemahan",
"Translation Service": "Layanan Terjemahan",
"Translate To": "Terjemahkan Ke",
"Disable Translation": "Nonaktifkan Terjemahan",
"Scroll": "Scroll",
"Overlap Pixels": "Overlap Piksel",
"Click": "Klik",
"System Language": "Bahasa Sistem"
}
@@ -5,7 +5,6 @@
"Animation": "Animazione",
"Annotate": "Annota",
"Apply": "Applica",
"Are you sure to delete the selected books?": "Sei sicuro di voler eliminare i libri selezionati?",
"Auto Mode": "Modalità automatica",
"Behavior": "Comportamento",
"Book": "Libro",
@@ -27,7 +26,6 @@
"Delete": "Elimina",
"Delete Highlight": "Elimina evidenziazione",
"Dictionary": "Dizionario",
"Disable Click-to-Flip": "Disattiva click per voltare pagina",
"Download Readest": "Scarica Readest",
"Edit": "Modifica",
"Enter your custom CSS here...": "Inserisci qui il tuo CSS personalizzato...",
@@ -43,15 +41,12 @@
"Global Settings": "Impostazioni globali",
"Go Back": "Indietro",
"Go Forward": "Avanti",
"Go Left": "Vai a sinistra",
"Go Right": "Vai a destra",
"Grass": "Erba",
"Gray": "Grigio",
"Gruvbox": "Gruvbox",
"Highlight": "Evidenzia",
"Horizontal Direction": "Direzione orizzontale",
"Hyphenation": "Sillabazione",
"Identifier:": "Identificatore:",
"Import Books": "Importa libri",
"Language:": "Lingua:",
"Layout": "Layout",
@@ -64,7 +59,6 @@
"Match Whole Words": "Parole intere",
"Maximum Number of Columns": "Numero massimo di colonne",
"Minimum Font Size": "Dimensione minima font",
"Misc": "Varie",
"Monospace Font": "Font monospazio",
"More Info": "Maggiori informazioni",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Sconosciuto",
"Untitled": "Senza titolo",
"Updated:": "Aggiornato:",
"User avatar": "Avatar utente",
"Version {{version}}": "Versione {{version}}",
"Vertical Direction": "Direzione verticale",
"Welcome to your library. You can import your books here and read them anytime.": "Benvenuto nella tua biblioteca. Puoi importare i tuoi libri qui e leggerli in qualsiasi momento.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Applica a tutti i libri",
"Apply to This Book": "Applica a questo libro",
"Unable to fetch the translation. Try again later.": "Impossibile recuperare la traduzione. Riprova più tardi.",
"Update Now!": "Aggiorna ora!",
"Update": "Aggiorna",
"Check Update": "Controlla aggiornamento",
"Already the latest version": "Già l'ultima versione",
"Book Details": "Dettagli libro",
@@ -152,7 +143,6 @@
"Text Indent": "Rientro testo",
"Paragraph Margin": "Margine paragrafo",
"Override Book Layout": "Sovrascrivi layout libro",
"Add to Group": "Aggiungi al gruppo",
"Untitled Group": "Gruppo senza titolo",
"Group Books": "Raggruppa libri",
"Remove From Group": "Rimuovi dal",
@@ -247,5 +237,99 @@
"Border Color": "Colore bordo",
"Border Frame": "Cornice bordo",
"Show Header": "Mostra intestazione",
"Show Footer": "Mostra piè di pagina"
"Show Footer": "Mostra piè di pagina",
"Small": "Piccolo",
"Large": "Grande",
"Auto": "Automatico",
"Language": "Lingua",
"No annotations to export": "Nessuna annotazione da esportare",
"Author": "Autore",
"Exported from Readest": "Esportato da Readest",
"Highlights & Annotations": "Evidenziazioni e annotazioni",
"Note": "Nota",
"Copied to clipboard": "Copiato negli appunti",
"Export Annotations": "Esporta annotazioni",
"Auto Import on File Open": "Importazione automatica all'apertura del file",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Nessun capitolo rilevato.",
"Failed to parse the EPUB file.": "Impossibile analizzare il file EPUB.",
"This book format is not supported.": "Questo formato di libro non è supportato.",
"Unable to fetch the translation. Please log in first and try again.": "Impossibile recuperare la traduzione. Accedi prima e riprova.",
"Group": "Gruppo",
"Always on Top": "Sempre in primo piano",
"No Timeout": "Nessun timeout",
"{{value}} minute": "{{value}} minuto",
"{{value}} minutes": "{{value}} minuti",
"{{value}} hour": "{{value}} ora",
"{{value}} hours": "{{value}} ore",
"CJK Font": "Font CJK",
"Clear Search": "Cancella ricerca",
"Header & Footer": "Intestazione e piè di pagina",
"Apply also in Scrolled Mode": "Applica anche in modalità scorrimento",
"A new version of Readest is Available!": "È disponibile una nuova versione di Readest!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} è disponibile (versione installata {{currentVersion}}).",
"Download and install now?": "Scarica e installa ora?",
"Downloading {{downloaded}} of {{contentLength}}": "Scaricando {{downloaded}} di {{contentLength}}",
"Download finished": "Download completato",
"DOWNLOAD & INSTALL": "SCARICA E INSTALLA",
"Changelog:": "Note di rilascio:",
"Software Update": "Aggiornamento software",
"Title": "Titolo",
"Date Read": "Data di lettura",
"Date Added": "Data aggiunta",
"Format": "Formato",
"Ascending": "Crescente",
"Descending": "Decrescente",
"Sort by...": "Ordina per...",
"Added:": "Aggiunto:",
"Format:": "Formato:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Descrizione:",
"No description available": "Nessuna descrizione disponibile",
"List": "Elenco",
"Grid": "Griglia",
"(from 'As You Like It', Act II)": "(da 'Come vi piace', Atto II)",
"Link Color": "Colore link",
"Volume Keys for Page Flip": "Tasti volume per girare pagina",
"Clicks for Page Flip": "Clic per girare pagina",
"Swap Clicks Area": "Inverti area dei clic",
"Screen": "Schermo",
"Orientation": "Orientamento",
"Portrait": "Ritratto",
"Landscape": "Paesaggio",
"Open Last Book on Start": "Apri ultimo libro all'avvio",
"Checking for updates...": "Controllo aggiornamenti...",
"Error checking for updates": "Errore durante il controllo degli aggiornamenti",
"Details": "Dettagli",
"File Size:": "Dimensione file:",
"Auto Detect": "Rilevamento automatico",
"Next Section": "Sezione successiva",
"Previous Section": "Sezione precedente",
"Next Page": "Pagina successiva",
"Previous Page": "Pagina precedente",
"Are you sure to delete {{count}} selected book(s)?_one": "Sei sicuro di voler eliminare {{count}} libro selezionato?",
"Are you sure to delete {{count}} selected book(s)?_many": "Sei sicuro di voler eliminare {{count}} libri selezionati?",
"Are you sure to delete {{count}} selected book(s)?_other": "Sei sicuro di voler eliminare {{count}} libri selezionati?",
"Are you sure to delete the selected book?": "Sei sicuro di voler eliminare il libro selezionato?",
"Deselect": "Annulla",
"Select All": "Tutti",
"No translation available.": "Nessuna traduzione disponibile.",
"Translated by {{provider}}.": "Tradotto da {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Translate",
"Azure Translator": "Traduttore Azure",
"Invert Image In Dark Mode": "Inverti immagine in scuro",
"Help improve Readest": "Aiuta a migliorare Readest",
"Sharing anonymized statistics": "Condivisione di statistiche anonime",
"Interface Language": "Lingua dell'interfaccia",
"Translation": "Traduzione",
"Enable Translation": "Abilita traduzione",
"Translation Service": "Servizio di traduzione",
"Translate To": "Traduci in",
"Disable Translation": "Disabilita traduzione",
"Scroll": "Scorri",
"Overlap Pixels": "Pixel di sovrapposizione",
"Click": "Fai clic",
"System Language": "Lingua di sistema"
}
@@ -5,7 +5,6 @@
"Animation": "アニメーション",
"Annotate": "注釈",
"Apply": "適用",
"Are you sure to delete the selected books?": "選択した書籍を削除してもよろしいですか?",
"Auto Mode": "自動モード",
"Behavior": "動作",
"Book": "書籍",
@@ -27,7 +26,6 @@
"Delete": "削除",
"Delete Highlight": "ハイライトを削除",
"Dictionary": "辞書",
"Disable Click-to-Flip": "クリックめくりを無効化",
"Download Readest": "Readestをダウンロード",
"Edit": "編集",
"Enter your custom CSS here...": "ここにカスタムCSSを入力...",
@@ -43,15 +41,12 @@
"Global Settings": "全体設定",
"Go Back": "戻る",
"Go Forward": "進む",
"Go Left": "左へ",
"Go Right": "右へ",
"Grass": "グリーン",
"Gray": "グレー",
"Gruvbox": "グルーブボックス",
"Highlight": "ハイライト",
"Horizontal Direction": "横書",
"Hyphenation": "ハイフネーション",
"Identifier:": "識別子:",
"Import Books": "書籍をインポート",
"Language:": "言語:",
"Layout": "レイアウト",
@@ -64,7 +59,6 @@
"Match Whole Words": "単語全体を一致",
"Maximum Number of Columns": "最大列数",
"Minimum Font Size": "最小フォントサイズ",
"Misc": "その他",
"Monospace Font": "等幅フォント",
"More Info": "詳細情報",
"Nord": "ノルド",
@@ -112,7 +106,6 @@
"Unknown": "不明",
"Untitled": "無題",
"Updated:": "更新日:",
"User avatar": "ユーザーアバター",
"Version {{version}}": "バージョン {{version}}",
"Vertical Direction": "縦書",
"Welcome to your library. You can import your books here and read them anytime.": "ライブラリーへようこそ。ここに書籍をインポートして、いつでも読むことができます。",
@@ -126,8 +119,6 @@
"Apply to All Books": "すべての書籍に適用",
"Apply to This Book": "この書籍に適用",
"Unable to fetch the translation. Try again later.": "翻訳を取得できません。後で再試行してください。",
"Update Now!": "今すぐ更新!",
"Update": "更新",
"Check Update": "更新を確認",
"Already the latest version": "すでに最新バージョン",
"Book Details": "書籍の詳細",
@@ -152,7 +143,6 @@
"Text Indent": "テキストインデント",
"Paragraph Margin": "段落マージン",
"Override Book Layout": "書籍のレイアウトを上書き",
"Add to Group": "グループに追加",
"Untitled Group": "無題のグループ",
"Group Books": "書籍をグループ化",
"Remove From Group": "グループから削除",
@@ -247,5 +237,97 @@
"Border Color": "ボーダーカラー",
"Border Frame": "ボーダーフレーム",
"Show Header": "ヘッダーを表示",
"Show Footer": "フッターを表示"
"Show Footer": "フッターを表示",
"Small": "小",
"Large": "大",
"Auto": "自動",
"Language": "言語",
"No annotations to export": "エクスポートする注釈はありません",
"Author": "著者",
"Exported from Readest": "Readestからエクスポート",
"Highlights & Annotations": "ハイライトと注釈",
"Note": "メモ",
"Copied to clipboard": "クリップボードにコピー",
"Export Annotations": "注釈をエクスポート",
"Auto Import on File Open": "ファイルを開くと自動インポート",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "章が検出されません。",
"Failed to parse the EPUB file.": "EPUBファイルの解析に失敗しました。",
"This book format is not supported.": "この書籍形式はサポートされていません。",
"Unable to fetch the translation. Please log in first and try again.": "翻訳を取得できません。まずログインして再試行してください。",
"Group": "グループ",
"Always on Top": "常に最前面",
"No Timeout": "タイムアウトなし",
"{{value}} minute": "{{value}}分",
"{{value}} minutes": "{{value}}分",
"{{value}} hour": "{{value}}時間",
"{{value}} hours": "{{value}}時間",
"CJK Font": "CJKフォント",
"Clear Search": "検索をクリア",
"Header & Footer": "ヘッダーとフッター",
"Apply also in Scrolled Mode": "スクロールモードでも適用",
"A new version of Readest is Available!": "新しいバージョンのReadestが利用可能です!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}が利用可能です(インストールされたバージョン{{currentVersion}})。",
"Download and install now?": "今すぐダウンロードしてインストールしますか?",
"Downloading {{downloaded}} of {{contentLength}}": "ダウンロード中 {{downloaded}} / {{contentLength}}",
"Download finished": "ダウンロード完了",
"DOWNLOAD & INSTALL": "更新",
"Changelog:": "変更ログ:",
"Software Update": "ソフトウェアの更新",
"Title": "タイトル",
"Date Read": "読書日",
"Date Added": "追加日",
"Format": "形式",
"Ascending": "昇順",
"Descending": "降順",
"Sort by...": "並べ替え...",
"Added:": "追加日:",
"Format:": "形式:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "説明:",
"No description available": "説明はありません",
"List": "リスト",
"Grid": "グリッド",
"(from 'As You Like It', Act II)": "(「お気に召すまま」第2幕)",
"Link Color": "リンクカラー",
"Volume Keys for Page Flip": "音量キーでページめくり",
"Clicks for Page Flip": "タップでページめくり",
"Swap Clicks Area": "タップ領域を入れ替え",
"Screen": "画面",
"Orientation": "向き",
"Portrait": "縦",
"Landscape": "横",
"Open Last Book on Start": "開始時に最後の書籍を開く",
"Checking for updates...": "更新を確認中...",
"Error checking for updates": "更新の確認中にエラーが発生しました",
"Details": "詳細",
"File Size:": "ファイルサイズ:",
"Auto Detect": "自動検出",
"Next Section": "次のセクション",
"Previous Section": "前のセクション",
"Next Page": "次のページ",
"Previous Page": "前のページ",
"Are you sure to delete {{count}} selected book(s)?_other": "選択した{{count}}冊の本を削除してもよろしいですか?",
"Are you sure to delete the selected book?": "選択した本を削除してもよろしいですか?",
"Deselect": "解除",
"Select All": "全選択",
"No translation available.": "翻訳はありません。",
"Translated by {{provider}}.": "{{provider}}によって翻訳されました。",
"DeepL": "DeepL",
"Google Translate": "Google翻訳",
"Azure Translator": "Azure翻訳",
"Invert Image In Dark Mode": "ダークモードで画像を反転",
"Help improve Readest": "Readestを改善する手助け",
"Sharing anonymized statistics": "匿名化された統計情報を共有",
"Interface Language": "インターフェースの言語",
"Translation": "翻訳",
"Enable Translation": "翻訳を有効にする",
"Translation Service": "翻訳サービス",
"Translate To": "翻訳先",
"Disable Translation": "翻訳を無効にする",
"Scroll": "スクロール",
"Overlap Pixels": "重なりピクセル",
"Click": "クリック",
"System Language": "システム言語"
}
@@ -5,7 +5,6 @@
"Animation": "애니메이션",
"Annotate": "주석",
"Apply": "적용",
"Are you sure to delete the selected books?": "선택한 책을 삭제하시겠습니까?",
"Auto Mode": "자동 모드",
"Behavior": "동작",
"Book": "책",
@@ -27,7 +26,6 @@
"Delete": "삭제",
"Delete Highlight": "하이라이트 삭제",
"Dictionary": "사전",
"Disable Click-to-Flip": "클릭 넘기기 비활성화",
"Download Readest": "Readest 다운로드",
"Edit": "편집",
"Enter your custom CSS here...": "여기에 사용자 정의 CSS를 입력하세요...",
@@ -43,15 +41,12 @@
"Global Settings": "전역 설정",
"Go Back": "뒤로",
"Go Forward": "앞으로",
"Go Left": "왼쪽으로",
"Go Right": "오른쪽으로",
"Grass": "잔디색",
"Gray": "회색",
"Gruvbox": "그러브박스",
"Highlight": "하이라이트",
"Horizontal Direction": "수평 방향",
"Hyphenation": "하이픈 넣기",
"Identifier:": "식별자:",
"Import Books": "책 가져오기",
"Language:": "언어:",
"Layout": "레이아웃",
@@ -64,7 +59,6 @@
"Match Whole Words": "전체 단어 일치",
"Maximum Number of Columns": "최대 열 수",
"Minimum Font Size": "최소 글꼴 크기",
"Misc": "기타",
"Monospace Font": "고정폭 글꼴",
"More Info": "추가 정보",
"Nord": "노드",
@@ -112,7 +106,6 @@
"Unknown": "알 수 없음",
"Untitled": "제목 없음",
"Updated:": "업데이트일:",
"User avatar": "사용자 아바타",
"Version {{version}}": "버전 {{version}}",
"Vertical Direction": "수직 방향",
"Welcome to your library. You can import your books here and read them anytime.": "내 서재에 오신 것을 환영합니다. 여기에서 책을 가져와 언제든지 읽을 수 있습니다.",
@@ -126,8 +119,6 @@
"Apply to All Books": "모든 책에 적용",
"Apply to This Book": "이 책에 적용",
"Unable to fetch the translation. Try again later.": "번역을 가져올 수 없습니다. 나중에 다시 시도하세요.",
"Update Now!": "지금 업데이트!",
"Update": "업데이트",
"Check Update": "업데이트 확인",
"Already the latest version": "이미 최신 버전",
"Book Details": "책 세부 정보",
@@ -152,7 +143,6 @@
"Text Indent": "들여쓰기",
"Paragraph Margin": "단락 여백",
"Override Book Layout": "책 레이아웃 덮어쓰기",
"Add to Group": "그룹에 추가",
"Untitled Group": "제목 없는 그룹",
"Group Books": "책 그룹화",
"Remove From Group": "그룹에서 제거",
@@ -247,5 +237,97 @@
"Border Color": "테두리 색상",
"Border Frame": "테두리 프레임",
"Show Header": "헤더 표시",
"Show Footer": "푸터 표시"
"Show Footer": "푸터 표시",
"Small": "작음",
"Large": "큼",
"Auto": "자동",
"Language": "언어",
"No annotations to export": "내보낼 주석이 없습니다",
"Author": "저자",
"Exported from Readest": "Readest에서 내보냄",
"Highlights & Annotations": "하이라이트 및 주석",
"Note": "노트",
"Copied to clipboard": "클립보드에 복사",
"Export Annotations": "주석 내보내기",
"Auto Import on File Open": "파일 열 때 자동 가져오기",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "장 발견되지 않음.",
"Failed to parse the EPUB file.": "EPUB 파일 구문 분석 실패.",
"This book format is not supported.": "이 책 형식은 지원되지 않습니다.",
"Unable to fetch the translation. Please log in first and try again.": "번역을 가져올 수 없습니다. 먼저 로그인하고 다시 시도하세요.",
"Group": "그룹",
"Always on Top": "항상 위에",
"No Timeout": "타임아웃 없음",
"{{value}} minute": "{{value}} 분",
"{{value}} minutes": "{{value}} 분",
"{{value}} hour": "{{value}} 시간",
"{{value}} hours": "{{value}} 시간",
"CJK Font": "CJK 글꼴",
"Clear Search": "검색 지우기",
"Header & Footer": "헤더 및 푸터",
"Apply also in Scrolled Mode": "스크롤 모드에서도 적용",
"A new version of Readest is Available!": "Readest의 새 버전이 있습니다!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}가 사용 가능 (설치된 버전 {{currentVersion}}).",
"Download and install now?": "지금 다운로드하고 설치하시겠습니까?",
"Downloading {{downloaded}} of {{contentLength}}": "다운로드 중 {{downloaded}} / {{contentLength}}",
"Download finished": "다운로드 완료",
"DOWNLOAD & INSTALL": "다운로드 및 설치",
"Changelog:": "변경 로그:",
"Software Update": "소프트웨어 업데이트",
"Title": "제목",
"Date Read": "읽은 날짜",
"Date Added": "추가된 날짜",
"Format": "형식",
"Ascending": "오름차순",
"Descending": "내림차순",
"Sort by...": "정렬 기준...",
"Added:": "추가됨:",
"Format:": "형식:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "설명:",
"No description available": "설명 없음",
"List": "목록",
"Grid": "격자",
"(from 'As You Like It', Act II)": "(당신이 좋아하는 대로, 2막)",
"Link Color": "링크 색상",
"Volume Keys for Page Flip": "페이지 넘김을 위한 볼륨 키",
"Clicks for Page Flip": "페이지 넘김을 위한 클릭",
"Swap Clicks Area": "클릭 영역 교환",
"Screen": "화면",
"Orientation": "방향",
"Portrait": "세로",
"Landscape": "가로",
"Open Last Book on Start": "시작 시 마지막 책 열기",
"Checking for updates...": "업데이트 확인 중...",
"Error checking for updates": "업데이트 확인 중 오류 발생",
"Details": "세부정보",
"File Size:": "파일 크기:",
"Auto Detect": "자동 감지",
"Next Section": "다음 섹션",
"Previous Section": "이전 섹션",
"Next Page": "다음 페이지",
"Previous Page": "이전 페이지",
"Are you sure to delete {{count}} selected book(s)?_other": "선택한 {{count}}권의 책을 삭제하시겠습니까?",
"Are you sure to delete the selected book?": "선택한 책을 삭제하시겠습니까?",
"Deselect": "해제",
"Select All": "전체선택",
"No translation available.": "번역이 없습니다.",
"Translated by {{provider}}.": "{{provider}}에 의해 번역됨.",
"DeepL": "DeepL",
"Google Translate": "구글 번역",
"Azure Translator": "Azure 번역기",
"Invert Image In Dark Mode": "어두운 곳에서 이미지 반전",
"Help improve Readest": "Readest 개선에 도움을 주세요",
"Sharing anonymized statistics": "익명화된 통계 공유",
"Interface Language": "인터페이스 언어",
"Translation": "번역",
"Enable Translation": "번역 활성화",
"Translation Service": "번역 서비스",
"Translate To": "번역 대상 언어",
"Disable Translation": "번역 비활성화",
"Scroll": "스크롤",
"Overlap Pixels": "겹치는 픽셀",
"Click": "클릭",
"System Language": "시스템 언어"
}
@@ -0,0 +1,334 @@
{
"Email address": "E-mailadres",
"Your Password": "Uw wachtwoord",
"Your email address": "Uw e-mailadres",
"Your password": "Uw wachtwoord",
"Sign in": "Inloggen",
"Signing in...": "Bezig met inloggen...",
"Sign in with {{provider}}": "Inloggen met {{provider}}",
"Already have an account? Sign in": "Heeft u al een account? Log in",
"Create a Password": "Maak een wachtwoord aan",
"Sign up": "Registreren",
"Signing up...": "Bezig met registreren...",
"Check your email for the confirmation link": "Controleer uw e-mail voor de bevestigingslink",
"Signing in ...": "Bezig met inloggen ...",
"Send a magic link email": "Stuur een magic link e-mail",
"Check your email for the magic link": "Controleer uw e-mail voor de magic link",
"Send reset password instructions": "Stuur instructies voor wachtwoordherstel",
"Sending reset instructions ...": "Bezig met versturen van herstelinstructies ...",
"Forgot your password?": "Wachtwoord vergeten?",
"Check your email for the password reset link": "Controleer uw e-mail voor de link om uw wachtwoord te herstellen",
"Phone number": "Telefoonnummer",
"Your phone number": "Uw telefoonnummer",
"Token": "Token",
"Your OTP token": "Uw OTP-token",
"Verify token": "Token verifiëren",
"Sign in with Google": "Inloggen met Google",
"Sign in with Apple": "Inloggen met Apple",
"Sign in with GitHub": "Inloggen met GitHub",
"New Password": "Nieuw wachtwoord",
"Your new password": "Uw nieuwe wachtwoord",
"Update password": "Wachtwoord bijwerken",
"Updating password ...": "Wachtwoord bijwerken ...",
"Your password has been updated": "Uw wachtwoord is bijgewerkt",
"Open": "Openen",
"Group": "Groep",
"Details": "Details",
"Delete": "Verwijderen",
"Cancel": "Annuleren",
"Confirm Deletion": "Verwijdering bevestigen",
"Deselect Book": "Boek deselecteren",
"Select Book": "Boek selecteren",
"Show Book Details": "Boekdetails weergeven",
"Download Book": "Boek downloaden",
"Upload Book": "Boek uploaden",
"Deselect Group": "Groep deselecteren",
"Select Group": "Groep selecteren",
"Untitled Group": "Groep zonder titel",
"Group Books": "Boeken groeperen",
"Remove From Group": "Uit groep verwijderen",
"Create New Group": "Nieuwe groep maken",
"Save": "Opslaan",
"Confirm": "Bevestigen",
"From Local File": "Van lokaal bestand",
"Go Back": "Terug",
"Search Books...": "Boeken zoeken...",
"Clear Search": "Zoekopdracht wissen",
"Import Books": "Boeken importeren",
"Select Multiple Books": "Meerdere boeken selecteren",
"Select Books": "Boeken selecteren",
"Storage": "Opslag",
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% van cloudopslag gebruikt.",
"Logged in as {{userDisplayName}}": "Ingelogd als {{userDisplayName}}",
"Logged in": "Ingelogd",
"Account": "Account",
"Sign In": "Inloggen",
"Auto Upload Books to Cloud": "Boeken automatisch uploaden naar cloud",
"Auto Import on File Open": "Automatisch importeren bij bestand openen",
"Open Last Book on Start": "Open laatste boek bij opstarten",
"Check Updates on Start": "Controleer updates bij opstarten",
"Fullscreen": "Volledig scherm",
"Always on Top": "Altijd bovenaan",
"Keep Screen Awake": "Scherm wakker houden",
"Reload Page": "Pagina herladen",
"Download Readest": "Readest downloaden",
"About Readest": "Over Readest",
"List": "Lijst",
"Grid": "Raster",
"Title": "Titel",
"Author": "Auteur",
"Format": "Formaat",
"Date Read": "Datum gelezen",
"Date Added": "Datum toegevoegd",
"Ascending": "Oplopend",
"Descending": "Aflopend",
"Sort by...": "Sorteren op...",
"No supported files found. Supported formats: {{formats}}": "Geen ondersteunde bestanden gevonden. Ondersteunde formaten: {{formats}}",
"No chapters detected.": "Geen hoofdstukken gedetecteerd.",
"Failed to parse the EPUB file.": "EPUB-bestand kon niet worden verwerkt.",
"This book format is not supported.": "Dit boekformaat wordt niet ondersteund.",
"Failed to import book(s): {{filenames}}": "Importeren van boek(en) mislukt: {{filenames}}",
"Book uploaded: {{title}}": "Boek geüpload: {{title}}",
"Insufficient storage quota": "Onvoldoende opslagruimte",
"Failed to upload book: {{title}}": "Uploaden van boek mislukt: {{title}}",
"Book downloaded: {{title}}": "Boek gedownload: {{title}}",
"Failed to download book: {{title}}": "Downloaden van boek mislukt: {{title}}",
"Book deleted: {{title}}": "Boek verwijderd: {{title}}",
"Failed to delete book: {{title}}": "Verwijderen van boek mislukt: {{title}}",
"Your Library": "Uw bibliotheek",
"Welcome to your library. You can import your books here and read them anytime.": "Welkom bij uw bibliotheek. U kunt hier uw boeken importeren en ze op elk moment lezen.",
"Copied to notebook": "Gekopieerd naar notitieblok",
"No annotations to export": "Geen annotaties om te exporteren",
"Exported from Readest": "Geëxporteerd uit Readest",
"Highlights & Annotations": "Markeringen & annotaties",
"Untitled": "Zonder titel",
"Note": "Notitie",
"Copied to clipboard": "Gekopieerd naar klembord",
"Copy": "Kopiëren",
"Delete Highlight": "Markering verwijderen",
"Highlight": "Markeren",
"Annotate": "Annoteren",
"Search": "Zoeken",
"Dictionary": "Woordenboek",
"Wikipedia": "Wikipedia",
"Translate": "Vertalen",
"Speak": "Voorlezen",
"Unable to fetch the translation. Please log in first and try again.": "Kan de vertaling niet ophalen. Log eerst in en probeer het opnieuw.",
"Unable to fetch the translation. Try again later.": "Kan de vertaling niet ophalen. Probeer het later opnieuw.",
"Original Text": "Originele tekst",
"Auto Detect": "Automatisch detecteren",
"(detected)": "(gedetecteerd)",
"Translated Text": "Vertaalde tekst",
"Loading...": "Laden...",
"Bookmark": "Bladwijzer",
"Go Forward": "Vooruit",
"Small": "Klein",
"Large": "Groot",
"Notebook": "Notitieblok",
"Excerpts": "Fragmenten",
"Notes": "Notities",
"Add your notes here...": "Voeg hier uw notities toe...",
"Theme Mode": "Thema modus",
"Auto Mode": "Automatische modus",
"Light Mode": "Lichte modus",
"Dark Mode": "Donkere modus",
"Theme Color": "Themakleur",
"Custom": "Aangepast",
"Apply to All Books": "Toepassen op alle boeken",
"Apply to This Book": "Toepassen op dit boek",
"Global Settings": "Algemene instellingen",
"System Fonts": "Systeemlettertypen",
"Serif Font": "Serif lettertype",
"Sans-Serif Font": "Sans-serif lettertype",
"Font Size": "Lettergrootte",
"Default Font Size": "Standaard lettergrootte",
"Minimum Font Size": "Minimale lettergrootte",
"Font Weight": "Letterdikte",
"Font Family": "Lettertypefamilie",
"Default Font": "Standaard lettertype",
"CJK Font": "CJK lettertype",
"Override Book Font": "Boek lettertype overschrijven",
"Font Face": "Lettertype",
"Monospace Font": "Monospace lettertype",
"Scrolled Mode": "Scroll-modus",
"Writing Mode": "Schrijfmodus",
"Default": "Standaard",
"Horizontal Direction": "Horizontale richting",
"Vertical Direction": "Verticale richting",
"RTL Direction": "RTL-richting",
"Border Frame": "Randkader",
"Double Border": "Dubbele rand",
"Border Color": "Randkleur",
"Paragraph": "Alinea",
"Paragraph Margin": "Alinea marge",
"Line Spacing": "Regelafstand",
"Word Spacing": "Woordafstand",
"Letter Spacing": "Letterafstand",
"Text Indent": "Tekstinspringing",
"Full Justification": "Volledig uitvullen",
"Hyphenation": "Woordafbreking",
"Override Book Layout": "Boekopmaak overschrijven",
"Page": "Pagina",
"Vertical Margins (px)": "Verticale marges (px)",
"Horizontal Margins (%)": "Horizontale marges (%)",
"Maximum Number of Columns": "Maximum aantal kolommen",
"Maximum Column Height": "Maximale kolomhoogte",
"Maximum Column Width": "Maximale kolombreedte",
"Header & Footer": "Koptekst & voettekst",
"Show Header": "Koptekst weergeven",
"Show Footer": "Voettekst weergeven",
"Apply also in Scrolled Mode": "Ook toepassen in scroll-modus",
"Auto": "Automatisch",
"Language": "Taal",
"Animation": "Animatie",
"Paging Animation": "Paginering animatie",
"Screen": "Scherm",
"Orientation": "Oriëntatie",
"Portrait": "Portret",
"Landscape": "Landschap",
"Behavior": "Gedrag",
"Continuous Scroll": "Doorlopend scrollen",
"Volume Keys for Page Flip": "Volumetoetsen voor pagina omslaan",
"Clicks for Page Flip": "Klikken voor pagina omslaan",
"Swap Clicks Area": "Klikgebieden omwisselen",
"Custom CSS": "Aangepaste CSS",
"Enter your custom CSS here...": "Voer hier uw aangepaste CSS in...",
"Apply": "Toepassen",
"Font": "Lettertype",
"Layout": "Opmaak",
"Color": "Kleur",
"(from 'As You Like It', Act II)": "(uit 'As You Like It', Akte II)",
"Custom Theme": "Aangepast thema",
"Theme Name": "Themanaam",
"Text Color": "Tekstkleur",
"Background Color": "Achtergrondkleur",
"Link Color": "Linkkleur",
"Preview": "Voorbeeld",
"Font & Layout": "Lettertype & opmaak",
"Book Cover": "Boekomslag",
"More Info": "Meer informatie",
"Parallel Read": "Parallel lezen",
"Export Annotations": "Annotaties exporteren",
"Edit": "Bewerken",
"Search...": "Zoeken...",
"Book": "Boek",
"Chapter": "Hoofdstuk",
"Match Case": "Hoofdlettergevoelig",
"Match Whole Words": "Hele woorden",
"Match Diacritics": "Diakritische tekens",
"TOC": "Inhoudsopgave",
"Sidebar": "Zijbalk",
"TTS not supported for PDF": "TTS niet ondersteund voor PDF",
"TTS not supported in this device": "TTS niet ondersteund op dit apparaat",
"No Timeout": "Geen time-out",
"{{value}} minute": "{{value}} minuut",
"{{value}} minutes": "{{value}} minuten",
"{{value}} hour": "{{value}} uur",
"{{value}} hours": "{{value}} uren",
"Slow": "Langzaam",
"Fast": "Snel",
"Reading Progress Synced": "Leesvoortgang gesynchroniseerd",
"Cloud Storage": "Cloudopslag",
"Failed to delete user. Please try again later.": "Gebruiker verwijderen mislukt. Probeer het later opnieuw.",
"Free Tier": "Gratis niveau",
"Community Support": "Community-ondersteuning",
"DeepL Free Access": "DeepL gratis toegang",
"Essential Text-to-Speech Voices": "Essentiële tekst-naar-spraak stemmen",
"Unlimited Offline/Online Reading": "Onbeperkt offline/online lezen",
"Unlimited Cloud Sync Devices": "Onbeperkt aantal cloud-synchronisatie apparaten",
"500 MB Cloud Sync Space": "500 MB cloud-synchronisatieruimte",
"Plus Tier": "Plus niveau",
"Includes All Free Tier Benefits": "Inclusief alle voordelen van het gratis niveau",
"Priority Support": "Prioriteitsondersteuning",
"AI Summaries": "AI-samenvattingen",
"AI Translations": "AI-vertalingen",
"DeepL Pro Access": "DeepL Pro toegang",
"Expanded Text-to-Speech Voices": "Uitgebreide tekst-naar-spraak stemmen",
"2000 MB Cloud Sync Space": "2000 MB cloud-synchronisatieruimte",
"Pro Tier": "Pro niveau",
"Includes All Plus Tier Benefits": "Inclusief alle voordelen van het Plus niveau",
"Unlimited AI Summaries": "Onbeperkte AI-samenvattingen",
"Unlimited AI Translations": "Onbeperkte AI-vertalingen",
"Unlimited DeepL Pro Access": "Onbeperkte DeepL Pro toegang",
"Advanced AI Tools": "Geavanceerde AI-tools",
"Early Feature Access": "Vroege toegang tot functies",
"10 GB Cloud Sync Space": "10 GB cloud-synchronisatieruimte",
"Loading profile...": "Profiel laden...",
"Features": "Functies",
"Sign Out": "Uitloggen",
"Delete Account": "Account verwijderen",
"Delete Your Account?": "Uw account verwijderen?",
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Deze actie kan niet ongedaan worden gemaakt. Al uw gegevens in de cloud worden permanent verwijderd.",
"Delete Permanently": "Permanent verwijderen",
"Version {{version}}": "Versie {{version}}",
"Check Update": "Update controleren",
"Already the latest version": "Reeds de nieuwste versie",
"Checking for updates...": "Controleren op updates...",
"Error checking for updates": "Fout bij controleren op updates",
"Book Details": "Boekdetails",
"Unknown": "Onbekend",
"Publisher:": "Uitgever:",
"Published:": "Gepubliceerd:",
"Updated:": "Bijgewerkt:",
"Added:": "Toegevoegd:",
"Language:": "Taal:",
"Subjects:": "Onderwerpen:",
"Format:": "Formaat:",
"File Size:": "Bestandsgrootte:",
"Description:": "Beschrijving:",
"No description available": "Geen beschrijving beschikbaar",
"Drop to Import Books": "Sleep om boeken te importeren",
"A new version of Readest is Available!": "Een nieuwe versie van Readest is beschikbaar!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} is beschikbaar (geïnstalleerde versie {{currentVersion}}).",
"Download and install now?": "Nu downloaden en installeren?",
"Downloading {{downloaded}} of {{contentLength}}": "Downloaden {{downloaded}} van {{contentLength}}",
"Download finished": "Download voltooid",
"DOWNLOAD & INSTALL": "DOWNLOADEN & INSTALLEREN",
"Changelog:": "Wijzigingslogboek:",
"Software Update": "Software-update",
"LXGW WenKai GB Screen": "LXGW WenKai GB Scherm",
"LXGW WenKai TC": "LXGW WenKai TC",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Gray": "Grijs",
"Sepia": "Sepia",
"Grass": "Gras",
"Cherry": "Kers",
"Sky": "Hemel",
"Solarized": "Gesolariseerd",
"Gruvbox": "Gruvbox",
"Nord": "Noord",
"Contrast": "Contrast",
"Sunset": "Zonsondergang",
"Reveal in Finder": "Tonen in Finder",
"Reveal in File Explorer": "Tonen in Verkenner",
"Reveal in Folder": "Tonen in map",
"Dont have an account? Sign up": "Nog geen account? Registreer u",
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "De hele wereld is een toneel,\nEn alle mannen en vrouwen slechts spelers;\nZij hebben hun uitgangen en hun entrees,\nEn één man speelt in zijn tijd vele rollen,\nZijn daden zijnde zeven leeftijden.\n\n— William Shakespeare",
"Next Section": "Volgende sectie",
"Previous Section": "Vorige sectie",
"Next Page": "Volgende pagina",
"Previous Page": "Vorige pagina",
"Are you sure to delete {{count}} selected book(s)?_one": "Weet je zeker dat je {{count}} geselecteerd boek wilt verwijderen?",
"Are you sure to delete {{count}} selected book(s)?_other": "Weet je zeker dat je {{count}} geselecteerde boeken wilt verwijderen?",
"Are you sure to delete the selected book?": "Weet je zeker dat je het geselecteerde boek wilt verwijderen?",
"Deselect": "Deselecteer",
"Select All": "Alles",
"No translation available.": "Geen vertaling beschikbaar.",
"Translated by {{provider}}.": "Vertaald door {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Vertalen",
"Azure Translator": "Azure Vertaler",
"Invert Image In Dark Mode": "Inverteer afbeelding in het donker",
"Help improve Readest": "Help Readest verbeteren",
"Sharing anonymized statistics": "Anonieme statistieken delen",
"Interface Language": "Interface-taal",
"Translation": "Vertaling",
"Enable Translation": "Vertaling inschakelen",
"Translation Service": "Vertaalservice",
"Translate To": "Vertalen naar",
"Disable Translation": "Vertaling uitschakelen",
"Scroll": "Scrollen",
"Overlap Pixels": "Overlap pixels",
"Click": "Klik",
"System Language": "Systemtaal"
}
@@ -5,7 +5,6 @@
"Animation": "Animacja",
"Annotate": "Adnotacja",
"Apply": "Zastosuj",
"Are you sure to delete the selected books?": "Czy na pewno chcesz usunąć wybrane książki?",
"Auto Mode": "Tryb automatyczny",
"Behavior": "Zachowanie",
"Book": "Książka",
@@ -27,7 +26,6 @@
"Delete": "Usuń",
"Delete Highlight": "Usuń zaznaczenie",
"Dictionary": "Słownik",
"Disable Click-to-Flip": "Wyłącz przewracanie kliknięciem",
"Download Readest": "Pobierz Readest",
"Edit": "Edytuj",
"Enter your custom CSS here...": "Wprowadź własny CSS tutaj...",
@@ -43,15 +41,12 @@
"Global Settings": "Ustawienia globalne",
"Go Back": "Wstecz",
"Go Forward": "Dalej",
"Go Left": "W lewo",
"Go Right": "W prawo",
"Grass": "Trawiasty",
"Gray": "Szary",
"Gruvbox": "Ciepły pomarańczowy",
"Highlight": "Zaznacz",
"Horizontal Direction": "Kierunek poziomy",
"Hyphenation": "Dzielenie wyrazów",
"Identifier:": "Identyfikator:",
"Import Books": "Importuj książki",
"Language:": "Język:",
"Layout": "Układ",
@@ -64,7 +59,6 @@
"Match Whole Words": "Uwzględnij całe słowa",
"Maximum Number of Columns": "Maksymalna liczba kolumn",
"Minimum Font Size": "Minimalny rozmiar czcionki",
"Misc": "Różne",
"Monospace Font": "Czcionka o stałej szerokości",
"More Info": "Więcej informacji",
"Nord": "Północny",
@@ -112,7 +106,6 @@
"Unknown": "Nieznane",
"Untitled": "Bez tytułu",
"Updated:": "Zaktualizowano:",
"User avatar": "Awatar użytkownika",
"Version {{version}}": "Wersja {{version}}",
"Vertical Direction": "Kierunek pionowy",
"Welcome to your library. You can import your books here and read them anytime.": "Witaj w swojej bibliotece. Możesz tutaj zaimportować swoje książki i czytać je w dowolnym momencie.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Zastosuj do wszystkich książek",
"Apply to This Book": "Zastosuj do tej książki",
"Unable to fetch the translation. Try again later.": "Nie udało się pobrać tłumaczenia. Spróbuj ponownie później.",
"Update Now!": "Zaktualizuj teraz!",
"Update": "Zaktualizuj",
"Check Update": "Sprawdź aktualizację",
"Already the latest version": "Już najnowsza wersja",
"Book Details": "Szczegóły książki",
@@ -152,7 +143,6 @@
"Text Indent": "Wcięcie tekstu",
"Paragraph Margin": "Margines akapitu",
"Override Book Layout": "Nadpisz układ książki",
"Add to Group": "Dodaj do grupy",
"Untitled Group": "Grupa bez tytułu",
"Group Books": "Grupuj książki",
"Remove From Group": "Usuń z grupy",
@@ -247,5 +237,100 @@
"Border Color": "Kolor ramki",
"Border Frame": "Ramka",
"Show Header": "Pokaż nagłówek",
"Show Footer": "Pokaż stopkę"
"Show Footer": "Pokaż stopkę",
"Small": "Mały",
"Large": "Duży",
"Auto": "Automatyczny",
"Language": "Język",
"No annotations to export": "Brak adnotacji do eksportu",
"Author": "Autor",
"Exported from Readest": "Eksportowane z Readest",
"Highlights & Annotations": "Zaznaczenia i adnotacje",
"Note": "Notatka",
"Copied to clipboard": "Skopiowano do schowka",
"Export Annotations": "Eksportuj adnotacje",
"Auto Import on File Open": "Automatyczne importowanie przy otwieraniu pliku",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Nie wykryto rozdziałów.",
"Failed to parse the EPUB file.": "Nie udało się przeanalizować pliku EPUB.",
"This book format is not supported.": "Ten format książki nie jest obsługiwany.",
"Unable to fetch the translation. Please log in first and try again.": "Nie można pobrać tłumaczenia. Proszę najpierw się zalogować i spróbować ponownie.",
"Group": "Grupa",
"Always on Top": "Zawsze na wierzchu",
"No Timeout": "Brak limitu czasu",
"{{value}} minute": "{{value}} minuta",
"{{value}} minutes": "{{value}} minut",
"{{value}} hour": "{{value}} godzina",
"{{value}} hours": "{{value}} godziny",
"CJK Font": "Czcionka CJK",
"Clear Search": "Wyczyść wyszukiwanie",
"Header & Footer": "Nagłówek i stopka",
"Apply also in Scrolled Mode": "Zastosuj również w trybie przewijania",
"A new version of Readest is Available!": "Nowa wersja Readest jest dostępna!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} jest dostępny (zainstalowana wersja {{currentVersion}}).",
"Download and install now?": "Pobierz i zainstaluj teraz?",
"Downloading {{downloaded}} of {{contentLength}}": "Pobieranie {{downloaded}} z {{contentLength}}",
"Download finished": "Pobieranie zakończone",
"DOWNLOAD & INSTALL": "POBIERZ I ZAINSTALUJ",
"Changelog:": "Dziennik zmian:",
"Software Update": "Aktualizacja oprogramowania",
"Title": "Tytuł",
"Date Read": "Data przeczytania",
"Date Added": "Data dodania",
"Format": "Format",
"Ascending": "Rosnąco",
"Descending": "Malejąco",
"Sort by...": "Sortuj według...",
"Added:": "Data dodania:",
"Format:": "Format:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Opis:",
"No description available": "Brak opisu",
"List": "Lista",
"Grid": "Siatka",
"(from 'As You Like It', Act II)": "(z „Jak wam się podoba”, Akt II)",
"Link Color": "Kolor linku",
"Volume Keys for Page Flip": "Klucze głośności do przewracania stron",
"Clicks for Page Flip": "Kliknięcia do przewracania stron",
"Swap Clicks Area": "Zamień obszar kliknięcia",
"Screen": "Ekran",
"Orientation": "Orientacja",
"Portrait": "Portret",
"Landscape": "Krajobraz",
"Open Last Book on Start": "Otwórz ostatnią książkę przy starcie",
"Checking for updates...": "Sprawdzanie aktualizacji...",
"Error checking for updates": "Błąd podczas sprawdzania aktualizacji",
"Details": "Szczegóły",
"File Size:": "Rozmiar pliku:",
"Auto Detect": "Automatyczne wykrywanie",
"Next Section": "Następna sekcja",
"Previous Section": "Poprzednia sekcja",
"Next Page": "Następna strona",
"Previous Page": "Poprzednia strona",
"Are you sure to delete {{count}} selected book(s)?_one": "Czy na pewno chcesz usunąć {{count}} wybraną książkę?",
"Are you sure to delete {{count}} selected book(s)?_few": "Czy na pewno chcesz usunąć {{count}} wybrane książki?",
"Are you sure to delete {{count}} selected book(s)?_many": "Czy na pewno chcesz usunąć {{count}} wybranych książek?",
"Are you sure to delete {{count}} selected book(s)?_other": "Czy na pewno chcesz usunąć {{count}} wybranych książek?",
"Are you sure to delete the selected book?": "Czy na pewno chcesz usunąć wybraną książkę?",
"Deselect": "Odznacz",
"Select All": "Zaznacz",
"No translation available.": "Brak dostępnego tłumaczenia.",
"Translated by {{provider}}.": "Przetłumaczone przez {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Tłumacz Google",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "Odwróć obraz w ciemności",
"Help improve Readest": "Pomóż ulepszyć Readest",
"Sharing anonymized statistics": "Udostępnianie zanonimizowanych statystyk",
"Interface Language": "Język interfejsu",
"Translation": "Tłumaczenie",
"Enable Translation": "Włącz tłumaczenie",
"Translation Service": "Usługa tłumaczenia",
"Translate To": "Przetłumacz na",
"Disable Translation": "Wyłącz tłumaczenie",
"Scroll": "Przewijaj",
"Overlap Pixels": "Przesunięcie pikseli",
"Click": "Kliknij",
"System Language": "Język systemu"
}
@@ -5,7 +5,6 @@
"Animation": "Animação",
"Annotate": "Anotar",
"Apply": "Aplicar",
"Are you sure to delete the selected books?": "Tem certeza de que deseja excluir os livros selecionados?",
"Auto Mode": "Modo Automático",
"Behavior": "Comportamento",
"Book": "Livro",
@@ -27,7 +26,6 @@
"Delete": "Excluir",
"Delete Highlight": "Excluir Destaque",
"Dictionary": "Dicionário",
"Disable Click-to-Flip": "Desativar Clique para Virar",
"Download Readest": "Baixar Readest",
"Edit": "Editar",
"Enter your custom CSS here...": "Insira seu CSS personalizado aqui...",
@@ -43,15 +41,12 @@
"Global Settings": "Configurações Globais",
"Go Back": "Voltar",
"Go Forward": "Avançar",
"Go Left": "Ir para Esquerda",
"Go Right": "Ir para Direita",
"Grass": "Grama",
"Gray": "Cinza",
"Gruvbox": "Gruvbox",
"Highlight": "Destaque",
"Horizontal Direction": "Direção Horizontal",
"Hyphenation": "Hifenização",
"Identifier:": "Identificador:",
"Import Books": "Importar Livros",
"Language:": "Idioma:",
"Layout": "Layout",
@@ -64,7 +59,6 @@
"Match Whole Words": "Corresponder Palavras Inteiras",
"Maximum Number of Columns": "Número Máximo de Colunas",
"Minimum Font Size": "Tamanho Mínimo da Fonte",
"Misc": "Diversos",
"Monospace Font": "Fonte Monoespaçada",
"More Info": "Mais Informações",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Desconhecido",
"Untitled": "Sem Título",
"Updated:": "Atualizado:",
"User avatar": "Avatar do usuário",
"Version {{version}}": "Versão {{version}}",
"Vertical Direction": "Direção Vertical",
"Welcome to your library. You can import your books here and read them anytime.": "Bem-vindo à sua biblioteca. Você pode importar seus livros aqui e lê-los a qualquer momento.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Aplicar a todos os livros",
"Apply to This Book": "Aplicar a este livro",
"Unable to fetch the translation. Try again later.": "Não foi possível buscar a tradução. Tente novamente mais tarde.",
"Update Now!": "Atualizar Agora!",
"Update": "Atualizar",
"Check Update": "Verificar atualização",
"Already the latest version": "Já é a versão mais recente",
"Book Details": "Detalhes do Livro",
@@ -152,7 +143,6 @@
"Text Indent": "Recuo de Texto",
"Paragraph Margin": "Margem de Parágrafo",
"Override Book Layout": "Sobrescrever Layout do Livro",
"Add to Group": "Adicionar ao Grupo",
"Untitled Group": "Grupo Sem Título",
"Group Books": "Agrupar Livros",
"Remove From Group": "Remover do Grupo",
@@ -247,5 +237,99 @@
"Border Color": "Cor da Borda",
"Border Frame": "Moldura da Borda",
"Show Header": "Mostrar Cabeçalho",
"Show Footer": "Mostrar Rodapé"
"Show Footer": "Mostrar Rodapé",
"Small": "Pequeno",
"Large": "Grande",
"Auto": "Automático",
"Language": "Idioma",
"No annotations to export": "Nenhuma anotação para exportar",
"Author": "Autor",
"Exported from Readest": "Exportado do Readest",
"Highlights & Annotations": "Destaques e Anotações",
"Note": "Nota",
"Copied to clipboard": "Copiado para a área de transferência",
"Export Annotations": "Exportar Anotações",
"Auto Import on File Open": "Importação Automática ao Abrir Arquivo",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Nenhum capítulo detectado.",
"Failed to parse the EPUB file.": "Falha ao analisar o arquivo EPUB.",
"This book format is not supported.": "Este formato de livro não é suportado.",
"Unable to fetch the translation. Please log in first and try again.": "Não foi possível buscar a tradução. Faça login primeiro e tente novamente.",
"Group": "Grupo",
"Always on Top": "Sempre no Topo",
"No Timeout": "Sem Tempo Limite",
"{{value}} minute": "{{value}} minuto",
"{{value}} minutes": "{{value}} minutos",
"{{value}} hour": "{{value}} hora",
"{{value}} hours": "{{value}} horas",
"CJK Font": "Fonte CJK",
"Clear Search": "Limpar Pesquisa",
"Header & Footer": "Cabeçalho e Rodapé",
"Apply also in Scrolled Mode": "Aplicar também no Modo Rolado",
"A new version of Readest is Available!": "Uma nova versão do Readest está disponível!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponível (versão instalada {{currentVersion}}).",
"Download and install now?": "Baixar e instalar agora?",
"Downloading {{downloaded}} of {{contentLength}}": "Baixando {{downloaded}} de {{contentLength}}",
"Download finished": "Download concluído",
"DOWNLOAD & INSTALL": "BAIXAR E INSTALAR",
"Changelog:": "Notas de versão:",
"Software Update": "Atualização de Software",
"Title": "Título",
"Date Read": "Data de leitura",
"Date Added": "Data de adição",
"Format": "Formato",
"Ascending": "Crescente",
"Descending": "Decrescente",
"Sort by...": "Ordenar por...",
"Added:": "Adicionado:",
"Format:": "Formato:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Descrição:",
"No description available": "Nenhuma descrição disponível",
"List": "Lista",
"Grid": "Grade",
"(from 'As You Like It', Act II)": "(de 'Como Gostais', Ato II)",
"Link Color": "Cor do Link",
"Volume Keys for Page Flip": "Teclas de Volume para Virar Página",
"Clicks for Page Flip": "Cliques para Virar Página",
"Swap Clicks Area": "Trocar Área de Cliques",
"Screen": "Tela",
"Orientation": "Orientação",
"Portrait": "Retrato",
"Landscape": "Paisagem",
"Open Last Book on Start": "Abrir o Último Livro ao Iniciar",
"Checking for updates...": "Verificando atualizações...",
"Error checking for updates": "Erro ao verificar atualizações",
"Details": "Detalhes",
"File Size:": "Tamanho do arquivo:",
"Auto Detect": "Detectar Automaticamente",
"Next Section": "Próxima seção",
"Previous Section": "Seção anterior",
"Next Page": "Próxima página",
"Previous Page": "Página anterior",
"Are you sure to delete {{count}} selected book(s)?_one": "Tem certeza que deseja excluir {{count}} livro selecionado?",
"Are you sure to delete {{count}} selected book(s)?_many": "Tem certeza que deseja excluir {{count}} livros selecionados?",
"Are you sure to delete {{count}} selected book(s)?_other": "Tem certeza que deseja excluir {{count}} livros selecionados?",
"Are you sure to delete the selected book?": "Tem certeza que deseja excluir o livro selecionado?",
"Deselect": "Desmarcar",
"Select All": "Marcar tudo",
"No translation available.": "Sem tradução disponível.",
"Translated by {{provider}}.": "Traduzido por {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Tradutor",
"Azure Translator": "Tradutor Azure",
"Invert Image In Dark Mode": "Inverter Imagem no Escuro",
"Help improve Readest": "Ajude a melhorar o Readest",
"Sharing anonymized statistics": "Compartilhando estatísticas anonimizadas",
"Interface Language": "Idioma da interface",
"Translation": "Tradução",
"Enable Translation": "Ativar tradução",
"Translation Service": "Serviço de tradução",
"Translate To": "Traduzir para",
"Disable Translation": "Desativar tradução",
"Scroll": "Rolar",
"Overlap Pixels": "Sobreposição de Pixels",
"Click": "Clique",
"System Language": "Idioma do Sistema"
}
@@ -5,7 +5,6 @@
"Animation": "Анимация",
"Annotate": "Аннотация",
"Apply": "Применить",
"Are you sure to delete the selected books?": "Вы уверены, что хотите удалить выбранные книги?",
"Auto Mode": "Автоматический режим",
"Behavior": "Поведение",
"Book": "Книга",
@@ -27,7 +26,6 @@
"Delete": "Удалить",
"Delete Highlight": "Удалить выделение",
"Dictionary": "Словарь",
"Disable Click-to-Flip": "Отключить переворот по клику",
"Download Readest": "Скачать Readest",
"Edit": "Редактировать",
"Enter your custom CSS here...": "Введите ваш пользовательский CSS здесь...",
@@ -43,15 +41,12 @@
"Global Settings": "Общие настройки",
"Go Back": "Назад",
"Go Forward": "Вперёд",
"Go Left": "Влево",
"Go Right": "Вправо",
"Grass": "Трава",
"Gray": "Серый",
"Gruvbox": "Gruvbox",
"Highlight": "Выделить",
"Horizontal Direction": "Горизонтальное направление",
"Hyphenation": "Перенос слов",
"Identifier:": "Идентификатор:",
"Import Books": "Импорт книг",
"Language:": "Язык:",
"Layout": "Макет",
@@ -64,7 +59,6 @@
"Match Whole Words": "Только целые слова",
"Maximum Number of Columns": "Максимальное количество колонок",
"Minimum Font Size": "Минимальный размер шрифта",
"Misc": "Разное",
"Monospace Font": "Моноширинный шрифт",
"More Info": "Подробнее",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Неизвестно",
"Untitled": "Без названия",
"Updated:": "Обновлено:",
"User avatar": "Аватар пользователя",
"Version {{version}}": "Версия {{version}}",
"Vertical Direction": "Вертикальное направление",
"Welcome to your library. You can import your books here and read them anytime.": "Добро пожаловать в вашу библиотеку. Вы можете импортировать сюда книги и читать их в любое время.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Применить ко всем книгам",
"Apply to This Book": "Применить к этой книге",
"Unable to fetch the translation. Try again later.": "Не удалось получить перевод. Попробуйте позже.",
"Update Now!": "Обновить сейчас!",
"Update": "Обновить",
"Check Update": "Проверить обновление",
"Already the latest version": "Уже последняя версия",
"Book Details": "Детали книги",
@@ -152,7 +143,6 @@
"Text Indent": "Отступ текста",
"Paragraph Margin": "Отступ абзаца",
"Override Book Layout": "Переопределить макет книги",
"Add to Group": "Добавить в группу",
"Untitled Group": "Группа без названия",
"Group Books": "Группировать книги",
"Remove From Group": "Удалить из группы",
@@ -247,5 +237,100 @@
"Border Color": "Цвет границы",
"Border Frame": "Граница рамки",
"Show Header": "Показать заголовок",
"Show Footer": "Показать нижний колонтитул"
"Show Footer": "Показать нижний колонтитул",
"Small": "Маленький",
"Large": "Большой",
"Auto": "Авто",
"Language": "Язык",
"No annotations to export": "Нет аннотаций для экспорта",
"Author": "Автор",
"Exported from Readest": "Экспортировано из Readest",
"Highlights & Annotations": "Выделения и аннотации",
"Note": "Заметка",
"Copied to clipboard": "Скопировано в буфер обмена",
"Export Annotations": "Экспортировать аннотации",
"Auto Import on File Open": "Автоматический импорт при открытии файла",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Не обнаружено глав.",
"Failed to parse the EPUB file.": "Не удалось разобрать файл EPUB.",
"This book format is not supported.": "Этот формат книги не поддерживается.",
"Unable to fetch the translation. Please log in first and try again.": "Не удалось получить перевод. Пожалуйста, войдите в систему и попробуйте снова.",
"Group": "Группа",
"Always on Top": "Всегда сверху",
"No Timeout": "Нет таймаута",
"{{value}} minute": "{{value}} минута",
"{{value}} minutes": "{{value}} минуты",
"{{value}} hour": "{{value}} час",
"{{value}} hours": "{{value}} часа",
"CJK Font": "CJK Шрифт",
"Clear Search": "Очистить поиск",
"Header & Footer": "Заголовок и нижний колонтитул",
"Apply also in Scrolled Mode": "Применить также в прокручиваемом режиме",
"A new version of Readest is Available!": "Доступна новая версия Readest!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступен (установленная версия {{currentVersion}}).",
"Download and install now?": "Скачать и установить сейчас?",
"Downloading {{downloaded}} of {{contentLength}}": "Скачано {{downloaded}} из {{contentLength}}",
"Download finished": "Загрузка завершена",
"DOWNLOAD & INSTALL": "СКАЧАТЬ И УСТАНОВИТЬ",
"Changelog:": "Изменения:",
"Software Update": "Обновление программного обеспечения",
"Title": "Название",
"Date Read": "Дата прочтения",
"Date Added": "Дата добавления",
"Format": "Формат",
"Ascending": "По возрастанию",
"Descending": "По убыванию",
"Sort by...": "Сортировать по...",
"Added:": "Добавлено:",
"Format:": "Формат:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Описание:",
"No description available": "Нет доступного описания",
"List": "Список",
"Grid": "Сетка",
"(from 'As You Like It', Act II)": "(из 'Как вам это понравится', Акт II)",
"Link Color": "Цвет ссылки",
"Volume Keys for Page Flip": "Клавиши громкости для перелистывания страниц",
"Clicks for Page Flip": "Клики для перелистывания страниц",
"Swap Clicks Area": "Переключить область кликов",
"Screen": "Экран",
"Orientation": "Ориентация",
"Portrait": "Портрет",
"Landscape": "Ландшафт",
"Open Last Book on Start": "Открыть последнюю книгу при запуске",
"Checking for updates...": "Проверка обновлений...",
"Error checking for updates": "Ошибка при проверке обновлений",
"Details": "Инфо",
"File Size:": "Размер файла:",
"Auto Detect": "Автоопределение",
"Next Section": "Следующий раздел",
"Previous Section": "Предыдущий раздел",
"Next Page": "Следующая страница",
"Previous Page": "Предыдущая страница",
"Are you sure to delete {{count}} selected book(s)?_one": "Вы уверены, что хотите удалить {{count}} выбранную книгу?",
"Are you sure to delete {{count}} selected book(s)?_few": "Вы уверены, что хотите удалить {{count}} выбранные книги?",
"Are you sure to delete {{count}} selected book(s)?_many": "Вы уверены, что хотите удалить {{count}} выбранных книг?",
"Are you sure to delete {{count}} selected book(s)?_other": "Вы уверены, что хотите удалить {{count}} выбранных книг?",
"Are you sure to delete the selected book?": "Вы уверены, что хотите удалить выбранную книгу?",
"Deselect": "Снять",
"Select All": "Выбрать все",
"No translation available.": "Нет доступного перевода.",
"Translated by {{provider}}.": "Переведено {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Translate",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "Инвертировать в тёмной теме",
"Help improve Readest": "Помогите улучшить Readest",
"Sharing anonymized statistics": "Отправка анонимной статистики",
"Interface Language": "Язык интерфейса",
"Translation": "Перевод",
"Enable Translation": "Включить перевод",
"Translation Service": "Сервис перевода",
"Translate To": "Перевести на",
"Disable Translation": "Отключить перевод",
"Scroll": "Прокрутка",
"Overlap Pixels": "Перекрывающиеся пиксели",
"Click": "Клик",
"System Language": "Системный язык"
}
@@ -5,7 +5,6 @@
"Animation": "Animasyon",
"Annotate": "Not Ekle",
"Apply": "Uygula",
"Are you sure to delete the selected books?": "Seçili kitapları silmek istediğinizden emin misiniz?",
"Auto Mode": "Otomatik Mod",
"Behavior": "Davranış",
"Book": "Kitap",
@@ -27,7 +26,6 @@
"Delete": "Sil",
"Delete Highlight": "Vurguyu Sil",
"Dictionary": "Sözlük",
"Disable Click-to-Flip": "Tıkla-Çevir'i Devre Dışı Bırak",
"Download Readest": "Readest'i İndir",
"Edit": "Düzenle",
"Enter your custom CSS here...": "Özel CSS'nizi buraya girin...",
@@ -43,15 +41,12 @@
"Global Settings": "Genel Ayarlar",
"Go Back": "Geri Git",
"Go Forward": "İleri Git",
"Go Left": "Sola Git",
"Go Right": "Sağa Git",
"Grass": "Çimen",
"Gray": "Gri",
"Gruvbox": "Gruvbox",
"Highlight": "Vurgula",
"Horizontal Direction": "Yatay Yön",
"Hyphenation": "Heceleme",
"Identifier:": "Tanımlayıcı:",
"Import Books": "Kitapları İçe Aktar",
"Language:": "Dil:",
"Layout": "Düzen",
@@ -64,7 +59,6 @@
"Match Whole Words": "Tam Kelimeleri Eşleştir",
"Maximum Number of Columns": "Maksimum Sütun Sayısı",
"Minimum Font Size": "Minimum Yazı Boyutu",
"Misc": "Diğer",
"Monospace Font": "Eş Aralıklı Yazı Tipi",
"More Info": "Daha Fazla Bilgi",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Bilinmiyor",
"Untitled": "Başlıksız",
"Updated:": "Güncellendi:",
"User avatar": "Kullanıcı avatarı",
"Version {{version}}": "Sürüm {{version}}",
"Vertical Direction": "Dikey Yön",
"Welcome to your library. You can import your books here and read them anytime.": "Kütüphanenize hoş geldiniz. Buradan kitaplarınızı içe aktarabilir ve istediğiniz zaman okuyabilirsiniz.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Tüm kitaplara uygula",
"Apply to This Book": "Bu kitaba uygula",
"Unable to fetch the translation. Try again later.": "Çeviri alınamıyor. Daha sonra tekrar deneyin.",
"Update Now!": "Şimdi Güncelle!",
"Update": "Güncelle",
"Check Update": "Güncellemeyi kontrol et",
"Already the latest version": "Zaten en son sürüm",
"Book Details": "Kitap Detayları",
@@ -152,7 +143,6 @@
"Text Indent": "Metin Girintisi",
"Paragraph Margin": "Paragraf Kenarı",
"Override Book Layout": "Kitap Düzenini Geçersiz Kıl",
"Add to Group": "Gruba Ekle",
"Untitled Group": "Başlıksız Grup",
"Group Books": "Kitapları Grupla",
"Remove From Group": "Grubu Kaldır",
@@ -247,5 +237,98 @@
"Border Color": "Sınır Rengi",
"Border Frame": "Sınır Çerçevesi",
"Show Header": "Başlık Göster",
"Show Footer": "Altbilgi Göster"
"Show Footer": "Altbilgi Göster",
"Small": "Küçük",
"Large": "Büyük",
"Auto": "Otomatik",
"Language": "Dil",
"No annotations to export": "Dışa aktarılacak not yok",
"Author": "Yazar",
"Exported from Readest": "Readest'ten dışa aktarıldı",
"Highlights & Annotations": "Vurgular ve Notlar",
"Note": "Not",
"Copied to clipboard": "Panoya kopyalandı",
"Export Annotations": "Notları Dışa Aktar",
"Auto Import on File Open": "Dosya Açıldığında Otomatik İçe Aktar",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Bölüm bulunamadı.",
"Failed to parse the EPUB file.": "EPUB dosyası ayrıştırılamadı.",
"This book format is not supported.": "Bu kitap formatı desteklenmiyor.",
"Unable to fetch the translation. Please log in first and try again.": "Çeviri alınamıyor. Lütfen önce giriş yapın ve tekrar deneyin.",
"Group": "Grup",
"Always on Top": "Her Zaman Üstte",
"No Timeout": "Zaman Aşımı Yok",
"{{value}} minute": "{{value}} dakika",
"{{value}} minutes": "{{value}} dakika",
"{{value}} hour": "{{value}} saat",
"{{value}} hours": "{{value}} saat",
"CJK Font": "CJK Yazı Tipi",
"Clear Search": "Aramayı Temizle",
"Header & Footer": "Başlık ve Altbilgi",
"Apply also in Scrolled Mode": "Kaydırılmış Modda da Uygula",
"A new version of Readest is Available!": "Yeni bir Readest sürümü mevcut!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} mevcut (yüklü sürüm {{currentVersion}}).",
"Download and install now?": "Şimdi indirip yükleyelim mi?",
"Downloading {{downloaded}} of {{contentLength}}": "İndiriliyor {{downloaded}}/{{contentLength}}",
"Download finished": "İndirme tamamlandı",
"DOWNLOAD & INSTALL": "İNDİR & YÜKLE",
"Changelog:": "Değişiklik Günlüğü:",
"Software Update": "Yazılım Güncellemesi",
"Title": "Başlık",
"Date Read": "Okunma Tarihi",
"Date Added": "Eklenme Tarihi",
"Format": "Biçim",
"Ascending": "Artan",
"Descending": "Azalan",
"Sort by...": "Şuna göre sırala...",
"Added:": "Eklenme:",
"Format:": "Biçim:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Tanım:",
"No description available": "Tanım mevcut değil",
"List": "Liste",
"Grid": "Izgara",
"(from 'As You Like It', Act II)": "(As You Like It, 2. Perde)",
"Link Color": "Bağlantı Rengi",
"Volume Keys for Page Flip": "Sayfa Çevirme için Ses Seviyesi Tuşları",
"Clicks for Page Flip": "Sayfa Çevirme için Tıklamalar",
"Swap Clicks Area": "Tıklama Alanını Değiştir",
"Screen": "Ekran",
"Orientation": "Yönlendirme",
"Portrait": "Portre",
"Landscape": "Yatay",
"Open Last Book on Start": "Açılışta Son Kitabı Aç",
"Checking for updates...": "Yenilikler kontrol ediliyor...",
"Error checking for updates": "Güncellemeleri kontrol ederken hata oluştu",
"Details": "Detaylar",
"File Size:": "Dosya Boyutu:",
"Auto Detect": "Algıla",
"Next Section": "Sonraki Bölüm",
"Previous Section": "Önceki Bölüm",
"Next Page": "Sonraki Sayfa",
"Previous Page": "Önceki Sayfa",
"Are you sure to delete {{count}} selected book(s)?_one": "{{count}} seçili kitabı silmek istediğinize emin misiniz?",
"Are you sure to delete {{count}} selected book(s)?_other": "{{count}} seçili kitabı silmek istediğinize emin misiniz?",
"Are you sure to delete the selected book?": "Seçili kitabı silmek istediğinize emin misiniz?",
"Deselect": "Kaldır",
"Select All": "Hepsi",
"No translation available.": "Çeviri mevcut değil.",
"Translated by {{provider}}.": "{{provider}} tarafından çevrildi.",
"DeepL": "DeepL",
"Google Translate": "Google Çeviri",
"Azure Translator": "Azure Çevirmen",
"Invert Image In Dark Mode": "Karartılmış Görüntüyü Ters Çevir",
"Help improve Readest": "Readesti geliştirin",
"Sharing anonymized statistics": "Anonim verileri paylaş",
"Interface Language": "Arayüz Dili",
"Translation": "Çeviri",
"Enable Translation": "Çeviriyi Etkinleştir",
"Translation Service": "Çeviri Hizmeti",
"Translate To": "Şuna Çevir",
"Disable Translation": "Çeviriyi Devre Dışı Bırak",
"Scroll": "Kaydır",
"Overlap Pixels": "Örtüşen Pikseller",
"Click": "Klik",
"System Language": "System Dili"
}
@@ -5,7 +5,6 @@
"Animation": "Анімація",
"Annotate": "Анотація",
"Apply": "Застосувати",
"Are you sure to delete the selected books?": "Ви впевнені, що хочете видалити вибрані книги?",
"Auto Mode": "Автоматичний режим",
"Behavior": "Поведінка",
"Book": "Книга",
@@ -27,7 +26,6 @@
"Delete": "Видалити",
"Delete Highlight": "Видалити виділення",
"Dictionary": "Словник",
"Disable Click-to-Flip": "Вимкнути гортання кліком",
"Download Readest": "Завантажити Readest",
"Edit": "Редагувати",
"Enter your custom CSS here...": "Введіть ваш користувацький CSS тут...",
@@ -43,15 +41,12 @@
"Global Settings": "Глобальні налаштування",
"Go Back": "Назад",
"Go Forward": "Вперед",
"Go Left": "Вліво",
"Go Right": "Вправо",
"Grass": "Трав'яний",
"Gray": "Сірий",
"Gruvbox": "Теплий помаранчевий",
"Highlight": "Виділення",
"Horizontal Direction": "Горизонтальний напрямок",
"Hyphenation": "Перенесення слів",
"Identifier:": "Ідентифікатор:",
"Import Books": "Імпортувати книги",
"Language:": "Мова:",
"Layout": "Макет",
@@ -64,7 +59,6 @@
"Match Whole Words": "Шукати цілі слова",
"Maximum Number of Columns": "Максимальна кількість колонок",
"Minimum Font Size": "Мінімальний розмір шрифту",
"Misc": "Різне",
"Monospace Font": "Моноширинний шрифт",
"More Info": "Більше інформації",
"Nord": "Північний",
@@ -112,7 +106,6 @@
"Unknown": "Невідомо",
"Untitled": "Без назви",
"Updated:": "Оновлено:",
"User avatar": "Аватар користувача",
"Version {{version}}": "Версія {{version}}",
"Vertical Direction": "Вертикальний напрямок",
"Welcome to your library. You can import your books here and read them anytime.": "Ласкаво просимо до вашої бібліотеки. Ви можете імпортувати сюди свої книги та читати їх будь-коли.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Застосувати до всіх книг",
"Apply to This Book": "Застосувати до цієї книги",
"Unable to fetch the translation. Try again later.": "Не вдалося отримати переклад. Спробуйте пізніше.",
"Update Now!": "Оновити зараз!",
"Update": "Оновити",
"Check Update": "Перевірити оновлення",
"Already the latest version": "Вже остання версія",
"Book Details": "Деталі книги",
@@ -152,7 +143,6 @@
"Text Indent": "Відступ тексту",
"Paragraph Margin": "Відступ абзацу",
"Override Book Layout": "Перевизначити макет книги",
"Add to Group": "Додати до групи",
"Untitled Group": "Група без назви",
"Group Books": "Групувати книги",
"Remove From Group": "Видалити з групи",
@@ -247,5 +237,100 @@
"Border Color": "Колір рамки",
"Border Frame": "Рамка рамки",
"Show Header": "Показати заголовок",
"Show Footer": "Показати нижню частину"
"Show Footer": "Показати нижню частину",
"Small": "Малий",
"Large": "Великий",
"Auto": "Автоматичний",
"Language": "Мова",
"No annotations to export": "Немає анотацій для експорту",
"Author": "Автор",
"Exported from Readest": "Експортовано з Readest",
"Highlights & Annotations": "Виділення та анотації",
"Note": "Примітка",
"Copied to clipboard": "Скопійовано в буфер обміну",
"Export Annotations": "Експортувати анотації",
"Auto Import on File Open": "Автоматичний імпорт при відкритті файлу",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Не виявлено жодного розділу.",
"Failed to parse the EPUB file.": "Не вдалося розібрати файл EPUB.",
"This book format is not supported.": "Цей формат книги не підтримується.",
"Unable to fetch the translation. Please log in first and try again.": "Не вдалося отримати переклад. Будь ласка, спочатку увійдіть у систему та спробуйте ще раз.",
"Group": "Група",
"Always on Top": "Завжди зверху",
"No Timeout": "Без тайм-ауту",
"{{value}} minute": "{{value}} хвилина",
"{{value}} minutes": "{{value}} хвилин",
"{{value}} hour": "{{value}} година",
"{{value}} hours": "{{value}} години",
"CJK Font": "CJK шрифт",
"Clear Search": "Очистити пошук",
"Header & Footer": "Заголовок і нижній колонтитул",
"Apply also in Scrolled Mode": "Застосувати також у прокрученому режимі",
"A new version of Readest is Available!": "Доступна нова версія Readest!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступна (встановлена версія {{currentVersion}}).",
"Download and install now?": "Завантажити та встановити зараз?",
"Downloading {{downloaded}} of {{contentLength}}": "Завантаження {{downloaded}} з {{contentLength}}",
"Download finished": "Завантаження завершено",
"DOWNLOAD & INSTALL": "ЗАВАНТАЖИТИ І ВСТАНОВИТИ",
"Changelog:": "Журнал змін:",
"Software Update": "Оновлення програмного забезпечення",
"Title": "Назва",
"Date Read": "Дата прочитання",
"Date Added": "Дата додавання",
"Format": "Формат",
"Ascending": "За зростанням",
"Descending": "За спаданням",
"Sort by...": "Сортувати за...",
"Added:": "Додано:",
"Format:": "Формат:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Опис:",
"No description available": "Опис недоступний",
"List": "Список",
"Grid": "Сітка",
"(from 'As You Like It', Act II)": "(з 'Як вам це подобається', Акт II)",
"Link Color": "Колір посилання",
"Volume Keys for Page Flip": "Клавіші гучності для перевороту сторінки",
"Clicks for Page Flip": "Клацання для перевороту сторінки",
"Swap Clicks Area": "Змінити область клацання",
"Screen": "Екран",
"Orientation": "Орієнтація",
"Portrait": "Портрет",
"Landscape": "Ландшафт",
"Open Last Book on Start": "Відкрити останню книгу при запуску",
"Checking for updates...": "Перевірка наявності оновлень...",
"Error checking for updates": "Помилка перевірки наявності оновлень",
"Details": "Деталі",
"File Size:": "Розмір файлу:",
"Auto Detect": "Авто виявлення",
"Next Section": "Наступний розділ",
"Previous Section": "Попередній розділ",
"Next Page": "Наступна сторінка",
"Previous Page": "Попередня сторінка",
"Are you sure to delete {{count}} selected book(s)?_one": "Ви впевнені, що хочете видалити {{count}} вибрану книгу?",
"Are you sure to delete {{count}} selected book(s)?_few": "Ви впевнені, що хочете видалити {{count}} вибрані книги?",
"Are you sure to delete {{count}} selected book(s)?_many": "Ви впевнені, що хочете видалити {{count}} вибраних книг?",
"Are you sure to delete {{count}} selected book(s)?_other": "Ви впевнені, що хочете видалити {{count}} вибраних книг?",
"Are you sure to delete the selected book?": "Ви впевнені, що хочете видалити вибрану книгу?",
"Deselect": "Зняти",
"Select All": "Усі",
"No translation available.": "Переклад недоступний.",
"Translated by {{provider}}.": "Перекладено {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Translate",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "Інвертувати зображення в темряві",
"Help improve Readest": "Допоможіть покращити Readest",
"Sharing anonymized statistics": "Поділ анонімною статистикою",
"Interface Language": "Мова інтерфейсу",
"Translation": "Переклад",
"Enable Translation": "Увімкнути переклад",
"Translation Service": "Служба перекладу",
"Translate To": "Перекласти на",
"Disable Translation": "Вимкнути переклад",
"Scroll": "Прокрутка",
"Overlap Pixels": "Пікселі перекриття",
"Click": "Клацнути",
"System Language": "Системна мова"
}
@@ -5,7 +5,6 @@
"Animation": "Hiệu ứng động",
"Annotate": "Chú thích",
"Apply": "Áp dụng",
"Are you sure to delete the selected books?": "Bạn có chắc chắn muốn xóa các sách đã chọn?",
"Auto Mode": "Chế độ tự động",
"Behavior": "Hành vi",
"Book": "Sách",
@@ -27,7 +26,6 @@
"Delete": "Xóa",
"Delete Highlight": "Xóa văn bản được đánh dấu",
"Dictionary": "Từ điển",
"Disable Click-to-Flip": "Tắt tính năng nhấp để lật trang",
"Download Readest": "Tải Readest",
"Edit": "Chỉnh sửa",
"Enter your custom CSS here...": "Nhập CSS tùy chỉnh của bạn vào đây...",
@@ -43,15 +41,12 @@
"Global Settings": "Cài đặt chung",
"Go Back": "Quay lại",
"Go Forward": "Tiến tới",
"Go Left": "Sang trái",
"Go Right": "Sang phải",
"Grass": "Xanh cỏ",
"Gray": "Xám",
"Gruvbox": "Gruvbox",
"Highlight": "Đánh dấu",
"Horizontal Direction": "Hướng ngang",
"Hyphenation": "Gạch nối từ",
"Identifier:": "Định danh:",
"Import Books": "Nhập sách",
"Language:": "Ngôn ngữ:",
"Layout": "Bố cục",
@@ -64,7 +59,6 @@
"Match Whole Words": "Khớp toàn bộ từ",
"Maximum Number of Columns": "Số cột tối đa",
"Minimum Font Size": "Cỡ chữ tối thiểu",
"Misc": "Khác",
"Monospace Font": "Phông chữ đơn cách",
"More Info": "Thông tin thêm",
"Nord": "Nord",
@@ -112,7 +106,6 @@
"Unknown": "Không xác định",
"Untitled": "Không có tiêu đề",
"Updated:": "Cập nhật:",
"User avatar": "Ảnh đại diện",
"Version {{version}}": "Phiên bản {{version}}",
"Vertical Direction": "Hướng dọc",
"Welcome to your library. You can import your books here and read them anytime.": "Chào mừng đến với thư viện của bạn. Bạn có thể nhập sách vào đây và đọc bất cứ lúc nào.",
@@ -126,8 +119,6 @@
"Apply to All Books": "Áp dụng cho tất cả sách",
"Apply to This Book": "Áp dụng cho sách này",
"Unable to fetch the translation. Try again later.": "Không thể tải dịch vụ. Vui lòng thử lại sau.",
"Update Now!": "Cập nhật ngay!",
"Update": "Cập nhật",
"Check Update": "Kiểm tra cập nhật",
"Already the latest version": "Đã là phiên bản mới nhất",
"Book Details": "Chi tiết sách",
@@ -152,7 +143,6 @@
"Text Indent": "Thụt lề",
"Paragraph Margin": "Lề đoạn",
"Override Book Layout": "Ghi đè bố cục sách",
"Add to Group": "Thêm vào nhóm",
"Untitled Group": "Nhóm không có tiêu đề",
"Group Books": "Nhóm sách",
"Remove From Group": "Xóa khỏi nhóm",
@@ -247,5 +237,97 @@
"Border Color": "Màu viền",
"Border Frame": "Khung viền",
"Show Header": "Hiển thị tiêu đề",
"Show Footer": "Hiển thị chân trang"
"Show Footer": "Hiển thị chân trang",
"Small": "Nhỏ",
"Large": "Lớn",
"Auto": "Tự động",
"Language": "Ngôn ngữ",
"No annotations to export": "Không có chú thích để xuất",
"Author": "Tác giả",
"Exported from Readest": "Đã xuất từ Readest",
"Highlights & Annotations": "Điểm nổi bật & Chú thích",
"Note": "Ghi chú",
"Copied to clipboard": "Đã sao chép vào bảng ghi",
"Export Annotations": "Xuất chú thích",
"Auto Import on File Open": "Tự động nhập khi mở tệp",
"LXGW WenKai GB Screen": "LXGW WenKai SC",
"LXGW WenKai TC": "LXGW WenKai TC",
"No chapters detected.": "Không phát hiện thấy chương nào.",
"Failed to parse the EPUB file.": "Không thể phân tích tệp EPUB.",
"This book format is not supported.": "Định dạng sách này không được hỗ trợ.",
"Unable to fetch the translation. Please log in first and try again.": "Không thể tải dịch vụ. Vui lòng đăng nhập trước và thử lại.",
"Group": "Nhóm",
"Always on Top": "Luôn ở trên cùng",
"No Timeout": "Không có thời gian chờ",
"{{value}} minute": "{{value}} phút",
"{{value}} minutes": "{{value}} phút",
"{{value}} hour": "{{value}} giờ",
"{{value}} hours": "{{value}} giờ",
"CJK Font": "Phông chữ CJK",
"Clear Search": "Xóa tìm kiếm",
"Header & Footer": "Đầu trang & Chân trang",
"Apply also in Scrolled Mode": "Áp dụng cũng trong chế độ cuộn",
"A new version of Readest is Available!": "Đã có phiên bản mới của Readest!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} đã có sẵn (phiên bản đã cài đặt {{currentVersion}}).",
"Download and install now?": "Tải xuống và cài đặt ngay bây giờ?",
"Downloading {{downloaded}} of {{contentLength}}": "Đang tải {{downloaded}} trong {{contentLength}}",
"Download finished": "Tải xuống hoàn tất",
"DOWNLOAD & INSTALL": "TẢI XUỐNG & CÀI ĐẶT",
"Changelog:": "Thay đổi:",
"Software Update": "Cập nhật phần mềm",
"Title": "Tiêu đề",
"Date Read": "Ngày đã đọc",
"Date Added": "Ngày thêm",
"Format": "Định dạng",
"Ascending": "Tăng dần",
"Descending": "Giảm dần",
"Sort by...": "Sắp xếp theo...",
"Added:": "Đã thêm:",
"Format:": "Định dạng:",
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
"Description:": "Miêu tả:",
"No description available": "Không có mô tả nào",
"List": "Danh sách",
"Grid": "Lưới",
"(from 'As You Like It', Act II)": "(từ 'Như bạn thích', Hành động II)",
"Link Color": "Màu liên kết",
"Volume Keys for Page Flip": "Phím âm lượng để lật trang",
"Clicks for Page Flip": "Nhấp để lật trang",
"Swap Clicks Area": "Đổi khu vực nhấp",
"Screen": "Màn hình",
"Orientation": "Định hướng",
"Portrait": "Chân dung",
"Landscape": "Phong cảnh",
"Open Last Book on Start": "Mở sách cuối cùng khi khởi động",
"Checking for updates...": "Đang kiểm tra cập nhật...",
"Error checking for updates": "Lỗi khi kiểm tra cập nhật",
"Details": "Chi tiết",
"File Size:": "Kích thước tệp:",
"Auto Detect": "Phát hiện tự động",
"Next Section": "Phần tiếp theo",
"Previous Section": "Phần trước",
"Next Page": "Trang tiếp theo",
"Previous Page": "Trang trước",
"Are you sure to delete {{count}} selected book(s)?_other": "Bạn có chắc muốn xóa {{count}} cuốn sách đã chọn không?",
"Are you sure to delete the selected book?": "Bạn có chắc muốn xóa cuốn sách đã chọn không?",
"Deselect": "Bỏ chọn",
"Select All": "Chọn tất",
"No translation available.": "Không có bản dịch nào.",
"Translated by {{provider}}.": "Được dịch bởi {{provider}}.",
"DeepL": "DeepL",
"Google Translate": "Google Dịch",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "Đảo ảnh chế độ tối",
"Help improve Readest": "Giúp cải thiện Readest",
"Sharing anonymized statistics": "Chia sẻ thống kê ẩn danh",
"Interface Language": "Ngôn ngữ giao diện",
"Translation": "Dịch",
"Enable Translation": "Bật dịch",
"Translation Service": "Dịch vụ dịch thuật",
"Translate To": "Dịch sang",
"Disable Translation": "Tắt dịch",
"Scroll": "Cuộn",
"Overlap Pixels": "Chồng lấn pixel",
"Click": "Nhấp",
"System Language": "Ngôn ngữ hệ thống"
}
@@ -5,7 +5,6 @@
"Animation": "动画",
"Annotate": "笔记",
"Apply": "应用",
"Are you sure to delete the selected books?": "您确定要删除所选书籍吗?",
"Auto Mode": "自动主题",
"Behavior": "行为",
"Book": "书籍",
@@ -27,7 +26,6 @@
"Delete": "删除",
"Delete Highlight": "删除划线",
"Dictionary": "词典",
"Disable Click-to-Flip": "禁用点击翻页",
"Download Readest": "下载 Readest",
"Edit": "编辑",
"Enter your custom CSS here...": "在此处输入您的自定义 CSS...",
@@ -43,15 +41,12 @@
"Global Settings": "全局设置",
"Go Back": "返回",
"Go Forward": "前进",
"Go Left": "向左",
"Go Right": "向右",
"Grass": "青草",
"Gray": "素雅",
"Gruvbox": "暖橘",
"Highlight": "划线",
"Horizontal Direction": "横排",
"Hyphenation": "断字",
"Identifier:": "识别码",
"Import Books": "导入书籍",
"Language:": "语言",
"Layout": "布局",
@@ -64,7 +59,6 @@
"Match Whole Words": "匹配整个单词",
"Maximum Number of Columns": "分栏数",
"Minimum Font Size": "最小字号",
"Misc": "杂项",
"Monospace Font": "等宽字体",
"More Info": "更多信息",
"Nord": "极光",
@@ -112,7 +106,6 @@
"Unknown": "未知",
"Untitled": "无标题",
"Updated:": "更新日期",
"User avatar": "用户头像",
"Version {{version}}": "版本 {{version}}",
"Vertical Direction": "竖排",
"Welcome to your library. You can import your books here and read them anytime.": "书库空空如也,您可以导入您的书籍并随时阅读。",
@@ -126,8 +119,6 @@
"Apply to All Books": "应用于所有书籍",
"Apply to This Book": "应用于此书籍",
"Unable to fetch the translation. Try again later.": "无法获取翻译,请稍后重试。",
"Update Now!": "立即更新!",
"Update": "更新",
"Check Update": "检查更新",
"Already the latest version": "已经是最新版本",
"Book Details": "书籍详情",
@@ -152,7 +143,6 @@
"Text Indent": "首行缩进",
"Paragraph Margin": "段间距",
"Override Book Layout": "覆盖版面布局",
"Add to Group": "添加到分组",
"Untitled Group": "无标题分组",
"Group Books": "分组书籍",
"Remove From Group": "从分组中移除",
@@ -231,7 +221,7 @@
"Maximum Column Height": "最大列高",
"Maximum Column Width": "最大列宽",
"Continuous Scroll": "连续滚动",
"Fullscreen": "全屏",
"Fullscreen": "全屏显示",
"No supported files found. Supported formats: {{formats}}": "未找到支持的文件。支持的格式:{{formats}}",
"Drop to Import Books": "拖放导入书籍",
"Custom": "自定义",
@@ -247,5 +237,97 @@
"Border Color": "版框颜色",
"Border Frame": "页面版框",
"Show Header": "显示页眉",
"Show Footer": "显示页脚"
"Show Footer": "显示页脚",
"Small": "小",
"Large": "大",
"Auto": "自动",
"Language": "语言",
"No annotations to export": "没有要导出的笔记",
"Author": "作者",
"Exported from Readest": "从 Readest 导出",
"Highlights & Annotations": "划线和笔记",
"Note": "笔记",
"Copied to clipboard": "已复制到剪贴板",
"Export Annotations": "导出笔记",
"Auto Import on File Open": "打开文件时自动导入",
"LXGW WenKai GB Screen": "霞鹜文楷",
"LXGW WenKai TC": "霞鶩文楷",
"No chapters detected.": "未检测到章节。",
"Failed to parse the EPUB file.": "无法解析 EPUB 文件。",
"This book format is not supported.": "不支持此书籍格式。",
"Unable to fetch the translation. Please log in first and try again.": "无法获取翻译。请登录后重试。",
"Group": "分组",
"Always on Top": "窗口置顶",
"No Timeout": "关闭定时",
"{{value}} minute": "{{value}}分钟",
"{{value}} minutes": "{{value}}分钟",
"{{value}} hour": "{{value}}小时",
"{{value}} hours": "{{value}}小时",
"CJK Font": "中文字体",
"Clear Search": "清除搜索",
"Header & Footer": "页眉页脚",
"Apply also in Scrolled Mode": "应用到滚动模式",
"A new version of Readest is Available!": "有新版本的 Readest 可更新!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安装版本 {{currentVersion}})。",
"Download and install now?": "立即下载并安装?",
"Downloading {{downloaded}} of {{contentLength}}": "正在下载 {{downloaded}} / {{contentLength}}",
"Download finished": "下载完成",
"DOWNLOAD & INSTALL": "下载并安装",
"Changelog:": "更新日志:",
"Software Update": "软件更新",
"Title": "书名",
"Date Read": "阅读日期",
"Date Added": "添加日期",
"Format": "格式",
"Ascending": "升序",
"Descending": "降序",
"Sort by...": "排序方式...",
"Added:": "添加日期",
"Format:": "格式",
"GuanKiapTsingKhai-T": "原俠正楷-T",
"Description:": "简介",
"No description available": "暂无简介",
"List": "列表",
"Grid": "网格",
"(from 'As You Like It', Act II)": "出自《皆大欢喜》,第二幕",
"Link Color": "链接颜色",
"Volume Keys for Page Flip": "音量键翻页",
"Clicks for Page Flip": "点击翻页",
"Swap Clicks Area": "交换点击区域",
"Screen": "屏幕",
"Orientation": "屏幕方向",
"Portrait": "竖屏",
"Landscape": "横屏",
"Open Last Book on Start": "启动时打开上次阅读的书籍",
"Checking for updates...": "检查更新中...",
"Error checking for updates": "检查更新时出错",
"Details": "详情",
"File Size:": "文件大小",
"Auto Detect": "自动检测",
"Next Section": "下一章",
"Previous Section": "上一章",
"Next Page": "下一页",
"Previous Page": "上一页",
"Are you sure to delete {{count}} selected book(s)?_other": "您确定要删除 {{count}} 本选中的书籍吗?",
"Are you sure to delete the selected book?": "您确定要删除选中的书籍吗?",
"Deselect": "取消选择",
"Select All": "全选",
"No translation available.": "暂无可用的翻译",
"Translated by {{provider}}.": "由 {{provider}} 提供翻译",
"DeepL": "DeepL",
"Google Translate": "Google Translate",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "深色主题下图片反色",
"Help improve Readest": "帮助改进 Readest",
"Sharing anonymized statistics": "分享匿名统计数据",
"Interface Language": "界面语言",
"Translation": "翻译",
"Enable Translation": "启用翻译",
"Translation Service": "翻译服务",
"Translate To": "翻译到",
"Disable Translation": "禁用翻译",
"Scroll": "滚动",
"Overlap Pixels": "重叠像素",
"Click": "点击",
"System Language": "系统语言"
}
@@ -5,7 +5,6 @@
"Animation": "動畫",
"Annotate": "筆記",
"Apply": "應用",
"Are you sure to delete the selected books?": "您確定要刪除所選書籍嗎?",
"Auto Mode": "自動主題",
"Behavior": "行為",
"Book": "書籍",
@@ -27,7 +26,6 @@
"Delete": "刪除",
"Delete Highlight": "刪除劃線",
"Dictionary": "詞典",
"Disable Click-to-Flip": "禁用點擊翻頁",
"Download Readest": "下載 Readest",
"Edit": "編輯",
"Enter your custom CSS here...": "在此處輸入您的自定義 CSS...",
@@ -43,15 +41,12 @@
"Global Settings": "全局設置",
"Go Back": "返回",
"Go Forward": "前進",
"Go Left": "向左",
"Go Right": "向右",
"Grass": "青草",
"Gray": "素雅",
"Gruvbox": "暖橘",
"Highlight": "劃線",
"Horizontal Direction": "橫排",
"Hyphenation": "斷字",
"Identifier:": "識別碼",
"Import Books": "導入書籍",
"Language:": "語言",
"Layout": "版面",
@@ -64,7 +59,6 @@
"Match Whole Words": "匹配整個單詞",
"Maximum Number of Columns": "分欄數",
"Minimum Font Size": "最小字號",
"Misc": "雜項",
"Monospace Font": "等寬字體",
"More Info": "更多信息",
"Nord": "極光",
@@ -112,7 +106,6 @@
"Unknown": "未知",
"Untitled": "無標題",
"Updated:": "更新日期",
"User avatar": "用戶頭像",
"Version {{version}}": "版本 {{version}}",
"Vertical Direction": "豎排",
"Welcome to your library. You can import your books here and read them anytime.": "書庫空空如也,您可以導入您的書籍並隨時閱讀。",
@@ -126,8 +119,6 @@
"Apply to All Books": "應用於所有書籍",
"Apply to This Book": "應用於此書籍",
"Unable to fetch the translation. Try again later.": "無法獲取翻譯,請稍後再試。",
"Update Now!": "立即更新!",
"Update": "更新",
"Check Update": "檢查更新",
"Already the latest version": "已經是最新版本",
"Book Details": "書籍詳情",
@@ -152,7 +143,6 @@
"Text Indent": "首行縮進",
"Paragraph Margin": "段間距",
"Override Book Layout": "覆蓋版面佈局",
"Add to Group": "添加到組",
"Untitled Group": "無標題組",
"Group Books": "分組書籍",
"Remove From Group": "從組中移除",
@@ -247,5 +237,97 @@
"Border Color": "邊框顏色",
"Border Frame": "頁面邊框",
"Show Header": "顯示頁眉",
"Show Footer": "顯示頁腳"
"Show Footer": "顯示頁腳",
"Small": "小",
"Large": "大",
"Auto": "自動",
"Language": "語言",
"No annotations to export": "沒有要匯出的筆記",
"Author": "作者",
"Exported from Readest": "從 Readest 匯出",
"Highlights & Annotations": "高亮和筆記",
"Note": "筆記",
"Copied to clipboard": "已複製到剪貼板",
"Export Annotations": "匯出筆記",
"Auto Import on File Open": "打開文件時自動導入",
"LXGW WenKai GB Screen": "霞鶩文楷 SC",
"LXGW WenKai TC": "霞鶩文楷 TC",
"No chapters detected.": "未檢測到章節。",
"Failed to parse the EPUB file.": "無法解析 EPUB 文件。",
"This book format is not supported.": "不支援此書籍格式。",
"Unable to fetch the translation. Please log in first and try again.": "無法獲取翻譯。請先登入然後重試。",
"Group": "分組",
"Always on Top": "視窗置頂",
"No Timeout": "關閉定時",
"{{value}} minute": "{{value}}分鐘",
"{{value}} minutes": "{{value}}分鐘",
"{{value}} hour": "{{value}}小時",
"{{value}} hours": "{{value}}小時",
"CJK Font": "中文字體",
"Clear Search": "清除搜索",
"Header & Footer": "頁眉頁腳",
"Apply also in Scrolled Mode": "滾動模式同樣適用",
"A new version of Readest is Available!": "有新版本的 Readest 可更新!",
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安裝版本 {{currentVersion}})。",
"Download and install now?": "立即下載並安裝?",
"Downloading {{downloaded}} of {{contentLength}}": "正在下載 {{downloaded}} / {{contentLength}}",
"Download finished": "下載完成",
"DOWNLOAD & INSTALL": "下載並安裝",
"Changelog:": "更新日誌:",
"Software Update": "軟體更新",
"Title": "書名",
"Date Read": "閱讀日期",
"Date Added": "添加日期",
"Format": "格式",
"Ascending": "升序",
"Descending": "降序",
"Sort by...": "排序方式...",
"Added:": "添加日期",
"Format:": "格式",
"GuanKiapTsingKhai-T": "原俠正楷-T",
"Description:": "簡介",
"No description available": "暫無簡介",
"List": "列表",
"Grid": "網格",
"(from 'As You Like It', Act II)": "(出自《皆大歡喜》,第二幕)",
"Link Color": "連結顏色",
"Volume Keys for Page Flip": "音量鍵翻頁",
"Clicks for Page Flip": "點擊翻頁",
"Swap Clicks Area": "交換點擊區域",
"Screen": "螢幕",
"Orientation": "螢幕方向",
"Portrait": "豎屏",
"Landscape": "橫屏",
"Open Last Book on Start": "啟動時打開上次閱讀的書籍",
"Checking for updates...": "檢查更新中...",
"Error checking for updates": "檢查更新時出錯",
"Details": "詳情",
"File Size:": "檔案大小",
"Auto Detect": "自動檢測",
"Next Section": "下一章",
"Previous Section": "上一章",
"Next Page": "下一頁",
"Previous Page": "上一頁",
"Are you sure to delete {{count}} selected book(s)?_other": "確定要刪除已選擇的 {{count}} 本書嗎?",
"Are you sure to delete the selected book?": "確定要刪除已選擇的書籍嗎?",
"Deselect": "取消選取",
"Select All": "全選",
"No translation available.": "暫無翻譯",
"Translated by {{provider}}.": "由 {{provider}} 提供翻譯",
"DeepL": "DeepL",
"Google Translate": "Google Translate",
"Azure Translator": "Azure Translator",
"Invert Image In Dark Mode": "深色主題下圖片反相",
"Help improve Readest": "幫助改進 Readest",
"Sharing anonymized statistics": "分享匿名統計數據",
"Interface Language": "介面語言",
"Translation": "翻譯",
"Enable Translation": "啟用翻譯",
"Translation Service": "翻譯服務",
"Translate To": "翻譯至",
"Disable Translation": "停用翻譯",
"Scroll": "滾動",
"Overlap Pixels": "重疊像素",
"Click": "點擊",
"System Language": "系統語言"
}
+190
View File
@@ -1,5 +1,195 @@
{
"releases": {
"0.9.49": {
"date": "2025-05-27",
"notes": [
"Select All in Library: You can now select all books at once when in selection mode",
"More Translation Options: Added support for Azure and Google Translate services",
"Dark Mode Enhancement: New option to invert image colors for better visibility in dark mode",
"Bilingual TTS: Enjoy bilingual text-to-speech for books with content in two languages",
"Full Book Translation: Translate books in any language into bilingual ebooks",
"Privacy Settings: Added a new option to opt-out of telemetry"
]
},
"0.9.43": {
"date": "2025-05-19",
"notes": [
"Enhanced compatibility with Gutenberg and Feedbooks ebooks for better styling",
"Reduced sensitivity of trackpad and mouse during page flips",
"Cloud backup status is now visible for each book on mobile and desktop",
"New option to control scroll overlap when paginating in scroll mode",
"Added Dutch language translations",
"Added more font weight options for built-in fonts",
"Added previous and next section buttons in the footer bar for easier navigation"
]
},
"0.9.41": {
"date": "2025-05-13",
"notes": [
"Improved UI and layout on mobile devices",
"Avatar images are now cached for offline use",
"Book file size is now displayed in the book details view",
"Update status is now correctly shown in the About window",
"TTS control positioning improved for notebook and RTL modes",
"TXT files are segmented by paragraph count if no chapter titles are found",
"Android: App now respects system auto-rotation settings",
"Linux: Updater is disabled on Linux when not using AppImage",
"Linux: Added support for Linux ARMHF builds",
"CSS: Resolved layout and color issues caused by hardcoded CSS"
]
},
"0.9.40": {
"date": "2025-05-08",
"notes": [
"Prevented oversized images from causing layout issues",
"Resolved issue with transient navigation bar on Android 9",
"Action bar now closes properly when the footbar is dismissed",
"Smoother experience when expanding Table of Contents sections",
"Reverted unintended style overrides"
]
},
"0.9.39": {
"date": "2025-05-07",
"notes": [
"Enhanced Navigation: Added location information to the table of contents",
"Enhanced Navigation: Larger touch targets for table of contents expansion icons",
"Screen Orientation Lock: You can now lock your device's screen orientation when reading",
"Quick Access to Recent Books: Option to automatically open your last read book when starting the app",
"Better Language Support: Improved handling of CJK characters in search",
"Enhanced Text-to-Speech: More accurate language detection for TTS",
"Enhanced Text-to-Speech: Text-to-Speech now maintains your position in scroll mode",
"Enhanced Annotations: Better handling of annotations with ruby elements in Japanese books",
"Smoother Scrolling: We've made continuous scrolling more reliable for a seamless reading experience",
"Compact View: Reading view is more compact when header and footer are hidden",
"TXT Parsing: More robust handling of TXT book format"
]
},
"0.9.38": {
"date": "2025-04-29",
"notes": [
"Page Turning with Volume Keys: You can now turn pages using your devices volume buttons",
"Expanded English Voices: More English voices are available across all en locales",
"Theme Editor: Added a primary color picker for easier customization",
"System Fonts: Retrieve system fonts on iOS and Android, with improved font weight and style detection",
"Popup Footnotes: Popup footnotes now inherit the book's font for consistent appearance",
"Better Text Selection: Preserves text selection anchors when spanning paginated content",
"Android Compatibility: Navigation bar auto-hides correctly on Android 10 and below",
"Popup Dialogs: Back key now properly dismisses popup dialogs on Android"
]
},
"0.9.37": {
"date": "2025-04-25",
"notes": [
"Full-Screen Toggle: You can now press F11 to quickly toggle full-screen mode",
"List View: A new list layout is available for your bookshelf",
"Book Details: Book descriptions from metadata are now shown in the details view",
"Enhanced Android behavior: better navigation bar handling and status bar dismissal when resuming",
"TTS now defaults to the books metadata language when needed",
"Synced custom themes reliably across sessions"
]
},
"0.9.36": {
"date": "2025-04-20",
"notes": [
"📥 New download and upload buttons in the book detail dialog for easier file management",
"📂 You can now open files directly in Readest from your file manager on Android and iOS",
"🛠️ Fixed an issue where files with quotation marks in the filename could not be opened",
"📖 Improved the immersive reading experience on the reader page for Android and iOS",
"🌐 Added GuanKiapTsingKhai-T to the list of available CJK fonts"
]
},
"0.9.35": {
"date": "2025-04-15",
"notes": [
"🛠️ Fixed an issue where books without cover images can not be synced across devices",
"📱 Added in-app update support for Android",
"📚 You can now sort books by title or author in your library"
]
},
"0.9.33": {
"date": "2025-04-13",
"notes": [
"New: Redesigned update dialog with a progress bar during downloads for a smoother update experience.",
"New: You can now set header and footer visibility separately for paginated and scroll modes.",
"Fixed layout issues in the bottom configuration panel when using RTL (right-to-left) languages.",
"Fixed TXT file import issues on Android by improving support for content provider file paths.",
"Fixed transformed punctuations when highlighting text with vertical layout.",
"Improved login flow on macOS by handling OAuth with the native ASWebAuthenticationSession."
]
},
"0.9.32": {
"date": "2025-04-09",
"notes": [
"No more resetting of font size and color when you apply custom book styles",
"The underline and squiggly highlight decorations have been adjusted to sit neatly between text lines",
"With more font options available, the font menu is no longer forced in-line",
"LXGW WenKai TC is now part of our CJK font list"
]
},
"0.9.31": {
"date": "2025-04-08",
"notes": [
"iOS: Text-to-Speech (TTS) now works in the background",
"Android: Import files from native file manager",
"Desktop: Add option to keep window on top",
"Vertical layout for highlighting tools",
"Added options to auto-stop TTS after timeout",
"Added book search functionality in bookshelf",
"Improved font support for CJK users"
]
},
"0.9.30": {
"date": "2025-04-05",
"notes": [
"Added fixed storage quata when running the app in self-hosted mode",
"Link colors will only change when you override fonts",
"Added cache for native files for better performance"
]
},
"0.9.29": {
"date": "2025-04-02",
"notes": [
"Improved Login on Android without needing to rely on external Chrome browser",
"Local large book files now load more efficiently on Android",
"Balanced the load of online service between different types of users to keep things running smoothly for everyone"
]
},
"0.9.28": {
"date": "2025-03-30",
"notes": [
"Improved vertical reading mode with better support for Chinese punctuations",
"Fixed TXT file importing issue on desktop apps — TXT books now load reliably and display as expected",
"Improved annotation experience — highlighting tools are now properly dismissed when adding notes to avoid UI conflicts"
]
},
"0.9.27": {
"date": "2025-03-29",
"notes": [
"Added option to swap click-to-flip area and reset password",
"Added global shortcuts for full-text search",
"Added support for both R2 and S3 storage in cloud sync",
"Improved TTS with fewer unintended pauses, faster startup, and support for preferred voices",
"Various fixes for rendering styles and UI layout"
]
},
"0.9.26": {
"date": "2025-03-27",
"notes": [
"Support open book from file manager without importing",
"Add LXGW WenKai font in Serif fonts list",
"Various fixes on layout and styles on Windows and Android"
]
},
"0.9.25": {
"date": "2025-03-24",
"notes": [
"Support exporting annotations in markdown format",
"Support importing TXT files",
"Support setting reader language in the UI",
"Add bottom action bar on mobile platforms",
"Various fixes on layout settings and styles"
]
},
"0.9.23": {
"date": "2025-03-19",
"notes": [
@@ -1,3 +1,22 @@
#!/bin/bash
set -e
echo "Updating bundleVersion in tauri.appstore.conf.json..."
CONFIG_FILE="src-tauri/tauri.appstore.conf.json"
CURRENT_DATE=$(date "+%Y%m%d.%H%M%S")
if [ ! -f "$CONFIG_FILE" ]; then
echo "Error: Config file $CONFIG_FILE not found!"
exit 1
fi
TMP_FILE=$(mktemp)
cat "$CONFIG_FILE" | jq --arg version "$CURRENT_DATE" '.bundle.macOS.bundleVersion = $version' > "$TMP_FILE"
mv "$TMP_FILE" "$CONFIG_FILE"
echo "Updated bundleVersion to $CURRENT_DATE"
echo "Building macOS universal app for App Store..."
pnpm run build-macos-universial-appstore
BUNDLE_DIR=../../target/universal-apple-darwin/release/bundle/macos
+25 -23
View File
@@ -1,6 +1,6 @@
[package]
name = "Readest"
version = "0.2.1"
version = "0.2.2"
description = "Your online library"
authors = ["Bilingify LLC"]
license = ""
@@ -15,7 +15,7 @@ name = "readestlib"
crate-type = ["staticlib", "cdylib", "lib"]
[build-dependencies]
tauri-build = { version = "2.0.3", features = [] }
tauri-build = { version = "2.1.1", features = [] }
[dependencies]
serde_json = "1.0"
@@ -31,33 +31,35 @@ reqwest = { version = "0.12", default-features = false, features = [
"stream",
] }
# FIXME: remove the devtools feature in production
tauri = { version = "2.1.1", features = [ "protocol-asset", "devtools"] }
tauri-build = "2.0.3"
tauri-plugin-log = "2.0.2"
tauri-plugin-fs = "2.0.3"
tauri-plugin-dialog = "2.0.3"
tauri-plugin-os = "2.0.1"
tauri-plugin-http = "2.0.3"
tauri-plugin-devtools = "2.0.0"
tauri-plugin-shell = "2"
tauri-plugin-process = "2"
tauri = { version = "2.5.1", features = [ "protocol-asset", "devtools"] }
tauri-build = "2.1.1"
tauri-plugin-log = "2.4.0"
tauri-plugin-fs = "2.2.1"
tauri-plugin-dialog = "2.2.1"
tauri-plugin-os = "2.2.1"
tauri-plugin-http = "2.4.3"
tauri-plugin-shell = "2.2.1"
tauri-plugin-process = "2.2.1"
tauri-plugin-oauth = "2"
tauri-plugin-opener = "2.2.2"
tauri-plugin-deep-link = "2"
tauri-plugin-opener = "2.2.6"
tauri-plugin-deep-link = "2.2.1"
tauri-plugin-sign-in-with-apple = "1.0.2"
tauri-plugin-haptics = "2.2.3"
tauri-plugin-safari-auth = { path = "./plugins/tauri-plugin-safari-auth" }
[patch.crates-io]
tauri = { path = "../../../packages/tauri/crates/tauri" }
tauri-plugin-haptics = "2.2.4"
tauri-plugin-native-bridge = { path = "./plugins/tauri-plugin-native-bridge" }
[target."cfg(target_os = \"macos\")".dependencies]
rand = "0.8"
cocoa = "0.25"
objc = "0.2.7"
rand = "0.8"
objc-foundation = "0.1.1"
objc_id = "0.1.1"
block = "0.1.6"
objc2 = "0.6"
objc2-authentication-services = "0.3"
objc2-foundation = { version = "0.3", features = ["NSError", "NSArray"] }
[target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies]
tauri-plugin-cli = "2"
tauri-plugin-single-instance = "2.2.0"
tauri-plugin-updater = "2"
tauri-plugin-window-state = "2.2.0"
font-enumeration = "0.9.0"
tauri-plugin-single-instance = "2.2.3"
tauri-plugin-updater = "2.7.0"
tauri-plugin-window-state = "2.2.2"
+201
View File
@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<false/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIHomeIndicatorAutoHidden</key>
<true/>
<key>UIRequiresFullScreen</key>
<true/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>EPUB Document</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>org.idpf.epub-container</string>
</array>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>PDF Document</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.adobe.pdf</string>
</array>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>FB2 Document</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.readest.fb2</string>
</array>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>CBZ Archive</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.readest.cbz</string>
</array>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>MOBI Document</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>org.mobipocket.mobi</string>
</array>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>AZW Document</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.amazon.azw</string>
<string>com.amazon.azw3</string>
</array>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>Text File</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.plain-text</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>org.idpf.epub-container</string>
<key>UTTypeDescription</key>
<string>EPUB Document</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>epub</string>
</array>
<key>public.mime-type</key>
<string>application/epub+zip</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.readest.fb2</string>
<key>UTTypeDescription</key>
<string>FB2 Document</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fb2</string>
</array>
<key>public.mime-type</key>
<string>application/xml</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.readest.cbz</string>
<key>UTTypeDescription</key>
<string>CBZ Archive</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.archive</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>cbz</string>
</array>
<key>public.mime-type</key>
<string>application/x-cbz</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.mobipocket.mobi</string>
<key>UTTypeDescription</key>
<string>MOBI Document</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mobi</string>
</array>
<key>public.mime-type</key>
<string>application/x-mobipocket-ebook</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.amazon.azw</string>
<key>UTTypeDescription</key>
<string>AZW Document</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>azw</string>
<string>azw3</string>
</array>
<key>public.mime-type</key>
<string>application/vnd.amazon.ebook</string>
</dict>
</dict>
</array>
</dict>
</plist>
@@ -2,7 +2,7 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "enables the default permissions",
"windows": ["main"],
"windows": ["main", "updater"],
"permissions": [
"core:default",
"fs:default",
@@ -39,14 +39,37 @@
}
]
},
{
"identifier": "fs:allow-cache-read",
"allow": [
{
"path": "$CACHEDIR/**/*"
},
{
"path": "/private/var/mobile/Containers/Data/Application/**/*"
}
]
},
{
"identifier": "http:default",
"allow": [
{
"url": "https://*.deepl.com"
},
{
"url": "https://github.com/readest/*"
},
{
"url": "https://*.cloudflarestorage.com"
},
{
"url": "https://edge.microsoft.com"
},
{
"url": "https://*.microsofttranslator.com"
},
{
"url": "https://translate.googleapis.com"
}
]
},
@@ -57,12 +80,17 @@
"core:window:allow-center",
"core:window:allow-minimize",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-set-size",
"core:window:allow-set-focus",
"core:window:allow-is-maximized",
"core:window:allow-start-dragging",
"core:window:allow-toggle-maximize",
"core:window:allow-set-fullscreen",
"core:window:allow-set-always-on-top",
"core:webview:allow-create-webview-window",
"core:path:allow-resolve-directory",
"log:default",
"shell:default",
"process:default",
"process:allow-exit",
@@ -75,7 +103,7 @@
"haptics:allow-impact-feedback",
"haptics:allow-notification-feedback",
"haptics:allow-selection-feedback",
"safari-auth:default",
"native-bridge:default",
"deep-link:default"
]
}
@@ -1,10 +1,7 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"windows": ["main"],
"windows": ["main", "updater"],
"platforms": ["linux", "macOS", "windows"],
"permissions": [
"updater:default",
"cli:default"
]
"permissions": ["updater:default", "cli:default"]
}
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- AndroidTV support -->
<uses-feature android:name="android.software.leanback" android:required="false" />
<application
android:icon="@mipmap/ic_launcher"
android:largeHeap="true"
android:label="@string/app_name"
android:theme="@style/Theme.readest"
android:usesCleartextTraffic="${usesCleartextTraffic}">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:launchMode="singleTask"
android:label="@string/main_activity_title"
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="application/epub+zip" />
<data android:mimeType="application/pdf" />
<data android:mimeType="application/fb2" />
<data android:mimeType="application/x-fb2" />
<data android:mimeType="application/vnd.amazon.mobi8-ebook" />
<data android:mimeType="application/vnd.comicbook+zip" />
<data android:mimeType="application/x-cbz" />
<data android:mimeType="application/x-mobipocket-ebook" />
<data android:mimeType="application/zip" />
<data android:mimeType="text/plain" />
<data android:pathPattern=".*\\.epub" />
<data android:pathPattern=".*\\.pdf" />
<data android:pathPattern=".*\\.fb2" />
<data android:pathPattern=".*\\.fb2.zip" />
<data android:pathPattern=".*\\.mobi" />
<data android:pathPattern=".*\\.azw" />
<data android:pathPattern=".*\\.cbz" />
<data android:pathPattern=".*\\.zip" />
<data android:pathPattern=".*\\.txt" />
</intent-filter>
<intent-filter android:label="oauth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="readest" android:host="auth-callback" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- AndroidTV support -->
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="web.readest.com" />
</intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
@@ -1,33 +1,81 @@
package com.bilingify.readest
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.WindowInsets
import android.view.WindowInsetsController
import androidx.activity.enableEdgeToEdge
import android.view.KeyEvent
import android.webkit.WebView
import android.util.Log
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import com.readest.native_bridge.KeyDownInterceptor
class MainActivity : TauriActivity(), KeyDownInterceptor {
private lateinit var wv: WebView
private var interceptVolumeKeysEnabled = false
private var interceptBackKeyEnabled = false
override fun onWebViewCreate(webView: WebView) {
wv = webView
}
private val keyEventMap = mapOf(
KeyEvent.KEYCODE_BACK to "Back",
KeyEvent.KEYCODE_VOLUME_DOWN to "VolumeDown",
KeyEvent.KEYCODE_VOLUME_UP to "VolumeUp"
)
override fun interceptVolumeKeys(enabled: Boolean) {
Log.d("MainActivity", "Intercept volume keys: $enabled")
interceptVolumeKeysEnabled = enabled
}
override fun interceptBackKey(enabled: Boolean) {
Log.d("MainActivity", "Intercept back key: $enabled")
interceptBackKeyEnabled = enabled
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
val keyName = keyEventMap[keyCode]
if (keyName != null) {
wv.evaluateJavascript(
"""
try {
window.onNativeKeyDown("$keyName", $keyCode)
} catch (err) {
false
}
""".trimIndent()
) { result ->
run {
if (result.equals("true", ignoreCase = true)) {
Log.d("Key Event", "Key event $keyName intercepted")
}
}
}
return when (keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (interceptVolumeKeysEnabled) {
true
} else {
super.onKeyDown(keyCode, event)
}
}
KeyEvent.KEYCODE_BACK -> {
if (interceptBackKeyEnabled) {
true
} else {
super.onKeyDown(keyCode, event)
}
}
else -> super.onKeyDown(keyCode, event)
}
}
return super.onKeyDown(keyCode, event)
}
class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
hideSystemUI()
}
private fun hideSystemUI() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.decorView.windowInsetsController?.let { controller ->
controller.hide(WindowInsets.Type.systemBars())
controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
@Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
)
}
}
}
@@ -2,8 +2,12 @@
<!-- Base application theme. -->
<style name="Theme.readest" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:fitsSystemWindows">false</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>
</resources>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 87 KiB

@@ -0,0 +1,22 @@
[package]
name = "tauri-plugin-native-bridge"
version = "0.1.0"
authors = [ "You" ]
description = "a bridge between tauri app and native OS functionality"
edition = "2021"
rust-version = "1.77.2"
exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"]
links = "tauri-plugin-native-bridge"
[dependencies]
tauri = { version = "2.3.1" }
serde = "1.0"
thiserror = "2"
schemars = "0.8"
[build-dependencies]
tauri-plugin = { version = "2.0.4", features = ["build"] }
schemars = "0.8"
[target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies]
font-enumeration = "0.9.0"
@@ -0,0 +1 @@
# Tauri Plugin native-bridge
@@ -4,7 +4,7 @@ plugins {
}
android {
namespace = "com.plugin.safari_auth"
namespace = "com.readest.native_bridge"
compileSdk = 34
defaultConfig {
@@ -33,9 +33,9 @@ android {
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("androidx.browser:browser:1.8.0")
implementation("com.google.android.material:material:1.7.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
@@ -1,4 +1,4 @@
package com.plugin.safari_auth
package com.readest.native_bridge
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.plugin.safari_auth", appContext.packageName)
assertEquals("com.readest.native_bridge", appContext.packageName)
}
}
@@ -0,0 +1,6 @@
package com.readest.native_bridge
import android.util.Log
class NativeBridge {
}
@@ -0,0 +1,369 @@
package com.readest.native_bridge
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.util.Log
import android.os.Build
import android.view.View
import android.view.KeyEvent
import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowInsetsController
import android.graphics.Color
import android.webkit.WebView
import android.content.pm.ActivityInfo
import android.graphics.fonts.SystemFonts
import android.graphics.fonts.Font
import androidx.core.view.WindowCompat
import androidx.core.content.FileProvider
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.browser.customtabs.CustomTabsIntent
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke
import org.json.JSONArray
import java.io.*
@InvokeArg
class AuthRequestArgs {
var authUrl: String? = null
}
@InvokeArg
class CopyURIRequestArgs {
var uri: String? = null
var dst: String? = null
}
@InvokeArg
class InstallPackageRequestArgs {
var path: String? = null
}
@InvokeArg
class SetSystemUIVisibilityRequestArgs {
var visible: Boolean? = false
var darkMode: Boolean? = false
}
@InvokeArg
class InterceptKeysRequestArgs {
var volumeKeys: Boolean? = null
var backKey: Boolean? = null
}
@InvokeArg
class LockScreenOrientationRequestArgs {
var orientation: String? = null
}
interface KeyDownInterceptor {
fun interceptVolumeKeys(enabled: Boolean)
fun interceptBackKey(enabled: Boolean)
}
@TauriPlugin
class NativeBridgePlugin(private val activity: Activity): Plugin(activity) {
private val implementation = NativeBridge()
private var redirectScheme = "readest"
private var redirectHost = "auth-callback"
companion object {
var pendingInvoke: Invoke? = null
}
override fun load(webView: WebView) {
super.load(webView)
handleIntent(activity.intent)
}
override fun onNewIntent(intent: Intent) {
handleIntent(intent)
}
private fun handleIntent(intent: Intent?) {
val uri = intent?.data ?: return
Log.e("NativeBridgePlugin", "Received intent: $uri")
when {
uri.scheme == "readest" && uri.host == "auth-callback" -> {
val result = JSObject().apply {
put("redirectUrl", uri.toString())
}
pendingInvoke?.resolve(result)
pendingInvoke = null
}
intent.action == Intent.ACTION_VIEW -> {
try {
activity.contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
} catch (e: SecurityException) {
Log.e("NativeBridgePlugin", "Failed to take persistable URI permission: ${e.message}")
}
}
}
}
@Command
fun auth_with_custom_tab(invoke: Invoke) {
val args = invoke.parseArgs(AuthRequestArgs::class.java)
val uri = Uri.parse(args.authUrl)
val customTabsIntent = CustomTabsIntent.Builder().build()
customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
Log.d("NativeBridgePlugin", "Launching OAuth URL: ${args.authUrl}")
customTabsIntent.launchUrl(activity, uri)
pendingInvoke = invoke
}
@Command
fun copy_uri_to_path(invoke: Invoke) {
val args = invoke.parseArgs(CopyURIRequestArgs::class.java)
val ret = JSObject()
try {
val uri = Uri.parse(args.uri ?: "")
val dst = File(args.dst ?: "")
val inputStream = activity.contentResolver.openInputStream(uri)
if (inputStream != null) {
dst.outputStream().use { output ->
inputStream.use { input ->
input.copyTo(output)
}
}
ret.put("success", true)
} else {
ret.put("success", false)
ret.put("error", "Failed to open input stream from URI")
}
} catch (e: Exception) {
ret.put("success", false)
ret.put("error", e.message)
}
invoke.resolve(ret)
}
@Command
fun install_package(invoke: Invoke) {
val args = invoke.parseArgs(InstallPackageRequestArgs::class.java)
val ret = JSObject()
try {
val file = File(args.path ?: "")
if (file.exists()) {
val intent = Intent(Intent.ACTION_VIEW)
val apkUri = FileProvider.getUriForFile(activity, "${activity.packageName}.fileprovider", file)
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
val packageManager = activity.packageManager
val resolveInfos = packageManager.queryIntentActivities(intent, 0)
for (resolveInfo in resolveInfos) {
val packageName = resolveInfo.activityInfo.packageName
activity.grantUriPermission(packageName, apkUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
activity.startActivity(intent)
ret.put("success", true)
} else {
ret.put("success", false)
ret.put("error", "File does not exist")
}
} catch (e: Exception) {
ret.put("success", false)
ret.put("error", e.message)
}
invoke.resolve(ret)
}
@Command
fun set_system_ui_visibility(invoke: Invoke) {
val args = invoke.parseArgs(SetSystemUIVisibilityRequestArgs::class.java)
val visible = args.visible ?: false
var isDarkMode = args.darkMode ?: false
val ret = JSObject()
try {
val window = activity.window
val decorView = window.decorView
if (!visible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false)
val controller = window.insetsController
if (controller != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
} else {
val compatController = WindowCompat.getInsetsController(window, decorView)
compatController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
if (isDarkMode) {
controller.setSystemBarsAppearance(
0,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)
} else {
controller.setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)
}
if (visible) {
controller.show(WindowInsets.Type.statusBars())
controller.hide(WindowInsets.Type.navigationBars())
} else {
controller.hide(WindowInsets.Type.systemBars())
}
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val compatController = WindowCompat.getInsetsController(window, decorView)
compatController?.let {
it.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
if (!isDarkMode) {
it.isAppearanceLightStatusBars = true
} else {
it.isAppearanceLightStatusBars = false
}
if (visible) {
it.show(WindowInsetsCompat.Type.statusBars())
it.hide(WindowInsetsCompat.Type.navigationBars())
} else {
it.hide(WindowInsetsCompat.Type.systemBars())
}
}
} else {
@Suppress("DEPRECATION")
decorView.systemUiVisibility = buildList {
add(View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
add(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
add(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
add(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
add(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
if (!visible) {
add(View.SYSTEM_UI_FLAG_FULLSCREEN)
}
if (visible && !isDarkMode) {
add(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
}
}.reduce { acc, flag -> acc or flag }
}
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT
ret.put("success", true)
} catch (e: Exception) {
ret.put("success", false)
ret.put("error", e.message)
}
invoke.resolve(ret)
}
@Command
fun get_status_bar_height(invoke: Invoke) {
val ret = JSObject()
try {
val resourceId = activity.resources.getIdentifier("status_bar_height", "dimen", "android")
val height = if (resourceId > 0) {
activity.resources.getDimensionPixelSize(resourceId)
} else {
0
}
ret.put("height", height)
} catch (e: Exception) {
ret.put("height", -1)
ret.put("error", e.message)
}
invoke.resolve(ret)
}
@Command
fun get_sys_fonts_list(invoke: Invoke) {
val ret = JSObject()
try {
val fontList = mutableListOf<String>()
val fontFileList = mutableListOf<String>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val systemFonts = SystemFonts.getAvailableFonts()
for (font in systemFonts) {
val file = font.getFile()?: continue
if (file.isFile && (file.name.endsWith(".ttf", true) || file.name.endsWith(".otf", true))) {
fontFileList.add(file.name)
}
}
} else {
val fontDirs = listOf("/system/fonts", "/system/font", "/data/fonts")
for (dirPath in fontDirs) {
val dir = File(dirPath)
if (dir.exists() && dir.isDirectory) {
dir.listFiles()?.forEach { file ->
if (file.isFile && (file.name.endsWith(".ttf", true) || file.name.endsWith(".otf", true))) {
fontFileList.add(file.name)
}
}
}
}
}
for (fileFileName in fontFileList) {
var fontName = fileFileName
.replace(Regex("\\.(ttf|otf)$", RegexOption.IGNORE_CASE), "")
.trim()
fontList.add(fontName)
}
var fontDict = JSObject()
for (fontName in fontList) {
fontDict.put(fontName, fontName)
}
ret.put("fonts", fontDict)
} catch (e: Exception) {
ret.put("error", e.message)
}
invoke.resolve(ret)
}
@Command
fun intercept_keys(invoke: Invoke) {
val args = invoke.parseArgs(InterceptKeysRequestArgs::class.java)
if (activity is KeyDownInterceptor) {
when (args.backKey) {
true -> (activity as KeyDownInterceptor).interceptBackKey(true)
false -> (activity as KeyDownInterceptor).interceptBackKey(false)
else -> {}
}
when (args.volumeKeys) {
true -> (activity as KeyDownInterceptor).interceptVolumeKeys(true)
false -> (activity as KeyDownInterceptor).interceptVolumeKeys(false)
else -> {}
}
} else {
Log.e("NativeBridgePlugin", "Activity does not implement KeyDownInterceptor")
}
invoke.resolve()
}
@Command
fun lock_screen_orientation(invoke: Invoke) {
val args = invoke.parseArgs(LockScreenOrientationRequestArgs::class.java)
val orientation = args.orientation ?: "auto"
when (orientation) {
"portrait" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
"landscape" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
"auto" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
else -> {
invoke.reject("Invalid orientation mode")
return
}
}
invoke.resolve()
}
}
@@ -0,0 +1,19 @@
const COMMANDS: &[&str] = &[
"auth_with_safari",
"auth_with_custom_tab",
"copy_uri_to_path",
"use_background_audio",
"install_package",
"set_system_ui_visibility",
"get_status_bar_height",
"get_sys_fonts_list",
"intercept_keys",
"lock_screen_orientation",
];
fn main() {
tauri_plugin::Builder::new(COMMANDS)
.android_path("android")
.ios_path("ios")
.build();
}
@@ -0,0 +1,32 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "tauri-plugin-native-bridge",
platforms: [
.macOS(.v10_13),
.iOS(.v14),
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "tauri-plugin-native-bridge",
type: .static,
targets: ["tauri-plugin-native-bridge"])
],
dependencies: [
.package(name: "Tauri", path: "../.tauri/tauri-api")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "tauri-plugin-native-bridge",
dependencies: [
.byName(name: "Tauri")
],
path: "Sources")
]
)
@@ -1,3 +1,3 @@
# Tauri Plugin safari-auth
# Tauri Plugin native-bridge
A description of this package.
@@ -0,0 +1,433 @@
import AVFoundation
import AuthenticationServices
import CoreText
import MediaPlayer
import ObjectiveC
import SwiftRs
import Tauri
import UIKit
import WebKit
import os
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "NativeBridge")
func getLocalizedDisplayName(familyName: String) -> String? {
let fontDescriptor = CTFontDescriptorCreateWithAttributes(
[
kCTFontFamilyNameAttribute: familyName
] as CFDictionary)
let font = CTFontCreateWithFontDescriptor(fontDescriptor, 0.0, nil)
var actualLanguage: Unmanaged<CFString>?
if let localizedName = CTFontCopyLocalizedName(font, kCTFontFamilyNameKey, &actualLanguage) {
return localizedName as String
}
return nil
}
class SafariAuthRequestArgs: Decodable {
let authUrl: String
}
class UseBackgroundAudioRequestArgs: Decodable {
let enabled: Bool
}
class SetSystemUIVisibilityRequestArgs: Decodable {
let visible: Bool
let darkMode: Bool
}
class InterceptKeysRequestArgs: Decodable {
let backKey: Bool?
let volumeKeys: Bool?
}
class LockScreenOrientationRequestArgs: Decodable {
let orientation: String?
}
class VolumeKeyHandler: NSObject {
private var audioSession: AVAudioSession?
private var originalVolume: Float = 0.0
private var referenceVolume: Float = 0.5
private var previousVolume: Float = 0.5
private var volumeView: MPVolumeView?
private(set) var isIntercepting = false
private var webView: WKWebView?
private var volumeSlider: UISlider?
func startInterception(webView: WKWebView) {
if isIntercepting {
stopInterception()
}
logger.log("Starting volume key interception")
self.webView = webView
isIntercepting = true
audioSession = AVAudioSession.sharedInstance()
do {
try audioSession?.setCategory(.playback, mode: .default, options: [.mixWithOthers])
try audioSession?.setActive(true)
} catch {
logger.error("Failed to activate audio session: \(error)")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
guard let self = self else { return }
self.originalVolume = self.audioSession?.outputVolume ?? 0.1
if self.originalVolume > 0.9 {
self.referenceVolume = 0.9
} else if self.originalVolume < 0.1 {
self.referenceVolume = 0.1
} else {
self.referenceVolume = self.originalVolume
}
logger.log("Reference volume set to \(self.referenceVolume)")
self.previousVolume = self.referenceVolume
self.setSessionVolume(self.referenceVolume)
self.setupHiddenVolumeView()
self.audioSession?.addObserver(
self, forKeyPath: "outputVolume", options: [.new], context: nil)
}
audioSession?.addObserver(self, forKeyPath: "outputVolume", options: [.new], context: nil)
}
func stopInterception() {
if !isIntercepting {
return
}
logger.log("Stopping volume key interception")
isIntercepting = false
audioSession?.removeObserver(self, forKeyPath: "outputVolume")
DispatchQueue.main.async { [weak self] in
self?.setSessionVolume(self?.originalVolume ?? 0.1)
self?.volumeView?.removeFromSuperview()
self?.volumeView = nil
self?.volumeSlider = nil
}
}
private func setSessionVolume(_ volume: Float) {
DispatchQueue.main.async { [weak self] in
self?.volumeSlider?.value = volume
}
}
private func setupHiddenVolumeView() {
assert(Thread.isMainThread, "setupHiddenVolumeView must be called on main thread")
let frame = CGRect(x: -1000, y: -1000, width: 1, height: 1)
volumeView = MPVolumeView(frame: frame)
volumeSlider = volumeView?.subviews.first(where: { $0 is UISlider }) as? UISlider
if let window = UIApplication.shared.windows.first {
window.addSubview(volumeView!)
}
}
override func observeValue(
forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?
) {
if keyPath == "outputVolume", let audioSession = self.audioSession, isIntercepting {
let currentVolume = audioSession.outputVolume
if currentVolume > self.previousVolume {
DispatchQueue.main.async { [weak self] in
self?.webView?.evaluateJavaScript(
"window.onNativeKeyDown('VolumeUp');", completionHandler: nil)
}
} else if currentVolume < self.previousVolume {
DispatchQueue.main.async { [weak self] in
self?.webView?.evaluateJavaScript(
"window.onNativeKeyDown('VolumeDown');", completionHandler: nil)
}
}
self.previousVolume = currentVolume
self.setSessionVolume(self.referenceVolume)
}
}
}
class NativeBridgePlugin: Plugin {
private var webView: WKWebView?
private var authSession: ASWebAuthenticationSession?
private var isOrientationLocked = false
private var currentOrientationMask: UIInterfaceOrientationMask = .all
private var orientationObserver: NSObjectProtocol?
@objc public override func load(webview: WKWebView) {
self.webView = webview
logger.log("NativeBridgePlugin loaded")
NotificationCenter.default.addObserver(
self,
selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(appDidEnterBackground),
name: UIApplication.didEnterBackgroundNotification,
object: nil
)
}
@objc func appDidBecomeActive() {
if volumeKeyHandler != nil {
activateVolumeKeyInterception()
}
if orientationObserver != nil {
self.setupOrientationObserver()
}
}
@objc func appDidEnterBackground() {
if let handler = volumeKeyHandler, handler.isIntercepting {
handler.stopInterception()
}
}
func activateVolumeKeyInterception() {
if volumeKeyHandler == nil {
volumeKeyHandler = VolumeKeyHandler()
}
if let webView = self.webView {
volumeKeyHandler?.stopInterception()
volumeKeyHandler?.startInterception(webView: webView)
} else {
logger.warning("Cannot activate volume key interception: webView is nil")
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
private struct AssociatedKeys {
static var volumeKeyHandler = "volumeKeyHandler"
static var interceptingVolumeKeys = "interceptingVolumeKeys"
}
private var volumeKeyHandler: VolumeKeyHandler? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.volumeKeyHandler) as? VolumeKeyHandler
}
set {
objc_setAssociatedObject(
self, &AssociatedKeys.volumeKeyHandler, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
private var interceptingVolumeKeys: Bool {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.interceptingVolumeKeys) as? Bool
?? false
}
set {
objc_setAssociatedObject(
self, &AssociatedKeys.interceptingVolumeKeys, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
@objc public func use_background_audio(_ invoke: Invoke) {
do {
let args = try invoke.parseArgs(UseBackgroundAudioRequestArgs.self)
let enabled = args.enabled
let session = AVAudioSession.sharedInstance()
if enabled {
try session.setCategory(.playback, mode: .default, options: [.mixWithOthers])
try session.setActive(true)
logger.log("AVAudioSession activated")
} else {
try session.setActive(false)
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
logger.log("AVAudioSession deactivated")
}
invoke.resolve()
} catch {
logger.error("Failed to set up audio session: \(error)")
}
}
@objc public func auth_with_safari(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(SafariAuthRequestArgs.self)
let authUrl = URL(string: args.authUrl)!
authSession = ASWebAuthenticationSession(url: authUrl, callbackURLScheme: "readest") {
[weak self] callbackURL, error in
guard let strongSelf = self else { return }
if let error = error {
logger.error("Auth session error: \(error.localizedDescription)")
invoke.reject(error.localizedDescription)
return
}
if let callbackURL = callbackURL {
strongSelf.authSession?.cancel()
strongSelf.authSession = nil
invoke.resolve(["redirectUrl": callbackURL.absoluteString])
}
}
if #available(iOS 13.0, *) {
authSession?.presentationContextProvider = self
}
let started = authSession?.start() ?? false
logger.log("Auth session start result: \(started)")
}
@objc public func set_system_ui_visibility(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(SetSystemUIVisibilityRequestArgs.self)
let visible = args.visible
let darkMode = args.darkMode
DispatchQueue.main.async {
UIApplication.shared.isIdleTimerDisabled = !visible
UIApplication.shared.setStatusBarHidden(!visible, with: .none)
let windows = UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
let keyWindow = windows.first(where: { $0.isKeyWindow }) ?? windows.first
if let keyWindow = keyWindow {
keyWindow.overrideUserInterfaceStyle = darkMode ? .dark : .light
keyWindow.layoutIfNeeded()
} else {
logger.error("No key window found")
}
}
invoke.resolve(["success": true])
}
@objc public func get_sys_fonts_list(_ invoke: Invoke) throws {
var fontDict: [String: String] = [:]
for family in UIFont.familyNames.sorted() {
if let localized = getLocalizedDisplayName(familyName: family) {
fontDict[family] = localized
} else {
let fontNames = UIFont.fontNames(forFamilyName: family)
if fontNames.isEmpty {
fontDict[family] = family
} else {
for fontName in fontNames {
fontDict[fontName] = family
}
}
}
}
invoke.resolve(["fonts": fontDict])
}
@objc public func intercept_keys(_ invoke: Invoke) {
do {
let args = try invoke.parseArgs(InterceptKeysRequestArgs.self)
if let volumeKeys = args.volumeKeys {
if volumeKeys {
self.activateVolumeKeyInterception()
} else {
self.volumeKeyHandler?.stopInterception()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
self?.volumeKeyHandler = nil
}
}
}
invoke.resolve()
} catch {
invoke.reject(error.localizedDescription)
}
}
@objc public func lock_screen_orientation(_ invoke: Invoke) throws {
guard let args = try? invoke.parseArgs(LockScreenOrientationRequestArgs.self) else {
return invoke.reject("Invalid arguments")
}
DispatchQueue.main.async {
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
let orientation = args.orientation ?? "auto"
switch orientation.lowercased() {
case "portrait":
self.isOrientationLocked = true
self.currentOrientationMask = .portrait
self.forceOrientation(.portrait)
self.setupOrientationObserver()
case "landscape":
self.isOrientationLocked = true
self.currentOrientationMask = .landscape
self.forceOrientation(.landscapeRight)
self.setupOrientationObserver()
case "auto":
self.isOrientationLocked = false
self.currentOrientationMask = .all
default:
invoke.reject("Invalid orientation mode")
return
}
invoke.resolve()
}
}
private func forceOrientation(_ orientation: UIInterfaceOrientation) {
if #available(iOS 16.0, *) {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
let orientationMask: UIInterfaceOrientationMask =
orientation.isPortrait ? .portrait : .landscape
for window in windowScene.windows {
if let rootVC = window.rootViewController {
rootVC.setNeedsUpdateOfSupportedInterfaceOrientations()
}
}
windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientationMask)) { error in
print("Orientation update error: \(error.localizedDescription)")
}
}
} else {
UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
UIViewController.attemptRotationToDeviceOrientation()
}
}
private func setupOrientationObserver() {
orientationObserver = NotificationCenter.default.addObserver(
forName: UIDevice.orientationDidChangeNotification,
object: nil,
queue: .main
) { [weak self] _ in
guard let self = self, self.isOrientationLocked else { return }
if self.currentOrientationMask == .portrait {
self.forceOrientation(.portrait)
} else if self.currentOrientationMask == .landscape {
self.forceOrientation(.landscapeRight)
}
}
}
}
@_cdecl("init_plugin_native_bridge")
func initPlugin() -> Plugin {
return NativeBridgePlugin()
}
@available(iOS 13.0, *)
extension NativeBridgePlugin: ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return UIApplication.shared.windows.first ?? UIWindow()
}
}
@@ -0,0 +1,8 @@
import XCTest
@testable import NativeBridgePlugin
final class NativeBridgePluginTests: XCTestCase {
func testNativeBridge() throws {
let plugin = NativeBridgePlugin()
}
}
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-auth-with-custom-tab"
description = "Enables the auth_with_custom_tab command without any pre-configured scope."
commands.allow = ["auth_with_custom_tab"]
[[permission]]
identifier = "deny-auth-with-custom-tab"
description = "Denies the auth_with_custom_tab command without any pre-configured scope."
commands.deny = ["auth_with_custom_tab"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-copy-uri-to-path"
description = "Enables the copy_uri_to_path command without any pre-configured scope."
commands.allow = ["copy_uri_to_path"]
[[permission]]
identifier = "deny-copy-uri-to-path"
description = "Denies the copy_uri_to_path command without any pre-configured scope."
commands.deny = ["copy_uri_to_path"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-get-status-bar-height"
description = "Enables the get_status_bar_height command without any pre-configured scope."
commands.allow = ["get_status_bar_height"]
[[permission]]
identifier = "deny-get-status-bar-height"
description = "Denies the get_status_bar_height command without any pre-configured scope."
commands.deny = ["get_status_bar_height"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-get-sys-fonts-list"
description = "Enables the get_sys_fonts_list command without any pre-configured scope."
commands.allow = ["get_sys_fonts_list"]
[[permission]]
identifier = "deny-get-sys-fonts-list"
description = "Denies the get_sys_fonts_list command without any pre-configured scope."
commands.deny = ["get_sys_fonts_list"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-install-package"
description = "Enables the install_package command without any pre-configured scope."
commands.allow = ["install_package"]
[[permission]]
identifier = "deny-install-package"
description = "Denies the install_package command without any pre-configured scope."
commands.deny = ["install_package"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-intercept-keys"
description = "Enables the intercept_keys command without any pre-configured scope."
commands.allow = ["intercept_keys"]
[[permission]]
identifier = "deny-intercept-keys"
description = "Denies the intercept_keys command without any pre-configured scope."
commands.deny = ["intercept_keys"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-lock-screen-orientation"
description = "Enables the lock_screen_orientation command without any pre-configured scope."
commands.allow = ["lock_screen_orientation"]
[[permission]]
identifier = "deny-lock-screen-orientation"
description = "Denies the lock_screen_orientation command without any pre-configured scope."
commands.deny = ["lock_screen_orientation"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-set-system-ui-visibility"
description = "Enables the set_system_ui_visibility command without any pre-configured scope."
commands.allow = ["set_system_ui_visibility"]
[[permission]]
identifier = "deny-set-system-ui-visibility"
description = "Denies the set_system_ui_visibility command without any pre-configured scope."
commands.deny = ["set_system_ui_visibility"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-use-background-audio"
description = "Enables the use_background_audio command without any pre-configured scope."
commands.allow = ["use_background_audio"]
[[permission]]
identifier = "deny-use-background-audio"
description = "Denies the use_background_audio command without any pre-configured scope."
commands.deny = ["use_background_audio"]
@@ -0,0 +1,286 @@
## Default Permission
Default permissions for the plugin
#### This default permission set includes the following:
- `allow-auth-with-safari`
- `allow-auth-with-custom-tab`
- `allow-copy-uri-to-path`
- `allow-use-background-audio`
- `allow-install-package`
- `allow-set-system-ui-visibility`
- `allow-get-status-bar-height`
- `allow-get-sys-fonts-list`
- `allow-intercept-keys`
- `allow-lock-screen-orientation`
## Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`native-bridge:allow-auth-with-custom-tab`
</td>
<td>
Enables the auth_with_custom_tab command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-auth-with-custom-tab`
</td>
<td>
Denies the auth_with_custom_tab command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-auth-with-safari`
</td>
<td>
Enables the auth_with_safari command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-auth-with-safari`
</td>
<td>
Denies the auth_with_safari command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-copy-uri-to-path`
</td>
<td>
Enables the copy_uri_to_path command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-copy-uri-to-path`
</td>
<td>
Denies the copy_uri_to_path command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-get-status-bar-height`
</td>
<td>
Enables the get_status_bar_height command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-get-status-bar-height`
</td>
<td>
Denies the get_status_bar_height command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-get-sys-fonts-list`
</td>
<td>
Enables the get_sys_fonts_list command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-get-sys-fonts-list`
</td>
<td>
Denies the get_sys_fonts_list command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-install-package`
</td>
<td>
Enables the install_package command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-install-package`
</td>
<td>
Denies the install_package command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-intercept-keys`
</td>
<td>
Enables the intercept_keys command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-intercept-keys`
</td>
<td>
Denies the intercept_keys command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-lock-screen-orientation`
</td>
<td>
Enables the lock_screen_orientation command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-lock-screen-orientation`
</td>
<td>
Denies the lock_screen_orientation command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-set-system-ui-visibility`
</td>
<td>
Enables the set_system_ui_visibility command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-set-system-ui-visibility`
</td>
<td>
Denies the set_system_ui_visibility command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:allow-use-background-audio`
</td>
<td>
Enables the use_background_audio command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`native-bridge:deny-use-background-audio`
</td>
<td>
Denies the use_background_audio command without any pre-configured scope.
</td>
</tr>
</table>
@@ -0,0 +1,14 @@
[default]
description = "Default permissions for the plugin"
permissions = [
"allow-auth-with-safari",
"allow-auth-with-custom-tab",
"allow-copy-uri-to-path",
"allow-use-background-audio",
"allow-install-package",
"allow-set-system-ui-visibility",
"allow-get-status-bar-height",
"allow-get-sys-fonts-list",
"allow-intercept-keys",
"allow-lock-screen-orientation",
]
@@ -49,7 +49,7 @@
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.",
"description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [
"string",
"null"
@@ -111,7 +111,7 @@
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.",
"description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [
"string",
"null"
@@ -294,20 +294,131 @@
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "Enables the auth_with_custom_tab command without any pre-configured scope.",
"type": "string",
"const": "allow-auth-with-custom-tab",
"markdownDescription": "Enables the auth_with_custom_tab command without any pre-configured scope."
},
{
"description": "Denies the auth_with_custom_tab command without any pre-configured scope.",
"type": "string",
"const": "deny-auth-with-custom-tab",
"markdownDescription": "Denies the auth_with_custom_tab command without any pre-configured scope."
},
{
"description": "Enables the auth_with_safari command without any pre-configured scope.",
"type": "string",
"const": "allow-auth-with-safari"
"const": "allow-auth-with-safari",
"markdownDescription": "Enables the auth_with_safari command without any pre-configured scope."
},
{
"description": "Denies the auth_with_safari command without any pre-configured scope.",
"type": "string",
"const": "deny-auth-with-safari"
"const": "deny-auth-with-safari",
"markdownDescription": "Denies the auth_with_safari command without any pre-configured scope."
},
{
"description": "Default permissions for the plugin",
"description": "Enables the copy_uri_to_path command without any pre-configured scope.",
"type": "string",
"const": "default"
"const": "allow-copy-uri-to-path",
"markdownDescription": "Enables the copy_uri_to_path command without any pre-configured scope."
},
{
"description": "Denies the copy_uri_to_path command without any pre-configured scope.",
"type": "string",
"const": "deny-copy-uri-to-path",
"markdownDescription": "Denies the copy_uri_to_path command without any pre-configured scope."
},
{
"description": "Enables the get_status_bar_height command without any pre-configured scope.",
"type": "string",
"const": "allow-get-status-bar-height",
"markdownDescription": "Enables the get_status_bar_height command without any pre-configured scope."
},
{
"description": "Denies the get_status_bar_height command without any pre-configured scope.",
"type": "string",
"const": "deny-get-status-bar-height",
"markdownDescription": "Denies the get_status_bar_height command without any pre-configured scope."
},
{
"description": "Enables the get_sys_fonts_list command without any pre-configured scope.",
"type": "string",
"const": "allow-get-sys-fonts-list",
"markdownDescription": "Enables the get_sys_fonts_list command without any pre-configured scope."
},
{
"description": "Denies the get_sys_fonts_list command without any pre-configured scope.",
"type": "string",
"const": "deny-get-sys-fonts-list",
"markdownDescription": "Denies the get_sys_fonts_list command without any pre-configured scope."
},
{
"description": "Enables the install_package command without any pre-configured scope.",
"type": "string",
"const": "allow-install-package",
"markdownDescription": "Enables the install_package command without any pre-configured scope."
},
{
"description": "Denies the install_package command without any pre-configured scope.",
"type": "string",
"const": "deny-install-package",
"markdownDescription": "Denies the install_package command without any pre-configured scope."
},
{
"description": "Enables the intercept_keys command without any pre-configured scope.",
"type": "string",
"const": "allow-intercept-keys",
"markdownDescription": "Enables the intercept_keys command without any pre-configured scope."
},
{
"description": "Denies the intercept_keys command without any pre-configured scope.",
"type": "string",
"const": "deny-intercept-keys",
"markdownDescription": "Denies the intercept_keys command without any pre-configured scope."
},
{
"description": "Enables the lock_screen_orientation command without any pre-configured scope.",
"type": "string",
"const": "allow-lock-screen-orientation",
"markdownDescription": "Enables the lock_screen_orientation command without any pre-configured scope."
},
{
"description": "Denies the lock_screen_orientation command without any pre-configured scope.",
"type": "string",
"const": "deny-lock-screen-orientation",
"markdownDescription": "Denies the lock_screen_orientation command without any pre-configured scope."
},
{
"description": "Enables the set_system_ui_visibility command without any pre-configured scope.",
"type": "string",
"const": "allow-set-system-ui-visibility",
"markdownDescription": "Enables the set_system_ui_visibility command without any pre-configured scope."
},
{
"description": "Denies the set_system_ui_visibility command without any pre-configured scope.",
"type": "string",
"const": "deny-set-system-ui-visibility",
"markdownDescription": "Denies the set_system_ui_visibility command without any pre-configured scope."
},
{
"description": "Enables the use_background_audio command without any pre-configured scope.",
"type": "string",
"const": "allow-use-background-audio",
"markdownDescription": "Enables the use_background_audio command without any pre-configured scope."
},
{
"description": "Denies the use_background_audio command without any pre-configured scope.",
"type": "string",
"const": "deny-use-background-audio",
"markdownDescription": "Denies the use_background_audio command without any pre-configured scope."
},
{
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`",
"type": "string",
"const": "default",
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`"
}
]
}
@@ -0,0 +1,83 @@
use tauri::{command, AppHandle, Runtime};
use crate::models::*;
use crate::NativeBridgeExt;
use crate::Result;
#[command]
pub(crate) async fn auth_with_safari<R: Runtime>(
app: AppHandle<R>,
payload: AuthRequest,
) -> Result<AuthResponse> {
app.native_bridge().auth_with_safari(payload)
}
#[command]
pub(crate) async fn auth_with_custom_tab<R: Runtime>(
app: AppHandle<R>,
payload: AuthRequest,
) -> Result<AuthResponse> {
app.native_bridge().auth_with_custom_tab(payload)
}
#[command]
pub(crate) async fn copy_uri_to_path<R: Runtime>(
app: AppHandle<R>,
payload: CopyURIRequest,
) -> Result<CopyURIResponse> {
app.native_bridge().copy_uri_to_path(payload)
}
#[command]
pub(crate) async fn use_background_audio<R: Runtime>(
app: AppHandle<R>,
payload: UseBackgroundAudioRequest,
) -> Result<()> {
app.native_bridge().use_background_audio(payload)
}
#[command]
pub(crate) async fn install_package<R: Runtime>(
app: AppHandle<R>,
payload: InstallPackageRequest,
) -> Result<InstallPackageResponse> {
app.native_bridge().install_package(payload)
}
#[command]
pub(crate) async fn set_system_ui_visibility<R: Runtime>(
app: AppHandle<R>,
payload: SetSystemUIVisibilityRequest,
) -> Result<SetSystemUIVisibilityResponse> {
app.native_bridge().set_system_ui_visibility(payload)
}
#[command]
pub(crate) async fn get_status_bar_height<R: Runtime>(
app: AppHandle<R>,
) -> Result<GetStatusBarHeightResponse> {
app.native_bridge().get_status_bar_height()
}
#[command]
pub(crate) async fn get_sys_fonts_list<R: Runtime>(
app: AppHandle<R>,
) -> Result<GetSysFontsListResponse> {
app.native_bridge().get_sys_fonts_list()
}
#[command]
pub(crate) async fn intercept_keys<R: Runtime>(
app: AppHandle<R>,
payload: InterceptKeysRequest,
) -> Result<()> {
app.native_bridge().intercept_keys(payload)
}
#[command]
pub(crate) async fn lock_screen_orientation<R: Runtime>(
app: AppHandle<R>,
payload: LockScreenOrientationRequest,
) -> Result<()> {
app.native_bridge().lock_screen_orientation(payload)
}
@@ -0,0 +1,76 @@
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use tauri::{plugin::PluginApi, AppHandle, Runtime};
use crate::models::*;
pub fn init<R: Runtime, C: DeserializeOwned>(
app: &AppHandle<R>,
_api: PluginApi<R, C>,
) -> crate::Result<NativeBridge<R>> {
Ok(NativeBridge(app.clone()))
}
/// Access to the native-bridge APIs.
pub struct NativeBridge<R: Runtime>(AppHandle<R>);
impl<R: Runtime> NativeBridge<R> {
pub fn auth_with_safari(&self, _payload: AuthRequest) -> crate::Result<AuthResponse> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn auth_with_custom_tab(&self, _payload: AuthRequest) -> crate::Result<AuthResponse> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn copy_uri_to_path(&self, _payload: CopyURIRequest) -> crate::Result<CopyURIResponse> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn use_background_audio(&self, _payload: UseBackgroundAudioRequest) -> crate::Result<()> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn install_package(
&self,
_payload: InstallPackageRequest,
) -> crate::Result<InstallPackageResponse> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn set_system_ui_visibility(
&self,
_payload: SetSystemUIVisibilityRequest,
) -> crate::Result<SetSystemUIVisibilityResponse> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn get_status_bar_height(&self) -> crate::Result<GetStatusBarHeightResponse> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn get_sys_fonts_list(&self) -> crate::Result<GetSysFontsListResponse> {
let font_collection = font_enumeration::Collection::new().unwrap();
let mut fonts = HashMap::new();
for font in font_collection.all() {
if cfg!(target_os = "windows") {
// FIXME: temporarily disable font name with style for windows
fonts.insert(font.family_name.clone(), font.family_name.clone());
} else {
fonts.insert(font.font_name.clone(), font.family_name.clone());
}
}
Ok(GetSysFontsListResponse { fonts, error: None })
}
pub fn intercept_keys(&self, _payload: InterceptKeysRequest) -> crate::Result<()> {
Err(crate::Error::UnsupportedPlatformError)
}
pub fn lock_screen_orientation(
&self,
_payload: LockScreenOrientationRequest,
) -> crate::Result<()> {
Err(crate::Error::UnsupportedPlatformError)
}
}
@@ -6,6 +6,8 @@ pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
#[error("Unsupported platform for this plugin")]
UnsupportedPlatformError,
#[error("Native bridge error: {0}")]
NativeBridgeError(String),
#[error(transparent)]
Io(#[from] std::io::Error),
#[cfg(mobile)]
@@ -0,0 +1,60 @@
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
};
pub use models::*;
#[cfg(desktop)]
mod desktop;
#[cfg(mobile)]
mod mobile;
mod commands;
mod error;
mod models;
mod platform;
pub use error::{Error, Result};
#[cfg(desktop)]
use desktop::NativeBridge;
#[cfg(mobile)]
use mobile::NativeBridge;
/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the native-bridge APIs.
pub trait NativeBridgeExt<R: Runtime> {
fn native_bridge(&self) -> &NativeBridge<R>;
}
impl<R: Runtime, T: Manager<R>> crate::NativeBridgeExt<R> for T {
fn native_bridge(&self) -> &NativeBridge<R> {
self.state::<NativeBridge<R>>().inner()
}
}
/// Initializes the plugin.
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("native-bridge")
.invoke_handler(tauri::generate_handler![
commands::auth_with_safari,
commands::auth_with_custom_tab,
commands::copy_uri_to_path,
commands::use_background_audio,
commands::install_package,
commands::set_system_ui_visibility,
commands::get_status_bar_height,
commands::get_sys_fonts_list,
commands::intercept_keys,
commands::lock_screen_orientation,
])
.setup(|app, api| {
#[cfg(mobile)]
let native_bridge = mobile::init(app, api)?;
#[cfg(desktop)]
let native_bridge = desktop::init(app, api)?;
app.manage(native_bridge);
Ok(())
})
.build()
}
@@ -0,0 +1,114 @@
use serde::de::DeserializeOwned;
use tauri::{
plugin::{PluginApi, PluginHandle},
AppHandle, Runtime,
};
use crate::models::*;
#[cfg(target_os = "ios")]
tauri::ios_plugin_binding!(init_plugin_native_bridge);
// initializes the Kotlin or Swift plugin classes
pub fn init<R: Runtime, C: DeserializeOwned>(
_app: &AppHandle<R>,
api: PluginApi<R, C>,
) -> crate::Result<NativeBridge<R>> {
#[cfg(target_os = "android")]
let handle = api.register_android_plugin("com.readest.native_bridge", "NativeBridgePlugin")?;
#[cfg(target_os = "ios")]
let handle = api.register_ios_plugin(init_plugin_native_bridge)?;
Ok(NativeBridge(handle))
}
/// Access to the native-bridge APIs.
pub struct NativeBridge<R: Runtime>(PluginHandle<R>);
impl<R: Runtime> NativeBridge<R> {
pub fn auth_with_safari(&self, payload: AuthRequest) -> crate::Result<AuthResponse> {
self.0
.run_mobile_plugin("auth_with_safari", payload)
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn auth_with_custom_tab(&self, payload: AuthRequest) -> crate::Result<AuthResponse> {
self.0
.run_mobile_plugin("auth_with_custom_tab", payload)
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn copy_uri_to_path(&self, payload: CopyURIRequest) -> crate::Result<CopyURIResponse> {
self.0
.run_mobile_plugin("copy_uri_to_path", payload)
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn use_background_audio(&self, payload: UseBackgroundAudioRequest) -> crate::Result<()> {
self.0
.run_mobile_plugin("use_background_audio", payload)
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn install_package(
&self,
payload: InstallPackageRequest,
) -> crate::Result<InstallPackageResponse> {
self.0
.run_mobile_plugin("install_package", payload)
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn set_system_ui_visibility(
&self,
payload: SetSystemUIVisibilityRequest,
) -> crate::Result<SetSystemUIVisibilityResponse> {
self.0
.run_mobile_plugin("set_system_ui_visibility", payload)
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn get_status_bar_height(&self) -> crate::Result<GetStatusBarHeightResponse> {
self.0
.run_mobile_plugin("get_status_bar_height", ())
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn get_sys_fonts_list(&self) -> crate::Result<GetSysFontsListResponse> {
self.0
.run_mobile_plugin("get_sys_fonts_list", ())
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn intercept_keys(&self, payload: InterceptKeysRequest) -> crate::Result<()> {
self.0
.run_mobile_plugin("intercept_keys", payload)
.map_err(Into::into)
}
}
impl<R: Runtime> NativeBridge<R> {
pub fn lock_screen_orientation(
&self,
payload: LockScreenOrientationRequest,
) -> crate::Result<()> {
self.0
.run_mobile_plugin("lock_screen_orientation", payload)
.map_err(Into::into)
}
}
@@ -0,0 +1,88 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthRequest {
pub auth_url: String,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthResponse {
pub redirect_url: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CopyURIRequest {
pub uri: String,
pub dst: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CopyURIResponse {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UseBackgroundAudioRequest {
pub enabled: bool,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InstallPackageRequest {
pub path: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InstallPackageResponse {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetSystemUIVisibilityRequest {
pub visible: bool,
pub dark_mode: bool,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetSystemUIVisibilityResponse {
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetStatusBarHeightResponse {
pub height: u32,
pub error: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetSysFontsListResponse {
pub fonts: HashMap<String, String>,
pub error: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InterceptKeysRequest {
pub volume_keys: Option<bool>,
pub back_key: Option<bool>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LockScreenOrientationRequest {
pub orientation: String,
}
@@ -0,0 +1,2 @@
#[cfg(target_os = "macos")]
pub mod macos;
@@ -1,17 +0,0 @@
[package]
name = "tauri-plugin-safari-auth"
version = "0.1.0"
authors = [ "chrox" ]
description = "OAuth with ASWebAuthenticationSession on iOS"
edition = "2021"
rust-version = "1.77.2"
exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"]
links = "tauri-plugin-safari-auth"
[dependencies]
tauri = { version = "2.2.4" }
serde = "1.0"
thiserror = "2"
[build-dependencies]
tauri-plugin = { version = "2.0.3", features = ["build"] }
@@ -1 +0,0 @@
# Tauri Plugin safari-auth
@@ -1,10 +0,0 @@
package com.plugin.safari_auth
import android.util.Log
class Example {
fun pong(value: String): String {
Log.i("Pong", value)
return value
}
}
@@ -1,28 +0,0 @@
package com.plugin.safari_auth
import android.app.Activity
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke
@InvokeArg
class PingArgs {
var value: String? = null
}
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
private val implementation = Example()
@Command
fun ping(invoke: Invoke) {
val args = invoke.parseArgs(PingArgs::class.java)
val ret = JSObject()
ret.put("value", implementation.pong(args.value ?: "default value :("))
invoke.resolve(ret)
}
}
@@ -1,8 +0,0 @@
const COMMANDS: &[&str] = &["auth_with_safari"];
fn main() {
tauri_plugin::Builder::new(COMMANDS)
.android_path("android")
.ios_path("ios")
.build();
}
@@ -1,32 +0,0 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "tauri-plugin-safari-auth",
platforms: [
.macOS(.v10_13),
.iOS(.v13),
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "tauri-plugin-safari-auth",
type: .static,
targets: ["tauri-plugin-safari-auth"]),
],
dependencies: [
.package(name: "Tauri", path: "../.tauri/tauri-api")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "tauri-plugin-safari-auth",
dependencies: [
.byName(name: "Tauri")
],
path: "Sources")
]
)
@@ -1,59 +0,0 @@
import AuthenticationServices
import SwiftRs
import Tauri
import UIKit
import WebKit
class RequestArgs: Decodable {
let authUrl: String
}
struct Response: Encodable {
let redirectUrl: String
}
class SafariAuthPlugin: Plugin {
private var authSession: ASWebAuthenticationSession?
@objc public func auth_with_safari(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(RequestArgs.self)
let authUrl = URL(string: args.authUrl)!
authSession = ASWebAuthenticationSession(url: authUrl, callbackURLScheme: "readest") {
[weak self] callbackURL, error in
guard let strongSelf = self else { return }
if let error = error {
Logger.error("Auth session error: \(error.localizedDescription)")
invoke.reject(error.localizedDescription)
return
}
if let callbackURL = callbackURL {
Logger.info("Auth session callback URL: \(callbackURL.absoluteString)")
strongSelf.authSession?.cancel()
strongSelf.authSession = nil
invoke.resolve(["redirectUrl": callbackURL.absoluteString])
}
}
if #available(iOS 13.0, *) {
authSession?.presentationContextProvider = self
}
let started = authSession?.start() ?? false
Logger.info("Auth session start result: \(started)")
}
}
@_cdecl("init_plugin_safari_auth")
func initPlugin() -> Plugin {
return SafariAuthPlugin()
}
@available(iOS 13.0, *)
extension SafariAuthPlugin: ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return UIApplication.shared.windows.first ?? UIWindow()
}
}
@@ -1,8 +0,0 @@
import XCTest
@testable import SafariAuthPlugin
final class SafariAuthPluginTests: XCTestCase {
func testExample() throws {
let plugin = SafariAuthPlugin()
}
}
@@ -1,41 +0,0 @@
## Default Permission
Default permissions for the plugin
- `allow-auth-with-safari`
## Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`safari-auth:allow-auth-with-safari`
</td>
<td>
Enables the auth_with_safari command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`safari-auth:deny-auth-with-safari`
</td>
<td>
Denies the auth_with_safari command without any pre-configured scope.
</td>
</tr>
</table>
@@ -1,3 +0,0 @@
[default]
description = "Default permissions for the plugin"
permissions = ["allow-auth-with-safari"]
@@ -1,13 +0,0 @@
use tauri::{command, AppHandle, Runtime};
use crate::models::*;
use crate::Result;
use crate::SafariAuthExt;
#[command]
pub(crate) async fn auth_with_safari<R: Runtime>(
app: AppHandle<R>,
payload: SafariAuthRequest,
) -> Result<SafariAuthResponse> {
app.safari_auth().auth_with_safari(payload)
}
@@ -1,23 +0,0 @@
use serde::de::DeserializeOwned;
use tauri::{plugin::PluginApi, AppHandle, Runtime};
use crate::models::*;
pub fn init<R: Runtime, C: DeserializeOwned>(
app: &AppHandle<R>,
_api: PluginApi<R, C>,
) -> crate::Result<SafariAuth<R>> {
Ok(SafariAuth(app.clone()))
}
/// Access to the safari-auth APIs.
pub struct SafariAuth<R: Runtime>(AppHandle<R>);
impl<R: Runtime> SafariAuth<R> {
pub fn auth_with_safari(
&self,
_payload: SafariAuthRequest,
) -> crate::Result<SafariAuthResponse> {
Err(crate::Error::UnsupportedPlatformError)
}
}
@@ -1,48 +0,0 @@
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
};
pub use models::*;
#[cfg(desktop)]
mod desktop;
#[cfg(mobile)]
mod mobile;
mod commands;
mod error;
mod models;
pub use error::{Error, Result};
#[cfg(desktop)]
use desktop::SafariAuth;
#[cfg(mobile)]
use mobile::SafariAuth;
/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the safari-auth APIs.
pub trait SafariAuthExt<R: Runtime> {
fn safari_auth(&self) -> &SafariAuth<R>;
}
impl<R: Runtime, T: Manager<R>> crate::SafariAuthExt<R> for T {
fn safari_auth(&self) -> &SafariAuth<R> {
self.state::<SafariAuth<R>>().inner()
}
}
/// Initializes the plugin.
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("safari-auth")
.invoke_handler(tauri::generate_handler![commands::auth_with_safari])
.setup(|app, api| {
#[cfg(mobile)]
let safari_auth = mobile::init(app, api)?;
#[cfg(desktop)]
let safari_auth = desktop::init(app, api)?;
app.manage(safari_auth);
Ok(())
})
.build()
}
@@ -1,37 +0,0 @@
use serde::de::DeserializeOwned;
use tauri::{
plugin::{PluginApi, PluginHandle},
AppHandle, Runtime,
};
use crate::models::*;
#[cfg(target_os = "ios")]
tauri::ios_plugin_binding!(init_plugin_safari_auth);
// initializes the Kotlin or Swift plugin classes
pub fn init<R: Runtime, C: DeserializeOwned>(
_app: &AppHandle<R>,
api: PluginApi<R, C>,
) -> crate::Result<SafariAuth<R>> {
#[cfg(target_os = "android")]
let handle = api.register_android_plugin("com.bilingify.safari_auth", "ExamplePlugin")?;
#[cfg(target_os = "ios")]
let handle = api.register_ios_plugin(init_plugin_safari_auth)?;
Ok(SafariAuth(handle))
}
/// Access to the safari-auth APIs.
pub struct SafariAuth<R: Runtime>(PluginHandle<R>);
impl<R: Runtime> SafariAuth<R> {
pub fn auth_with_safari(
&self,
payload: SafariAuthRequest,
) -> crate::Result<SafariAuthResponse> {
self.0
.run_mobile_plugin("auth_with_safari", payload)
.map_err(Into::into)
}
}
@@ -1,13 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SafariAuthRequest {
pub auth_url: String,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SafariAuthResponse {
pub redirect_url: String,
}
+109 -62
View File
@@ -6,13 +6,6 @@ extern crate cocoa;
#[macro_use]
extern crate objc;
#[cfg(target_os = "macos")]
mod menu;
#[cfg(target_os = "macos")]
mod traffic_light;
#[cfg(target_os = "macos")]
use traffic_light::set_traffic_lights;
#[cfg(target_os = "macos")]
use tauri::TitleBarStyle;
@@ -23,6 +16,8 @@ use tauri::{AppHandle, Listener, Manager, Url};
#[cfg(desktop)]
use tauri_plugin_fs::FsExt;
#[cfg(target_os = "macos")]
mod macos;
mod transfer_file;
use tauri::{command, Emitter, WebviewUrl, WebviewWindowBuilder, Window};
use tauri_plugin_oauth::start;
@@ -46,12 +41,40 @@ fn allow_file_in_scopes(app: &AppHandle, files: Vec<PathBuf>) {
}
}
#[cfg(desktop)]
fn get_files_from_argv(argv: Vec<String>) -> Vec<PathBuf> {
let mut files = Vec::new();
// NOTICE: `args` may include URL protocol (`your-app-protocol://`)
// or arguments (`--`) if your app supports them.
// files may also be passed as `file://path/to/file`
for (_, maybe_file) in argv.iter().enumerate().skip(1) {
// skip flags like -f or --flag
if maybe_file.starts_with("-") {
continue;
}
// handle `file://` path urls and skip other urls
if let Ok(url) = Url::parse(maybe_file) {
if let Ok(path) = url.to_file_path() {
files.push(path);
} else {
files.push(PathBuf::from(maybe_file))
}
} else {
files.push(PathBuf::from(maybe_file))
}
}
files
}
#[cfg(desktop)]
fn set_window_open_with_files(app: &AppHandle, files: Vec<PathBuf>) {
let files = files
.into_iter()
.map(|f| {
let file = f.to_string_lossy().replace("\\", "\\\\");
let file = f
.to_string_lossy()
.replace("\\", "\\\\")
.replace("\"", "\\\"");
format!("\"{file}\"",)
})
.collect::<Vec<_>>()
@@ -63,6 +86,15 @@ fn set_window_open_with_files(app: &AppHandle, files: Vec<PathBuf>) {
}
}
#[cfg(desktop)]
fn set_rounded_window(app: &AppHandle, rounded: bool) {
let window = app.get_webview_window("main").unwrap();
let script = format!("window.IS_ROUNDED = {};", rounded);
if let Err(e) = window.eval(&script) {
eprintln!("Failed to set IS_ROUNDED variable: {}", e);
}
}
#[command]
async fn start_server(window: Window) -> Result<u16, String> {
start(move |url| {
@@ -73,17 +105,6 @@ async fn start_server(window: Window) -> Result<u16, String> {
.map_err(|err| err.to_string())
}
#[cfg(desktop)]
#[command]
async fn list_fonts() -> Result<Vec<String>, String> {
let font_collection = font_enumeration::Collection::new().unwrap();
let mut fonts = Vec::new();
for font in font_collection.all() {
fonts.push(font.family_name.clone());
}
Ok(fonts)
}
#[derive(Clone, serde::Serialize)]
#[allow(dead_code)]
struct Payload {
@@ -101,15 +122,18 @@ pub fn run() {
download_file,
upload_file,
#[cfg(target_os = "macos")]
set_traffic_lights,
#[cfg(desktop)]
list_fonts
macos::safari_auth::auth_with_safari,
#[cfg(target_os = "macos")]
macos::apple_auth::start_apple_sign_in,
#[cfg(target_os = "macos")]
macos::traffic_light::set_traffic_lights,
])
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_native_bridge::init())
.plugin(tauri_plugin_fs::init());
#[cfg(desktop)]
@@ -118,6 +142,10 @@ pub fn run() {
.get_webview_window("main")
.expect("no main window")
.set_focus();
let files = get_files_from_argv(argv.clone());
if !files.is_empty() {
allow_file_in_scopes(app, files.clone());
}
app.emit("single-instance", Payload { args: argv, cwd })
.unwrap();
}));
@@ -131,14 +159,14 @@ pub fn run() {
let builder = builder.plugin(tauri_plugin_window_state::Builder::default().build());
#[cfg(target_os = "macos")]
let builder = builder.plugin(traffic_light::init());
let builder = builder.plugin(macos::traffic_light::init());
#[cfg(target_os = "macos")]
let builder = builder.plugin(macos::safari_auth::init());
#[cfg(target_os = "ios")]
let builder = builder.plugin(tauri_plugin_sign_in_with_apple::init());
#[cfg(target_os = "ios")]
let builder = builder.plugin(tauri_plugin_safari_auth::init());
#[cfg(any(target_os = "ios", target_os = "android"))]
let builder = builder.plugin(tauri_plugin_haptics::init());
@@ -146,26 +174,7 @@ pub fn run() {
.setup(|#[allow(unused_variables)] app| {
#[cfg(desktop)]
{
let mut files = Vec::new();
// NOTICE: `args` may include URL protocol (`your-app-protocol://`)
// or arguments (`--`) if your app supports them.
// files may also be passed as `file://path/to/file`
for maybe_file in std::env::args().skip(1) {
// skip flags like -f or --flag
if maybe_file.starts_with("-") {
continue;
}
// handle `file://` path urls and skip other urls
if let Ok(url) = Url::parse(&maybe_file) {
if let Ok(path) = url.to_file_path() {
files.push(path);
} else {
files.push(PathBuf::from(maybe_file))
}
} else {
files.push(PathBuf::from(maybe_file))
}
}
let files = get_files_from_argv(std::env::args().collect());
if !files.is_empty() {
let app_handle = app.handle().clone();
allow_file_in_scopes(&app_handle, files.clone());
@@ -182,33 +191,70 @@ pub fn run() {
let app_handle = app.handle().clone();
app.listen("window-ready", move |_| {
app_handle.get_webview_window("main").unwrap()
.eval("window.__READEST_CLI_ACCESS = true; window.__READEST_UPDATER_ACCESS = true;")
let webview = app_handle.get_webview_window("main").unwrap();
webview
.eval("window.__READEST_CLI_ACCESS = true;")
.expect("Failed to set cli access config");
set_rounded_window(&app_handle, true);
#[cfg(target_os = "windows")]
if tauri_plugin_os::version()
<= tauri_plugin_os::Version::from_string("10.0.19045")
{
set_rounded_window(&app_handle, false);
}
#[cfg(target_os = "linux")]
{
let is_appimage = std::env::var("APPIMAGE").is_ok()
|| std::env::current_exe()
.map(|path| path.to_string_lossy().contains("/tmp/.mount_"))
.unwrap_or(false);
let script =
format!("window.__READEST_UPDATER_DISABLED = {};", !is_appimage);
webview
.eval(&script)
.expect("Failed to set updater disabled config");
}
});
}
#[cfg(any(windows, target_os = "linux"))]
#[cfg(target_os = "windows")]
{
use tauri_plugin_deep_link::DeepLinkExt;
app.deep_link().register_all()?;
}
#[cfg(desktop)]
if cfg!(debug_assertions) {
app.handle().plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.build(),
)?;
#[cfg(target_os = "linux")]
{
fn has_xdg_mime() -> bool {
std::process::Command::new("which")
.arg("xdg-mime")
.output()
.map(|output| output.status.success())
.unwrap_or(false)
}
if has_xdg_mime() {
use tauri_plugin_deep_link::DeepLinkExt;
app.deep_link().register_all()?;
} else {
println!("xdg-mime not found; skipping deep link setup on Linux.");
}
}
if let Err(e) = app.handle().plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.build(),
) {
eprintln!("Failed to initialize tauri_plugin_log: {}", e);
};
let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default());
#[cfg(desktop)]
let win_builder = win_builder
.inner_size(800.0, 600.0)
.resizable(true);
let win_builder = win_builder.inner_size(800.0, 600.0).resizable(true);
#[cfg(target_os = "macos")]
let win_builder = win_builder
@@ -225,8 +271,9 @@ pub fn run() {
.title("Readest");
if cfg!(target_os = "windows") {
let version = tauri_plugin_os::version();
if version <= tauri_plugin_os::Version::from_string("10.0.19045") {
if tauri_plugin_os::version()
<= tauri_plugin_os::Version::from_string("10.0.19045")
{
win_builder = win_builder.shadow(false);
} else {
win_builder = win_builder.shadow(true);
@@ -243,7 +290,7 @@ pub fn run() {
// win.open_devtools();
#[cfg(target_os = "macos")]
menu::setup_macos_menu(app.handle())?;
macos::menu::setup_macos_menu(app.handle())?;
app.handle().emit("window-ready", ()).unwrap();
@@ -0,0 +1,203 @@
use std::cell::RefCell;
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2::{
define_class, msg_send, AllocAnyThread, DefinedClass, MainThreadMarker, MainThreadOnly,
};
use objc2_authentication_services::{
ASAuthorization, ASAuthorizationAppleIDCredential, ASAuthorizationAppleIDProvider,
ASAuthorizationController, ASAuthorizationControllerDelegate, ASAuthorizationRequest,
ASAuthorizationScopeEmail, ASAuthorizationScopeFullName,
};
use objc2_foundation::{NSArray, NSError, NSObject, NSObjectProtocol, NSString};
use serde::{Deserialize, Serialize};
use serde_json::json;
use tauri::{command, AppHandle, Emitter};
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AppleIDAuthorizationRequest {
pub scope: Vec<String>,
pub nonce: Option<String>,
pub state: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AppleIDAuthorizationResponse {
pub user_identifier: Option<String>,
pub given_name: Option<String>,
pub family_name: Option<String>,
pub email: Option<String>,
pub authorization_code: Option<String>,
pub identity_token: Option<String>,
pub state: Option<String>,
}
thread_local! {
static APPLE_SIGN_IN_DELEGATE: RefCell<Option<Retained<ASAuthorizationControllerDelegateImpl>>> = RefCell::new(None);
static AUTHORIZATION_CONTROLLER: RefCell<Option<Retained<ASAuthorizationController>>> = RefCell::new(None);
}
#[derive(Clone)]
pub struct Ivars {
app: AppHandle,
}
define_class!(
#[unsafe(super(NSObject))]
#[thread_kind = MainThreadOnly]
#[name = "ASAuthorizationControllerDelegateImpl"]
#[ivars = Ivars]
pub struct ASAuthorizationControllerDelegateImpl;
unsafe impl NSObjectProtocol for ASAuthorizationControllerDelegateImpl {}
unsafe impl ASAuthorizationControllerDelegate for ASAuthorizationControllerDelegateImpl {
#[unsafe(method(authorizationController:didCompleteWithAuthorization:))]
#[allow(non_snake_case)]
unsafe fn authorizationController_didCompleteWithAuthorization(
&self,
_controller: &ASAuthorizationController,
authorization: &ASAuthorization,
) {
if let Ok(credential) = authorization
.credential()
.downcast::<ASAuthorizationAppleIDCredential>()
{
let user_identifier = {
let user = credential.user();
if user.len() > 0 {
Some(user.to_string())
} else {
None
}
};
let given_name = credential
.fullName()
.and_then(|name| name.givenName())
.map(|nsstr| nsstr.to_string());
let family_name = credential
.fullName()
.and_then(|name| name.familyName())
.map(|nsstr| nsstr.to_string());
let email = credential.email().map(|nsstr| nsstr.to_string());
let authorization_code = credential
.authorizationCode()
.and_then(|code_data| String::from_utf8(code_data.to_vec()).ok());
let identity_token = credential
.identityToken()
.and_then(|token_data| String::from_utf8(token_data.to_vec()).ok());
let state = credential.state().map(|nsstr| nsstr.to_string());
let resp = AppleIDAuthorizationResponse {
user_identifier,
given_name,
family_name,
email,
authorization_code,
identity_token,
state,
};
let _ = self.ivars().app.emit("apple-sign-in-complete", json!(resp));
log::info!("Apple authorization complete");
} else {
let _ = self
.ivars()
.app
.emit("apple-sign-in-error", "Invalid credential type");
log::error!("Invalid credential type received");
}
}
#[unsafe(method(authorizationController:didCompleteWithError:))]
#[allow(non_snake_case)]
unsafe fn authorizationController_didCompleteWithError(
&self,
_controller: &ASAuthorizationController,
error: &NSError,
) {
let code = error.code();
let description = error.localizedDescription().to_string();
let _ = self.ivars().app.emit(
"apple-sign-in-error",
json!({
"code": code,
"description": description,
}),
);
log::error!(
"Authorization error: code={}, description={}",
code,
description
);
}
}
);
impl ASAuthorizationControllerDelegateImpl {
fn new(app: AppHandle) -> Retained<Self> {
let mtm = MainThreadMarker::new().expect("must run on main thread");
let this = Self::alloc(mtm).set_ivars(Ivars { app });
unsafe { msg_send![super(this), init] }
}
}
#[command]
pub fn start_apple_sign_in(app: AppHandle, payload: AppleIDAuthorizationRequest) {
unsafe {
let provider = ASAuthorizationAppleIDProvider::new();
let request = provider.createRequest();
if let Some(ref nonce) = payload.nonce {
let ns_nonce = NSString::from_str(nonce);
request.setNonce(Some(&ns_nonce));
}
if let Some(ref state) = payload.state {
let ns_state = NSString::from_str(state);
request.setState(Some(&ns_state));
}
let mut scopes = Vec::new();
for scope in payload.scope.iter() {
match scope.as_str() {
"email" => scopes.push(ASAuthorizationScopeEmail),
"fullName" => scopes.push(ASAuthorizationScopeFullName),
_ => {
log::warn!("[Apple Sign-In] Unsupported scope: {}", scope);
}
}
}
if !scopes.is_empty() {
request.setRequestedScopes(Some(&*NSArray::from_slice(&scopes)));
}
let auth_request = &request as &ASAuthorizationRequest;
let controller = ASAuthorizationController::initWithAuthorizationRequests(
ASAuthorizationController::alloc(),
&*NSArray::from_slice(&[auth_request]),
);
let delegate = ASAuthorizationControllerDelegateImpl::new(app.clone());
APPLE_SIGN_IN_DELEGATE.with(|cell| {
*cell.borrow_mut() = Some(delegate.clone());
});
AUTHORIZATION_CONTROLLER.with(|cell| {
*cell.borrow_mut() = Some(controller.clone());
});
controller.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
log::info!("Starting Apple Sign-In authorization request");
controller.performRequests();
}
}
@@ -0,0 +1,4 @@
pub mod apple_auth;
pub mod menu;
pub mod safari_auth;
pub mod traffic_light;

Some files were not shown because too many files have changed in this diff Show More