Compare commits
1006 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66d2fdf999 | |||
| d8e0ceeff1 | |||
| f3ad97b989 | |||
| 1bb49ab023 | |||
| f9a0b39586 | |||
| c533da498d | |||
| b50bc0b854 | |||
| 96c465931c | |||
| 2bd54ac236 | |||
| 6ad549d13c | |||
| 67249370c9 | |||
| fe3ab011ca | |||
| 6cb4278b98 | |||
| 51468862a2 | |||
| b3a44d066f | |||
| 68d4538d40 | |||
| 80105af839 | |||
| 3904c1e8e7 | |||
| 99be6c58e2 | |||
| 664cc772d0 | |||
| 21208adbcf | |||
| 70eb59e2d6 | |||
| 3e7b57282e | |||
| 534582b125 | |||
| 4465e6986e | |||
| a535f6419e | |||
| 65da9c1d47 | |||
| 5f71fd9e47 | |||
| dcf75e07d1 | |||
| 79ba9b3818 | |||
| 128b238bcb | |||
| e26f7e6a2c | |||
| 4c5ff59bcf | |||
| 99b2a34bd2 | |||
| 40f3268ef3 | |||
| 4dac0850c5 | |||
| 118538ba35 | |||
| 4a92cacd84 | |||
| ce53cd2b47 | |||
| b99c1bc19a | |||
| eec2c39f19 | |||
| c6ae85484e | |||
| 7a9f46e93c | |||
| b9f6578127 | |||
| e75a3d254e | |||
| ea15906acf | |||
| 15d2784725 | |||
| ae3bb9da9a | |||
| 239b32fcc2 | |||
| f64739419b | |||
| af8f036ca3 | |||
| 24a87508c6 | |||
| 548d50a882 | |||
| 1e81ab5205 | |||
| a47a480aa2 | |||
| 411f9e236d | |||
| 950bbd0821 | |||
| 2824e50b51 | |||
| 129720c916 | |||
| fd3533dba1 | |||
| 920032155b | |||
| 226bf7033e | |||
| 9444be7fcc | |||
| c9a69a922b | |||
| 9a11b05833 | |||
| 6e603ee38f | |||
| 03fd6e2e6f | |||
| 7dd11c0fb0 | |||
| 968597e52c | |||
| a534050b19 | |||
| 0be828fd66 | |||
| c6d4e2bdd6 | |||
| bf72ab86cd | |||
| fb49ddf484 | |||
| d8817a88b9 | |||
| 475becafe3 | |||
| df165576e6 | |||
| 051f2e5b13 | |||
| 83f0d135c5 | |||
| 1ed84a3256 | |||
| 9d6394fe2b | |||
| fe9603ffb8 | |||
| 681e065ac4 | |||
| f64fc5723e | |||
| a9a1dc8e70 | |||
| b1a1e35790 | |||
| 3c538c3746 | |||
| 9834bd57cf | |||
| b89171a4d8 | |||
| 92116e7455 | |||
| 8dfab2c963 | |||
| 9f261f12e9 | |||
| e74615ac1d | |||
| 3b350d6945 | |||
| 9e0e3fde7d | |||
| 52c49ddef1 | |||
| e3d52891fb | |||
| 9cd88fe839 | |||
| fca3917a12 | |||
| c90de6967b | |||
| bbbd378f9b | |||
| 9f7147f8f8 | |||
| 8a468a6d1f | |||
| c848a319ff | |||
| 5cc80db438 | |||
| 94930baa1b | |||
| 1294ef90c1 | |||
| 570598520f | |||
| 7b4fc91994 | |||
| fb387c2384 | |||
| 4ce1ebe477 | |||
| c1460f4b85 | |||
| bc94f3f790 | |||
| c8c761b017 | |||
| bdb25999c9 | |||
| c58e172a54 | |||
| 160efcd37b | |||
| d7470f4139 | |||
| 01b4e3530d | |||
| 09c62d442f | |||
| d62ad60ce8 | |||
| 8cd34c8aaa | |||
| a52d9e3a2b | |||
| 64a1ea531a | |||
| 19f9f5ea24 | |||
| ffc51e75de | |||
| 2100071991 | |||
| 563d3478ba | |||
| 2c54e9ae2f | |||
| c2eb2e2fcc | |||
| e592f40429 | |||
| 4bd7cfe57c | |||
| c31ece6742 | |||
| aba9e87fc1 | |||
| 2c4df601d8 | |||
| 8bfc90a5b0 | |||
| 8bea05478a | |||
| 42fee90a27 | |||
| 45e57c3943 | |||
| 51b0b8483f | |||
| cbf7a501b7 | |||
| edfcb75ba5 | |||
| ce76843ccc | |||
| 5bbc5ceccc | |||
| 224acd68b1 | |||
| 6c0f1b8b5f | |||
| 481d8198e9 | |||
| b1153ba051 | |||
| aad532bfdd | |||
| a71347c897 | |||
| 034f41ca10 | |||
| 539ad8dea2 | |||
| 48920a87bf | |||
| d1d0d2d59c | |||
| ea811c90c6 | |||
| 5cd5b65e83 | |||
| ea24b5a97a | |||
| 06d620db83 | |||
| 0c25b85a8a | |||
| c536450ab0 | |||
| c83e380c5a | |||
| 894a7551aa | |||
| f875ba88ac | |||
| d9a6cffe78 | |||
| 1704736bc8 | |||
| 6ce9a263ee | |||
| 038eca4267 | |||
| f0722ec0fe | |||
| fd299a61a7 | |||
| 36b7386e30 | |||
| 2670b0dc0b | |||
| c44705e269 | |||
| 7e618d456e | |||
| b28ac99a9e | |||
| 2d7d6b08a9 | |||
| b1419f9c01 | |||
| 32ea42a835 | |||
| 6083de3293 | |||
| 1c9cfa49b3 | |||
| f85d6d4293 | |||
| 017d0b0b39 | |||
| 3146ae48a7 | |||
| 7f26e45ae7 | |||
| 7d97826e4b | |||
| 9c9c79176d | |||
| ed476a4fce | |||
| 44b4f7995b | |||
| da5e3a0bd3 | |||
| ba4678cc23 | |||
| e5eb3014b4 | |||
| ed77b0bc7f | |||
| 434a44e62c | |||
| aaee04c290 | |||
| 48e2bfa82c | |||
| c04f19ffb4 | |||
| 4a624e3902 | |||
| f53cee9616 | |||
| 30385ee5ec | |||
| b30dfb3e23 | |||
| c0d6102857 | |||
| 4537c55e84 | |||
| aff94c0ab8 | |||
| fd70836308 | |||
| 1b0c94b9a5 | |||
| 7cb523eefc | |||
| 941be80cc4 | |||
| 5eecc735aa | |||
| cfe51d01ee | |||
| 20ae09c52d | |||
| b868146129 | |||
| a312080f7c | |||
| 462ca46fee | |||
| 93228c4b2a | |||
| 2ff10f781f | |||
| 9614c62360 | |||
| 5ffaac5e67 | |||
| 71af91608f | |||
| 9603b61776 | |||
| eed84a059a | |||
| 604ef65719 | |||
| 483d536ca4 | |||
| 69a51c5880 | |||
| ca8d25341e | |||
| 8a263235ed | |||
| fb41ff5d0c | |||
| a5e09e8454 | |||
| 730ee21651 | |||
| dea43445c3 | |||
| 4f0ae78d43 | |||
| d9116d619a | |||
| c080e6fdd3 | |||
| d3752dadc6 | |||
| e21ef53a3d | |||
| 71e97998b6 | |||
| 22f9c45232 | |||
| a42897ec5c | |||
| 58a5c1625c | |||
| d53fc09e1e | |||
| 11fecb5dc0 | |||
| f4587663b5 | |||
| b76a2adf61 | |||
| 988fbc8c85 | |||
| f944ad9b9f | |||
| 335d91b9c9 | |||
| 173404eaad | |||
| 674fed5230 | |||
| 12d284cd22 | |||
| 77037c8adb | |||
| 6984393ed1 | |||
| 3abef6ea75 | |||
| 4ae1ab7dd0 | |||
| 69fb22119b | |||
| 3a6c18c6d5 | |||
| 7db1bc460d | |||
| a460e609fa | |||
| 4b4ebdbdaa | |||
| 9358a06839 | |||
| a7baf6cc9f | |||
| 15dc669f35 | |||
| c853957512 | |||
| 8a4e22e423 | |||
| 8a43c58fd4 | |||
| 54fdf5f1fd | |||
| fe50b513b3 | |||
| 17c7fa8f41 | |||
| 2533560d11 | |||
| 5850a16afd | |||
| 7063d62b13 | |||
| 0bd6a217ae | |||
| b7df294d78 | |||
| 5aa78f2554 | |||
| e740571c33 | |||
| e6d9913f4e | |||
| 1869a863a3 | |||
| 524de92f5e | |||
| c1530cc5c4 | |||
| 730fadb834 | |||
| 383e5c61b1 | |||
| 6d42086fa7 | |||
| 5a20fae204 | |||
| 34fd64c5c4 | |||
| 0874fb0764 | |||
| e03ed5b604 | |||
| 41edc89ac7 | |||
| f0a470398d | |||
| 51008d81fb | |||
| 9828904674 | |||
| 2670d835b3 | |||
| 8b7bafc4b6 | |||
| ca759e0246 | |||
| 5141be1c3f | |||
| 669d3950e2 | |||
| b95895cecf | |||
| 80e11bb0ce | |||
| de3a539621 | |||
| b425bfdc89 | |||
| 1d1fbdffdb | |||
| 50cd7f80c6 | |||
| 6eb7d91122 | |||
| 1fb468b3a6 | |||
| 3c7d95cf10 | |||
| ba3f060cc4 | |||
| 11bc7497e8 | |||
| fb5d149413 | |||
| 42b47d73b7 | |||
| 00f36af03a | |||
| b78466ca93 | |||
| 4e6f146b8f | |||
| cbdd4940d0 | |||
| d022cb984a | |||
| 8de6fa267e | |||
| b08b7de8e9 | |||
| a232a39f0e | |||
| fad7966fc4 | |||
| a1487fd60c | |||
| 9606e315d4 | |||
| 978673268b | |||
| 70158a7f15 | |||
| 18d65a2c5b | |||
| 1b0c2afad7 | |||
| cef444d374 | |||
| 75f6efe27a | |||
| 852f9f40ec | |||
| b9dadc0f4f | |||
| d120cf6573 | |||
| d2b3c165c7 | |||
| 742f06ce05 | |||
| e28dabce65 | |||
| 33ef781e21 | |||
| 02e885fbb7 | |||
| 2ca3561093 | |||
| 97f021da87 | |||
| bd2b45e1c1 | |||
| 2b6f7b71b0 | |||
| 0bad380a50 | |||
| ecc20e8521 | |||
| d5c813fac1 | |||
| d9b3757b1c | |||
| fd7f90236d | |||
| df9d21393d | |||
| 1f73f15ad8 | |||
| ba2aa4bee6 | |||
| f709a657fa | |||
| 514780a572 | |||
| 0c51a625f3 | |||
| 6f8b2d1dc4 | |||
| 0087ce2f19 | |||
| b98c2796c8 | |||
| 37d56b3205 | |||
| 5a54c0fb60 | |||
| 99b259836b | |||
| a54daaaa90 | |||
| fa66e6fca6 | |||
| b7864dded2 | |||
| 72d9698f38 | |||
| a7937cd657 | |||
| 998b14c5b0 | |||
| bff9c2a770 | |||
| b5acdffc87 | |||
| 127609160c | |||
| 8f22a5c570 | |||
| c792c18e01 | |||
| cd614e3845 | |||
| 40673f9cb8 | |||
| 911aa4c73d | |||
| 74b4cc2ceb | |||
| c0c463977d | |||
| 273bcafe01 | |||
| 9028059919 | |||
| c86af457e7 | |||
| b8e979be55 | |||
| 8849c19e8e | |||
| f1b4d02323 | |||
| 4aa8847037 | |||
| d2389400a9 | |||
| 721e70d027 | |||
| 3c1a7eac86 | |||
| f8e21dbd4c | |||
| d16a35d26f | |||
| f881caf794 | |||
| eb6fa276de | |||
| e987c8b37a | |||
| 26d76e27ac | |||
| 975549fca0 | |||
| 9a6bed52dd | |||
| efd9ad12a9 | |||
| 93a71a0fd0 | |||
| d373d7ac28 | |||
| e0e991b599 | |||
| 6d5b16ea8b | |||
| bfac67d3f0 | |||
| 0fd316d775 | |||
| c42fcf9ac4 | |||
| 04ade02a06 | |||
| 8ee53d3367 | |||
| 42111945b8 | |||
| 6fde157047 | |||
| 65ec5c9842 | |||
| a6444b5b33 | |||
| c7238cb04c | |||
| 366e5fafad | |||
| fb5710c134 | |||
| c4d9652335 | |||
| df6027cf9f | |||
| e3f7abf7f1 | |||
| c539917d7e | |||
| 5eb870c978 | |||
| b075c46ba3 | |||
| 4b6c7776f5 | |||
| 745a14195d | |||
| fb0f660eaf | |||
| 04a3030b79 | |||
| 5a3114de48 | |||
| b808fc5954 | |||
| 0ec4e37c13 | |||
| 9d301631ca | |||
| ec7145bb01 | |||
| f79d84b5fa | |||
| d00f1e0def | |||
| 637a813732 | |||
| 4f33c9280b | |||
| d54c752637 | |||
| 984d5d198d | |||
| 2568778a87 | |||
| 0d805a64f6 | |||
| 066d1c5b1e | |||
| cdc1950c3a | |||
| 7142874513 | |||
| 409c2f62be | |||
| 82922c5053 | |||
| 9c5bcb13e2 | |||
| 1d87dcdb7f | |||
| c7ba44a91e | |||
| 19b79c5b35 | |||
| 45216763e3 | |||
| 123293dc0e | |||
| 78e61060d2 | |||
| dd5371d2fd | |||
| da041d1f38 | |||
| d01599a2b4 | |||
| e80a2268ac | |||
| 3b86229c8f | |||
| c4d498d183 | |||
| 9bc46914e2 | |||
| 75f982e677 | |||
| f58bfeb1d0 | |||
| ebb077f6e0 | |||
| fdf6908fc6 | |||
| 7936660868 | |||
| 554dd3798f | |||
| 7f40d8e12c | |||
| 31d6f44ff5 | |||
| 86efcde927 | |||
| 4acbc33762 | |||
| 7cd3ebfcba | |||
| 55c97cab7e | |||
| 5bde091704 | |||
| 70489b46a5 | |||
| d7e89cc01f | |||
| a1f6030e75 | |||
| e7b2d8435e | |||
| f890a9633b | |||
| 5bdcd3124b | |||
| 752b3f8e4c | |||
| 001e836ae3 | |||
| 50d7f9a9dd | |||
| 34a5e58872 | |||
| 34fbea6a44 | |||
| 8737535b90 | |||
| 28ef414c3d | |||
| 16279949c1 | |||
| 444e22dd0e | |||
| a231506713 | |||
| 5dcabba73c | |||
| cd71c494da | |||
| f66642b8ec | |||
| 2283e1b0f2 | |||
| e0eb725d8b | |||
| 05e9549410 | |||
| 35fbce3506 | |||
| c1d6825793 | |||
| ae6c6970b4 | |||
| f8fef57cf3 | |||
| 4dc191735d | |||
| 4f11f437e1 | |||
| 5773e31990 | |||
| 9dda5d4c88 | |||
| 7f5b6959e1 | |||
| 126981d085 | |||
| 854a578929 | |||
| 6dd0daa597 | |||
| fd24a5e9ac | |||
| 948b35f244 | |||
| 96cc182a8c | |||
| e2291ed5b1 | |||
| 60ddb17547 | |||
| 9a991a31ba | |||
| 6de10c6c3b | |||
| c7d49c1504 | |||
| c721bd4d97 | |||
| 5b0a1968d8 | |||
| ad9064fa46 | |||
| f1c92e5702 | |||
| 92c1441922 | |||
| 3820950c7c | |||
| 08e558bd41 | |||
| 00af4a3d60 | |||
| ada427b134 | |||
| f696b9a573 | |||
| 195beeacac | |||
| 5b23ed35b6 | |||
| 42c8702704 | |||
| ad81376da8 | |||
| 562fea3a36 | |||
| b69d9ed69f | |||
| ad6a21a68f | |||
| 173aa0fcd1 | |||
| 788e08a75a | |||
| ec397a2daf | |||
| 2a5758fbc4 | |||
| 562ccd4b9e | |||
| 4d577774f3 | |||
| 4c10c16491 | |||
| 7695f31a5a | |||
| 06b27f8d39 | |||
| e1691661d9 | |||
| bc8e419d51 | |||
| ccad1ca0d9 | |||
| 75c315fca8 | |||
| bdef593cdd | |||
| d8943fc0c5 | |||
| cdd274e5ca | |||
| 2230741779 | |||
| 0e6950a60f | |||
| 1d4541e353 | |||
| 0a1e0212e2 | |||
| 25b44176b9 | |||
| e1deff341d | |||
| 3fc4c05e50 | |||
| 1843a74dbb | |||
| 9aa6a8e6b9 | |||
| 1de89069ce | |||
| 1e6525d085 | |||
| d9d71b7d38 | |||
| e85dec2b49 | |||
| c0f77b3368 | |||
| 8c9855d167 | |||
| 7c12bc8595 | |||
| 5102f6b0ff | |||
| dd40b9bae0 | |||
| 98b4026990 | |||
| b0640ba55b | |||
| c9a1557674 | |||
| 855f98722c | |||
| 21a82b70c7 | |||
| 519e222883 | |||
| 488a242787 | |||
| 523a03d212 | |||
| d49f3442e8 | |||
| 48dc570809 | |||
| 509320813f | |||
| 54d7e73acb | |||
| 1bc0eb3a4b | |||
| c0bf2d40dd | |||
| e30a39f9cb | |||
| 9727e4e9b8 | |||
| 3380912cdb | |||
| 91a5454b46 | |||
| e0c5acf4c8 | |||
| 7e062be0d0 | |||
| b23876c2b9 | |||
| e9a152683e | |||
| 34f1af727f | |||
| dfc24f3803 | |||
| ec233f7b37 | |||
| bb34b1ba59 | |||
| b1fb477359 | |||
| 3c877a19fc | |||
| b47baa6b74 | |||
| 14c85cbadf | |||
| a88b9139f8 | |||
| 251a4b52c0 | |||
| 1aaeaf5ebe | |||
| 9fd152d727 | |||
| 2571ceede4 | |||
| 932cfa3b9f | |||
| 01fde8573c | |||
| d95656b37b | |||
| 210eda0340 | |||
| 0d8b877d73 | |||
| 79d39b2295 | |||
| e03b0af58a | |||
| 71c2143cba | |||
| 70a7f5be19 | |||
| d2242423f5 | |||
| 782e9c8b20 | |||
| 55b2c678a4 | |||
| 48d754c184 | |||
| ff0443ace7 | |||
| cc3cc58de0 | |||
| 80d60266c2 | |||
| f06cdb5899 | |||
| dfe68988aa | |||
| d0264de3eb | |||
| 77c4303938 | |||
| 5724d2eeec | |||
| 64007b1188 | |||
| 50ee7bd8be | |||
| 2bcbde4bf1 | |||
| 99f75dd3b6 | |||
| 2b9132b4a8 | |||
| 82782be16e | |||
| 7067129887 | |||
| 6e706ac8f7 | |||
| 39b902c927 | |||
| 94a9cd7c9d | |||
| 0dae7db140 | |||
| 32954e025c | |||
| 61dda9a517 | |||
| 99a4cb50ac | |||
| f4b00d6dec | |||
| f69422a61c | |||
| 7f4c157a90 | |||
| 7d9724ff8b | |||
| 06db7f36f8 | |||
| f0c249bce2 | |||
| e8a96fba95 | |||
| 92b01c40a0 | |||
| 6003eeadba | |||
| 856ed4d3b3 | |||
| 267fe58a8c | |||
| 1b00d8c41c | |||
| cb126613b1 | |||
| 515bfee2a1 | |||
| 584f3511f8 | |||
| 3ea54e6725 | |||
| 48212d892f | |||
| 977c61b914 | |||
| 66fceb2313 | |||
| 76feefff00 | |||
| 1be43ff704 | |||
| b6d4a547e5 | |||
| 5ee860f1e8 | |||
| 0b01132d88 | |||
| b98c487919 | |||
| b43b08d423 | |||
| 3ddb6e6422 | |||
| ea9146be5b | |||
| 78cb1f45e4 | |||
| e472d1cf1e | |||
| 75dc04d598 | |||
| fc4c033cf3 | |||
| dbee054838 | |||
| 45b805dcd4 | |||
| a8ac54b51c | |||
| 86f705eae9 | |||
| d390209dab | |||
| 149f94be4c | |||
| ef1a1eab98 | |||
| dc7c6e14bc | |||
| 8c31dc85f9 | |||
| 7b8d1ddc35 | |||
| 8c12399732 | |||
| 93a4b1a448 | |||
| 04b0752b22 | |||
| 5137c08204 | |||
| ec0b44fb9f | |||
| 0bf83dfe67 | |||
| b151b70cb9 | |||
| f2640359a0 | |||
| 079aeaedb0 | |||
| bfacadb964 | |||
| 2e98ed44ee | |||
| 952d2a3553 | |||
| 225aa0ca59 | |||
| 37f718b84b | |||
| db8f53a6e1 | |||
| ce345ad67f | |||
| 8e53e625a4 | |||
| 3350bdbdd3 | |||
| 634d789654 | |||
| 0dfa8e96f5 | |||
| 3698e6ca28 | |||
| 595608bd62 | |||
| b8bb1ee71d | |||
| 4298213ce4 | |||
| 5671460618 | |||
| 452dc9f3e6 | |||
| 09e65211b4 | |||
| f46be89036 | |||
| f2f744a2fe | |||
| ceb1963b88 | |||
| 23d74b567a | |||
| 5014932a8f | |||
| 6a5694d2b2 | |||
| df074082a8 | |||
| 847d514441 | |||
| b2a71da2b0 | |||
| 3e5e4d2946 | |||
| e3c05e7648 | |||
| df9ca2d2fa | |||
| 1631ece2bd | |||
| 2ae25f3bbe | |||
| 511093a6ef | |||
| 1ef126f820 | |||
| 1788b86ced | |||
| 3111e93054 | |||
| 288abc678c | |||
| 7e559c5b90 | |||
| eaaeb844f3 | |||
| 097fde203d | |||
| efd85e67d9 | |||
| bd4bf459e3 | |||
| 8fb1745dbb | |||
| 47c6d6d058 | |||
| 564693d456 | |||
| aa3cd2a0c5 | |||
| c581f8ab47 | |||
| 63ed763499 | |||
| 213fc0d21e | |||
| 9bc1494582 | |||
| 33b2ba1609 | |||
| b9add62ba7 | |||
| 23fa2dc8b2 | |||
| 67ac09a290 | |||
| f10c14ae96 | |||
| 438d3ae0c2 | |||
| 842062fe44 | |||
| d01897d62a | |||
| 6eeb8e7580 | |||
| 9cebadba57 | |||
| e5ec9abca3 | |||
| 5e7f09d5c8 | |||
| ffa193d116 | |||
| 686f8988bc | |||
| 8d5c98004b | |||
| 214411919f | |||
| 42c2825e90 | |||
| 8974a87168 | |||
| f3e9983742 | |||
| beaf034035 | |||
| a5f18ab024 | |||
| d1f5934947 | |||
| f03d6f6861 | |||
| 20523b7eaf | |||
| f31c9b2a98 | |||
| 93071e727f | |||
| 2d12210606 | |||
| 88ccc528d4 | |||
| 9d3fc07865 | |||
| 6d465a40fb | |||
| 01c7e4c2e2 | |||
| 4005858242 | |||
| c24f951375 | |||
| dbe1efd1e9 | |||
| 0299cce343 | |||
| ca9f504329 | |||
| df3527ceca | |||
| 73d0838291 | |||
| 8e3574509e | |||
| 9bdf375304 | |||
| effa922597 | |||
| fe484257a0 | |||
| cc0cd9a114 | |||
| 916d63806e | |||
| 59d0118067 | |||
| 957c6b9f32 | |||
| 9c5a1147fa | |||
| 60ad6706ff | |||
| d9199d1182 | |||
| 219edb9faf | |||
| 30a0227ef3 | |||
| 29b5a7cb88 | |||
| abb727870e | |||
| af84a8b110 | |||
| 0dea5d3094 | |||
| 527b95521e | |||
| 396b23ddb4 | |||
| ee0d1549bd | |||
| 6da6b85530 | |||
| cde055dcb7 | |||
| 08957ce3d7 | |||
| 33515f1f09 | |||
| e3c411f043 | |||
| eda71725da | |||
| f19f45c00e | |||
| 6fb7897092 | |||
| 2de941d7fb | |||
| 3e4b7ebb13 | |||
| 2b52f82b57 | |||
| 9fb7e80757 | |||
| 38869ce438 | |||
| 1dbf93c1cf | |||
| aafcab8232 | |||
| bcfe1b8830 | |||
| 5db1847f7e | |||
| 165ca4a6e9 | |||
| 9556e1305f | |||
| f5b686abd9 | |||
| 74e1b733a9 | |||
| a9e0090275 | |||
| 68d57da9ca | |||
| 3cef529ebd | |||
| 85ac399e2c | |||
| e8d09773a2 | |||
| 23905407ca | |||
| 2ab1304b6d | |||
| d94236e127 | |||
| 630025c123 | |||
| 2b73c5a85a | |||
| a03f2c585f | |||
| 8a0216dcf8 | |||
| 59013be586 | |||
| c6a638dbfd | |||
| e54440ad80 | |||
| 446b014f7d | |||
| 9190433ea1 | |||
| d78d135600 | |||
| 04b6c89e58 | |||
| 8eefba2bee | |||
| a254139200 | |||
| 0514bed5b7 | |||
| 49978c5e3f | |||
| b959bf070c | |||
| 49e6877810 | |||
| bb759597e9 | |||
| bf0bb80009 | |||
| 5d61a9808f | |||
| bc36da6b25 | |||
| 280cb75ca4 | |||
| d5d0a9abd3 | |||
| 9f343992b2 | |||
| 7ab7058dba | |||
| c3113ade13 | |||
| 7737c05c8f | |||
| 4fde9955b2 | |||
| 3875c8f239 | |||
| 7bd1b03652 | |||
| 73878c14e3 | |||
| bc2c3a2c9a | |||
| 460d37c656 | |||
| fff9e3221e | |||
| f77fa17cbc | |||
| d4565f7d60 | |||
| e8b1976f4f | |||
| 6b4bb64885 | |||
| fc0c42f7db | |||
| 41e5f27d4d | |||
| a042b4c739 | |||
| a1c1a9f983 | |||
| 423ae77160 | |||
| 05c61a4b1a | |||
| ce8f06b7fb | |||
| f815844577 | |||
| 7fe4d38c51 | |||
| 43edcb4e9e | |||
| e259b29afa | |||
| cd5f57a004 | |||
| 8c0cdb0e22 | |||
| c0df9f1cb6 | |||
| cb1ddaeedb | |||
| 4447dd7b9a | |||
| dcb7602837 | |||
| 60e48742fe | |||
| dd43569778 | |||
| 8433dd6950 | |||
| 379802f580 | |||
| 2dcb5d7c1e | |||
| 98870d9f3e | |||
| 31aeb2289c | |||
| 0f66ecbba1 | |||
| f0d3c9dd4e | |||
| 92aae340d9 | |||
| 3cf40039ee | |||
| 7ed4937d9d | |||
| b3593c0365 | |||
| 6235202066 | |||
| b3d1085ddf | |||
| b2a4ddae60 | |||
| 42f50af27b | |||
| 07f74d8858 | |||
| 9477789db1 | |||
| 8a7d0e1b0d | |||
| 261ce95ed1 | |||
| c5a3b44cbf | |||
| ed68d25b15 | |||
| f7f8872a13 | |||
| be9802a936 | |||
| 84328dcfb2 | |||
| 89d48c72b0 | |||
| 467e80cd4b | |||
| 20955ec294 | |||
| 3cae183f51 | |||
| 99319ab4d1 | |||
| 658969e97a | |||
| 28c6c1ec58 | |||
| d8cc220c18 | |||
| 97d7eb659c | |||
| 9766ec0125 | |||
| e46d01448b | |||
| bb8bf99b40 | |||
| f8ac30adf1 | |||
| 69d418aa61 | |||
| 95af3bd3d1 | |||
| 5bdc29fe84 | |||
| 56f4b275f4 | |||
| 00b46dd8bb | |||
| 8ce89ca8f6 | |||
| 648d9ec260 | |||
| ebe1c10c84 | |||
| f2309ef496 | |||
| 8d71593f39 | |||
| 09e3c30a2a | |||
| eee4cbeaca | |||
| 00deca5029 | |||
| c7c20fec55 | |||
| 57e22dc7b6 | |||
| debd60fb70 | |||
| dfa17c1c0b | |||
| 2a26f8b04a | |||
| db955d2c8c | |||
| 51602a77fb | |||
| 48da5ab546 | |||
| 363b98dd3b | |||
| 43532a0ee8 | |||
| 03b2af1f97 | |||
| 3f8d03ae28 | |||
| 146a71fdef | |||
| 734e22d92c | |||
| a77986c0e6 | |||
| 4e7f9c49b0 | |||
| c51c95b883 | |||
| 62081bebfd | |||
| 15394d17d8 | |||
| 5d1deadc1e | |||
| b497ddacac | |||
| a9965fd52c | |||
| 87d610bb62 | |||
| dbc4651141 | |||
| dad2dfd078 | |||
| 1fe188814d | |||
| 6ec3a5b332 | |||
| 1acba2e08c | |||
| 32f9346f84 | |||
| 69c503026a | |||
| 84fc4cec5e | |||
| 5c9c11fafd | |||
| 77fa57438e | |||
| e4d217f3aa | |||
| c7a583c53a | |||
| 6c86917098 | |||
| 7c21f48d68 | |||
| 5820191c26 | |||
| b37a804192 | |||
| ebdfc39ef6 | |||
| 9f0d8b5c12 | |||
| a267671671 | |||
| aa62ebd3f0 | |||
| cc85e5c304 | |||
| b0cc0b6f1a | |||
| 6b033f7509 | |||
| 5c0670031a | |||
| 35735cd931 | |||
| 699a01c78f | |||
| 033e161455 | |||
| e6428656c2 | |||
| 68c3c45da3 | |||
| 36e87d4eb6 | |||
| 7f5059a8ea | |||
| 8dc04f4095 | |||
| 6a48f3feb4 | |||
| facf5fba75 | |||
| 9700942e24 | |||
| e238b49fbf | |||
| 32dfadf182 | |||
| 66744108e2 | |||
| 401f28847c | |||
| 9674b45cf0 | |||
| a4bc88d305 | |||
| b6b8b17760 | |||
| 14810d6737 | |||
| ed6c92a65a | |||
| 20d7a1185e | |||
| 32bcfda9ad | |||
| def157cacb | |||
| 7c464b9a8d | |||
| 90f3512ae2 | |||
| 12d93ba749 | |||
| 554d786105 | |||
| eb71c59524 | |||
| 93e81ee05b | |||
| b83972e366 | |||
| 967536aa23 | |||
| 4523437e34 | |||
| e49dd4accb | |||
| b7c1f5703c | |||
| 5373b67aae | |||
| ddc9673260 | |||
| 16b0821eab | |||
| 93876a73cc | |||
| 048867e595 | |||
| 602be376a1 | |||
| c52ae43cd9 | |||
| 348d743e82 | |||
| c7e495c379 | |||
| e368076cac |
@@ -0,0 +1,31 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
**/node_modules
|
||||
|
||||
# Rust build artifacts
|
||||
target
|
||||
**/target
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Build outputs
|
||||
.next
|
||||
**/.next
|
||||
out
|
||||
**/out
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
.vscode
|
||||
*.swp
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: ['readest']
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -8,6 +8,6 @@ updates:
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*" # Group all Actions updates into a single larger pull request
|
||||
- '*' # Group all Actions updates into a single larger pull request
|
||||
schedule:
|
||||
interval: weekly
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
env:
|
||||
RUSTFLAGS: '-C target-cpu=skylake'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Install minimal stable with clippy and rustfmt
|
||||
@@ -19,6 +19,7 @@ jobs:
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -32,28 +33,61 @@ jobs:
|
||||
|
||||
build_web_app:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- platform: 'web'
|
||||
- platform: 'tauri'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.15.1
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
- name: cache Next.js build
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: apps/readest-app/.next/cache
|
||||
key: nextjs-${{ matrix.config.platform }}-${{ github.sha }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
nextjs-${{ matrix.config.platform }}-${{ github.sha }}-
|
||||
nextjs-${{ matrix.config.platform }}-
|
||||
|
||||
- name: install Dependencies
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm install && pnpm setup-pdfjs
|
||||
pnpm install && pnpm setup-vendors
|
||||
|
||||
- name: build the web App
|
||||
- name: run format check
|
||||
run: |
|
||||
pnpm format:check || (pnpm format && git diff && exit 1)
|
||||
|
||||
- name: run tests
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm build-web
|
||||
pnpm test -- --watch=false
|
||||
|
||||
- name: run lint
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm lint
|
||||
|
||||
- name: build the web App
|
||||
if: matrix.config.platform == 'web'
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm build-web && pnpm check:all
|
||||
|
||||
- name: build the Tauri App
|
||||
if: matrix.config.platform == 'tauri'
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm build && pnpm check:all
|
||||
|
||||
@@ -17,14 +17,14 @@ jobs:
|
||||
release_version: ${{ steps.get-release-notes.outputs.release_version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
- name: get version
|
||||
run: echo "PACKAGE_VERSION=$(node -p "require('./apps/readest-app/package.json').version")" >> $GITHUB_ENV
|
||||
- name: get release
|
||||
id: get-release
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { data } = await github.rest.repos.getLatestRelease({
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
core.setOutput('release_tag', data.tag_name);
|
||||
- name: get release notes
|
||||
id: get-release-notes
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
@@ -48,6 +48,63 @@ jobs:
|
||||
core.setOutput('release_version', version);
|
||||
core.setOutput('release_note', releaseNote);
|
||||
|
||||
update-release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
needs: get-release
|
||||
|
||||
steps:
|
||||
- name: update release
|
||||
id: update-release
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
release_id: ${{ needs.get-release.outputs.release_id }}
|
||||
release_tag: ${{ needs.get-release.outputs.release_tag }}
|
||||
release_note: ${{ needs.get-release.outputs.release_note }}
|
||||
with:
|
||||
script: |
|
||||
const { data } = await github.rest.repos.generateReleaseNotes({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag_name: process.env.release_tag,
|
||||
})
|
||||
const notes = process.env.release_note.split(/\d+\.\s/).filter(Boolean);
|
||||
const formattedNotes = notes.map(note => `* ${note.trim()}`).join("\n");
|
||||
const body = `## Release Highlight\n${formattedNotes}\n\n${data.body}`;
|
||||
github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: process.env.release_id,
|
||||
body: body,
|
||||
draft: false,
|
||||
prerelease: false
|
||||
})
|
||||
|
||||
build-koreader-plugin:
|
||||
needs: get-release
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: create KOReader plugin zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
version=${{ needs.get-release.outputs.release_version }}
|
||||
plugin_zip="Readest-${version}-1.koplugin.zip"
|
||||
meta_file="apps/readest.koplugin/_meta.lua"
|
||||
perl -i -pe "s/^}/ version = \"${version}\",\n}/" "${meta_file}"
|
||||
|
||||
cd apps
|
||||
zip -r ../${plugin_zip} readest.koplugin
|
||||
cd ..
|
||||
|
||||
echo "Uploading ${plugin_zip} to GitHub release"
|
||||
gh release upload ${{ needs.get-release.outputs.release_tag }} ${plugin_zip} --clobber
|
||||
|
||||
build-tauri:
|
||||
needs: get-release
|
||||
permissions:
|
||||
@@ -81,7 +138,7 @@ jobs:
|
||||
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
|
||||
@@ -91,25 +148,23 @@ jobs:
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: initialize git submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.14.4
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
- name: setup Java (for Android build only)
|
||||
if: matrix.config.release == 'android'
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
@@ -120,13 +175,13 @@ jobs:
|
||||
|
||||
- name: install NDK (for Android build only)
|
||||
if: matrix.config.release == 'android'
|
||||
run: sdkmanager "ndk;27.0.11902837"
|
||||
run: sdkmanager "ndk;28.2.13676358"
|
||||
|
||||
- name: install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: copy pdfjs-dist to public directory
|
||||
run: pnpm --filter @readest/readest-app setup-pdfjs
|
||||
- name: copy pdfjs-dist and simplecc-dist to public directory
|
||||
run: pnpm --filter @readest/readest-app setup-vendors
|
||||
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@@ -167,7 +222,7 @@ jobs:
|
||||
if: matrix.config.release == 'android'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/27.0.11902837
|
||||
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: |
|
||||
@@ -229,10 +284,15 @@ jobs:
|
||||
echo "Uploading updated latest.json to GitHub release"
|
||||
gh release upload ${{ needs.get-release.outputs.release_tag }} latest.json --clobber
|
||||
|
||||
- name: Override tauri-cli with custom AppImage format (Linux)
|
||||
if: matrix.config.release == 'linux'
|
||||
run: cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
if: matrix.config.release != 'android'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: 'true'
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
@@ -255,12 +315,22 @@ jobs:
|
||||
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)
|
||||
- name: build and upload portable binaries (Windows only)
|
||||
if: matrix.config.os == 'windows-latest'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Building Portable Binaries"
|
||||
pushd apps/readest-app/
|
||||
echo "NEXT_PUBLIC_PORTABLE_APP=true" >> .env.local
|
||||
pnpm tauri build ${{ matrix.config.args }}
|
||||
|
||||
popd
|
||||
echo "Uploading Portable Binaries"
|
||||
arch=${{ matrix.config.arch }}
|
||||
version=${{ needs.get-release.outputs.release_version }}
|
||||
@@ -283,35 +353,49 @@ jobs:
|
||||
echo "Uploading $bin_file to GitHub release"
|
||||
gh release upload ${{ needs.get-release.outputs.release_tag }} $bin_file --clobber
|
||||
|
||||
update-release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-release, build-tauri]
|
||||
echo "Signing portable binary"
|
||||
pushd apps/readest-app/
|
||||
pnpm tauri signer sign "../../$bin_file"
|
||||
popd
|
||||
echo "Uploading signature to GitHub release"
|
||||
gh release upload ${{ needs.get-release.outputs.release_tag }} $bin_file.sig --clobber
|
||||
|
||||
steps:
|
||||
- name: update release
|
||||
id: update-release
|
||||
uses: actions/github-script@v7
|
||||
- name: download and update latest.json for Windows portable release
|
||||
if: matrix.config.os == 'windows-latest'
|
||||
env:
|
||||
release_id: ${{ needs.get-release.outputs.release_id }}
|
||||
release_tag: ${{ needs.get-release.outputs.release_tag }}
|
||||
release_note: ${{ needs.get-release.outputs.release_note }}
|
||||
with:
|
||||
script: |
|
||||
const { data } = await github.rest.repos.generateReleaseNotes({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag_name: process.env.release_tag,
|
||||
})
|
||||
const notes = process.env.release_note.split(/(?:\d\.\s)/).filter(Boolean);
|
||||
const formattedNotes = notes.map(note => `* ${note.trim()}`).join("\n");
|
||||
const body = `## Release Highlight\n${formattedNotes}\n\n${data.body}`;
|
||||
github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: process.env.release_id,
|
||||
body: body,
|
||||
draft: false,
|
||||
prerelease: false
|
||||
})
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
curl -sL https://github.com/readest/readest/releases/latest/download/latest.json -o latest.json
|
||||
|
||||
version=${{ needs.get-release.outputs.release_version }}
|
||||
arch=${{ matrix.config.arch }}
|
||||
|
||||
if [ "$arch" = "x86_64" ]; then
|
||||
bin_file="Readest_${version}_x64-portable.exe"
|
||||
platform_key="windows-x86_64-portable"
|
||||
elif [ "$arch" = "aarch64" ]; then
|
||||
bin_file="Readest_${version}_arm64-portable.exe"
|
||||
platform_key="windows-aarch64-portable"
|
||||
else
|
||||
echo "Unknown architecture: $arch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
portable_url="https://github.com/readest/readest/releases/download/${{ needs.get-release.outputs.release_tag }}/$bin_file"
|
||||
portable_sig=$(cat $bin_file.sig)
|
||||
|
||||
jq --arg url "$portable_url" \
|
||||
--arg sig "$portable_sig" \
|
||||
--arg key "$platform_key" \
|
||||
'.platforms[$key] = {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
|
||||
|
||||
upload-to-r2:
|
||||
needs: [get-release, build-tauri]
|
||||
uses: ./.github/workflows/upload-to-r2.yml
|
||||
with:
|
||||
tag: ${{ needs.get-release.outputs.release_tag }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
name: Retry workflow
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_id:
|
||||
required: true
|
||||
jobs:
|
||||
rerun:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: rerun ${{ inputs.run_id }}
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh run watch ${{ inputs.run_id }} > /dev/null 2>&1
|
||||
gh run rerun ${{ inputs.run_id }} --failed
|
||||
@@ -0,0 +1,77 @@
|
||||
name: Upload Release Assets to R2
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag name (e.g., v1.2.3)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload-to-r2:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 3
|
||||
strategy:
|
||||
fail-fast: false
|
||||
env:
|
||||
RELEASE_R2_BUCKET: readest-releases
|
||||
RELEASE_R2_ACCOUNT_ID: ${{ secrets.RELEASE_R2_ACCOUNT_ID }}
|
||||
RELEASE_R2_ACCESS_KEY_ID: ${{ secrets.RELEASE_R2_ACCESS_KEY_ID }}
|
||||
RELEASE_R2_SECRET_ACCESS_KEY: ${{ secrets.RELEASE_R2_SECRET_ACCESS_KEY }}
|
||||
|
||||
steps:
|
||||
- name: Download release assets
|
||||
run: |
|
||||
gh release download "${{ inputs.tag }}" --repo readest/readest --dir ./release-assets
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install rclone
|
||||
run: curl https://rclone.org/install.sh | sudo bash
|
||||
|
||||
- name: Configure rclone
|
||||
run: |
|
||||
mkdir -p ~/.config/rclone
|
||||
cat > ~/.config/rclone/rclone.conf <<EOF
|
||||
[r2]
|
||||
type = s3
|
||||
provider = Cloudflare
|
||||
access_key_id = $RELEASE_R2_ACCESS_KEY_ID
|
||||
secret_access_key = $RELEASE_R2_SECRET_ACCESS_KEY
|
||||
endpoint = https://${RELEASE_R2_ACCOUNT_ID}.r2.cloudflarestorage.com
|
||||
EOF
|
||||
|
||||
- name: Modify latest.json download URLs
|
||||
run: |
|
||||
GITHUB_BASE_URL="https://github.com/readest/readest/releases/download"
|
||||
READEST_BASE_URL="https://download.readest.com/releases"
|
||||
sed -i "s#${GITHUB_BASE_URL}#${READEST_BASE_URL}#g" ./release-assets/latest.json
|
||||
|
||||
- name: Upload to R2
|
||||
run: |
|
||||
mkdir releases
|
||||
mv ./release-assets/latest.json releases
|
||||
mv ./release-assets/release-notes.json releases
|
||||
rclone copy ./release-assets r2:${RELEASE_R2_BUCKET}/releases/${{ inputs.tag }}/
|
||||
rclone copy ./releases r2:${RELEASE_R2_BUCKET}/releases/
|
||||
|
||||
- name: Upload successful
|
||||
if: success()
|
||||
run: echo "Upload completed successfully"
|
||||
|
||||
retry-on-failure:
|
||||
if: failure() && fromJSON(github.run_attempt) < 3
|
||||
needs: [upload-to-r2]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh workflow run retry-workflow.yml -F run_id=${{ github.run_id }}
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: 'true'
|
||||
- uses: amondnet/vercel-action@v25
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
docker/.env
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
@@ -38,3 +39,11 @@ next-env.d.ts
|
||||
|
||||
# Rust build
|
||||
target
|
||||
|
||||
fastlane/report.xml
|
||||
|
||||
*.koplugin.zip
|
||||
|
||||
# nix
|
||||
result*
|
||||
.playwright-mcp/
|
||||
|
||||
@@ -4,3 +4,9 @@
|
||||
[submodule "packages/tauri"]
|
||||
path = packages/tauri
|
||||
url = https://github.com/readest/tauri.git
|
||||
[submodule "packages/tauri-plugins"]
|
||||
path = packages/tauri-plugins
|
||||
url = https://github.com/readest/tauri-plugins-workspace.git
|
||||
[submodule "packages/simplecc-wasm"]
|
||||
path = packages/simplecc-wasm
|
||||
url = https://github.com/readest/simplecc-wasm.git
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pnpm exec lint-staged
|
||||
@@ -0,0 +1,2 @@
|
||||
pnpm -C apps/readest-app lint
|
||||
pnpm -C apps/readest-app test
|
||||
@@ -1 +1,34 @@
|
||||
packages/foliate-js/
|
||||
# Dependencies
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Build Artifacts (Web & Rust)
|
||||
.next
|
||||
.open-next
|
||||
.build
|
||||
.tauri
|
||||
out
|
||||
build
|
||||
dist
|
||||
target
|
||||
fastlane
|
||||
.wrangler
|
||||
|
||||
# Autogenerated Tauri files
|
||||
gen
|
||||
**/autogenerated
|
||||
**/schemas
|
||||
|
||||
# Submodules (External Repos)
|
||||
packages
|
||||
|
||||
# Vendored Assets (Generated/External Code)
|
||||
apps/readest-app/public/*.js
|
||||
apps/readest-app/public/vendor
|
||||
|
||||
# Environment & Editor
|
||||
.env
|
||||
.env.*
|
||||
.vscode
|
||||
.idea
|
||||
*.log
|
||||
@@ -5,5 +5,6 @@
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-vscode.vscode-typescript-next",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
||||
@@ -4,4 +4,28 @@
|
||||
"packages/tauri/Cargo.toml",
|
||||
"apps/readest-app/src-tauri/Cargo.toml"
|
||||
],
|
||||
}
|
||||
// "editor.formatOnSave": true, // uncomment to add format on save
|
||||
"typescript.inlayHints.parameterNames.enabled": "all",
|
||||
"typescript.inlayHints.variableTypes.enabled": true,
|
||||
"typescript.inlayHints.propertyDeclarationTypes.enabled": true,
|
||||
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,
|
||||
"typescript.inlayHints.enumMemberValues.enabled": true,
|
||||
"javascript.validate.enable": false,
|
||||
"javascript.format.enable": false,
|
||||
"typescript.format.enable": false,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"prettier.requireConfig": true,
|
||||
"prettier.documentSelectors": ["**/*.{js,jsx,ts,tsx,css,json,md,html,yml}"]
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
When contributing to `Readest`, whether on GitHub or in other community spaces:
|
||||
|
||||
- Be respectful, civil, and open-minded.
|
||||
- Before opening a new pull request, try searching through the [issue tracker](https://github.com/chrox/readest/issues) for known issues or fixes.
|
||||
- Before opening a new pull request, try searching through the [issue tracker](https://github.com/readest/readest/issues) for known issues or fixes.
|
||||
- If you want to make code changes based on your personal opinion(s), make sure you open an issue first describing the changes you want to make, and open a pull request only when your suggestions get approved by maintainers.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### Prerequisites
|
||||
|
||||
In order to not waste your time implementing a change that has already been declined, or is generally not needed, start by [opening an issue](https://github.com/chrox/readest/issues/new/choose) describing the problem you would like to solve.
|
||||
In order to not waste your time implementing a change that has already been declined, or is generally not needed, start by [opening an issue](https://github.com/readest/readest/issues/new/choose) describing the problem you would like to solve.
|
||||
|
||||
For the best experience to build Readest for yourself, use a recent version of Node.js and Rust. Refer to the [Tauri documentation](https://v2.tauri.app/start/prerequisites/) for details on setting up the development environment prerequisites on different platforms.
|
||||
|
||||
@@ -43,8 +43,8 @@ git submodule update --init --recursive
|
||||
```bash
|
||||
# might need to rerun this when code is updated
|
||||
pnpm install
|
||||
# copy pdfjs-dist to Next.js public directory
|
||||
pnpm --filter @readest/readest-app setup-pdfjs
|
||||
# copy vendors dist libs to public directory
|
||||
pnpm --filter @readest/readest-app setup-vendors
|
||||
```
|
||||
|
||||
#### 3. Verify Dependencies Installation
|
||||
|
||||
@@ -2,9 +2,29 @@
|
||||
members = [
|
||||
"apps/readest-app/src-tauri",
|
||||
"packages/tauri/crates/tauri",
|
||||
"packages/tauri/crates/tauri-utils",
|
||||
"packages/tauri/crates/tauri-build",
|
||||
"packages/tauri-plugins/plugins/deep-link",
|
||||
"packages/tauri-plugins/plugins/single-instance"
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
log = "0.4"
|
||||
tauri = { version = "2", default-features = false }
|
||||
tauri-build = "2"
|
||||
tauri-plugin = "2"
|
||||
tauri-utils = "2"
|
||||
schemars = "0.8"
|
||||
serde_json = "1"
|
||||
thiserror = "2"
|
||||
glob = "0.3"
|
||||
zbus = "5.9"
|
||||
dunce = "1"
|
||||
url = "2"
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Bilingify LLC"]
|
||||
homepage = "https://readest.com"
|
||||
@@ -15,4 +35,8 @@ edition = "2021"
|
||||
rust-version = "1.77.2"
|
||||
|
||||
[patch.crates-io]
|
||||
tauri = { path = "packages/tauri/crates/tauri" }
|
||||
tauri = { path = "packages/tauri/crates/tauri" }
|
||||
tauri-utils = { path = "packages/tauri/crates/tauri-utils" }
|
||||
tauri-build = { path = "packages/tauri/crates/tauri-build" }
|
||||
tauri-plugin-deep-link = { path = "packages/tauri-plugins/plugins/deep-link" }
|
||||
tauri-plugin-single-instance = { path = "packages/tauri-plugins/plugins/single-instance" }
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
FROM node:22-slim
|
||||
|
||||
ENV PNPM_HOME="/root/.local/share/pnpm"
|
||||
ENV PATH="${PATH}:${PNPM_HOME}"
|
||||
|
||||
RUN npm install --global pnpm
|
||||
|
||||
# Install necessary packages
|
||||
RUN apt update -y && apt install -y --no-install-recommends \
|
||||
libwebkit2gtk-4.1-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
file \
|
||||
libxdo-dev \
|
||||
libssl-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev \
|
||||
ca-certificates \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Rust and Cargo
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
COPY . /app
|
||||
|
||||
FROM docker.io/node:22-slim AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
RUN corepack prepare pnpm@10.29.3 --activate
|
||||
WORKDIR /app
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
COPY apps/readest-app/package.json ./apps/readest-app/
|
||||
COPY patches/ ./patches/
|
||||
COPY packages/ ./packages/
|
||||
|
||||
RUN pnpm install
|
||||
|
||||
RUN pnpm --filter @readest/readest-app setup-pdfjs
|
||||
FROM base AS dependencies
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
RUN pnpm --filter @readest/readest-app setup-vendors
|
||||
|
||||
FROM dependencies AS development-stage
|
||||
COPY . .
|
||||
WORKDIR /app/apps/readest-app
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["pnpm", "dev-web", "-H", "0.0.0.0"]
|
||||
|
||||
FROM base AS build
|
||||
ARG NEXT_PUBLIC_SUPABASE_URL
|
||||
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
ARG NEXT_PUBLIC_APP_PLATFORM
|
||||
ARG NEXT_PUBLIC_API_BASE_URL
|
||||
ARG NEXT_PUBLIC_OBJECT_STORAGE_TYPE
|
||||
ARG NEXT_PUBLIC_STORAGE_FIXED_QUOTA
|
||||
ARG NEXT_PUBLIC_TRANSLATION_FIXED_QUOTA
|
||||
COPY --from=dependencies /app/node_modules /app/node_modules
|
||||
COPY --from=dependencies /app/apps/readest-app/node_modules /app/apps/readest-app/node_modules
|
||||
COPY --from=dependencies /app/apps/readest-app/public/vendor /app/apps/readest-app/public/vendor
|
||||
COPY --from=dependencies /app/packages/foliate-js/node_modules /app/packages/foliate-js/node_modules
|
||||
COPY . .
|
||||
WORKDIR /app/apps/readest-app
|
||||
RUN pnpm build-web
|
||||
|
||||
ENTRYPOINT ["pnpm", "start-web"]
|
||||
FROM build as production-stage
|
||||
ENTRYPOINT ["pnpm", "start-web", "-H", "0.0.0.0"]
|
||||
EXPOSE 3000
|
||||
|
||||
@@ -5,18 +5,22 @@
|
||||
<h1>Readest</h1>
|
||||
<br>
|
||||
|
||||
[Readest][link-website] is an open-source ebook reader designed for immersive and deep reading experiences. Built as a modern rewrite of [Foliate](https://github.com/johnfactotum/foliate), it leverages [Next.js 15](https://github.com/vercel/next.js) and [Tauri v2](https://github.com/tauri-apps/tauri) to deliver a smooth, cross-platform experience across macOS, Windows, Linux, Android, iOS, and the Web.
|
||||
[Readest][link-website] is an open-source ebook reader designed for immersive and deep reading experiences. Built as a modern rewrite of [Foliate](https://github.com/johnfactotum/foliate), it leverages [Next.js 16](https://github.com/vercel/next.js) and [Tauri v2](https://github.com/tauri-apps/tauri) to deliver a smooth, cross-platform experience across macOS, Windows, Linux, Android, iOS, and the Web.
|
||||
|
||||
[![Website][badge-website]][link-website]
|
||||
[![Web App][badge-web-app]][link-web-readest]
|
||||
[![OS][badge-platforms]][link-website]
|
||||
<br>
|
||||
[![][badge-hellogithub]][link-hellogithub]
|
||||
[![][badge-discord]][link-discord]
|
||||
[![Discord][badge-discord]][link-discord]
|
||||
[![Reddit][badge-reddit]][link-reddit]
|
||||
[![AGPL Licence][badge-license]](LICENSE)
|
||||
[![Language Coverage][badge-language-coverage]][link-locales]
|
||||
[![Donate][badge-donate]][link-donate]
|
||||
[![Latest release][badge-release]][link-gh-releases]
|
||||
[![Last commit][badge-last-commit]][link-gh-commits]
|
||||
[![Commits][badge-commit-activity]][link-gh-pulse]
|
||||
[![][badge-hellogithub]][link-hellogithub]
|
||||
[![Ask DeepWiki][badge-deepwiki]][link-deepwiki]
|
||||
|
||||
</div>
|
||||
|
||||
@@ -27,6 +31,7 @@
|
||||
<a href="#downloads">Downloads</a> •
|
||||
<a href="#getting-started">Getting Started</a> •
|
||||
<a href="#troubleshooting">Troubleshooting</a> •
|
||||
<a href="#support">Support</a> •
|
||||
<a href="#license">License</a>
|
||||
</p>
|
||||
|
||||
@@ -40,37 +45,38 @@
|
||||
|
||||
<div align="left">✅ Implemented</div>
|
||||
|
||||
| **Feature** | **Description** | **Status** |
|
||||
| --------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------- |
|
||||
| **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. | ✅ |
|
||||
| **Excerpt Text for Note-Taking** | Easily excerpt text from books for detailed notes and analysis. | ✅ |
|
||||
| **Dictionary/Wikipedia Lookup** | Instantly look up words and terms when reading. | ✅ |
|
||||
| **Translate with DeepL** | Translate selected text instantly using DeepL for accurate translations. | ✅ |
|
||||
| **[Parallel Read][link-parallel-read]** | Read two books or documents simultaneously in a split-screen view. | ✅ |
|
||||
| **Customize Font and Layout** | Adjust font, layout, theme mode, and theme colors for a personalized experience. | ✅ |
|
||||
| **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. | ✅ |
|
||||
| **Feature** | **Description** | **Status** |
|
||||
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------- |
|
||||
| **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 and use instant mode for quicker interactions. | ✅ |
|
||||
| **Dictionary/Wikipedia Lookup** | Instantly look up words and terms when reading. | ✅ |
|
||||
| **[Parallel Read][link-parallel-read]** | Read two books or documents simultaneously in a split-screen view. | ✅ |
|
||||
| **Customize Font and Layout** | Adjust font, layout, theme mode, and theme colors for a personalized experience. | ✅ |
|
||||
| **Code Syntax Highlighting** | Read software manuals with rich coloring of code examples. | ✅ |
|
||||
| **File Association and Open With** | Quickly open files in Readest in your file browser with one-click. | ✅ |
|
||||
| **Library Management** | Organize, sort, and manage your entire ebook library. | ✅ |
|
||||
| **OPDS/Calibre Integration** | Integrate OPDS/Calibre to access online libraries and catalogs. | ✅ |
|
||||
| **Translate with DeepL and Yandex** | From a single sentence to the entire book—translate instantly. | ✅ |
|
||||
| **Text-to-Speech (TTS) Support** | Enjoy smooth, multilingual narration—even within a single book. | ✅ |
|
||||
| **Sync across Platforms** | Synchronize book files, reading progress, notes, and bookmarks across all supported platforms. | ✅ |
|
||||
| **Accessibility** | Provides full keyboard navigation and supports for screen readers such as VoiceOver, TalkBack, NVDA, and Orca. | ✅ |
|
||||
| **Visual & Focus Aids** | Reading ruler, paragraph-by-paragraph reading mode, and speed reading features. | ✅ |
|
||||
|
||||
## Planned Features
|
||||
|
||||
<div align="left">🛠 Building</div>
|
||||
<div align="left">🔄 Planned</div>
|
||||
|
||||
| **Feature** | **Description** | **Priority** |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------ | ------------ |
|
||||
| **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. | 🔄 |
|
||||
| **Support OPDS/Calibre** | Integrate OPDS/Calibre to access online libraries and catalogs. | 🔄 |
|
||||
| **Audiobook Support** | Extend functionality to play and manage audiobooks. | 🔄 |
|
||||
| **Handwriting Annotations** | Add support for handwriting annotations using a pen on compatible devices. | 🔄 |
|
||||
| **Advanced Reading Stats** | Track reading time, pages read, and more for detailed insights. | 🔄 |
|
||||
| **In-Library Full-Text Search** | Search across your entire ebook library to find topics and quotes. | 🔄 |
|
||||
| **Feature** | **Description** | **Priority** |
|
||||
| ------------------------------------------ | ------------------------------------------------------------------------------------------ | ------------ |
|
||||
| [**Sync with Koreader**][link-kosync-wiki] | Synchronize reading progress, notes, and bookmarks with [Koreader][link-koreader] devices. | 🛠 |
|
||||
| **AI-Powered Summarization** | Generate summaries of books or chapters using AI for quick insights. | 🛠 |
|
||||
| **Advanced Reading Stats** | Track reading time, pages read, and more for detailed insights. | 🛠 |
|
||||
| **Audiobook Support** | Extend functionality to play and manage audiobooks. | 🔄 |
|
||||
| **Handwriting Annotations** | Add support for handwriting annotations using a pen on compatible devices. | 🔄 |
|
||||
| **In-Library Full-Text Search** | Search across your entire ebook library to find topics and quotes. | 🔄 |
|
||||
|
||||
Stay tuned for continuous improvements and updates! Contributions and suggestions are always welcome—let's build the ultimate reading experience together. 😊
|
||||
|
||||
@@ -78,25 +84,35 @@ Stay tuned for continuous improvements and updates! Contributions and suggestion
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## Downloads
|
||||
|
||||
The Readest app is available for download! 🥳 🚀
|
||||
### Mobile Apps
|
||||
|
||||
- macOS / iOS / iPadOS : Search for "Readest" on the [App Store][link-appstore], also available on TestFlight for beta test (send your Apple ID to readestapp@gmail.com to request access).
|
||||
- Windows / Linux / Android: Visit [https://readest.com][link-website] or the [Releases on GitHub][link-gh-releases].
|
||||
- Web: Visit [https://web.readest.com][link-web-readest].
|
||||
<div align="center">
|
||||
<a href="https://apps.apple.com/app/id6738622779">
|
||||
<img alt="Download on the App Store" src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg" style="height: 50px;" /></a>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.bilingify.readest">
|
||||
<img alt="Get it on Google Play" src="https://upload.wikimedia.org/wikipedia/commons/7/78/Google_Play_Store_badge_EN.svg" style="height: 50px;" /></a>
|
||||
</div>
|
||||
|
||||
### Platform-Specific Downloads
|
||||
|
||||
- macOS / iOS / iPadOS : Search and install **Readest** on the [App Store][link-appstore], _also_ available on TestFlight for beta test (send your Apple ID to <readestapp@gmail.com> to request access).
|
||||
- Windows / Linux / Android: Visit and download **Readest** at [https://readest.com][link-website] or the [Releases on GitHub][link-gh-releases].
|
||||
- Linux users can also install [Readest on Flathub][link-flathub].
|
||||
- Web: Visit and use **Readest for Web** at [https://web.readest.com][link-web-readest].
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -121,16 +137,16 @@ To get started with Readest, follow these steps to clone and build the project.
|
||||
```bash
|
||||
git clone https://github.com/readest/readest.git
|
||||
cd readest
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
### 2. Install Dependencies
|
||||
|
||||
```bash
|
||||
# might need to rerun this when code is updated
|
||||
git submodule update --init --recursive
|
||||
pnpm install
|
||||
# copy pdfjs-dist to Next.js public directory
|
||||
pnpm --filter @readest/readest-app setup-pdfjs
|
||||
# copy vendors dist libs to public directory
|
||||
pnpm --filter @readest/readest-app setup-vendors
|
||||
```
|
||||
|
||||
### 3. Verify Dependencies Installation
|
||||
@@ -152,13 +168,17 @@ For Windows targets, “Build Tools for Visual Studio 2022” (or a higher editi
|
||||
pnpm tauri dev
|
||||
# or start development for the Web app
|
||||
pnpm dev-web
|
||||
# preview with OpenNext build for the Web app
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
For Android:
|
||||
|
||||
```bash
|
||||
# Initialize the Android environment (run once)
|
||||
rm apps/readest-app/src-tauri/gen/android
|
||||
pnpm tauri android init
|
||||
git checkout apps/readest-app/src-tauri/gen/android
|
||||
|
||||
pnpm tauri android dev
|
||||
# or if you want to dev on a real device
|
||||
@@ -184,6 +204,9 @@ pnpm tauri android build
|
||||
pnpm tauri ios build
|
||||
```
|
||||
|
||||
Please refer to our release script if you experience any issues:
|
||||
https://github.com/readest/readest/blob/main/.github/workflows/release.yml
|
||||
|
||||
### 6. Setup dev environment with Nix
|
||||
|
||||
If you have Nix installed, you can leverage flake to enter a development shell
|
||||
@@ -232,6 +255,32 @@ Please check the [wiki][link-gh-wiki] of this project for more information on de
|
||||
|
||||
- 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 you’ve taken.
|
||||
|
||||
### 2. AppImage Launches but Only Shows a Taskbar Icon
|
||||
|
||||
On some Arch Linux systems—especially those using Wayland—the Readest AppImage may briefly show an icon in the taskbar and then exit without opening a window.
|
||||
|
||||
You might see logs such as:
|
||||
|
||||
```
|
||||
Could not create default EGL display: EGL_BAD_PARAMETER. Aborting...
|
||||
```
|
||||
|
||||
This behavior is usually caused by compatibility issues between the bundled AppImage libraries and the system’s EGL / Wayland environment.
|
||||
|
||||
**Workaround 1: Launch with LD_PRELOAD (recommended)**
|
||||
|
||||
You can preload the system Wayland client library before launching the AppImage:
|
||||
|
||||
```
|
||||
LD_PRELOAD=/usr/lib/libwayland-client.so /path/to/Readest.AppImage
|
||||
```
|
||||
|
||||
This workaround has been confirmed to resolve the issue on affected systems.
|
||||
|
||||
**Workaround 2: Use the Flatpak Version**
|
||||
|
||||
If you prefer a more reliable out-of-the-box experience on Arch Linux, consider using the [Flatpak build on Flathub][link-flathub] instead. The Flatpak runtime helps avoid system library mismatches and tends to behave more consistently across different Wayland and X11 setups.
|
||||
|
||||
## 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.
|
||||
@@ -242,6 +291,18 @@ Readest is open-source, and contributions are welcome! Feel free to open issues,
|
||||
</p>
|
||||
</a>
|
||||
|
||||
## Support
|
||||
|
||||
If Readest has been useful to you, consider supporting its development. You can [become a sponsor on GitHub](https://github.com/sponsors/readest), [donate via Stripe](https://donate.stripe.com/4gMcN5aZdcE52kW3TFgjC01), or [donate with crypto](https://donate.readest.com). Your contribution helps us squash bugs faster, improve performance, and keep building great features.
|
||||
|
||||
### Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a title="Browser testing via TestMu AI" href="https://www.testmuai.com/?utm_medium=sponsor&utm_source=readest" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/readest/readest/refs/heads/main/data/sponsors/testmu-ai-logo.png" style="vertical-align: middle;" width="250" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## License
|
||||
|
||||
Readest is free software: you can redistribute it and/or modify it under the terms of the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See the [LICENSE](LICENSE) file for details.
|
||||
@@ -253,6 +314,7 @@ The following libraries and frameworks are used in this software:
|
||||
- [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.
|
||||
- [marked](https://github.com/markedjs/marked), which is MIT licensed.
|
||||
- [next.js](https://github.com/vercel/next.js), 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.
|
||||
@@ -260,7 +322,9 @@ The following libraries and frameworks are used in this software:
|
||||
|
||||
The following fonts are utilized in this software, either bundled within the application or provided through web fonts:
|
||||
|
||||
[Bitter](https://fonts.google.com/?query=Bitter), [Fira Code](https://fonts.google.com/?query=Fira+Code), [Literata](https://fonts.google.com/?query=Literata), [Merriweather](https://fonts.google.com/?query=Merriweather), [Noto Sans](https://fonts.google.com/?query=Noto+Sans), [Roboto](https://fonts.google.com/?query=Roboto), [LXGW WenKai](https://github.com/lxgw/LxgwWenKai), [MiSans](https://hyperos.mi.com/font/en/), [Source Han](https://github.com/adobe-fonts/source-han-sans/), [WenQuanYi Micro Hei](http://wenq.org/wqy2/)
|
||||
[Bitter](https://fonts.google.com/specimen/Bitter), [Fira Code](https://fonts.google.com/specimen/Fira+Code), [Inter](https://fonts.google.com/specimen/Inter), [Literata](https://fonts.google.com/specimen/Literata), [Merriweather](https://fonts.google.com/specimen/Merriweather), [Noto Sans](https://fonts.google.com/specimen/Noto+Sans), [Roboto](https://fonts.google.com/specimen/Roboto), [LXGW WenKai](https://github.com/lxgw/LxgwWenKai), [MiSans](https://hyperos.mi.com/font/en/), [Source Han](https://github.com/adobe-fonts/source-han-sans/), [WenQuanYi Micro Hei](http://wenq.org/wqy2/)
|
||||
|
||||
We would also like to thank the [Web Chinese Fonts Plan](https://chinese-font.netlify.app) for offering open-source tools that enable the use of Chinese fonts on the web.
|
||||
|
||||
---
|
||||
|
||||
@@ -271,12 +335,18 @@ The following fonts are utilized in this software, either bundled within the app
|
||||
[badge-license]: https://img.shields.io/github/license/readest/readest?color=teal
|
||||
[badge-release]: https://img.shields.io/github/release/readest/readest?color=green
|
||||
[badge-platforms]: https://img.shields.io/badge/platforms-macOS%2C%20Windows%2C%20Linux%2C%20Android%2C%20iOS%2C%20Web%2C%20PWA-green
|
||||
[badge-last-commit]: https://img.shields.io/github/last-commit/readest/readest?color=green
|
||||
[badge-commit-activity]: https://img.shields.io/github/commit-activity/m/readest/readest
|
||||
[badge-last-commit]: https://img.shields.io/github/last-commit/readest/readest?color=blue
|
||||
[badge-commit-activity]: https://img.shields.io/github/commit-activity/m/readest/readest?color=blue
|
||||
[badge-discord]: https://img.shields.io/discord/1314226120886976544?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square
|
||||
[badge-hellogithub]: https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=8a5b6ade2aee461a8bd94e59200682a7&claim_uid=eRLUbPOy2qZtDgw&theme=small
|
||||
[badge-donate]: https://donate.readest.com/badge.svg
|
||||
[badge-deepwiki]: https://deepwiki.com/badge.svg
|
||||
[badge-reddit]: https://img.shields.io/reddit/subreddit-subscribers/readest?style=flat&logo=reddit&color=F37E41
|
||||
[badge-language-coverage]: https://img.shields.io/badge/coverage-53%25%20population%20🌍-green
|
||||
[link-donate]: https://donate.readest.com/?tickers=btc%2Ceth%2Csol%2Cusdc
|
||||
[link-appstore]: https://apps.apple.com/app/apple-store/id6738622779?pt=127463130&ct=github&mt=8
|
||||
[link-website]: https://readest.com?utm_source=github&utm_medium=referral&utm_campaign=readme
|
||||
[link-flathub]: https://flathub.org/en/apps/com.bilingify.readest
|
||||
[link-web-readest]: https://web.readest.com
|
||||
[link-gh-releases]: https://github.com/readest/readest/releases
|
||||
[link-gh-commits]: https://github.com/readest/readest/commits/main
|
||||
@@ -286,3 +356,7 @@ The following fonts are utilized in this software, either bundled within the app
|
||||
[link-parallel-read]: https://readest.com/#parallel-read
|
||||
[link-koreader]: https://github.com/koreader/koreader
|
||||
[link-hellogithub]: https://hellogithub.com/repository/8a5b6ade2aee461a8bd94e59200682a7
|
||||
[link-deepwiki]: https://deepwiki.com/readest/readest
|
||||
[link-locales]: https://github.com/readest/readest/tree/main/apps/readest-app/public/locales
|
||||
[link-kosync-wiki]: https://github.com/readest/readest/wiki/Sync-with-Koreader-devices
|
||||
[link-reddit]: https://reddit.com/r/readest/
|
||||
|
||||
@@ -2,5 +2,11 @@ PDFJS_BUILD_PATH=../../packages/foliate-js/node_modules/pdfjs-dist/legacy/build
|
||||
PDFJS_FONTS_PATH=../../packages/foliate-js/node_modules/pdfjs-dist
|
||||
PDFJS_STYLE_PATH=../../packages/foliate-js/vendor/pdfjs
|
||||
|
||||
NEXT_PUBLIC_DEV_SUPABASE_URL=https://gxkhxxxeapexynpouyjz.supabase.co
|
||||
NEXT_PUBLIC_DEV_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd4a2h4eHhlYXBleHlucG91eWp6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzQ0MzAwNTksImV4cCI6MjA1MDAwNjA1OX0.jhinkQsimQoidsg_U59YD5ROw4PmMJQNKuyXbr4TiQA
|
||||
NEXT_PUBLIC_DEFAULT_POSTHOG_URL_BASE64="aHR0cHM6Ly91cy5pLnBvc3Rob2cuY29t"
|
||||
NEXT_PUBLIC_DEFAULT_POSTHOG_KEY_BASE64="cGhjX2ViNXowbVRxWm8yZm5YYnZGNmE3bFh5TThpTmRSNTNsR1A3VFM3VGh4S08="
|
||||
|
||||
NEXT_PUBLIC_DEFAULT_SUPABASE_URL_BASE64="aHR0cHM6Ly9yZWFkZXN0LnN1cGFiYXNlLmNv"
|
||||
NEXT_PUBLIC_DEFAULT_SUPABASE_KEY_BASE64="ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKemRYQmhZbUZ6WlNJc0luSmxaaUk2SW5aaWMzbDRablZ6YW1weFpIaHJhbkZzZVhOaklpd2ljbTlzWlNJNkltRnViMjRpTENKcFlYUWlPakUzTXpReE1qTTJOekVzSW1WNGNDSTZNakEwT1RZNU9UWTNNWDAuM1U1VXFhb3VfMVNnclZlMWVvOXJBcGMwdUtqcWhwUWRVWGh2d1VIbVVmZw=="
|
||||
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY_DEV_BASE64="cGtfdGVzdF81MVJmQmdLRTdSWW5pTWsxc0tDV2RUd2hMZzcySzk4eDRWcjlIdDdsRFBONngzcnpZYmhydGtNQnpDdzZKbHFaRVVITVp5eVNjVXhCZXVkcGppWTk0WXNHcDAweFlRRnRRaUU="
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY_BASE64="cGtfbGl2ZV81MVFYN3dRRU5ndjJFOUxQRHpZUlE5TlJJeTNjd09EZ1AzSkNFRHRPWlFtdFJWc3Brd053ZE1NNUpIVnVPTmJWcjZ3VGFCMUNZR1pJMmRPVWppTkY0bHJvVjAwalE4TkpkdWk="
|
||||
|
||||
@@ -6,6 +6,8 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
|
||||
|
||||
NEXT_PUBLIC_STORAGE_FIXED_QUOTA=1073741824
|
||||
|
||||
NEXT_PUBLIC_API_BASE_URL=https://your-api-base-url.com
|
||||
|
||||
SUPABASE_ADMIN_KEY=YOUR_SUPABASE_ADMIN_KEY
|
||||
|
||||
DEEPL_PRO_API_KEYS=YOUR_DEEPL_PRO_API_KEYS
|
||||
@@ -26,3 +28,5 @@ S3_ACCESS_KEY_ID=PLACE_HOLDER
|
||||
S3_SECRET_ACCESS_KEY=PLACE_HOLDER
|
||||
S3_BUCKET_NAME=PLACE_HOLDER
|
||||
S3_REGION=PLACE_HOLDER
|
||||
|
||||
TEMP_STORAGE_PUBLIC_BUCKET_NAME=PLACE_HOLDER
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
NEXT_PUBLIC_APP_PLATFORM=tauri
|
||||
NEXT_PUBLIC_APP_PLATFORM=tauri
|
||||
|
||||
DBUS_ID=com.bilingify.readest
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
NEXT_PUBLIC_APP_PLATFORM=tauri
|
||||
AI_GATEWAY_API_KEY=your_key_here
|
||||
@@ -0,0 +1,3 @@
|
||||
NEXT_PUBLIC_APP_PLATFORM=web
|
||||
AI_GATEWAY_API_KEY=your_key_here
|
||||
NEXT_PUBLIC_AI_GATEWAY_API_KEY=your_key_here
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src/styles/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "lucide",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/libs",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {}
|
||||
}
|
||||
@@ -1,13 +1,35 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import next from 'eslint-config-next';
|
||||
import nextVitals from 'eslint-config-next/core-web-vitals';
|
||||
import tseslint from 'eslint-config-next/typescript';
|
||||
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
export default [...compat.extends("next/core-web-vitals", "next/typescript")];
|
||||
const eslintConfig = defineConfig([
|
||||
...tseslint,
|
||||
...next,
|
||||
...nextVitals,
|
||||
{
|
||||
rules: {
|
||||
...jsxA11y.configs.recommended.rules,
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
globalIgnores([
|
||||
'node_modules/**',
|
||||
'.next/**',
|
||||
'.open-next/**',
|
||||
'out/**',
|
||||
'build/**',
|
||||
'public/**',
|
||||
'next-env.d.ts',
|
||||
]),
|
||||
]);
|
||||
|
||||
export default eslintConfig;
|
||||
|
||||
@@ -0,0 +1,588 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-rs-sys",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f954a9e9159ec994f73a30a12b96a702dde78f5547bcb561174597924f7d4162"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"color_quant",
|
||||
"gif",
|
||||
"image-webp",
|
||||
"moxcms",
|
||||
"num-traits",
|
||||
"png",
|
||||
"zune-core",
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image-webp"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
|
||||
dependencies = [
|
||||
"byteorder-lite",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "md5"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moxcms"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80986bbbcf925ebd3be54c26613d861255284584501595cf418320c078945608"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pxfm"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3502d6155304a4173a5f2c34b52b7ed0dd085890326cb50fd625fdf39e86b3b"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_thumbnail"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"directories-next",
|
||||
"image",
|
||||
"md5",
|
||||
"once_cell",
|
||||
"windows",
|
||||
"windows-core",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"crc32fast",
|
||||
"log",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6fb7703e32e9a07fb3f757360338b3a567a5054f21b5f52a666752e333d58e"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "windows_thumbnail"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
name = "windows_thumbnail"
|
||||
path = "src/mod.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
base64 = "0.22"
|
||||
directories-next = "2.0"
|
||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "gif", "webp"] }
|
||||
md5 = "0.8"
|
||||
once_cell = "1.19"
|
||||
zip = { version = "6.0", default-features = false, features = ["deflate"] }
|
||||
windows = { version = "0.62", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Security",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Registry",
|
||||
"Win32_UI_Shell",
|
||||
] }
|
||||
windows-core = "0.62"
|
||||
@@ -0,0 +1,134 @@
|
||||
# Windows Thumbnail Provider for Readest
|
||||
|
||||
This crate provides Windows Explorer thumbnail support for eBook files when Readest is set as the default application.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Cover Extraction**: Extracts cover images from EPUB, MOBI, AZW, AZW3, FB2, CBZ, CBR files
|
||||
- **Readest Branding**: Adds a small Readest icon overlay at the bottom-right corner
|
||||
- **Smart Caching**: Caches generated thumbnails for faster subsequent loads
|
||||
- **File Association Aware**: Only shows thumbnails when Readest is the default app for the file type
|
||||
- **COM Integration**: Full Windows Shell extension implementation via `IThumbnailProvider`
|
||||
|
||||
## Supported Formats
|
||||
|
||||
| Format | Extension | Cover Source |
|
||||
| ---------- | ----------------------- | ---------------------------- |
|
||||
| EPUB | `.epub` | OPF manifest cover reference |
|
||||
| MOBI/AZW | `.mobi`, `.azw`, `.prc` | EXTH cover offset |
|
||||
| AZW3/KF8 | `.azw3`, `.kf8` | KF8 format cover |
|
||||
| FB2 | `.fb2` | `<binary>` coverpage element |
|
||||
| Comic Book | `.cbz`, `.cbr` | First image in archive |
|
||||
| Plain Text | `.txt` | Generated placeholder |
|
||||
|
||||
## Building
|
||||
|
||||
### Library Only
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
### COM DLL (for Windows Explorer integration)
|
||||
|
||||
```bash
|
||||
cargo build --release --features com
|
||||
```
|
||||
|
||||
### CLI Tool
|
||||
|
||||
```bash
|
||||
cargo build --release --features cli
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
The thumbnail provider DLL is automatically registered when Readest is installed via the NSIS installer.
|
||||
|
||||
### Manual Registration (for development)
|
||||
|
||||
```powershell
|
||||
# Register the DLL
|
||||
regsvr32 /s target\release\windows_thumbnail.dll
|
||||
|
||||
# Unregister the DLL
|
||||
regsvr32 /s /u target\release\windows_thumbnail.dll
|
||||
|
||||
# Refresh Explorer to see changes
|
||||
ie4uinit.exe -show
|
||||
```
|
||||
|
||||
After registration, you may need to restart Windows Explorer or log out/in for changes to take effect.
|
||||
|
||||
## Usage (Development / Manual testing)
|
||||
|
||||
For local development and testing, build the Windows DLL (or the library) from the Readest Tauri app folder and register it manually. The legacy CLI test harness used to live in the separate `packages/tauri` workspace, but the thumbnail handler implementation now lives inside Readest's Tauri app.
|
||||
|
||||
Build the DLL (for Windows explorer integration):
|
||||
|
||||
```powershell
|
||||
cd apps/readest-app/src-tauri
|
||||
cargo build --release --manifest-path Cargo.toml --features com
|
||||
```
|
||||
|
||||
The standalone CLI test harness is no longer distributed with the app. To test the thumbnail provider locally, build and register the DLL as shown above and use a small test harness that imports `readestlib`'s thumbnail code or use Explorer after registering the handler.
|
||||
|
||||
Manual registration for development (register the generated DLL):
|
||||
|
||||
```powershell
|
||||
# Register the DLL
|
||||
regsvr32 /s target\release\windows_thumbnail.dll
|
||||
|
||||
# Unregister the DLL
|
||||
regsvr32 /s /u target\release\windows_thumbnail.dll
|
||||
|
||||
# Refresh Explorer to see changes
|
||||
ie4uinit.exe -show
|
||||
```
|
||||
|
||||
This generates a thumbnail with the Readest overlay at the specified size.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Windows Explorer │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ IThumbnailProvider ───┼──► ThumbnailProvider │
|
||||
│ │ │ │
|
||||
│ │ ▼ │
|
||||
│ │ Check File Association │
|
||||
│ │ (is Readest the default?) │
|
||||
│ │ │ │
|
||||
│ │ ▼ (if yes) │
|
||||
│ │ Extract Cover Image │
|
||||
│ │ │ │
|
||||
│ │ ▼ │
|
||||
│ │ Add Readest Overlay │
|
||||
│ │ │ │
|
||||
│ │ ▼ │
|
||||
│ │ Return HBITMAP │
|
||||
│ │ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## COM Details
|
||||
|
||||
- **CLSID**: `{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}`
|
||||
- **Shell Thumbnail Handler GUID**: `{e357fccd-a995-4576-b01f-234630154e96}`
|
||||
- **Threading Model**: Apartment
|
||||
|
||||
## How It Works
|
||||
|
||||
1. When Windows Explorer needs a thumbnail, it queries the registered shell extension
|
||||
2. The COM DLL implements `IInitializeWithItem` to receive the file path
|
||||
3. It checks if Readest.exe is the default application for that file type using `AssocQueryStringW`
|
||||
4. If Readest is the default, it extracts the cover and generates the thumbnail
|
||||
5. If Readest is NOT the default, it returns `S_FALSE` to let Windows use other handlers
|
||||
|
||||
This ensures thumbnails only appear for files the user has associated with Readest.
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See LICENSE file for details.
|
||||
@@ -0,0 +1,506 @@
|
||||
/// Windows COM Thumbnail Provider for Readest
|
||||
///
|
||||
/// Implements IThumbnailProvider and IInitializeWithItem for Windows Shell integration.
|
||||
/// This allows Windows Explorer to show book covers as thumbnails for eBook files.
|
||||
///
|
||||
/// **Important**: Thumbnails are only shown when Readest.exe is the default application
|
||||
/// for the file type.
|
||||
///
|
||||
/// ## CLSID: {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ffi::c_void;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicIsize, AtomicU32, Ordering};
|
||||
|
||||
use windows::core::{IUnknown, Interface, GUID, HRESULT, PCWSTR, PWSTR};
|
||||
use windows::Win32::Foundation::{
|
||||
CLASS_E_NOAGGREGATION, E_FAIL, E_INVALIDARG, E_NOINTERFACE, HMODULE, S_FALSE, S_OK,
|
||||
};
|
||||
use windows::Win32::Graphics::Gdi::{
|
||||
CreateDIBSection, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, HBITMAP,
|
||||
};
|
||||
use windows::Win32::System::Com::{CoTaskMemFree, IClassFactory, IClassFactory_Impl};
|
||||
use windows::Win32::System::LibraryLoader::GetModuleFileNameW;
|
||||
use windows::Win32::System::Registry::{
|
||||
RegCloseKey, RegCreateKeyExW, RegDeleteTreeW, RegSetValueExW, HKEY, HKEY_CLASSES_ROOT,
|
||||
KEY_WRITE, REG_OPTION_NON_VOLATILE, REG_SZ,
|
||||
};
|
||||
use windows::Win32::UI::Shell::{
|
||||
AssocQueryStringW, IInitializeWithItem, IInitializeWithItem_Impl, IShellItem,
|
||||
IThumbnailProvider, IThumbnailProvider_Impl, ASSOCF_NONE, ASSOCSTR_EXECUTABLE,
|
||||
SIGDN_FILESYSPATH, WTSAT_ARGB, WTS_ALPHATYPE,
|
||||
};
|
||||
use windows_core::BOOL;
|
||||
use windows_core::{implement, Ref};
|
||||
|
||||
use super::cached_thumbnail_for_path;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// CLSID for Readest Thumbnail Provider
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// CLSID: {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
|
||||
pub const CLSID_READEST_THUMBNAIL: GUID = GUID::from_u128(0xA1B2C3D4_E5F6_7890_ABCD_EF1234567890);
|
||||
|
||||
/// Supported file extensions
|
||||
pub const SUPPORTED_EXTENSIONS: &[&str] = &[
|
||||
".epub", ".mobi", ".azw", ".azw3", ".kf8", ".prc", ".fb2", ".cbz", ".cbr", ".txt",
|
||||
];
|
||||
|
||||
// DLL reference counting
|
||||
static DLL_REF_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
static DLL_MODULE_PTR: AtomicIsize = AtomicIsize::new(0);
|
||||
|
||||
fn dll_add_ref() {
|
||||
DLL_REF_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
fn dll_release() {
|
||||
DLL_REF_COUNT.fetch_sub(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn set_dll_module(h: HMODULE) {
|
||||
DLL_MODULE_PTR.store(h.0 as isize, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn get_dll_module() -> Option<HMODULE> {
|
||||
let ptr = DLL_MODULE_PTR.load(Ordering::SeqCst);
|
||||
if ptr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(HMODULE(ptr as *mut c_void))
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// File Association Check
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Check if Readest.exe is the default application for a given file extension.
|
||||
fn is_readest_default_for_extension(ext: &str) -> bool {
|
||||
let ext_wide: Vec<u16> = ext.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
let mut buffer = [0u16; 260];
|
||||
let mut buffer_size = buffer.len() as u32;
|
||||
|
||||
unsafe {
|
||||
let result = AssocQueryStringW(
|
||||
ASSOCF_NONE,
|
||||
ASSOCSTR_EXECUTABLE,
|
||||
PCWSTR(ext_wide.as_ptr()),
|
||||
None,
|
||||
Some(PWSTR(buffer.as_mut_ptr())),
|
||||
&mut buffer_size,
|
||||
);
|
||||
|
||||
if result.is_ok() {
|
||||
let len = buffer.iter().position(|&c| c == 0).unwrap_or(buffer.len());
|
||||
let path = String::from_utf16_lossy(&buffer[..len]).to_lowercase();
|
||||
return path.contains("readest");
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if Readest is the default app for a specific file path.
|
||||
fn is_readest_default_for_file(path: &PathBuf) -> bool {
|
||||
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
|
||||
let ext_with_dot = format!(".{}", ext.to_lowercase());
|
||||
return is_readest_default_for_extension(&ext_with_dot);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// ThumbnailProvider
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Interior mutability wrapper for COM single-threaded apartment
|
||||
struct ComCell<T>(UnsafeCell<T>);
|
||||
|
||||
impl<T> ComCell<T> {
|
||||
fn new(value: T) -> Self {
|
||||
Self(UnsafeCell::new(value))
|
||||
}
|
||||
fn get(&self) -> &T {
|
||||
unsafe { &*self.0.get() }
|
||||
}
|
||||
fn set(&self, value: T) {
|
||||
unsafe {
|
||||
*self.0.get() = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: COM thumbnail providers run in single-threaded apartment (STA)
|
||||
unsafe impl<T> Sync for ComCell<T> {}
|
||||
unsafe impl<T> Send for ComCell<T> {}
|
||||
|
||||
#[implement(IThumbnailProvider, IInitializeWithItem)]
|
||||
pub struct ThumbnailProvider {
|
||||
file_path: ComCell<Option<PathBuf>>,
|
||||
file_ext: ComCell<Option<String>>,
|
||||
should_provide: ComCell<bool>,
|
||||
}
|
||||
|
||||
impl ThumbnailProvider {
|
||||
pub fn new() -> Self {
|
||||
dll_add_ref();
|
||||
Self {
|
||||
file_path: ComCell::new(None),
|
||||
file_ext: ComCell::new(None),
|
||||
should_provide: ComCell::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ThumbnailProvider {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ThumbnailProvider {
|
||||
fn drop(&mut self) {
|
||||
dll_release();
|
||||
}
|
||||
}
|
||||
|
||||
impl IInitializeWithItem_Impl for ThumbnailProvider_Impl {
|
||||
fn Initialize(&self, psi: Ref<'_, IShellItem>, _grfmode: u32) -> windows::core::Result<()> {
|
||||
let item = psi.ok()?;
|
||||
|
||||
unsafe {
|
||||
let path_pwstr = item.GetDisplayName(SIGDN_FILESYSPATH)?;
|
||||
|
||||
let mut len = 0usize;
|
||||
let mut ptr = path_pwstr.0;
|
||||
while *ptr != 0 {
|
||||
len += 1;
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
|
||||
let slice = std::slice::from_raw_parts(path_pwstr.0, len);
|
||||
let path_str = String::from_utf16_lossy(slice);
|
||||
let path = PathBuf::from(&path_str);
|
||||
|
||||
CoTaskMemFree(Some(path_pwstr.0 as *const c_void));
|
||||
|
||||
let is_default = is_readest_default_for_file(&path);
|
||||
self.should_provide.set(is_default);
|
||||
|
||||
if !is_default {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ext = path
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.map(|s| s.to_lowercase())
|
||||
.unwrap_or_default();
|
||||
|
||||
self.file_path.set(Some(path));
|
||||
self.file_ext.set(Some(ext));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IThumbnailProvider_Impl for ThumbnailProvider_Impl {
|
||||
fn GetThumbnail(
|
||||
&self,
|
||||
cx: u32,
|
||||
phbmp: *mut HBITMAP,
|
||||
pdwalpha: *mut WTS_ALPHATYPE,
|
||||
) -> windows::core::Result<()> {
|
||||
if !*self.should_provide.get() {
|
||||
return Err(E_FAIL.into());
|
||||
}
|
||||
|
||||
let path = self.file_path.get().as_ref().ok_or(E_FAIL)?;
|
||||
let ext = self.file_ext.get().as_ref().ok_or(E_FAIL)?;
|
||||
|
||||
let png_bytes = cached_thumbnail_for_path(path, ext, cx).map_err(|_| E_FAIL)?;
|
||||
let img = image::load_from_memory(&png_bytes).map_err(|_| E_FAIL)?;
|
||||
let rgba = img.to_rgba8();
|
||||
let (width, height) = (rgba.width(), rgba.height());
|
||||
|
||||
let bmi = BITMAPINFO {
|
||||
bmiHeader: BITMAPINFOHEADER {
|
||||
biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,
|
||||
biWidth: width as i32,
|
||||
biHeight: -(height as i32),
|
||||
biPlanes: 1,
|
||||
biBitCount: 32,
|
||||
biCompression: BI_RGB.0,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut bits: *mut c_void = std::ptr::null_mut();
|
||||
|
||||
unsafe {
|
||||
let hbmp = CreateDIBSection(None, &bmi, DIB_RGB_COLORS, &mut bits, None, 0)
|
||||
.map_err(|_| E_FAIL)?;
|
||||
if bits.is_null() {
|
||||
return Err(E_FAIL.into());
|
||||
}
|
||||
|
||||
// RGBA -> BGRA
|
||||
let dst =
|
||||
std::slice::from_raw_parts_mut(bits as *mut u8, (width * height * 4) as usize);
|
||||
let src = rgba.as_raw();
|
||||
for i in 0..(width * height) as usize {
|
||||
let si = i * 4;
|
||||
dst[si] = src[si + 2]; // B
|
||||
dst[si + 1] = src[si + 1]; // G
|
||||
dst[si + 2] = src[si]; // R
|
||||
dst[si + 3] = src[si + 3]; // A
|
||||
}
|
||||
|
||||
*phbmp = hbmp;
|
||||
*pdwalpha = WTSAT_ARGB;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// ClassFactory
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[implement(IClassFactory)]
|
||||
pub struct ThumbnailProviderFactory;
|
||||
|
||||
impl ThumbnailProviderFactory {
|
||||
pub fn new() -> Self {
|
||||
dll_add_ref();
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ThumbnailProviderFactory {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ThumbnailProviderFactory {
|
||||
fn drop(&mut self) {
|
||||
dll_release();
|
||||
}
|
||||
}
|
||||
|
||||
impl IClassFactory_Impl for ThumbnailProviderFactory_Impl {
|
||||
fn CreateInstance(
|
||||
&self,
|
||||
punkouter: Ref<'_, IUnknown>,
|
||||
riid: *const GUID,
|
||||
ppvobject: *mut *mut c_void,
|
||||
) -> windows::core::Result<()> {
|
||||
unsafe {
|
||||
if ppvobject.is_null() {
|
||||
return Err(E_INVALIDARG.into());
|
||||
}
|
||||
*ppvobject = std::ptr::null_mut();
|
||||
if !punkouter.is_null() {
|
||||
return Err(CLASS_E_NOAGGREGATION.into());
|
||||
}
|
||||
|
||||
let provider: IThumbnailProvider = ThumbnailProvider::new().into();
|
||||
provider.query(&*riid, ppvobject).ok()
|
||||
}
|
||||
}
|
||||
|
||||
fn LockServer(&self, flock: BOOL) -> windows::core::Result<()> {
|
||||
if flock.as_bool() {
|
||||
dll_add_ref();
|
||||
} else {
|
||||
dll_release();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// DLL Exports
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn DllMain(hinstance: HMODULE, reason: u32, _reserved: *mut c_void) -> BOOL {
|
||||
const DLL_PROCESS_ATTACH: u32 = 1;
|
||||
if reason == DLL_PROCESS_ATTACH {
|
||||
set_dll_module(hinstance);
|
||||
}
|
||||
BOOL::from(true)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn DllCanUnloadNow() -> HRESULT {
|
||||
if DLL_REF_COUNT.load(Ordering::SeqCst) == 0 {
|
||||
S_OK
|
||||
} else {
|
||||
S_FALSE
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn DllGetClassObject(
|
||||
rclsid: *const GUID,
|
||||
riid: *const GUID,
|
||||
ppv: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
if ppv.is_null() || rclsid.is_null() || riid.is_null() {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
*ppv = std::ptr::null_mut();
|
||||
|
||||
if *rclsid != CLSID_READEST_THUMBNAIL {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
if *riid != IClassFactory::IID && *riid != IUnknown::IID {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
let factory: IClassFactory = ThumbnailProviderFactory::new().into();
|
||||
factory.query(&*riid, ppv)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn DllRegisterServer() -> HRESULT {
|
||||
match register_server_impl() {
|
||||
Ok(()) => S_OK,
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn DllUnregisterServer() -> HRESULT {
|
||||
let _ = unregister_server_impl();
|
||||
S_OK
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Registry helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn get_dll_path() -> Option<String> {
|
||||
let module = get_dll_module()?;
|
||||
let mut buffer = [0u16; 260];
|
||||
unsafe {
|
||||
let len = GetModuleFileNameW(Some(module), &mut buffer);
|
||||
if len == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(String::from_utf16_lossy(&buffer[..len as usize]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clsid_string() -> String {
|
||||
format!(
|
||||
"{{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}",
|
||||
CLSID_READEST_THUMBNAIL.data1,
|
||||
CLSID_READEST_THUMBNAIL.data2,
|
||||
CLSID_READEST_THUMBNAIL.data3,
|
||||
CLSID_READEST_THUMBNAIL.data4[0],
|
||||
CLSID_READEST_THUMBNAIL.data4[1],
|
||||
CLSID_READEST_THUMBNAIL.data4[2],
|
||||
CLSID_READEST_THUMBNAIL.data4[3],
|
||||
CLSID_READEST_THUMBNAIL.data4[4],
|
||||
CLSID_READEST_THUMBNAIL.data4[5],
|
||||
CLSID_READEST_THUMBNAIL.data4[6],
|
||||
CLSID_READEST_THUMBNAIL.data4[7]
|
||||
)
|
||||
}
|
||||
|
||||
fn to_wide(s: &str) -> Vec<u16> {
|
||||
s.encode_utf16().chain(std::iter::once(0)).collect()
|
||||
}
|
||||
|
||||
unsafe fn set_reg_value(key: HKEY, name: &str, value: &str) -> Result<(), HRESULT> {
|
||||
let name_w = to_wide(name);
|
||||
let value_w = to_wide(value);
|
||||
let bytes: &[u8] = std::slice::from_raw_parts(value_w.as_ptr() as *const u8, value_w.len() * 2);
|
||||
if RegSetValueExW(key, PCWSTR(name_w.as_ptr()), Some(0), REG_SZ, Some(bytes)).is_err() {
|
||||
Err(E_FAIL)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_reg_key(parent: HKEY, subkey: &str) -> Result<HKEY, HRESULT> {
|
||||
let subkey_w = to_wide(subkey);
|
||||
let mut hkey = HKEY::default();
|
||||
let result = RegCreateKeyExW(
|
||||
parent,
|
||||
PCWSTR(subkey_w.as_ptr()),
|
||||
Some(0),
|
||||
None,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
KEY_WRITE,
|
||||
None,
|
||||
&mut hkey,
|
||||
None,
|
||||
);
|
||||
if result.is_err() {
|
||||
Err(E_FAIL)
|
||||
} else {
|
||||
Ok(hkey)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn register_server_impl() -> Result<(), HRESULT> {
|
||||
let dll_path = get_dll_path().ok_or(E_FAIL)?;
|
||||
let clsid = clsid_string();
|
||||
|
||||
// CLSID key
|
||||
let clsid_key = create_reg_key(HKEY_CLASSES_ROOT, &format!("CLSID\\{}", clsid))?;
|
||||
set_reg_value(clsid_key, "", "Readest Thumbnail Provider")?;
|
||||
|
||||
// CRITICAL: DisableProcessIsolation = 1
|
||||
let disable_isolation_name = to_wide("DisableProcessIsolation");
|
||||
let value: u32 = 1;
|
||||
let _ = windows::Win32::System::Registry::RegSetValueExW(
|
||||
clsid_key,
|
||||
PCWSTR(disable_isolation_name.as_ptr()),
|
||||
Some(0),
|
||||
windows::Win32::System::Registry::REG_DWORD,
|
||||
Some(std::slice::from_raw_parts(
|
||||
&value as *const u32 as *const u8,
|
||||
4,
|
||||
)),
|
||||
);
|
||||
|
||||
let inproc_key = create_reg_key(clsid_key, "InprocServer32")?;
|
||||
set_reg_value(inproc_key, "", &dll_path)?;
|
||||
set_reg_value(inproc_key, "ThreadingModel", "Apartment")?;
|
||||
let _ = RegCloseKey(inproc_key);
|
||||
let _ = RegCloseKey(clsid_key);
|
||||
|
||||
// Register ShellEx thumbnail handler for each extension
|
||||
for ext in SUPPORTED_EXTENSIONS {
|
||||
let ext_shellex_path =
|
||||
format!("{}\\ShellEx\\{{e357fccd-a995-4576-b01f-234630154e96}}", ext);
|
||||
if let Ok(ext_shellex_key) = create_reg_key(HKEY_CLASSES_ROOT, &ext_shellex_path) {
|
||||
let _ = set_reg_value(ext_shellex_key, "", &clsid);
|
||||
let _ = RegCloseKey(ext_shellex_key);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn unregister_server_impl() -> Result<(), HRESULT> {
|
||||
let clsid = clsid_string();
|
||||
let clsid_path = to_wide(&format!("CLSID\\{}", clsid));
|
||||
let _ = RegDeleteTreeW(HKEY_CLASSES_ROOT, PCWSTR(clsid_path.as_ptr()));
|
||||
|
||||
for ext in SUPPORTED_EXTENSIONS {
|
||||
let ext_path = to_wide(&format!(
|
||||
"{}\\ShellEx\\{{e357fccd-a995-4576-b01f-234630154e96}}",
|
||||
ext
|
||||
));
|
||||
let _ = RegDeleteTreeW(HKEY_CLASSES_ROOT, PCWSTR(ext_path.as_ptr()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,648 @@
|
||||
/// Cover image extraction for various eBook formats
|
||||
///
|
||||
/// Supports: EPUB, MOBI/AZW3/KF8, FB2, CBZ/CBR, TXT
|
||||
use anyhow::{anyhow, Result};
|
||||
use base64::engine::general_purpose;
|
||||
use base64::Engine as _;
|
||||
use directories_next::ProjectDirs;
|
||||
use image::{imageops, DynamicImage, Rgba};
|
||||
use md5::Context;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
use zip::ZipArchive;
|
||||
|
||||
/// Thumbnail cache directory (per-user)
|
||||
static CACHE_DIR: Lazy<Option<std::path::PathBuf>> = Lazy::new(|| {
|
||||
ProjectDirs::from("app", "Readest", "").map(|pd| {
|
||||
let dir = pd.cache_dir().join("thumbnails");
|
||||
let _ = std::fs::create_dir_all(&dir);
|
||||
dir
|
||||
})
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// EPUB extraction
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Extract cover image bytes from an EPUB file.
|
||||
pub fn extract_epub_cover_bytes<R: Read + Seek>(reader: R) -> Result<Vec<u8>> {
|
||||
let mut archive = ZipArchive::new(reader)?;
|
||||
|
||||
// Pass 1: Look for files with "cover" in the name
|
||||
let mut candidates: Vec<(usize, String, u64)> = Vec::new();
|
||||
for i in 0..archive.len() {
|
||||
let file = archive.by_index(i)?;
|
||||
let name = file.name().to_lowercase();
|
||||
let size = file.size();
|
||||
drop(file);
|
||||
|
||||
if is_image_extension(&name) && (name.contains("cover") || name.contains("front")) {
|
||||
candidates.push((i, name, size));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by priority: exact "cover" match first, then by size
|
||||
if !candidates.is_empty() {
|
||||
candidates.sort_by(|a, b| {
|
||||
let a_exact = a.1.contains("cover.") || a.1.ends_with("cover");
|
||||
let b_exact = b.1.contains("cover.") || b.1.ends_with("cover");
|
||||
match (a_exact, b_exact) {
|
||||
(true, false) => std::cmp::Ordering::Less,
|
||||
(false, true) => std::cmp::Ordering::Greater,
|
||||
_ => b.2.cmp(&a.2),
|
||||
}
|
||||
});
|
||||
|
||||
let idx = candidates[0].0;
|
||||
let mut file = archive.by_index(idx)?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
return Ok(buf);
|
||||
}
|
||||
|
||||
// Pass 2: Parse container.xml to find OPF, then parse OPF for cover
|
||||
let container_xml = read_zip_file_to_string(&mut archive, "META-INF/container.xml");
|
||||
if let Ok(xml) = container_xml {
|
||||
if let Some(rootfile) = extract_attribute(&xml, "rootfile", "full-path") {
|
||||
let opf_content = read_zip_file_to_string(&mut archive, &rootfile);
|
||||
if let Ok(opf) = opf_content {
|
||||
if let Some(cover_id) = find_cover_id_in_opf(&opf) {
|
||||
if let Some(href) = find_href_by_id_in_opf(&opf, &cover_id) {
|
||||
let base = Path::new(&rootfile).parent().unwrap_or(Path::new(""));
|
||||
let cover_path = base.join(&href).to_string_lossy().replace('\\', "/");
|
||||
if let Ok(bytes) = read_zip_file_to_bytes(&mut archive, &cover_path) {
|
||||
return Ok(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(href) = find_first_image_in_manifest(&opf) {
|
||||
let base = Path::new(&rootfile).parent().unwrap_or(Path::new(""));
|
||||
let cover_path = base.join(&href).to_string_lossy().replace('\\', "/");
|
||||
if let Ok(bytes) = read_zip_file_to_bytes(&mut archive, &cover_path) {
|
||||
return Ok(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 3: Just grab the largest image file
|
||||
let mut largest: Option<(usize, u64)> = None;
|
||||
for i in 0..archive.len() {
|
||||
let file = archive.by_index(i)?;
|
||||
let name = file.name().to_lowercase();
|
||||
let size = file.size();
|
||||
drop(file);
|
||||
|
||||
if is_image_extension(&name) && (largest.is_none() || size > largest.unwrap().1) {
|
||||
largest = Some((i, size));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((idx, _)) = largest {
|
||||
let mut file = archive.by_index(idx)?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
return Ok(buf);
|
||||
}
|
||||
|
||||
Err(anyhow!("No cover image found in EPUB"))
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// MOBI/AZW3/KF8 extraction
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Extract cover image from MOBI/AZW3/KF8 files.
|
||||
pub fn extract_mobi_cover_bytes<R: Read + Seek>(mut reader: R) -> Result<Vec<u8>> {
|
||||
let mut header = [0u8; 78];
|
||||
reader.read_exact(&mut header)?;
|
||||
|
||||
if &header[60..68] != b"BOOKMOBI" {
|
||||
return Err(anyhow!("Not a valid MOBI file"));
|
||||
}
|
||||
|
||||
let num_records = u16::from_be_bytes([header[76], header[77]]) as usize;
|
||||
|
||||
let mut record_offsets: Vec<u32> = Vec::with_capacity(num_records);
|
||||
for _ in 0..num_records {
|
||||
let mut rec = [0u8; 8];
|
||||
reader.read_exact(&mut rec)?;
|
||||
record_offsets.push(u32::from_be_bytes([rec[0], rec[1], rec[2], rec[3]]));
|
||||
}
|
||||
|
||||
if record_offsets.is_empty() {
|
||||
return Err(anyhow!("No records in MOBI file"));
|
||||
}
|
||||
|
||||
reader.seek(SeekFrom::Start(record_offsets[0] as u64))?;
|
||||
let mut mobi_header = [0u8; 256];
|
||||
reader.read_exact(&mut mobi_header)?;
|
||||
|
||||
if &mobi_header[16..20] != b"MOBI" {
|
||||
return Err(anyhow!("Invalid MOBI header"));
|
||||
}
|
||||
|
||||
let header_length = u32::from_be_bytes([
|
||||
mobi_header[20],
|
||||
mobi_header[21],
|
||||
mobi_header[22],
|
||||
mobi_header[23],
|
||||
]) as usize;
|
||||
|
||||
let exth_flags = u32::from_be_bytes([
|
||||
mobi_header[128],
|
||||
mobi_header[129],
|
||||
mobi_header[130],
|
||||
mobi_header[131],
|
||||
]);
|
||||
if exth_flags & 0x40 == 0 {
|
||||
return Err(anyhow!("No EXTH header in MOBI file"));
|
||||
}
|
||||
|
||||
let exth_offset = record_offsets[0] as u64 + 16 + header_length as u64;
|
||||
reader.seek(SeekFrom::Start(exth_offset))?;
|
||||
|
||||
let mut exth_magic = [0u8; 4];
|
||||
reader.read_exact(&mut exth_magic)?;
|
||||
if &exth_magic != b"EXTH" {
|
||||
return Err(anyhow!("EXTH header not found"));
|
||||
}
|
||||
|
||||
let mut exth_len_bytes = [0u8; 4];
|
||||
reader.read_exact(&mut exth_len_bytes)?;
|
||||
|
||||
let mut exth_count_bytes = [0u8; 4];
|
||||
reader.read_exact(&mut exth_count_bytes)?;
|
||||
let exth_count = u32::from_be_bytes(exth_count_bytes) as usize;
|
||||
|
||||
let mut cover_offset: Option<u32> = None;
|
||||
let first_img_idx = u32::from_be_bytes([
|
||||
mobi_header[108],
|
||||
mobi_header[109],
|
||||
mobi_header[110],
|
||||
mobi_header[111],
|
||||
]);
|
||||
|
||||
for _ in 0..exth_count {
|
||||
let mut rec_header = [0u8; 8];
|
||||
if reader.read_exact(&mut rec_header).is_err() {
|
||||
break;
|
||||
}
|
||||
let rec_type =
|
||||
u32::from_be_bytes([rec_header[0], rec_header[1], rec_header[2], rec_header[3]]);
|
||||
let rec_len =
|
||||
u32::from_be_bytes([rec_header[4], rec_header[5], rec_header[6], rec_header[7]])
|
||||
as usize;
|
||||
|
||||
let data_len = rec_len.saturating_sub(8);
|
||||
let mut data = vec![0u8; data_len];
|
||||
if reader.read_exact(&mut data).is_err() {
|
||||
break;
|
||||
}
|
||||
|
||||
if rec_type == 201 && data_len >= 4 {
|
||||
cover_offset = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]));
|
||||
}
|
||||
}
|
||||
|
||||
let cover_record_idx = if let Some(offset) = cover_offset {
|
||||
first_img_idx + offset
|
||||
} else {
|
||||
first_img_idx
|
||||
};
|
||||
|
||||
if cover_record_idx as usize >= record_offsets.len() {
|
||||
return Err(anyhow!("Cover record index out of bounds"));
|
||||
}
|
||||
|
||||
let start = record_offsets[cover_record_idx as usize] as u64;
|
||||
let end = if (cover_record_idx as usize + 1) < record_offsets.len() {
|
||||
record_offsets[cover_record_idx as usize + 1] as u64
|
||||
} else {
|
||||
reader.seek(SeekFrom::End(0))?;
|
||||
reader.stream_position()?
|
||||
};
|
||||
|
||||
let len = (end - start) as usize;
|
||||
reader.seek(SeekFrom::Start(start))?;
|
||||
let mut cover_data = vec![0u8; len];
|
||||
reader.read_exact(&mut cover_data)?;
|
||||
|
||||
if cover_data.starts_with(&[0xFF, 0xD8, 0xFF])
|
||||
|| cover_data.starts_with(&[0x89, 0x50, 0x4E, 0x47])
|
||||
|| cover_data.starts_with(b"GIF")
|
||||
{
|
||||
return Ok(cover_data);
|
||||
}
|
||||
|
||||
Err(anyhow!("No valid cover image found in MOBI"))
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// CBZ extraction
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Extract cover image from CBZ (comic book ZIP) file.
|
||||
pub fn extract_cbz_cover_bytes<R: Read + Seek>(reader: R) -> Result<Vec<u8>> {
|
||||
let mut archive = ZipArchive::new(reader)?;
|
||||
|
||||
let mut images: Vec<(usize, String)> = Vec::new();
|
||||
for i in 0..archive.len() {
|
||||
let file = archive.by_index(i)?;
|
||||
let name = file.name().to_string();
|
||||
drop(file);
|
||||
|
||||
if is_image_extension(&name.to_lowercase()) {
|
||||
images.push((i, name));
|
||||
}
|
||||
}
|
||||
|
||||
images.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
|
||||
if let Some((idx, _)) = images.first() {
|
||||
let mut file = archive.by_index(*idx)?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
return Ok(buf);
|
||||
}
|
||||
|
||||
Err(anyhow!("No images found in CBZ"))
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// FB2 extraction
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Extract cover image from FB2 (FictionBook) file.
|
||||
pub fn extract_fb2_cover_bytes<R: Read>(mut reader: R) -> Result<Vec<u8>> {
|
||||
let mut content = String::new();
|
||||
reader.read_to_string(&mut content)?;
|
||||
|
||||
let cover_id = if let Some(start) = content.find("<coverpage>") {
|
||||
let end = content[start..].find("</coverpage>").unwrap_or(500);
|
||||
let coverpage = &content[start..start + end];
|
||||
if let Some(href_pos) = coverpage.find("href=\"#") {
|
||||
let id_start = href_pos + 7;
|
||||
let id_end = coverpage[id_start..].find('"').unwrap_or(50);
|
||||
Some(coverpage[id_start..id_start + id_end].to_string())
|
||||
} else if let Some(href_pos) = coverpage.find("l:href=\"#") {
|
||||
let id_start = href_pos + 9;
|
||||
let id_end = coverpage[id_start..].find('"').unwrap_or(50);
|
||||
Some(coverpage[id_start..id_start + id_end].to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let search_pattern = if let Some(ref id) = cover_id {
|
||||
format!("<binary id=\"{}\"", id)
|
||||
} else {
|
||||
"<binary".to_string()
|
||||
};
|
||||
|
||||
if let Some(pos) = content.find(&search_pattern) {
|
||||
if let Some(tag_end) = content[pos..].find('>') {
|
||||
let data_start = pos + tag_end + 1;
|
||||
if let Some(data_end) = content[data_start..].find("</binary>") {
|
||||
let b64_data = content[data_start..data_start + data_end].trim();
|
||||
let b64_clean: String = b64_data.chars().filter(|c| !c.is_whitespace()).collect();
|
||||
let bytes = general_purpose::STANDARD.decode(&b64_clean)?;
|
||||
return Ok(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cover_id.is_some() {
|
||||
if let Some(pos) = content.find("<binary") {
|
||||
if let Some(tag_end) = content[pos..].find('>') {
|
||||
let data_start = pos + tag_end + 1;
|
||||
if let Some(data_end) = content[data_start..].find("</binary>") {
|
||||
let b64_data = content[data_start..data_start + data_end].trim();
|
||||
let b64_clean: String =
|
||||
b64_data.chars().filter(|c| !c.is_whitespace()).collect();
|
||||
let bytes = general_purpose::STANDARD.decode(&b64_clean)?;
|
||||
return Ok(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("No cover image found in FB2"))
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// TXT "cover" (placeholder)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Generate a placeholder thumbnail for TXT files.
|
||||
pub fn extract_txt_cover_bytes<R: Read>(mut reader: R, size: u32) -> Result<Vec<u8>> {
|
||||
let mut buf = vec![0u8; 4096];
|
||||
let _n = reader.read(&mut buf)?;
|
||||
|
||||
let mut img = image::RgbaImage::from_pixel(size, size, Rgba([245, 245, 245, 255]));
|
||||
|
||||
for x in 0..size {
|
||||
img.put_pixel(x, 0, Rgba([200, 200, 200, 255]));
|
||||
img.put_pixel(x, size - 1, Rgba([200, 200, 200, 255]));
|
||||
}
|
||||
for y in 0..size {
|
||||
img.put_pixel(0, y, Rgba([200, 200, 200, 255]));
|
||||
img.put_pixel(size - 1, y, Rgba([200, 200, 200, 255]));
|
||||
}
|
||||
|
||||
let mut out = Vec::new();
|
||||
DynamicImage::ImageRgba8(img).write_to(&mut Cursor::new(&mut out), image::ImageFormat::Png)?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Unified extraction by extension
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Extract cover image bytes based on file extension.
|
||||
pub fn extract_cover_bytes_by_ext(path: &Path, ext: &str) -> Result<Vec<u8>> {
|
||||
let file = std::fs::File::open(path)?;
|
||||
match ext.to_lowercase().as_str() {
|
||||
"epub" => extract_epub_cover_bytes(file),
|
||||
"mobi" | "azw" | "azw3" | "kf8" | "prc" => extract_mobi_cover_bytes(file),
|
||||
"cbz" | "cbr" => extract_cbz_cover_bytes(file),
|
||||
"fb2" => extract_fb2_cover_bytes(file),
|
||||
"txt" => extract_txt_cover_bytes(file, 256),
|
||||
_ => Err(anyhow!("Unsupported format: {}", ext)),
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Thumbnail creation with overlay
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Create a thumbnail from cover image bytes with Readest icon overlay.
|
||||
pub fn create_thumbnail_with_overlay(cover_bytes: &[u8], requested_size: u32) -> Result<Vec<u8>> {
|
||||
let img = image::load_from_memory(cover_bytes)?;
|
||||
let thumbnail = img.thumbnail(requested_size, requested_size);
|
||||
|
||||
let overlay_img = load_overlay_icon();
|
||||
|
||||
let mut base = thumbnail.to_rgba8();
|
||||
let (base_w, base_h) = (base.width(), base.height());
|
||||
|
||||
if let Some(ov) = overlay_img {
|
||||
let overlay_size = (requested_size / 5).clamp(24, 48);
|
||||
let ov_resized = ov.resize(overlay_size, overlay_size, imageops::FilterType::Lanczos3);
|
||||
let ovb = ov_resized.to_rgba8();
|
||||
let (ov_w, ov_h) = (ovb.width(), ovb.height());
|
||||
|
||||
let x = base_w.saturating_sub(ov_w + 4);
|
||||
let y = base_h.saturating_sub(ov_h + 4);
|
||||
|
||||
for oy in 0..ov_h {
|
||||
for ox in 0..ov_w {
|
||||
let dst_x = x + ox;
|
||||
let dst_y = y + oy;
|
||||
|
||||
if dst_x < base_w && dst_y < base_h {
|
||||
let src_pixel = ovb.get_pixel(ox, oy);
|
||||
let alpha = src_pixel.0[3] as f32 / 255.0;
|
||||
|
||||
if alpha > 0.0 {
|
||||
let dst_pixel = base.get_pixel(dst_x, dst_y);
|
||||
let mut result = dst_pixel.0;
|
||||
|
||||
for c in 0..3 {
|
||||
let fg = src_pixel.0[c] as f32;
|
||||
let bg = result[c] as f32;
|
||||
result[c] = (fg * alpha + bg * (1.0 - alpha)) as u8;
|
||||
}
|
||||
result[3] = 255;
|
||||
|
||||
base.put_pixel(dst_x, dst_y, Rgba(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut out = Vec::new();
|
||||
DynamicImage::ImageRgba8(base).write_to(&mut Cursor::new(&mut out), image::ImageFormat::Png)?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Load the Readest overlay icon.
|
||||
fn load_overlay_icon() -> Option<DynamicImage> {
|
||||
// Try embedded icon
|
||||
let icon_bytes = include_bytes!("../../../public/icon.png");
|
||||
if let Ok(img) = image::load_from_memory(icon_bytes) {
|
||||
return Some(img);
|
||||
}
|
||||
|
||||
// Fallback: try loading from filesystem
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
let candidates = [
|
||||
exe.parent().map(|p| p.join("icon.png")),
|
||||
exe.parent().map(|p| p.join("resources").join("icon.png")),
|
||||
exe.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| p.join("resources").join("icon.png")),
|
||||
];
|
||||
|
||||
for candidate in candidates.into_iter().flatten() {
|
||||
if candidate.exists() {
|
||||
if let Ok(bytes) = std::fs::read(&candidate) {
|
||||
if let Ok(img) = image::load_from_memory(&bytes) {
|
||||
return Some(img);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Caching
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Generate a thumbnail with disk caching.
|
||||
pub fn cached_thumbnail_for_path(path: &Path, ext: &str, size: u32) -> Result<Vec<u8>> {
|
||||
// Compute cache key by hashing file parts for stability without loading entire file
|
||||
let mut hasher = Context::new();
|
||||
hasher.consume(ext.as_bytes());
|
||||
hasher.consume(&size.to_le_bytes());
|
||||
|
||||
let file = std::fs::File::open(path)?;
|
||||
let metadata = file.metadata()?;
|
||||
let file_len = metadata.len();
|
||||
|
||||
// Read partial chunks like the TypeScript partialMD5 implementation
|
||||
const STEP: u64 = 1024;
|
||||
const SIZE: u64 = 1024;
|
||||
let mut file = file;
|
||||
|
||||
for i in -1i32..=10 {
|
||||
let pos = if i == -1 {
|
||||
256u64
|
||||
} else {
|
||||
STEP << (2 * i as u32)
|
||||
};
|
||||
let start = pos.min(file_len);
|
||||
let end = (start + SIZE).min(file_len);
|
||||
|
||||
if start >= file_len {
|
||||
break;
|
||||
}
|
||||
|
||||
file.seek(SeekFrom::Start(start))?;
|
||||
let mut buf = vec![0u8; (end - start) as usize];
|
||||
file.read_exact(&mut buf)?;
|
||||
hasher.consume(&buf);
|
||||
}
|
||||
|
||||
let digest = hasher.finalize();
|
||||
let key = format!("{:x}.png", digest);
|
||||
|
||||
if let Some(ref dir) = *CACHE_DIR {
|
||||
let cache_path = dir.join(&key);
|
||||
if cache_path.exists() {
|
||||
if let Ok(cached) = std::fs::read(&cache_path) {
|
||||
return Ok(cached);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cover = extract_cover_bytes_by_ext(path, ext)?;
|
||||
let thumbnail = create_thumbnail_with_overlay(&cover, size)?;
|
||||
|
||||
if let Some(ref dir) = *CACHE_DIR {
|
||||
let cache_path = dir.join(&key);
|
||||
let _ = std::fs::write(&cache_path, &thumbnail);
|
||||
}
|
||||
|
||||
Ok(thumbnail)
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Helper functions
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn is_image_extension(name: &str) -> bool {
|
||||
name.ends_with(".jpg")
|
||||
|| name.ends_with(".jpeg")
|
||||
|| name.ends_with(".png")
|
||||
|| name.ends_with(".gif")
|
||||
|| name.ends_with(".webp")
|
||||
|| name.ends_with(".bmp")
|
||||
}
|
||||
|
||||
fn read_zip_file_to_string<R: Read + Seek>(
|
||||
archive: &mut ZipArchive<R>,
|
||||
name: &str,
|
||||
) -> Result<String> {
|
||||
let mut file = archive.by_name(name)?;
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content)?;
|
||||
Ok(content)
|
||||
}
|
||||
|
||||
fn read_zip_file_to_bytes<R: Read + Seek>(
|
||||
archive: &mut ZipArchive<R>,
|
||||
name: &str,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut file = archive.by_name(name)?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn extract_attribute(xml: &str, tag: &str, attr: &str) -> Option<String> {
|
||||
let pattern = format!("<{}", tag);
|
||||
if let Some(tag_pos) = xml.find(&pattern) {
|
||||
let tag_end = xml[tag_pos..].find('>').unwrap_or(500) + tag_pos;
|
||||
let tag_content = &xml[tag_pos..tag_end];
|
||||
|
||||
let attr_pattern = format!("{}=\"", attr);
|
||||
if let Some(attr_pos) = tag_content.find(&attr_pattern) {
|
||||
let value_start = attr_pos + attr_pattern.len();
|
||||
if let Some(value_end) = tag_content[value_start..].find('"') {
|
||||
return Some(tag_content[value_start..value_start + value_end].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_cover_id_in_opf(opf: &str) -> Option<String> {
|
||||
if let Some(pos) = opf.find("name=\"cover\"") {
|
||||
let window_start = pos.saturating_sub(50);
|
||||
let window_end = (pos + 100).min(opf.len());
|
||||
let window = &opf[window_start..window_end];
|
||||
|
||||
if let Some(content_pos) = window.find("content=\"") {
|
||||
let start = content_pos + 9;
|
||||
if let Some(end) = window[start..].find('"') {
|
||||
return Some(window[start..start + end].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pos) = opf.find("properties=\"cover-image\"") {
|
||||
let window_start = pos.saturating_sub(200);
|
||||
let window_end = pos;
|
||||
let window = &opf[window_start..window_end];
|
||||
|
||||
if let Some(id_pos) = window.rfind("id=\"") {
|
||||
let start = id_pos + 4;
|
||||
if let Some(end) = window[start..].find('"') {
|
||||
return Some(window[start..start + end].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn find_href_by_id_in_opf(opf: &str, id: &str) -> Option<String> {
|
||||
let pattern = format!("id=\"{}\"", id);
|
||||
if let Some(pos) = opf.find(&pattern) {
|
||||
let window_start = pos.saturating_sub(10);
|
||||
let window_end = (pos + 200).min(opf.len());
|
||||
let window = &opf[window_start..window_end];
|
||||
|
||||
if let Some(href_pos) = window.find("href=\"") {
|
||||
let start = href_pos + 6;
|
||||
if let Some(end) = window[start..].find('"') {
|
||||
return Some(window[start..start + end].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_first_image_in_manifest(opf: &str) -> Option<String> {
|
||||
let manifest_start = opf.find("<manifest")?;
|
||||
let manifest_end = opf[manifest_start..]
|
||||
.find("</manifest>")
|
||||
.map(|e| manifest_start + e)?;
|
||||
let manifest = &opf[manifest_start..manifest_end];
|
||||
|
||||
for media_type in ["image/jpeg", "image/png", "image/gif", "image/webp"] {
|
||||
let pattern = format!("media-type=\"{}\"", media_type);
|
||||
if let Some(pos) = manifest.find(&pattern) {
|
||||
let window_start = pos.saturating_sub(200);
|
||||
let window = &manifest[window_start..pos];
|
||||
|
||||
if let Some(href_pos) = window.rfind("href=\"") {
|
||||
let start = href_pos + 6;
|
||||
if let Some(end) = window[start..].find('"') {
|
||||
return Some(window[start..start + end].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//! Windows Thumbnail Provider for Readest
|
||||
//!
|
||||
//! This module provides Windows Explorer thumbnail support for eBook files.
|
||||
//! Thumbnails are only shown when Readest is set as the default application.
|
||||
//!
|
||||
//! Supported formats: EPUB, MOBI, AZW, AZW3, KF8, FB2, CBZ, CBR
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
mod com_provider;
|
||||
mod extraction;
|
||||
|
||||
pub use extraction::*;
|
||||
@@ -12,11 +12,14 @@ module.exports = {
|
||||
'de',
|
||||
'ja',
|
||||
'es',
|
||||
'fa',
|
||||
'fr',
|
||||
'it',
|
||||
'el',
|
||||
'ko',
|
||||
'uk',
|
||||
'nl',
|
||||
'sv',
|
||||
'pl',
|
||||
'pt',
|
||||
'ru',
|
||||
@@ -24,7 +27,14 @@ module.exports = {
|
||||
'hi',
|
||||
'id',
|
||||
'vi',
|
||||
'ms',
|
||||
'he',
|
||||
'ar',
|
||||
'th',
|
||||
'bo',
|
||||
'bn',
|
||||
'ta',
|
||||
'si',
|
||||
'zh-CN',
|
||||
'zh-TW',
|
||||
],
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import withPWAInit from '@ducanh2912/next-pwa';
|
||||
import withSerwistInit from '@serwist/next';
|
||||
import withBundleAnalyzer from '@next/bundle-analyzer';
|
||||
|
||||
const isDev = process.env['NODE_ENV'] === 'development';
|
||||
const appPlatform = process.env['NEXT_PUBLIC_APP_PLATFORM'];
|
||||
|
||||
if (isDev) {
|
||||
const { initOpenNextCloudflareForDev } = await import('@opennextjs/cloudflare');
|
||||
initOpenNextCloudflareForDev();
|
||||
}
|
||||
|
||||
const exportOutput = appPlatform !== 'web' && !isDev;
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Ensure Next.js uses SSG instead of SSR
|
||||
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
|
||||
output: appPlatform === 'web' || isDev ? undefined : 'export',
|
||||
output: exportOutput ? 'export' : undefined,
|
||||
pageExtensions: exportOutput ? ['jsx', 'tsx'] : ['js', 'jsx', 'ts', 'tsx'],
|
||||
// Note: This feature is required to use the Next.js Image component in SSG mode.
|
||||
// See https://nextjs.org/docs/messages/export-image-api for different workarounds.
|
||||
images: {
|
||||
@@ -17,6 +26,39 @@ const nextConfig = {
|
||||
// Configure assetPrefix or else the server won't properly resolve your assets.
|
||||
assetPrefix: '',
|
||||
reactStrictMode: true,
|
||||
serverExternalPackages: ['isows'],
|
||||
webpack: (config) => {
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
nunjucks: 'nunjucks/browser/nunjucks.js',
|
||||
};
|
||||
return config;
|
||||
},
|
||||
turbopack: {
|
||||
resolveAlias: {
|
||||
nunjucks: 'nunjucks/browser/nunjucks.js',
|
||||
},
|
||||
},
|
||||
transpilePackages: [
|
||||
'ai',
|
||||
'ai-sdk-ollama',
|
||||
'@ai-sdk/react',
|
||||
'@assistant-ui/react',
|
||||
'@assistant-ui/react-ai-sdk',
|
||||
'@assistant-ui/react-markdown',
|
||||
'streamdown',
|
||||
...(isDev
|
||||
? []
|
||||
: [
|
||||
'i18next-browser-languagedetector',
|
||||
'react-i18next',
|
||||
'i18next',
|
||||
'@tauri-apps',
|
||||
'highlight.js',
|
||||
'foliate-js',
|
||||
'marked',
|
||||
]),
|
||||
],
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
@@ -28,23 +70,37 @@ const nextConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/_next/static/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: isDev
|
||||
? 'public, max-age=0, must-revalidate'
|
||||
: 'public, max-age=31536000, immutable',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const withPWA = withPWAInit({
|
||||
dest: 'public',
|
||||
disable: isDev || appPlatform !== 'web',
|
||||
cacheOnFrontEndNav: true,
|
||||
aggressiveFrontEndNavCaching: true,
|
||||
reloadOnOnline: true,
|
||||
swcMinify: true,
|
||||
fallbacks: {
|
||||
document: '/offline',
|
||||
},
|
||||
workboxOptions: {
|
||||
disableDevLogs: true,
|
||||
},
|
||||
const pwaDisabled = isDev || appPlatform !== 'web';
|
||||
|
||||
const withPWA = pwaDisabled
|
||||
? (config) => config
|
||||
: withSerwistInit({
|
||||
swSrc: 'src/sw.ts',
|
||||
swDest: 'public/sw.js',
|
||||
cacheOnNavigation: true,
|
||||
reloadOnOnline: true,
|
||||
disable: false,
|
||||
register: true,
|
||||
scope: '/',
|
||||
});
|
||||
|
||||
const withAnalyzer = withBundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
export default withPWA(nextConfig);
|
||||
export default withPWA(withAnalyzer(nextConfig));
|
||||
|
||||
@@ -1,29 +1,6 @@
|
||||
// @ts-nocheck
|
||||
// default open-next.config.ts file created by @opennextjs/cloudflare
|
||||
import { defineCloudflareConfig } from '@opennextjs/cloudflare';
|
||||
import r2IncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache';
|
||||
|
||||
import type { OpenNextConfig } from '@opennextjs/aws/types/open-next.js';
|
||||
import cache from '@opennextjs/cloudflare/kv-cache';
|
||||
|
||||
const config: OpenNextConfig = {
|
||||
default: {
|
||||
override: {
|
||||
wrapper: 'cloudflare-node',
|
||||
converter: 'edge',
|
||||
// set `incrementalCache` to "dummy" to disable KV cache
|
||||
incrementalCache: async () => cache,
|
||||
tagCache: 'dummy',
|
||||
queue: 'dummy',
|
||||
},
|
||||
},
|
||||
|
||||
middleware: {
|
||||
external: true,
|
||||
override: {
|
||||
wrapper: 'cloudflare-edge',
|
||||
converter: 'edge',
|
||||
proxyExternalRequest: 'fetch',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
export default defineCloudflareConfig({
|
||||
incrementalCache: r2IncrementalCache,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@readest/readest-app",
|
||||
"version": "0.9.41",
|
||||
"version": "0.9.101",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "dotenv -e .env.tauri -- next dev",
|
||||
@@ -9,17 +9,26 @@
|
||||
"dev-web": "dotenv -e .env.web -- next dev",
|
||||
"build-web": "dotenv -e .env.web -- next build",
|
||||
"start-web": "dotenv -e .env.web -- next start",
|
||||
"build-tauri": "dotenv -e .env.tauri -- next build",
|
||||
"i18n:extract": "i18next-scanner",
|
||||
"lint": "next lint",
|
||||
"lint": "eslint .",
|
||||
"test": "dotenv -e .env -e .env.test.local vitest",
|
||||
"tauri": "tauri",
|
||||
"prepare-public-vendor": "mkdirp ./public/vendor/pdfjs",
|
||||
"copy-pdfjs-js": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/legacy/build/{pdf.worker.min.mjs,pdf.mjs,pdf.d.mts}\" ./public/vendor/pdfjs",
|
||||
"clippy": "cargo clippy -p Readest --no-deps -- -D warnings",
|
||||
"format": "pnpm -w format",
|
||||
"format:check": "pnpm -w format:check",
|
||||
"prepare-public-vendor": "mkdirp ./public/vendor/pdfjs ./public/vendor/simplecc",
|
||||
"copy-pdfjs-js": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/legacy/build/{pdf.worker.min.mjs,pdf.min.mjs,pdf.d.mts}\" ./public/vendor/pdfjs",
|
||||
"copy-pdfjs-wasm": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/wasm/{openjpeg.wasm,qcms_bg.wasm}\" ./public/vendor/pdfjs",
|
||||
"copy-pdfjs-fonts": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/{cmaps,standard_fonts}/*\" ./public/vendor/pdfjs",
|
||||
"copy-flatten-pdfjs-annotation-layer-css": "npx postcss \"../../packages/foliate-js/vendor/pdfjs/annotation_layer_builder.css\" --no-map -u postcss-nested > ./public/vendor/pdfjs/annotation_layer_builder.css",
|
||||
"copy-flatten-pdfjs-text-layer-css": "npx postcss \"../../packages/foliate-js/vendor/pdfjs/text_layer_builder.css\" --no-map -u postcss-nested > ./public/vendor/pdfjs/text_layer_builder.css",
|
||||
"copy-flatten-pdfjs-css": "pnpm copy-flatten-pdfjs-annotation-layer-css && pnpm copy-flatten-pdfjs-text-layer-css",
|
||||
"copy-pdfjs": "pnpm copy-pdfjs-js && pnpm copy-pdfjs-fonts && pnpm copy-flatten-pdfjs-css",
|
||||
"copy-pdfjs": "pnpm copy-pdfjs-js && pnpm copy-pdfjs-wasm && pnpm copy-pdfjs-fonts && pnpm copy-flatten-pdfjs-css",
|
||||
"copy-simplecc": "cpx \"../../packages/simplecc-wasm/dist/web/*\" ./public/vendor/simplecc",
|
||||
"setup-pdfjs": "pnpm prepare-public-vendor && pnpm copy-pdfjs",
|
||||
"setup-simplecc": "pnpm prepare-public-vendor && pnpm copy-simplecc",
|
||||
"setup-vendors": "pnpm setup-pdfjs && pnpm setup-simplecc",
|
||||
"build-win-x64": "dotenv -e .env.tauri.local -- tauri build --target i686-pc-windows-msvc --bundles nsis",
|
||||
"build-win-arm64": "dotenv -e .env.tauri.local -- tauri build --target aarch64-pc-windows-msvc --bundles nsis",
|
||||
"build-linux-x64": "dotenv -e .env.tauri.local -- tauri build --target x86_64-unknown-linux-gnu --bundles appimage",
|
||||
@@ -30,80 +39,161 @@
|
||||
"build-ios-appstore": "dotenv -e .env.ios-appstore.local -- tauri ios build --export-method app-store-connect",
|
||||
"release-macos-universial-appstore": "dotenv -e .env.tauri.local -e .env.apple-appstore.local -- bash scripts/release-mac-appstore.sh",
|
||||
"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",
|
||||
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
|
||||
"release-google-play": "dotenv -e .env.google-play.local -- bash scripts/release-google-play.sh",
|
||||
"config-wrangler": "sed -i \"s/\\${TRANSLATIONS_KV_ID}/$TRANSLATIONS_KV_ID/g\" wrangler.toml",
|
||||
"preview": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare preview --ip 0.0.0.0 --port 3001",
|
||||
"deploy": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare deploy",
|
||||
"upload": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare upload",
|
||||
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts",
|
||||
"patch-build-webpack": "if [ \"$(uname)\" = \"Darwin\" ]; then sed -i '' 's/next build\"/next build --webpack\"/' package.json; else sed -i 's/next build\"/next build --webpack\"/' package.json; fi",
|
||||
"restore-build-original": "if [ \"$(uname)\" = \"Darwin\" ]; then sed -i '' 's/next build --webpack\"/next build\"/' package.json; else sed -i 's/next build --webpack\"/next build\"/' package.json; fi",
|
||||
"update-metadata": "bash ./scripts/sync-release-notes.sh release-notes.json ../../data/metainfo/appdata.xml",
|
||||
"check:optional-chaining": "count=$(grep -rno '\\?\\.[a-zA-Z_$]' .next/static/chunks/* out/_next/static/chunks/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Optional chaining found in output!'; exit 1; else echo '✅ No optional chaining found.'; fi",
|
||||
"check:translations": "count=$(grep -rno '__STRING_NOT_TRANSLATED__' public/locales/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Untranslated strings found!'; exit 1; else echo '✅ All strings translated.'; fi",
|
||||
"check:lookbehind-regex": "count=$(grep -rnoE '\\(\\?<[!=]' .next/static/chunks/* out/_next/static/chunks/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Lookbehind regex found in output!'; exit 1; else echo '✅ No lookbehind regex found.'; fi",
|
||||
"check:all": "pnpm check:translations && pnpm check:lookbehind-regex",
|
||||
"build-check": "pnpm build && pnpm build-web && pnpm check:all"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/react": "^3.0.49",
|
||||
"@assistant-ui/react": "0.11.56",
|
||||
"@assistant-ui/react-ai-sdk": "1.1.21",
|
||||
"@assistant-ui/react-markdown": "0.11.9",
|
||||
"@aws-sdk/client-s3": "^3.735.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.735.0",
|
||||
"@ducanh2912/next-pwa": "^10.2.9",
|
||||
"@choochmeque/tauri-plugin-sharekit-api": "^0.3.0",
|
||||
"@fabianlars/tauri-plugin-oauth": "2",
|
||||
"@opennextjs/cloudflare": "^1.15.1",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@serwist/next": "^9.4.2",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.4.0",
|
||||
"@supabase/auth-ui-react": "^0.4.7",
|
||||
"@supabase/auth-ui-shared": "^0.1.8",
|
||||
"@supabase/supabase-js": "^2.47.7",
|
||||
"@tauri-apps/api": "2.5.0",
|
||||
"@tauri-apps/plugin-cli": "^2.2.0",
|
||||
"@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.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",
|
||||
"@supabase/supabase-js": "^2.76.1",
|
||||
"@tauri-apps/api": "2.9.1",
|
||||
"@tauri-apps/plugin-cli": "^2.4.1",
|
||||
"@tauri-apps/plugin-deep-link": "^2.4.6",
|
||||
"@tauri-apps/plugin-dialog": "^2.6.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.5",
|
||||
"@tauri-apps/plugin-haptics": "^2.3.2",
|
||||
"@tauri-apps/plugin-http": "^2.5.6",
|
||||
"@tauri-apps/plugin-log": "^2.8.0",
|
||||
"@tauri-apps/plugin-opener": "^2.5.3",
|
||||
"@tauri-apps/plugin-os": "^2.3.2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-shell": "~2.3.4",
|
||||
"@tauri-apps/plugin-updater": "^2.9.0",
|
||||
"@tauri-apps/plugin-websocket": "~2.4.2",
|
||||
"@zip.js/zip.js": "^2.8.16",
|
||||
"abortcontroller-polyfill": "^1.7.8",
|
||||
"ai": "^6.0.47",
|
||||
"ai-sdk-ollama": "^3.2.0",
|
||||
"app-store-server-api": "^0.17.1",
|
||||
"aws4fetch": "^1.0.20",
|
||||
"buffer": "^6.0.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"cssbeautify": "^0.3.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"dompurify": "^3.3.0",
|
||||
"foliate-js": "workspace:*",
|
||||
"franc-min": "^6.2.0",
|
||||
"fzf": "^0.5.2",
|
||||
"google-auth-library": "^10.5.0",
|
||||
"googleapis": "^164.1.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"i18next": "^24.2.0",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"i18next-http-backend": "^3.0.1",
|
||||
"iso-639-2": "^3.0.2",
|
||||
"iso-639-3": "^3.0.1",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
"js-md5": "^0.8.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"next": "15.2.4",
|
||||
"posthog-js": "^1.205.0",
|
||||
"react": "19.0.0",
|
||||
"lucide-react": "^0.562.0",
|
||||
"lunr": "^2.3.9",
|
||||
"marked": "^15.0.12",
|
||||
"nanoid": "^5.1.6",
|
||||
"next": "16.1.6",
|
||||
"next-view-transitions": "^0.3.5",
|
||||
"nunjucks": "^3.2.4",
|
||||
"overlayscrollbars": "^2.11.4",
|
||||
"overlayscrollbars-react": "^0.5.6",
|
||||
"posthog-js": "^1.246.0",
|
||||
"react": "19.2.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "19.0.0",
|
||||
"react-dom": "19.2.0",
|
||||
"react-i18next": "^15.2.0",
|
||||
"react-icons": "^5.4.0",
|
||||
"react-responsive": "^10.0.0",
|
||||
"react-virtuoso": "^4.17.0",
|
||||
"react-window": "^1.8.11",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"semver": "^7.7.1",
|
||||
"streamdown": "^1.6.10",
|
||||
"stripe": "^18.2.1",
|
||||
"styled-jsx": "^5.1.7",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"zustand": "5.0.1"
|
||||
"uuid": "^11.1.0",
|
||||
"ws": "^8.18.3",
|
||||
"zod": "^4.0.8",
|
||||
"zustand": "5.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opennextjs/cloudflare": "^0.5.12",
|
||||
"@tauri-apps/cli": "2.5.0",
|
||||
"@next/bundle-analyzer": "^15.4.2",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tauri-apps/cli": "2.9.6",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/cssbeautify": "^0.3.5",
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/react": "18.3.12",
|
||||
"@types/lunr": "^2.3.7",
|
||||
"@types/node": "^22.15.31",
|
||||
"@types/nunjucks": "^3.2.6",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-color": "^3.0.13",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
||||
"@typescript-eslint/parser": "^8.48.0",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"caniuse-lite": "^1.0.30001746",
|
||||
"cpx2": "^8.0.0",
|
||||
"daisyui": "^4.12.24",
|
||||
"dotenv-cli": "^7.4.4",
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-config-next": "15.0.3",
|
||||
"eslint-config-next": "16.0.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"i18next-scanner": "^4.6.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"mkdirp": "^3.0.1",
|
||||
"node-env-run": "^4.0.2",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-cli": "^11.0.0",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"raw-loader": "^4.0.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"serwist": "^9.3.0",
|
||||
"tailwindcss": "^3.4.18",
|
||||
"typescript": "^5.7.2",
|
||||
"wrangler": "^4.4.0"
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^4.0.15",
|
||||
"wrangler": "^4.60.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
[
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"relation": [
|
||||
"delegate_permission/common.handle_all_urls",
|
||||
"delegate_permission/common.get_login_creds"
|
||||
],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.bilingify.readest",
|
||||
"sha256_cert_fingerprints": [
|
||||
"65:2D:11:67:76:12:29:14:18:42:CB:3D:18:50:B6:E4:7E:46:E1:2F:4B:E4:7F:5A:6C:14:B6:D7:12:74:1E:82"
|
||||
"65:2D:11:67:76:12:29:14:18:42:CB:3D:18:50:B6:E4:7E:46:E1:2F:4B:E4:7F:5A:6C:14:B6:D7:12:74:1E:82",
|
||||
"E0:E7:60:55:80:8D:3A:DE:A0:D1:CF:7C:20:85:40:A3:DD:4B:E6:4D:17:5C:0F:DE:26:57:7D:9C:5B:29:5F:51"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ed533042-5626-4704-b5f2-fa3bbd1136ed
|
||||
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 437 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 449 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 318 KiB |
@@ -1,4 +1,27 @@
|
||||
{
|
||||
"LXGW WenKai GB Screen": "LXGW WenKai SC",
|
||||
"LXGW WenKai TC": "LXGW WenKai TC"
|
||||
"LXGW WenKai TC": "LXGW WenKai TC",
|
||||
"Source Han Serif CN": "Source Han Serif",
|
||||
"Huiwen-MinchoGBK": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Are you sure to delete {{count}} selected book(s)?_one": "Are you sure to delete {{count}} selected book?",
|
||||
"Are you sure to delete {{count}} selected book(s)?_other": "Are you sure to delete {{count}} selected books?",
|
||||
"Search in {{count}} Book(s)..._one": "Search in {{count}} book...",
|
||||
"Search in {{count}} Book(s)..._other": "Search in {{count}} books...",
|
||||
"{{count}} pages left in chapter_one": "<0>{{count}}</0><1> page left in chapter</1>",
|
||||
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> pages left in chapter</1>",
|
||||
"Deleted {{count}} file(s)_one": "Deleted {{count}} file",
|
||||
"Deleted {{count}} file(s)_other": "Deleted {{count}} files",
|
||||
"Failed to delete {{count}} file(s)_one": "Failed to delete {{count}} file",
|
||||
"Failed to delete {{count}} file(s)_other": "Failed to delete {{count}} files",
|
||||
"{{count}} selected_one": "{{count}} selected",
|
||||
"{{count}} selected_other": "{{count}} selected",
|
||||
"Are you sure to delete {{count}} selected file(s)?_one": "Are you sure to delete {{count}} selected file?",
|
||||
"Are you sure to delete {{count}} selected file(s)?_other": "Are you sure to delete {{count}} selected files?",
|
||||
"Successfully imported {{count}} book(s)_one": "Successfully imported {{count}} book",
|
||||
"Successfully imported {{count}} book(s)_other": "Successfully imported {{count}} books",
|
||||
"Set status for {{count}} book(s)_one": "Set status for {{count}} book",
|
||||
"Set status for {{count}} book(s)_other": "Set status for {{count}} books",
|
||||
"{{count}} book(s) synced_one": "{{count}} book synced",
|
||||
"{{count}} book(s) synced_other": "{{count}} books synced"
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
"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",
|
||||
"Book Cover": "Sampul Buku",
|
||||
"Bookmark": "Penanda",
|
||||
"Cancel": "Batal",
|
||||
"Chapter": "Bab",
|
||||
@@ -19,7 +17,6 @@
|
||||
"Confirm Deletion": "Konfirmasi Penghapusan",
|
||||
"Copied to notebook": "Disalin ke buku catatan",
|
||||
"Copy": "Salin",
|
||||
"Custom CSS": "CSS Kustom",
|
||||
"Dark Mode": "Mode Gelap",
|
||||
"Default": "Default",
|
||||
"Default Font": "Font Default",
|
||||
@@ -29,7 +26,6 @@
|
||||
"Dictionary": "Kamus",
|
||||
"Download Readest": "Unduh Readest",
|
||||
"Edit": "Edit",
|
||||
"Enter your custom CSS here...": "Masukkan CSS kustom Anda di sini...",
|
||||
"Excerpts": "Kutipan",
|
||||
"Failed to import book(s): {{filenames}}": "Gagal mengimpor buku: {{filenames}}",
|
||||
"Fast": "Cepat",
|
||||
@@ -42,8 +38,6 @@
|
||||
"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",
|
||||
@@ -51,7 +45,6 @@
|
||||
"Horizontal Direction": "Arah Horizontal",
|
||||
"Hyphenation": "Pemenggalan Kata",
|
||||
"Import Books": "Impor Buku",
|
||||
"Language:": "Bahasa:",
|
||||
"Layout": "Tata Letak",
|
||||
"Light Mode": "Mode Terang",
|
||||
"Loading...": "Memuat...",
|
||||
@@ -62,7 +55,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",
|
||||
@@ -74,8 +66,8 @@
|
||||
"Paging Animation": "Animasi Halaman",
|
||||
"Paragraph": "Paragraf",
|
||||
"Parallel Read": "Baca Paralel",
|
||||
"Published:": "Diterbitkan:",
|
||||
"Publisher:": "Penerbit:",
|
||||
"Published": "Diterbitkan",
|
||||
"Publisher": "Penerbit",
|
||||
"Reading Progress Synced": "Progres membaca disinkronkan",
|
||||
"Reload Page": "Muat Ulang Halaman",
|
||||
"Reveal in File Explorer": "Tampilkan di File Explorer",
|
||||
@@ -89,7 +81,6 @@
|
||||
"Search...": "Cari...",
|
||||
"Select Book": "Pilih Buku",
|
||||
"Select Books": "Pilih buku",
|
||||
"Select Multiple Books": "Pilih beberapa buku",
|
||||
"Sepia": "Sepia",
|
||||
"Serif Font": "Font Serif",
|
||||
"Show Book Details": "Tampilkan Detail Buku",
|
||||
@@ -100,16 +91,15 @@
|
||||
"Slow": "Lambat",
|
||||
"Solarized": "Solarized",
|
||||
"Speak": "Bicara",
|
||||
"Subjects:": "Subjek:",
|
||||
"Subjects": "Subjek",
|
||||
"System Fonts": "Font Sistem",
|
||||
"TTS not supported in this device": "TTS tidak didukung di perangkat ini",
|
||||
"Theme Color": "Warna Tema",
|
||||
"Theme Mode": "Mode Tema",
|
||||
"Translate": "Terjemahkan",
|
||||
"Translated Text": "Teks Terjemahan",
|
||||
"Unknown": "Tidak Diketahui",
|
||||
"Untitled": "Tanpa Judul",
|
||||
"Updated:": "Diperbarui:",
|
||||
"Updated": "Diperbarui",
|
||||
"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.",
|
||||
@@ -118,8 +108,6 @@
|
||||
"Your Library": "Perpustakaan Anda",
|
||||
"TTS not supported for PDF": "TTS tidak didukung untuk PDF",
|
||||
"Override Book Font": "Ganti Font Buku",
|
||||
"Vertical Margins (px)": "Margin Vertikal (px)",
|
||||
"Horizontal Margins (%)": "Margin Horizontal (%)",
|
||||
"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.",
|
||||
@@ -128,14 +116,13 @@
|
||||
"Book Details": "Detail Buku",
|
||||
"From Local File": "Dari file lokal",
|
||||
"TOC": "Daftar Isi",
|
||||
"Table of Contents": "Daftar Isi",
|
||||
"Book uploaded: {{title}}": "Buku diunggah: {{title}}",
|
||||
"Failed to upload book: {{title}}": "Gagal mengunggah buku: {{title}}",
|
||||
"Book downloaded: {{title}}": "Buku diunduh: {{title}}",
|
||||
"Failed to download book: {{title}}": "Gagal mengunduh buku: {{title}}",
|
||||
"Upload Book": "Unggah Buku",
|
||||
"Auto Upload Books to Cloud": "Unggah Buku Otomatis ke Cloud",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% dari Penyimpanan Cloud Digunakan.",
|
||||
"Storage": "Penyimpanan",
|
||||
"Book deleted: {{title}}": "Buku dihapus: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Gagal menghapus buku: {{title}}",
|
||||
"Check Updates on Start": "Periksa Pembaruan saat Memulai",
|
||||
@@ -167,7 +154,7 @@
|
||||
"Create a Password": "Buat kata sandi",
|
||||
"Sign up": "Daftar",
|
||||
"Signing up...": "Sedang mendaftar...",
|
||||
"Don’t have an account? Sign up": "Belum punya akun? Daftar",
|
||||
"Don't have an account? Sign up": "Belum punya akun? Daftar",
|
||||
"Check your email for the confirmation link": "Periksa email Anda untuk tautan konfirmasi",
|
||||
"Signing in ...": "Sedang masuk ...",
|
||||
"Send a magic link email": "Kirim email tautan ajaib",
|
||||
@@ -186,41 +173,15 @@
|
||||
"Token": "Token",
|
||||
"Your OTP token": "Token OTP Anda",
|
||||
"Verify token": "Verifikasi token",
|
||||
"Sign in with Google": "Masuk dengan Google",
|
||||
"Sign in with Apple": "Masuk dengan Apple",
|
||||
"Sign in with GitHub": "Masuk dengan GitHub",
|
||||
"Account": "Akun",
|
||||
"Cloud Storage": "Penyimpanan Cloud",
|
||||
"Failed to delete user. Please try again later.": "Gagal menghapus pengguna. Silakan coba lagi nanti.",
|
||||
"Unlimited Offline/Online Reading": "Membaca Offline/Online Tanpa Batas",
|
||||
"Unlimited Cloud Sync Devices": "Perangkat Sinkronisasi Cloud Tanpa Batas",
|
||||
"Essential Text-to-Speech Voices": "Suara Text-to-Speech Esensial",
|
||||
"DeepL Free Access": "Akses DeepL Gratis",
|
||||
"Community Support": "Dukungan Komunitas",
|
||||
"500 MB Cloud Sync Space": "Ruang Sinkronisasi Cloud 500 MB",
|
||||
"Includes All Free Tier Benefits": "Termasuk Semua Manfaat Tingkat Gratis",
|
||||
"AI Summaries": "Ringkasan AI",
|
||||
"AI Translations": "Terjemahan AI",
|
||||
"Priority Support": "Dukungan Prioritas",
|
||||
"DeepL Pro Access": "Akses DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Suara Text-to-Speech yang Diperluas",
|
||||
"2000 MB Cloud Sync Space": "Ruang Sinkronisasi Cloud 2000 MB",
|
||||
"Includes All Plus Tier Benefits": "Termasuk Semua Manfaat Tingkat Plus",
|
||||
"Unlimited AI Summaries": "Ringkasan AI Tanpa Batas",
|
||||
"Unlimited AI Translations": "Terjemahan AI Tanpa Batas",
|
||||
"Unlimited DeepL Pro Access": "Akses DeepL Pro Tanpa Batas",
|
||||
"Advanced AI Tools": "Alat AI Lanjutan",
|
||||
"Early Feature Access": "Akses Fitur Awal",
|
||||
"10 GB Cloud Sync Space": "Ruang Sinkronisasi Cloud 10 GB",
|
||||
"Loading profile...": "Memuat profil...",
|
||||
"Features": "Fitur",
|
||||
"Delete Account": "Hapus Akun",
|
||||
"Delete Your Account?": "Hapus Akun Anda?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Tindakan ini tidak dapat dibatalkan. Semua data Anda di cloud akan dihapus secara permanen.",
|
||||
"Delete Permanently": "Hapus Secara Permanen",
|
||||
"Free Tier": "Tingkat Gratis",
|
||||
"Plus Tier": "Tingkat Plus",
|
||||
"Pro Tier": "Tingkat Pro",
|
||||
"RTL Direction": "Arah RTL",
|
||||
"Maximum Column Height": "Tinggi Kolom Maksimum",
|
||||
"Maximum Column Width": "Lebar Kolom Maksimum",
|
||||
@@ -229,7 +190,7 @@
|
||||
"No supported files found. Supported formats: {{formats}}": "Tidak ada file yang didukung ditemukan. Format yang didukung: {{formats}}",
|
||||
"Drop to Import Books": "Jatuhkan untuk Mengimpor Buku",
|
||||
"Custom": "Kustom",
|
||||
"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": "Seluruh dunia adalah panggung,\nDan semua pria dan wanita hanyalah pemain;\nMereka memiliki keluar dan masuk mereka,\nDan seorang pria dalam hidupnya memainkan banyak peran,\nTindakannya ada tujuh zaman.\n\n— William Shakespeare",
|
||||
"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": "Seluruh dunia adalah panggung,\nDan semua pria dan wanita hanyalah pemain;\nMereka memiliki keluar dan masuk mereka,\nDan seorang pria dalam hidupnya memainkan banyak peran,\nTindakannya ada tujuh zaman.\n\n— William Shakespeare",
|
||||
"Custom Theme": "Tema Kustom",
|
||||
"Theme Name": "Nama Tema",
|
||||
"Text Color": "Warna Teks",
|
||||
@@ -256,9 +217,9 @@
|
||||
"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.",
|
||||
"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",
|
||||
@@ -271,13 +232,13 @@
|
||||
"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!",
|
||||
"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:",
|
||||
"Changelog": "Catatan Perubahan",
|
||||
"Software Update": "Pembaruan Perangkat Lunak",
|
||||
"Title": "Judul",
|
||||
"Date Read": "Tanggal Dibaca",
|
||||
@@ -286,18 +247,15 @@
|
||||
"Ascending": "Naik",
|
||||
"Descending": "Turun",
|
||||
"Sort by...": "Urutkan berdasarkan...",
|
||||
"Added:": "Ditambahkan:",
|
||||
"Format:": "Format:",
|
||||
"Added": "Ditambahkan",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Deskripsi:",
|
||||
"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",
|
||||
@@ -306,6 +264,788 @@
|
||||
"Checking for updates...": "Memeriksa pembaruan...",
|
||||
"Error checking for updates": "Kesalahan saat memeriksa pembaruan",
|
||||
"Details": "Detail",
|
||||
"Cancel Selection": "Batalkan Pilihan",
|
||||
"File Size:": "Ukuran File:"
|
||||
"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",
|
||||
"System Language": "Bahasa Sistem",
|
||||
"Security": "Keamanan",
|
||||
"Allow JavaScript": "Izinkan JavaScript",
|
||||
"Enable only if you trust the file.": "Aktifkan hanya jika Anda mempercayai file tersebut.",
|
||||
"Sort TOC by Page": "Urutkan TOC berdasarkan Halaman",
|
||||
"Search in {{count}} Book(s)..._other": "Cari di {{count}} Buku...",
|
||||
"No notes match your search": "Tidak ada catatan ditemukan",
|
||||
"Search notes and excerpts...": "Cari catatan...",
|
||||
"Sign in to Sync": "Masuk untuk Sinkronisasi",
|
||||
"Synced at {{time}}": "Disinkronkan pada {{time}}",
|
||||
"Never synced": "Belum pernah disinkronkan",
|
||||
"Show Remaining Time": "Perlihatkan Waktu Tersisa",
|
||||
"{{time}} min left in chapter": "{{time}} menit tersisa di bab",
|
||||
"Override Book Color": "Ubah Warna Buku",
|
||||
"Login Required": "Perlu masuk",
|
||||
"Quota Exceeded": "Kuota terlampaui",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% dari Karakter Terjemahan Harian Digunakan.",
|
||||
"Translation Characters": "Karakter Terjemahan",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} suara",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Terjadi kesalahan. Jangan khawatir, tim kami telah diberitahu dan kami sedang bekerja untuk memperbaikinya.",
|
||||
"Error Details:": "Rincian Kesalahan:",
|
||||
"Try Again": "Coba Lagi",
|
||||
"Need help?": "Butuh bantuan?",
|
||||
"Contact Support": "Hubungi Dukungan",
|
||||
"Code Highlighting": "Penyorotan Kode",
|
||||
"Enable Highlighting": "Aktifkan Penyorotan",
|
||||
"Code Language": "Bahasa Kode",
|
||||
"Top Margin (px)": "Margin Atas (px)",
|
||||
"Bottom Margin (px)": "Margin Bawah (px)",
|
||||
"Right Margin (px)": "Margin Kanan (px)",
|
||||
"Left Margin (px)": "Margin Kiri (px)",
|
||||
"Column Gap (%)": "Jarak Antar Kolom (%)",
|
||||
"Always Show Status Bar": "Selalu Tampilkan Bilah Status",
|
||||
"Custom Content CSS": "CSS konten",
|
||||
"Enter CSS for book content styling...": "Masukkan CSS untuk gaya konten buku...",
|
||||
"Custom Reader UI CSS": "CSS antarmuka",
|
||||
"Enter CSS for reader interface styling...": "Masukkan CSS untuk gaya antarmuka pembaca...",
|
||||
"Crop": "Potong",
|
||||
"Book Covers": "Sampul Buku",
|
||||
"Fit": "Pas",
|
||||
"Reset {{settings}}": "Atur Ulang {{settings}}",
|
||||
"Reset Settings": "Atur Ulang Pengaturan",
|
||||
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> halaman tersisa di bab ini</1>",
|
||||
"Show Remaining Pages": "Tampilkan halaman tersisa",
|
||||
"Source Han Serif CN": "Source Han Serif",
|
||||
"Huiwen-MinchoGBK": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Kelola Langganan",
|
||||
"Coming Soon": "Segera Hadir",
|
||||
"Upgrade to {{plan}}": "Tingkatkan ke {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Tingkatkan ke Plus atau Pro",
|
||||
"Current Plan": "Paket Saat Ini",
|
||||
"Plan Limits": "Batas Paket",
|
||||
"Processing your payment...": "Memproses pembayaran Anda...",
|
||||
"Please wait while we confirm your subscription.": "Mohon tunggu saat kami mengonfirmasi langganan Anda.",
|
||||
"Payment Processing": "Memproses Pembayaran",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Pembayaran Anda sedang diproses. Ini biasanya memerlukan beberapa saat.",
|
||||
"Payment Failed": "Pembayaran Gagal",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Kami tidak dapat memproses langganan Anda. Silakan coba lagi atau hubungi dukungan jika masalah berlanjut.",
|
||||
"Back to Profile": "Kembali ke Profil",
|
||||
"Subscription Successful!": "Langganan Berhasil!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Terima kasih atas langganan Anda! Pembayaran Anda telah berhasil diproses.",
|
||||
"Email:": "Email:",
|
||||
"Plan:": "Paket:",
|
||||
"Amount:": "Jumlah:",
|
||||
"Go to Library": "Pergi ke Perpustakaan",
|
||||
"Need help? Contact our support team at support@readest.com": "Perlu bantuan? Hubungi tim dukungan kami di support@readest.com",
|
||||
"Free Plan": "Paket Gratis",
|
||||
"month": "bulan",
|
||||
"AI Translations (per day)": "Terjemahan AI (per hari)",
|
||||
"Plus Plan": "Paket Plus",
|
||||
"Includes All Free Plan Benefits": "Termasuk Semua Manfaat Paket Gratis",
|
||||
"Pro Plan": "Paket Pro",
|
||||
"More AI Translations": "Lebih Banyak Terjemahan AI",
|
||||
"Complete Your Subscription": "Selesaikan Langganan Anda",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% ruang sinkronisasi cloud terpakai",
|
||||
"Cloud Sync Storage": "Penyimpanan Sinkronisasi Cloud",
|
||||
"Disable": "Nonaktifkan",
|
||||
"Enable": "Aktifkan",
|
||||
"Upgrade to Readest Premium": "Tingkatkan ke Readest Premium",
|
||||
"Show Source Text": "Perlihatkan Teks Sumber",
|
||||
"Cross-Platform Sync": "Sinkronisasi Lintas Platform",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Sinkronkan pustaka, kemajuan, dan catatan di semua perangkat Anda.",
|
||||
"Customizable Reading": "Membaca yang Dapat Disesuaikan",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Atur font, tata letak, dan tema sesuai keinginan.",
|
||||
"AI Read Aloud": "Pembacaan AI",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Dengarkan buku dengan suara AI alami.",
|
||||
"AI Translations": "Terjemahan AI",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Terhubung dengan komunitas dan dapatkan bantuan cepat.",
|
||||
"Unlimited AI Read Aloud Hours": "Waktu Baca AI Tanpa Batas",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Dengarkan sebanyak yang Anda mau tanpa batas.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Dapatkan lebih banyak kuota dan fitur terjemahan lanjutan.",
|
||||
"DeepL Pro Access": "Akses DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Nikmati dukungan prioritas kapan saja.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Kuota harian habis. Tingkatkan paket Anda untuk lanjut.",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Terjemahkan teks apa pun secara instan dengan kekuatan Google, Azure, atau DeepL—pahami konten dalam bahasa apa pun.",
|
||||
"Includes All Plus Plan Benefits": "Termasuk Semua Manfaat Paket Plus",
|
||||
"Early Feature Access": "Akses Fitur Lebih Awal",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Jadi yang pertama menjelajahi fitur baru, pembaruan, dan inovasi.",
|
||||
"Advanced AI Tools": "Alat AI Canggih",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Manfaatkan alat AI canggih untuk membaca cerdas, terjemahan, dan penemuan konten.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Terjemahkan hingga 100.000 karakter harian dengan mesin terjemahan paling akurat.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Terjemahkan hingga 500.000 karakter harian dengan mesin terjemahan paling akurat.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Simpan dan akses koleksi bacaan lengkap dengan aman menggunakan penyimpanan cloud 5 GB.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Simpan dan akses koleksi bacaan lengkap dengan aman menggunakan penyimpanan cloud 20 GB.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Cadangan cloud buku dihapus: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Apakah Anda yakin ingin menghapus cadangan cloud dari buku yang dipilih?",
|
||||
"What's New in Readest": "Yang Baru di Readest",
|
||||
"Enter book title": "Masukkan judul buku",
|
||||
"Subtitle": "Subjudul",
|
||||
"Enter book subtitle": "Masukkan subjudul buku",
|
||||
"Enter author name": "Masukkan nama penulis",
|
||||
"Series": "Seri",
|
||||
"Enter series name": "Masukkan nama seri",
|
||||
"Series Index": "Nomor seri",
|
||||
"Enter series index": "Masukkan nomor seri",
|
||||
"Total in Series": "Total dalam seri",
|
||||
"Enter total books in series": "Masukkan total buku dalam seri",
|
||||
"Enter publisher": "Masukkan penerbit",
|
||||
"Publication Date": "Tanggal terbit",
|
||||
"Identifier": "Pengidentifikasi",
|
||||
"Enter book description": "Masukkan deskripsi buku",
|
||||
"Change cover image": "Ubah gambar sampul",
|
||||
"Replace": "Ganti",
|
||||
"Unlock cover": "Buka kunci sampul",
|
||||
"Lock cover": "Kunci sampul",
|
||||
"Auto-Retrieve Metadata": "Ambil metadata otomatis",
|
||||
"Auto-Retrieve": "Ambil otomatis",
|
||||
"Unlock all fields": "Buka kunci semua field",
|
||||
"Unlock All": "Buka semua",
|
||||
"Lock all fields": "Kunci semua field",
|
||||
"Lock All": "Kunci semua",
|
||||
"Reset": "Reset",
|
||||
"Edit Metadata": "Edit metadata",
|
||||
"Locked": "Terkunci",
|
||||
"Select Metadata Source": "Pilih sumber metadata",
|
||||
"Keep manual input": "Pertahankan input manual",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Fiksi, Sains, Sejarah",
|
||||
"Open Book in New Window": "Buka Buku di Jendela Baru",
|
||||
"Voices for {{lang}}": "Suara untuk {{lang}}",
|
||||
"Yandex Translate": "Yandex Translate",
|
||||
"YYYY or YYYY-MM-DD": "YYYY atau YYYY-MM-DD",
|
||||
"Restore Purchase": "Pulihkan Pembelian",
|
||||
"No purchases found to restore.": "Tidak ada pembelian yang ditemukan untuk dipulihkan.",
|
||||
"Failed to restore purchases.": "Gagal memulihkan pembelian.",
|
||||
"Failed to manage subscription.": "Gagal mengelola langganan.",
|
||||
"Failed to load subscription plans.": "Gagal memuat rencana langganan.",
|
||||
"year": "tahun",
|
||||
"Failed to create checkout session": "Gagal membuat sesi checkout",
|
||||
"Storage": "Penyimpanan",
|
||||
"Terms of Service": "Ketentuan Layanan",
|
||||
"Privacy Policy": "Kebijakan Privasi",
|
||||
"Disable Double Click": "Nonaktifkan Klik Ganda",
|
||||
"TTS not supported for this document": "TTS tidak didukung untuk dokumen ini",
|
||||
"Reset Password": "Reset Kata Sandi",
|
||||
"Show Reading Progress": "Perlihatkan Progres Membaca",
|
||||
"Reading Progress Style": "Gaya Progres Membaca",
|
||||
"Page Number": "Nomor Halaman",
|
||||
"Percentage": "Persentase",
|
||||
"Deleted local copy of the book: {{title}}": "Salinan lokal buku dihapus: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Gagal menghapus cadangan cloud buku: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Gagal menghapus salinan lokal buku: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Apakah Anda yakin ingin menghapus salinan lokal buku yang dipilih?",
|
||||
"Remove from Cloud & Device": "Hapus dari Cloud & Perangkat",
|
||||
"Remove from Cloud Only": "Hapus dari Cloud Saja",
|
||||
"Remove from Device Only": "Hapus dari Perangkat Saja",
|
||||
"Disconnected": "Terputus",
|
||||
"KOReader Sync Settings": "Pengaturan Sinkronisasi KOReader",
|
||||
"Sync Strategy": "Strategi Sinkronisasi",
|
||||
"Ask on conflict": "Tanya saat terjadi konflik",
|
||||
"Always use latest": "Selalu gunakan yang terbaru",
|
||||
"Send changes only": "Kirim perubahan saja",
|
||||
"Receive changes only": "Terima perubahan saja",
|
||||
"Checksum Method": "Metode Checksum",
|
||||
"File Content (recommended)": "Konten File (disarankan)",
|
||||
"File Name": "Nama File",
|
||||
"Device Name": "Nama Perangkat",
|
||||
"Connect to your KOReader Sync server.": "Hubungkan ke server Sinkronisasi KOReader Anda.",
|
||||
"Server URL": "URL Server",
|
||||
"Username": "Nama Pengguna",
|
||||
"Your Username": "Nama Pengguna Anda",
|
||||
"Password": "Kata Sandi",
|
||||
"Connect": "Hubungkan",
|
||||
"KOReader Sync": "Sinkronisasi KOReader",
|
||||
"Sync Conflict": "Konflik Sinkronisasi",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Sinkronkan progres membaca dari \"{{deviceName}}\"?",
|
||||
"another device": "perangkat lain",
|
||||
"Local Progress": "Progres Lokal",
|
||||
"Remote Progress": "Progres Jarak Jauh",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Halaman {{page}} dari {{total}} ({{percentage}}%)",
|
||||
"Current position": "Posisi Saat Ini",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Sekitar halaman {{page}} dari {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Sekitar {{percentage}}%",
|
||||
"Failed to connect": "Gagal terhubung",
|
||||
"Sync Server Connected": "Server sinkronisasi terhubung",
|
||||
"Sync as {{userDisplayName}}": "Sinkronkan sebagai {{userDisplayName}}",
|
||||
"Custom Fonts": "Font Kustom",
|
||||
"Cancel Delete": "Batal Hapus",
|
||||
"Import Font": "Impor Font",
|
||||
"Delete Font": "Hapus Font",
|
||||
"Tips": "Tips",
|
||||
"Custom fonts can be selected from the Font Face menu": "Font kustom dapat dipilih dari menu Font Face",
|
||||
"Manage Custom Fonts": "Kelola Font Kustom",
|
||||
"Select Files": "Pilih File",
|
||||
"Select Image": "Pilih Gambar",
|
||||
"Select Video": "Pilih Video",
|
||||
"Select Audio": "Pilih Audio",
|
||||
"Select Fonts": "Pilih Font",
|
||||
"Supported font formats: .ttf, .otf, .woff, .woff2": "Format font yang didukung: .ttf, .otf, .woff, .woff2",
|
||||
"Push Progress": "Dorong Progres",
|
||||
"Pull Progress": "Tarik Progres",
|
||||
"Previous Paragraph": "Paragraf Sebelumnya",
|
||||
"Previous Sentence": "Kalimat Sebelumnya",
|
||||
"Pause": "Jeda",
|
||||
"Play": "Putar",
|
||||
"Next Sentence": "Kalimat Berikutnya",
|
||||
"Next Paragraph": "Paragraf Berikutnya",
|
||||
"Separate Cover Page": "Halaman Sampul Terpisah",
|
||||
"Resize Notebook": "Ubah Ukuran Buku Catatan",
|
||||
"Resize Sidebar": "Ubah Ukuran Bilah Samping",
|
||||
"Get Help from the Readest Community": "Dapatkan Bantuan dari Komunitas Readest",
|
||||
"Remove cover image": "Hapus gambar sampul",
|
||||
"Bookshelf": "Rak Buku",
|
||||
"View Menu": "Lihat Menu",
|
||||
"Settings Menu": "Menu Pengaturan",
|
||||
"View account details and quota": "Lihat detail akun dan kuota",
|
||||
"Library Header": "Header Perpustakaan",
|
||||
"Book Content": "Isi Buku",
|
||||
"Footer Bar": "Bilah Footer",
|
||||
"Header Bar": "Bilah Header",
|
||||
"View Options": "Opsi Tampilan",
|
||||
"Book Menu": "Menu Buku",
|
||||
"Search Options": "Opsi Pencarian",
|
||||
"Close": "Tutup",
|
||||
"Delete Book Options": "Opsi Hapus Buku",
|
||||
"ON": "AKTIF",
|
||||
"OFF": "TIDAK AKTIF",
|
||||
"Reading Progress": "Progres Membaca",
|
||||
"Page Margin": "Margin Halaman",
|
||||
"Remove Bookmark": "Hapus Penanda",
|
||||
"Add Bookmark": "Tambahkan Penanda",
|
||||
"Books Content": "Isi Buku",
|
||||
"Jump to Location": "Lompat ke Lokasi",
|
||||
"Unpin Notebook": "Batal Pin Notebook",
|
||||
"Pin Notebook": "Pin Notebook",
|
||||
"Hide Search Bar": "Sembunyikan Bilah Pencarian",
|
||||
"Show Search Bar": "Tampilkan Bilah Pencarian",
|
||||
"On {{current}} of {{total}} page": "Pada {{current}} dari {{total}} halaman",
|
||||
"Section Title": "Judul Bagian",
|
||||
"Decrease": "Kecilkan",
|
||||
"Increase": "Besarakan",
|
||||
"Settings Panels": "Panel Pengaturan",
|
||||
"Settings": "Pengaturan",
|
||||
"Unpin Sidebar": "Batal Pin Bilah Samping",
|
||||
"Pin Sidebar": "Pin Bilah Samping",
|
||||
"Toggle Sidebar": "Toggel Bilah Samping",
|
||||
"Toggle Translation": "Toggel Terjemahan",
|
||||
"Translation Disabled": "Terjemahan Dinonaktifkan",
|
||||
"Minimize": "Minimalkan",
|
||||
"Maximize or Restore": "Maksimalkan atau Pulihkan",
|
||||
"Exit Parallel Read": "Keluar dari Baca Paralel",
|
||||
"Enter Parallel Read": "Masuk ke Baca Paralel",
|
||||
"Zoom Level": "Level Zoom",
|
||||
"Zoom Out": "Perbesar Zoom",
|
||||
"Reset Zoom": "Reset Zoom",
|
||||
"Zoom In": "Perkecil Zoom",
|
||||
"Zoom Mode": "Mode Zoom",
|
||||
"Single Page": "Halaman Tunggal",
|
||||
"Auto Spread": "Penyebaran Otomatis",
|
||||
"Fit Page": "Sesuaikan Halaman",
|
||||
"Fit Width": "Sesuaikan Lebar",
|
||||
"Failed to select directory": "Gagal memilih direktori",
|
||||
"The new data directory must be different from the current one.": "Direktori data baru harus berbeda dari yang saat ini.",
|
||||
"Migration failed: {{error}}": "Migrasi gagal: {{error}}",
|
||||
"Change Data Location": "Ubah Lokasi Data",
|
||||
"Current Data Location": "Lokasi Data Saat Ini",
|
||||
"Total size: {{size}}": "Ukuran total: {{size}}",
|
||||
"Calculating file info...": "Menghitung informasi file...",
|
||||
"New Data Location": "Lokasi Data Baru",
|
||||
"Choose Different Folder": "Pilih Folder Berbeda",
|
||||
"Choose New Folder": "Pilih Folder Baru",
|
||||
"Migrating data...": "Migrasi data...",
|
||||
"Copying: {{file}}": "Menyalin: {{file}}",
|
||||
"{{current}} of {{total}} files": "{{current}} dari {{total}} file",
|
||||
"Migration completed successfully!": "Migrasi selesai dengan sukses!",
|
||||
"Your data has been moved to the new location. Please restart the application to complete the process.": "Data Anda telah dipindahkan ke lokasi baru. Silakan restart aplikasi untuk menyelesaikan proses.",
|
||||
"Migration failed": "Migrasi gagal",
|
||||
"Important Notice": "Pemberitahuan Penting",
|
||||
"This will move all your app data to the new location. Make sure the destination has enough free space.": "Ini akan memindahkan semua data aplikasi Anda ke lokasi baru. Pastikan tujuan memiliki cukup ruang kosong.",
|
||||
"Restart App": "Restart Aplikasi",
|
||||
"Start Migration": "Mulai Migrasi",
|
||||
"Advanced Settings": "Pengaturan Lanjutan",
|
||||
"File count: {{size}}": "Jumlah file: {{size}}",
|
||||
"Background Read Aloud": "Bacakan Latar Belakang",
|
||||
"Ready to read aloud": "Siap untuk dibacakan",
|
||||
"Read Aloud": "Bacakan",
|
||||
"Screen Brightness": "Kecerahan Layar",
|
||||
"Background Image": "Gambar Latar Belakang",
|
||||
"Import Image": "Impor Gambar",
|
||||
"Opacity": "Keburaman",
|
||||
"Size": "Ukuran",
|
||||
"Cover": "Sampul",
|
||||
"Contain": "Mengandung",
|
||||
"{{number}} pages left in chapter": "<0>{{number}}</0><1> halaman tersisa di bab</1>",
|
||||
"Device": "Perangkat",
|
||||
"E-Ink Mode": "Mode E-Ink",
|
||||
"Highlight Colors": "Warna Sorotan",
|
||||
"Pagination": "Paginasi",
|
||||
"Disable Double Tap": "Nonaktifkan Ketuk Ganda",
|
||||
"Tap to Paginate": "Ketuk untuk Paginasi",
|
||||
"Click to Paginate": "Klik untuk Paginasi",
|
||||
"Tap Both Sides": "Ketuk Kedua Sisi",
|
||||
"Click Both Sides": "Klik Kedua Sisi",
|
||||
"Swap Tap Sides": "Tukar Sisi Ketuk",
|
||||
"Swap Click Sides": "Tukar Sisi Klik",
|
||||
"Source and Translated": "Sumber dan Terjemahan",
|
||||
"Translated Only": "Hanya Terjemahan",
|
||||
"Source Only": "Hanya Sumber",
|
||||
"TTS Text": "Teks TTS",
|
||||
"The book file is corrupted": "File buku rusak",
|
||||
"The book file is empty": "File buku kosong",
|
||||
"Failed to open the book file": "Gagal membuka file buku",
|
||||
"On-Demand Purchase": "Pembelian Sesuai Permintaan",
|
||||
"Full Customization": "Kustomisasi Penuh",
|
||||
"Lifetime Plan": "Rencana Seumur Hidup",
|
||||
"One-Time Payment": "Pembayaran Sekali",
|
||||
"Make a single payment to enjoy lifetime access to specific features on all devices. Purchase specific features or services only when you need them.": "Lakukan pembayaran sekali untuk menikmati akses seumur hidup ke fitur tertentu di semua perangkat. Beli fitur atau layanan tertentu hanya saat Anda membutuhkannya.",
|
||||
"Expand Cloud Sync Storage": "Perluas Penyimpanan Sinkronisasi Cloud",
|
||||
"Expand your cloud storage forever with a one-time purchase. Each additional purchase adds more space.": "Perluas penyimpanan cloud Anda selamanya dengan pembelian sekali. Setiap pembelian tambahan menambah lebih banyak ruang.",
|
||||
"Unlock All Customization Options": "Buka Semua Opsi Kustomisasi",
|
||||
"Unlock additional themes, fonts, layout options and read aloud, translators, cloud storage services.": "Buka tema tambahan, font, opsi tata letak, dan baca nyaring, penerjemah, layanan penyimpanan cloud.",
|
||||
"Purchase Successful!": "Pembelian Berhasil!",
|
||||
"lifetime": "seumur hidup",
|
||||
"Thank you for your purchase! Your payment has been processed successfully.": "Terima kasih atas pembelian Anda! Pembayaran Anda telah berhasil diproses.",
|
||||
"Order ID:": "ID Pesanan:",
|
||||
"TTS Highlighting": "TTS Menyorot",
|
||||
"Style": "Gaya",
|
||||
"Underline": "Garisan Bawah",
|
||||
"Strikethrough": "Garis Tengah",
|
||||
"Squiggly": "Bergelombang",
|
||||
"Outline": "Garis Besar",
|
||||
"Save Current Color": "Simpan Warna Saat Ini",
|
||||
"Quick Colors": "Warna Cepat",
|
||||
"Highlighter": "Penyorot",
|
||||
"Save Book Cover": "Simpan Sampul Buku",
|
||||
"Auto-save last book cover": "Simpan otomatis sampul buku terakhir",
|
||||
"Back": "Kembali",
|
||||
"Confirmation email sent! Please check your old and new email addresses to confirm the change.": "Email konfirmasi telah dikirim! Silakan periksa alamat email lama dan baru Anda untuk mengonfirmasi perubahan.",
|
||||
"Failed to update email": "Gagal memperbarui email",
|
||||
"New Email": "Email baru",
|
||||
"Your new email": "Email baru Anda",
|
||||
"Updating email ...": "Memperbarui email ...",
|
||||
"Update email": "Perbarui email",
|
||||
"Current email": "Email saat ini",
|
||||
"Update Email": "Perbarui email",
|
||||
"All": "Semua",
|
||||
"Unable to open book": "Tidak dapat membuka buku",
|
||||
"Punctuation": "Tanda Baca",
|
||||
"Replace Quotation Marks": "Ganti Tanda Kutip",
|
||||
"Enabled only in vertical layout.": "Hanya diaktifkan dalam tata letak vertikal.",
|
||||
"No Conversion": "Tanpa konversi",
|
||||
"Simplified to Traditional": "Sederhana → Tradisional",
|
||||
"Traditional to Simplified": "Tradisional → Sederhana",
|
||||
"Simplified to Traditional (Taiwan)": "Sederhana → Tradisional (Taiwan)",
|
||||
"Simplified to Traditional (Hong Kong)": "Sederhana → Tradisional (Hong Kong)",
|
||||
"Simplified to Traditional (Taiwan), with phrases": "Sederhana → Tradisional (Taiwan • frasa)",
|
||||
"Traditional (Taiwan) to Simplified": "Tradisional (Taiwan) → Sederhana",
|
||||
"Traditional (Hong Kong) to Simplified": "Tradisional (Hong Kong) → Sederhana",
|
||||
"Traditional (Taiwan) to Simplified, with phrases": "Tradisional (Taiwan • frasa) → Sederhana",
|
||||
"Convert Simplified and Traditional Chinese": "Konversi Tionghoa Sederhana/Tradisional",
|
||||
"Convert Mode": "Mode Konversi",
|
||||
"Failed to auto-save book cover for lock screen: {{error}}": "Gagal menyimpan otomatis sampul buku untuk layar kunci: {{error}}",
|
||||
"Download from Cloud": "Unduh dari Cloud",
|
||||
"Upload to Cloud": "Unggah ke Cloud",
|
||||
"Clear Custom Fonts": "Bersihkan Font Kustom",
|
||||
"Columns": "Kolom",
|
||||
"OPDS Catalogs": "Katalog OPDS",
|
||||
"Adding LAN addresses is not supported in the web app version.": "Menambahkan alamat LAN tidak didukung di versi web.",
|
||||
"Invalid OPDS catalog. Please check the URL.": "Katalog OPDS tidak valid. Silakan periksa URL-nya.",
|
||||
"Browse and download books from online catalogs": "Jelajahi dan unduh buku dari katalog online",
|
||||
"My Catalogs": "Katalog Saya",
|
||||
"Add Catalog": "Tambah Katalog",
|
||||
"No catalogs yet": "Belum ada katalog",
|
||||
"Add your first OPDS catalog to start browsing books": "Tambahkan katalog OPDS pertama Anda untuk mulai menjelajahi buku",
|
||||
"Add Your First Catalog": "Tambah Katalog Pertama Anda",
|
||||
"Browse": "Jelajahi",
|
||||
"Popular Catalogs": "Katalog Populer",
|
||||
"Add": "Tambah",
|
||||
"Add OPDS Catalog": "Tambah Katalog OPDS",
|
||||
"Catalog Name": "Nama Katalog",
|
||||
"My Calibre Library": "Perpustakaan Calibre Saya",
|
||||
"OPDS URL": "URL OPDS",
|
||||
"Username (optional)": "Username (opsional)",
|
||||
"Password (optional)": "Password (opsional)",
|
||||
"Description (optional)": "Deskripsi (opsional)",
|
||||
"A brief description of this catalog": "Deskripsi singkat untuk katalog ini",
|
||||
"Validating...": "Memvalidasi...",
|
||||
"View All": "Lihat Semua",
|
||||
"Forward": "Maju",
|
||||
"Home": "Beranda",
|
||||
"{{count}} items_other": "{{count}} item",
|
||||
"Download completed": "Unduhan selesai",
|
||||
"Download failed": "Unduhan gagal",
|
||||
"Open Access": "Akses Terbuka",
|
||||
"Borrow": "Pinjam",
|
||||
"Buy": "Beli",
|
||||
"Subscribe": "Berlangganan",
|
||||
"Sample": "Contoh",
|
||||
"Download": "Unduh",
|
||||
"Open & Read": "Buka & Baca",
|
||||
"Tags": "Tag",
|
||||
"Tag": "Tag",
|
||||
"First": "Pertama",
|
||||
"Previous": "Sebelumnya",
|
||||
"Next": "Berikutnya",
|
||||
"Last": "Terakhir",
|
||||
"Cannot Load Page": "Tidak dapat memuat halaman",
|
||||
"An error occurred": "Terjadi kesalahan",
|
||||
"Online Library": "Perpustakaan Online",
|
||||
"URL must start with http:// or https://": "URL harus diawali dengan http:// atau https://",
|
||||
"Title, Author, Tag, etc...": "Judul, Penulis, Tag, dll...",
|
||||
"Query": "Kueri",
|
||||
"Subject": "Subjek",
|
||||
"Enter {{terms}}": "Masukkan {{terms}}",
|
||||
"No search results found": "Tidak ada hasil pencarian ditemukan",
|
||||
"Failed to load OPDS feed: {{status}} {{statusText}}": "Gagal memuat feed OPDS: {{status}} {{statusText}}",
|
||||
"Search in {{title}}": "Cari di {{title}}",
|
||||
"Manage Storage": "Kelola Penyimpanan",
|
||||
"Failed to load files": "Gagal memuat file",
|
||||
"Deleted {{count}} file(s)_other": "{{count}} file dihapus",
|
||||
"Failed to delete {{count}} file(s)_other": "Gagal menghapus {{count}} file",
|
||||
"Failed to delete files": "Gagal menghapus file",
|
||||
"Total Files": "Total File",
|
||||
"Total Size": "Total Ukuran",
|
||||
"Quota": "Kuota",
|
||||
"Used": "Digunakan",
|
||||
"Files": "File",
|
||||
"Search files...": "Cari file...",
|
||||
"Newest First": "Terbaru",
|
||||
"Oldest First": "Terlama",
|
||||
"Largest First": "Terbesar",
|
||||
"Smallest First": "Terkecil",
|
||||
"Name A-Z": "Nama A-Z",
|
||||
"Name Z-A": "Nama Z-A",
|
||||
"{{count}} selected_other": "{{count}} dipilih",
|
||||
"Delete Selected": "Hapus yang Dipilih",
|
||||
"Created": "Dibuat",
|
||||
"No files found": "Tidak ada file ditemukan",
|
||||
"No files uploaded yet": "Belum ada file diunggah",
|
||||
"files": "file",
|
||||
"Page {{current}} of {{total}}": "Halaman {{current}} dari {{total}}",
|
||||
"Are you sure to delete {{count}} selected file(s)?_other": "Yakin ingin menghapus {{count}} file yang dipilih?",
|
||||
"Cloud Storage Usage": "Penggunaan Penyimpanan Cloud",
|
||||
"Rename Group": "Ganti Nama Grup",
|
||||
"From Directory": "Dari Direktori",
|
||||
"Successfully imported {{count}} book(s)_other": "Berhasil mengimpor {{count}} buku",
|
||||
"Count": "Jumlah",
|
||||
"Start Page": "Halaman Awal",
|
||||
"Search in OPDS Catalog...": "Cari di Katalog OPDS...",
|
||||
"Please log in to use advanced TTS features": "Silakan masuk untuk menggunakan fitur TTS lanjutan",
|
||||
"Word limit of 30 words exceeded.": "Batas 30 kata telah terlampaui.",
|
||||
"Proofread": "Koreksi",
|
||||
"Current selection": "Pilihan saat ini",
|
||||
"All occurrences in this book": "Semua kemunculan di buku ini",
|
||||
"All occurrences in your library": "Semua kemunculan di perpustakaan Anda",
|
||||
"Selected text:": "Teks terpilih:",
|
||||
"Replace with:": "Ganti dengan:",
|
||||
"Enter text...": "Masukkan teks…",
|
||||
"Case sensitive:": "Peka huruf besar dan kecil:",
|
||||
"Scope:": "Cakupan:",
|
||||
"Selection": "Pilihan",
|
||||
"Library": "Perpustakaan",
|
||||
"Yes": "Ya",
|
||||
"No": "Tidak",
|
||||
"Proofread Replacement Rules": "Aturan penggantian koreksi",
|
||||
"Selected Text Rules": "Aturan teks terpilih",
|
||||
"No selected text replacement rules": "Tidak ada aturan penggantian untuk teks terpilih",
|
||||
"Book Specific Rules": "Aturan khusus buku",
|
||||
"No book-level replacement rules": "Tidak ada aturan penggantian tingkat buku",
|
||||
"Disable Quick Action": "Nonaktifkan Aksi Cepat",
|
||||
"Enable Quick Action on Selection": "Aktifkan Aksi Cepat saat memilih",
|
||||
"None": "Tidak ada",
|
||||
"Annotation Tools": "Alat Anotasi",
|
||||
"Enable Quick Actions": "Aktifkan Aksi Cepat",
|
||||
"Quick Action": "Aksi Cepat",
|
||||
"Copy to Notebook": "Salin ke Buku Catatan",
|
||||
"Copy text after selection": "Salin teks setelah pemilihan",
|
||||
"Highlight text after selection": "Sorot teks setelah pemilihan",
|
||||
"Annotate text after selection": "Anotasi teks setelah pemilihan",
|
||||
"Search text after selection": "Cari teks setelah pemilihan",
|
||||
"Look up text in dictionary after selection": "Cari teks di kamus setelah pemilihan",
|
||||
"Look up text in Wikipedia after selection": "Cari teks di Wikipedia setelah pemilihan",
|
||||
"Translate text after selection": "Terjemahkan teks setelah pemilihan",
|
||||
"Read text aloud after selection": "Bacakan teks setelah pemilihan",
|
||||
"Proofread text after selection": "Periksa teks setelah pemilihan",
|
||||
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} aktif, {{pendingCount}} menunggu",
|
||||
"{{failedCount}} failed": "{{failedCount}} gagal",
|
||||
"Waiting...": "Menunggu...",
|
||||
"Failed": "Gagal",
|
||||
"Completed": "Selesai",
|
||||
"Cancelled": "Dibatalkan",
|
||||
"Retry": "Coba lagi",
|
||||
"Active": "Aktif",
|
||||
"Transfer Queue": "Antrian Transfer",
|
||||
"Upload All": "Unggah Semua",
|
||||
"Download All": "Unduh Semua",
|
||||
"Resume Transfers": "Lanjutkan Transfer",
|
||||
"Pause Transfers": "Jeda Transfer",
|
||||
"Pending": "Menunggu",
|
||||
"No transfers": "Tidak ada transfer",
|
||||
"Retry All": "Coba Semua Lagi",
|
||||
"Clear Completed": "Hapus Selesai",
|
||||
"Clear Failed": "Hapus Gagal",
|
||||
"Upload queued: {{title}}": "Unggahan dalam antrian: {{title}}",
|
||||
"Download queued: {{title}}": "Unduhan dalam antrian: {{title}}",
|
||||
"Book not found in library": "Buku tidak ditemukan di perpustakaan",
|
||||
"Unknown error": "Kesalahan tidak diketahui",
|
||||
"Please log in to continue": "Silakan masuk untuk melanjutkan",
|
||||
"Cloud File Transfers": "Transfer File Cloud",
|
||||
"Show Search Results": "Tampilkan hasil pencarian",
|
||||
"Search results for '{{term}}'": "Hasil untuk '{{term}}'",
|
||||
"Close Search": "Tutup pencarian",
|
||||
"Previous Result": "Hasil sebelumnya",
|
||||
"Next Result": "Hasil berikutnya",
|
||||
"Bookmarks": "Penanda",
|
||||
"Annotations": "Anotasi",
|
||||
"Show Results": "Tampilkan Hasil",
|
||||
"Clear search": "Hapus pencarian",
|
||||
"Clear search history": "Hapus riwayat pencarian",
|
||||
"Tap to Toggle Footer": "Ketuk untuk beralih footer",
|
||||
"Exported successfully": "Berhasil diekspor",
|
||||
"Book exported successfully.": "Buku berhasil diekspor.",
|
||||
"Failed to export the book.": "Gagal mengekspor buku.",
|
||||
"Export Book": "Ekspor Buku",
|
||||
"Whole word:": "Kata utuh:",
|
||||
"Error": "Kesalahan",
|
||||
"Unable to load the article. Try searching directly on {{link}}.": "Tidak dapat memuat artikel. Coba cari langsung di {{link}}.",
|
||||
"Unable to load the word. Try searching directly on {{link}}.": "Tidak dapat memuat kata. Coba cari langsung di {{link}}.",
|
||||
"Date Published": "Tanggal terbit",
|
||||
"Only for TTS:": "Hanya untuk TTS:",
|
||||
"Uploaded": "Diunggah",
|
||||
"Downloaded": "Diunduh",
|
||||
"Deleted": "Dihapus",
|
||||
"Note:": "Catatan:",
|
||||
"Time:": "Waktu:",
|
||||
"Format Options": "Opsi Format",
|
||||
"Export Date": "Tanggal Ekspor",
|
||||
"Chapter Titles": "Judul Bab",
|
||||
"Chapter Separator": "Pemisah Bab",
|
||||
"Highlights": "Sorotan",
|
||||
"Note Date": "Tanggal Catatan",
|
||||
"Advanced": "Lanjutan",
|
||||
"Hide": "Sembunyikan",
|
||||
"Show": "Tampilkan",
|
||||
"Use Custom Template": "Gunakan Template Kustom",
|
||||
"Export Template": "Template Ekspor",
|
||||
"Template Syntax:": "Sintaks Template:",
|
||||
"Insert value": "Masukkan nilai",
|
||||
"Format date (locale)": "Format tanggal (lokal)",
|
||||
"Format date (custom)": "Format tanggal (kustom)",
|
||||
"Conditional": "Kondisional",
|
||||
"Loop": "Loop",
|
||||
"Available Variables:": "Variabel yang Tersedia:",
|
||||
"Book title": "Judul buku",
|
||||
"Book author": "Penulis buku",
|
||||
"Export date": "Tanggal ekspor",
|
||||
"Array of chapters": "Daftar bab",
|
||||
"Chapter title": "Judul bab",
|
||||
"Array of annotations": "Daftar anotasi",
|
||||
"Highlighted text": "Teks yang disorot",
|
||||
"Annotation note": "Catatan anotasi",
|
||||
"Date Format Tokens:": "Token Format Tanggal:",
|
||||
"Year (4 digits)": "Tahun (4 digit)",
|
||||
"Month (01-12)": "Bulan (01-12)",
|
||||
"Day (01-31)": "Hari (01-31)",
|
||||
"Hour (00-23)": "Jam (00-23)",
|
||||
"Minute (00-59)": "Menit (00-59)",
|
||||
"Second (00-59)": "Detik (00-59)",
|
||||
"Show Source": "Tampilkan Sumber",
|
||||
"No content to preview": "Tidak ada konten untuk dipratinjau",
|
||||
"Export": "Ekspor",
|
||||
"Set Timeout": "Atur batas waktu",
|
||||
"Select Voice": "Pilih suara",
|
||||
"Toggle Sticky Bottom TTS Bar": "Alihkan bilah TTS tetap",
|
||||
"Display what I'm reading on Discord": "Tampilkan buku yang sedang dibaca di Discord",
|
||||
"Show on Discord": "Tampilkan di Discord",
|
||||
"Instant {{action}}": "{{action}} Instan",
|
||||
"Instant {{action}} Disabled": "{{action}} Instan Dinonaktifkan",
|
||||
"Annotation": "Anotasi",
|
||||
"Reset Template": "Atur Ulang Template",
|
||||
"Annotation style": "Gaya anotasi",
|
||||
"Annotation color": "Warna anotasi",
|
||||
"Annotation time": "Waktu anotasi",
|
||||
"AI": "AI",
|
||||
"Are you sure you want to re-index this book?": "Apakah Anda yakin ingin mengindeks ulang buku ini?",
|
||||
"Enable AI in Settings": "Aktifkan AI di Pengaturan",
|
||||
"Index This Book": "Indeks Buku Ini",
|
||||
"Enable AI search and chat for this book": "Aktifkan pencarian AI dan obrolan untuk buku ini",
|
||||
"Start Indexing": "Mulai Mengindeks",
|
||||
"Indexing book...": "Mengindeks buku...",
|
||||
"Preparing...": "Mempersiapkan...",
|
||||
"Delete this conversation?": "Hapus percakapan ini?",
|
||||
"No conversations yet": "Belum ada percakapan",
|
||||
"Start a new chat to ask questions about this book": "Mulai obrolan baru untuk mengajukan pertanyaan tentang buku ini",
|
||||
"Rename": "Ubah nama",
|
||||
"New Chat": "Obrolan Baru",
|
||||
"Chat": "Obrolan",
|
||||
"Please enter a model ID": "Silakan masukkan ID model",
|
||||
"Model not available or invalid": "Model tidak tersedia atau tidak valid",
|
||||
"Failed to validate model": "Gagal memvalidasi model",
|
||||
"Couldn't connect to Ollama. Is it running?": "Tidak dapat terhubung ke Ollama. Apakah sedang berjalan?",
|
||||
"Invalid API key or connection failed": "Kunci API tidak valid atau koneksi gagal",
|
||||
"Connection failed": "Koneksi gagal",
|
||||
"AI Assistant": "Asisten AI",
|
||||
"Enable AI Assistant": "Aktifkan Asisten AI",
|
||||
"Provider": "Penyedia",
|
||||
"Ollama (Local)": "Ollama (Lokal)",
|
||||
"AI Gateway (Cloud)": "Gateway AI (Cloud)",
|
||||
"Ollama Configuration": "Konfigurasi Ollama",
|
||||
"Refresh Models": "Segarkan Model",
|
||||
"AI Model": "Model AI",
|
||||
"No models detected": "Tidak ada model terdeteksi",
|
||||
"AI Gateway Configuration": "Konfigurasi Gateway AI",
|
||||
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Pilih dari pilihan model AI berkualitas tinggi dan ekonomis. Anda juga dapat menggunakan model Anda sendiri dengan memilih \"Model Kustom\" di bawah.",
|
||||
"API Key": "Kunci API",
|
||||
"Get Key": "Dapatkan Kunci",
|
||||
"Model": "Model",
|
||||
"Custom Model...": "Model Kustom...",
|
||||
"Custom Model ID": "ID Model Kustom",
|
||||
"Validate": "Validasi",
|
||||
"Model available": "Model tersedia",
|
||||
"Connection": "Koneksi",
|
||||
"Test Connection": "Uji Koneksi",
|
||||
"Connected": "Terhubung",
|
||||
"Custom Colors": "Warna Kustom",
|
||||
"Color E-Ink Mode": "Mode E-Ink Berwarna",
|
||||
"Reading Ruler": "Penggaris Membaca",
|
||||
"Enable Reading Ruler": "Aktifkan Penggaris Membaca",
|
||||
"Lines to Highlight": "Baris untuk Disorot",
|
||||
"Ruler Color": "Warna Penggaris",
|
||||
"Command Palette": "Palet Perintah",
|
||||
"Search settings and actions...": "Cari pengaturan dan tindakan...",
|
||||
"No results found for": "Tidak ada hasil ditemukan untuk",
|
||||
"Type to search settings and actions": "Ketik untuk mencari pengaturan dan tindakan",
|
||||
"Recent": "Terbaru",
|
||||
"navigate": "navigasi",
|
||||
"select": "pilih",
|
||||
"close": "tutup",
|
||||
"Search Settings": "Cari Pengaturan",
|
||||
"Page Margins": "Margin Halaman",
|
||||
"AI Provider": "Penyedia AI",
|
||||
"Ollama URL": "URL Ollama",
|
||||
"Ollama Model": "Model Ollama",
|
||||
"AI Gateway Model": "Model AI Gateway",
|
||||
"Actions": "Tindakan",
|
||||
"Navigation": "Navigasi",
|
||||
"Set status for {{count}} book(s)_other": "Atur status untuk {{count}} buku",
|
||||
"Mark as Unread": "Tandai sebagai Belum Dibaca",
|
||||
"Mark as Finished": "Tandai sebagai Selesai",
|
||||
"Finished": "Selesai",
|
||||
"Unread": "Belum Dibaca",
|
||||
"Clear Status": "Hapus Status",
|
||||
"Status": "Status",
|
||||
"Loading": "Memuat...",
|
||||
"Exit Paragraph Mode": "Keluar dari Mode Paragraf",
|
||||
"Paragraph Mode": "Mode Paragraf",
|
||||
"Embedding Model": "Model Penyematan",
|
||||
"{{count}} book(s) synced_other": "{{count}} buku telah disinkronkan",
|
||||
"Unable to start RSVP": "Tidak dapat memulai RSVP",
|
||||
"RSVP not supported for PDF": "RSVP tidak didukung untuk PDF",
|
||||
"Select Chapter": "Pilih Bab",
|
||||
"Context": "Konteks",
|
||||
"Ready": "Siap",
|
||||
"Chapter Progress": "Kemajuan Bab",
|
||||
"words": "kata",
|
||||
"{{time}} left": "{{time}} tersisa",
|
||||
"Reading progress": "Kemajuan membaca",
|
||||
"Click to seek": "Klik untuk mencari",
|
||||
"Skip back 15 words": "Lompat mundur 15 kata",
|
||||
"Back 15 words (Shift+Left)": "Mundur 15 kata (Shift+Kiri)",
|
||||
"Pause (Space)": "Jeda (Spasi)",
|
||||
"Play (Space)": "Putar (Spasi)",
|
||||
"Skip forward 15 words": "Lompat maju 15 kata",
|
||||
"Forward 15 words (Shift+Right)": "Maju 15 kata (Shift+Kanan)",
|
||||
"Pause:": "Jeda:",
|
||||
"Decrease speed": "Kurangi kecepatan",
|
||||
"Slower (Left/Down)": "Lebih lambat (Kiri/Bawah)",
|
||||
"Current speed": "Kecepatan saat ini",
|
||||
"Increase speed": "Tambah kecepatan",
|
||||
"Faster (Right/Up)": "Lebih cepat (Kanan/Atas)",
|
||||
"Start RSVP Reading": "Mulai Membaca RSVP",
|
||||
"Choose where to start reading": "Pilih tempat untuk mulai membaca",
|
||||
"From Chapter Start": "Dari Awal Bab",
|
||||
"Start reading from the beginning of the chapter": "Mulai membaca dari awal bab",
|
||||
"Resume": "Lanjutkan",
|
||||
"Continue from where you left off": "Lanjutkan dari tempat Anda berhenti",
|
||||
"From Current Page": "Dari Halaman Saat Ini",
|
||||
"Start from where you are currently reading": "Mulai dari tempat Anda sedang membaca",
|
||||
"From Selection": "Dari Pilihan",
|
||||
"Speed Reading Mode": "Mode Baca Cepat",
|
||||
"Scroll left": "Gulir ke kiri",
|
||||
"Scroll right": "Gulir ke kanan",
|
||||
"Library Sync Progress": "Kemajuan Sinkronisasi Pustaka",
|
||||
"Back to library": "Kembali ke perpustakaan",
|
||||
"Group by...": "Kelompokkan menurut...",
|
||||
"Export as Plain Text": "Ekspor sebagai Teks Biasa",
|
||||
"Export as Markdown": "Ekspor sebagai Markdown",
|
||||
"Show Page Navigation Buttons": "Tombol navigasi",
|
||||
"Page {{number}}": "Halaman {{number}}",
|
||||
"highlight": "sorotan",
|
||||
"underline": "garis bawah",
|
||||
"squiggly": "bergelombang",
|
||||
"red": "merah",
|
||||
"violet": "ungu",
|
||||
"blue": "biru",
|
||||
"green": "hijau",
|
||||
"yellow": "kuning",
|
||||
"Select {{style}} style": "Pilih gaya {{style}}",
|
||||
"Select {{color}} color": "Pilih warna {{color}}",
|
||||
"Close Book": "Tutup Buku",
|
||||
"Speed Reading": "Membaca Cepat",
|
||||
"Close Speed Reading": "Tutup Membaca Cepat",
|
||||
"Authors": "Penulis",
|
||||
"Books": "Buku",
|
||||
"Groups": "Grup",
|
||||
"Back to TTS Location": "Kembali ke Lokasi TTS",
|
||||
"Metadata": "Metadata",
|
||||
"Image viewer": "Penampil gambar",
|
||||
"Previous Image": "Gambar Sebelumnya",
|
||||
"Next Image": "Gambar Berikutnya",
|
||||
"Zoomed": "Diperbesar",
|
||||
"Zoom level": "Tingkat zoom",
|
||||
"Table viewer": "Penampil tabel",
|
||||
"Unable to connect to Readwise. Please check your network connection.": "Tidak dapat terhubung ke Readwise. Silakan periksa koneksi jaringan Anda.",
|
||||
"Invalid Readwise access token": "Token akses Readwise tidak valid",
|
||||
"Disconnected from Readwise": "Terputus dari Readwise",
|
||||
"Never": "Tidak pernah",
|
||||
"Readwise Settings": "Pengaturan Readwise",
|
||||
"Connected to Readwise": "Terhubung ke Readwise",
|
||||
"Last synced: {{time}}": "Terakhir disinkronkan: {{time}}",
|
||||
"Sync Enabled": "Sinkronisasi Diaktifkan",
|
||||
"Disconnect": "Putuskan sambungan",
|
||||
"Connect your Readwise account to sync highlights.": "Hubungkan akun Readwise Anda untuk menyinkronkan sorotan.",
|
||||
"Get your access token at": "Dapatkan token akses Anda di",
|
||||
"Access Token": "Token Akses",
|
||||
"Paste your Readwise access token": "Tempelkan token akses Readwise Anda",
|
||||
"Config": "Konfigurasi",
|
||||
"Readwise Sync": "Sinkronisasi Readwise",
|
||||
"Push Highlights": "Kirim Sorotan",
|
||||
"Highlights synced to Readwise": "Sorotan disinkronkan ke Readwise",
|
||||
"Readwise sync failed: no internet connection": "Sinkronisasi Readwise gagal: tidak ada koneksi internet",
|
||||
"Readwise sync failed: {{error}}": "Sinkronisasi Readwise gagal: {{error}}",
|
||||
"System Screen Brightness": "Kecerahan Layar Sistem",
|
||||
"Page:": "Halaman:",
|
||||
"Page: {{number}}": "Halaman: {{number}}",
|
||||
"Annotation page number": "Nomor halaman anotasi"
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
"Animation": "アニメーション",
|
||||
"Annotate": "注釈",
|
||||
"Apply": "適用",
|
||||
"Are you sure to delete the selected books?": "選択した書籍を削除してもよろしいですか?",
|
||||
"Auto Mode": "自動モード",
|
||||
"Behavior": "動作",
|
||||
"Book": "書籍",
|
||||
"Book Cover": "表紙",
|
||||
"Bookmark": "ブックマーク",
|
||||
"Cancel": "キャンセル",
|
||||
"Chapter": "章",
|
||||
@@ -19,7 +17,6 @@
|
||||
"Confirm Deletion": "削除の確認",
|
||||
"Copied to notebook": "ノートブックにコピー",
|
||||
"Copy": "コピー",
|
||||
"Custom CSS": "カスタムCSS",
|
||||
"Dark Mode": "ダークモード",
|
||||
"Default": "デフォルト",
|
||||
"Default Font": "デフォルトフォント",
|
||||
@@ -29,7 +26,6 @@
|
||||
"Dictionary": "辞書",
|
||||
"Download Readest": "Readestをダウンロード",
|
||||
"Edit": "編集",
|
||||
"Enter your custom CSS here...": "ここにカスタムCSSを入力...",
|
||||
"Excerpts": "抜粋",
|
||||
"Failed to import book(s): {{filenames}}": "書籍のインポートに失敗しました:{{filenames}}",
|
||||
"Fast": "高速",
|
||||
@@ -42,8 +38,6 @@
|
||||
"Global Settings": "全体設定",
|
||||
"Go Back": "戻る",
|
||||
"Go Forward": "進む",
|
||||
"Go Left": "左へ",
|
||||
"Go Right": "右へ",
|
||||
"Grass": "グリーン",
|
||||
"Gray": "グレー",
|
||||
"Gruvbox": "グルーブボックス",
|
||||
@@ -51,7 +45,6 @@
|
||||
"Horizontal Direction": "横書",
|
||||
"Hyphenation": "ハイフネーション",
|
||||
"Import Books": "書籍をインポート",
|
||||
"Language:": "言語:",
|
||||
"Layout": "レイアウト",
|
||||
"Light Mode": "ライトモード",
|
||||
"Loading...": "読み込み中...",
|
||||
@@ -62,7 +55,6 @@
|
||||
"Match Whole Words": "単語全体を一致",
|
||||
"Maximum Number of Columns": "最大列数",
|
||||
"Minimum Font Size": "最小フォントサイズ",
|
||||
"Misc": "その他",
|
||||
"Monospace Font": "等幅フォント",
|
||||
"More Info": "詳細情報",
|
||||
"Nord": "ノルド",
|
||||
@@ -74,8 +66,8 @@
|
||||
"Paging Animation": "ページめくりアニメーション",
|
||||
"Paragraph": "段落",
|
||||
"Parallel Read": "並列読書",
|
||||
"Published:": "出版日:",
|
||||
"Publisher:": "出版社:",
|
||||
"Published": "出版日",
|
||||
"Publisher": "出版社",
|
||||
"Reading Progress Synced": "読書進捗が同期されました",
|
||||
"Reload Page": "ページを再読み込み",
|
||||
"Reveal in File Explorer": "エクスプローラーで表示",
|
||||
@@ -89,7 +81,6 @@
|
||||
"Search...": "検索...",
|
||||
"Select Book": "書籍を選択",
|
||||
"Select Books": "書籍を選択",
|
||||
"Select Multiple Books": "複数の書籍を選択",
|
||||
"Sepia": "セピア",
|
||||
"Serif Font": "セリフ体",
|
||||
"Show Book Details": "書籍の詳細を表示",
|
||||
@@ -100,16 +91,15 @@
|
||||
"Slow": "低速",
|
||||
"Solarized": "ソーラライズド",
|
||||
"Speak": "読み上げ",
|
||||
"Subjects:": "主題:",
|
||||
"Subjects": "主題",
|
||||
"System Fonts": "システムフォント",
|
||||
"TTS not supported in this device": "このデバイスではTTSがサポートされていません",
|
||||
"Theme Color": "テーマカラー",
|
||||
"Theme Mode": "テーマモード",
|
||||
"Translate": "翻訳",
|
||||
"Translated Text": "翻訳されたテキスト",
|
||||
"Unknown": "不明",
|
||||
"Untitled": "無題",
|
||||
"Updated:": "更新日:",
|
||||
"Updated": "更新日",
|
||||
"Version {{version}}": "バージョン {{version}}",
|
||||
"Vertical Direction": "縦書",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "ライブラリーへようこそ。ここに書籍をインポートして、いつでも読むことができます。",
|
||||
@@ -118,8 +108,6 @@
|
||||
"Your Library": "ライブラリー",
|
||||
"TTS not supported for PDF": "PDFではTTSがサポートされていません",
|
||||
"Override Book Font": "書籍フォントを上書き",
|
||||
"Vertical Margins (px)": "縦マージン(px)",
|
||||
"Horizontal Margins (%)": "横マージン(%)",
|
||||
"Apply to All Books": "すべての書籍に適用",
|
||||
"Apply to This Book": "この書籍に適用",
|
||||
"Unable to fetch the translation. Try again later.": "翻訳を取得できません。後で再試行してください。",
|
||||
@@ -128,14 +116,13 @@
|
||||
"Book Details": "書籍の詳細",
|
||||
"From Local File": "ローカルファイルから",
|
||||
"TOC": "目次",
|
||||
"Table of Contents": "目次",
|
||||
"Book uploaded: {{title}}": "書籍がアップロードされました:{{title}}",
|
||||
"Failed to upload book: {{title}}": "書籍のアップロードに失敗しました:{{title}}",
|
||||
"Book downloaded: {{title}}": "書籍がダウンロードされました:{{title}}",
|
||||
"Failed to download book: {{title}}": "書籍のダウンロードに失敗しました:{{title}}",
|
||||
"Upload Book": "書籍をアップロード",
|
||||
"Auto Upload Books to Cloud": "書籍を自動的にクラウドにアップロード",
|
||||
"{{percentage}}% of Cloud Storage Used.": "クラウドストレージの{{percentage}}%が使用されています。",
|
||||
"Storage": "ストレージ",
|
||||
"Book deleted: {{title}}": "書籍が削除されました:{{title}}",
|
||||
"Failed to delete book: {{title}}": "書籍の削除に失敗しました:{{title}}",
|
||||
"Check Updates on Start": "開始時に更新を確認",
|
||||
@@ -167,7 +154,7 @@
|
||||
"Create a Password": "パスワードを作成",
|
||||
"Sign up": "サインアップ",
|
||||
"Signing up...": "サインアップ中...",
|
||||
"Don’t have an account? Sign up": "アカウントをお持ちでないですか? サインアップ",
|
||||
"Don't have an account? Sign up": "アカウントをお持ちでないですか? サインアップ",
|
||||
"Check your email for the confirmation link": "確認リンクのためにメールを確認してください",
|
||||
"Signing in ...": "サインイン中 ...",
|
||||
"Send a magic link email": "マジックリンクメールを送信",
|
||||
@@ -186,41 +173,15 @@
|
||||
"Token": "トークン",
|
||||
"Your OTP token": "あなたのOTPトークン",
|
||||
"Verify token": "トークンを確認",
|
||||
"Sign in with Google": "Googleでサインイン",
|
||||
"Sign in with Apple": "Appleでサインイン",
|
||||
"Sign in with GitHub": "GitHubでサインイン",
|
||||
"Account": "アカウント",
|
||||
"Cloud Storage": "クラウドストレージ",
|
||||
"Failed to delete user. Please try again later.": "ユーザーの削除に失敗しました。後ほど再試行してください。",
|
||||
"Unlimited Offline/Online Reading": "無制限のオフライン/オンライン読書",
|
||||
"Unlimited Cloud Sync Devices": "無制限のクラウド同期デバイス",
|
||||
"Essential Text-to-Speech Voices": "基本的な音声合成ボイス",
|
||||
"DeepL Free Access": "DeepL無料アクセス",
|
||||
"Community Support": "コミュニティサポート",
|
||||
"500 MB Cloud Sync Space": "500 MBのクラウド同期スペース",
|
||||
"Includes All Free Tier Benefits": "無料プランの全特典を含む",
|
||||
"AI Summaries": "AI要約",
|
||||
"AI Translations": "AI翻訳",
|
||||
"Priority Support": "優先サポート",
|
||||
"DeepL Pro Access": "DeepL Proアクセス",
|
||||
"Expanded Text-to-Speech Voices": "拡張された音声合成ボイス",
|
||||
"2000 MB Cloud Sync Space": "2000 MBのクラウド同期スペース",
|
||||
"Includes All Plus Tier Benefits": "Plusプランの全特典を含む",
|
||||
"Unlimited AI Summaries": "無制限のAI要約",
|
||||
"Unlimited AI Translations": "無制限のAI翻訳",
|
||||
"Unlimited DeepL Pro Access": "無制限のDeepL Proアクセス",
|
||||
"Advanced AI Tools": "高度なAIツール",
|
||||
"Early Feature Access": "新機能への早期アクセス",
|
||||
"10 GB Cloud Sync Space": "10 GBのクラウド同期スペース",
|
||||
"Loading profile...": "プロフィール読み込み中...",
|
||||
"Features": "機能",
|
||||
"Delete Account": "アカウント削除",
|
||||
"Delete Your Account?": "アカウントを削除しますか?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "この操作は元に戻せません。クラウド上のすべてのデータが完全に削除されます。",
|
||||
"Delete Permanently": "完全に削除",
|
||||
"Free Tier": "無料プラン",
|
||||
"Plus Tier": "Plusプラン",
|
||||
"Pro Tier": "Proプラン",
|
||||
"RTL Direction": "RTL方向",
|
||||
"Maximum Column Height": "最大列高",
|
||||
"Maximum Column Width": "最大列幅",
|
||||
@@ -229,7 +190,7 @@
|
||||
"No supported files found. Supported formats: {{formats}}": "サポートされているファイルが見つかりません。サポートされている形式:{{formats}}",
|
||||
"Drop to Import Books": "書籍をインポートするにはドロップ",
|
||||
"Custom": "カスタム",
|
||||
"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": "全世界は舞台であり、\nそしてすべての男性と女性は単なる役者です。\n彼らは出入り口を持っており、\nそして一人の男性は時に多くの役を演じます。\n彼の行為は七つの時代です。\n\n— ウィリアム・シェイクスピア",
|
||||
"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": "全世界は舞台であり、\nそしてすべての男性と女性は単なる役者です。\n彼らは出入り口を持っており、\nそして一人の男性は時に多くの役を演じます。\n彼の行為は七つの時代です。\n\n— ウィリアム・シェイクスピア",
|
||||
"Custom Theme": "カスタムテーマ",
|
||||
"Theme Name": "テーマ名",
|
||||
"Text Color": "テキストカラー",
|
||||
@@ -256,9 +217,9 @@
|
||||
"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.": "この書籍形式はサポートされていません。",
|
||||
"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": "常に最前面",
|
||||
@@ -271,13 +232,13 @@
|
||||
"Clear Search": "検索をクリア",
|
||||
"Header & Footer": "ヘッダーとフッター",
|
||||
"Apply also in Scrolled Mode": "スクロールモードでも適用",
|
||||
"A new version of Readest is Available!": "新しいバージョンのReadestが利用可能です!",
|
||||
"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:": "変更ログ:",
|
||||
"Changelog": "変更ログ",
|
||||
"Software Update": "ソフトウェアの更新",
|
||||
"Title": "タイトル",
|
||||
"Date Read": "読書日",
|
||||
@@ -286,18 +247,15 @@
|
||||
"Ascending": "昇順",
|
||||
"Descending": "降順",
|
||||
"Sort by...": "並べ替え...",
|
||||
"Added:": "追加日:",
|
||||
"Format:": "形式:",
|
||||
"Added": "追加日",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "説明:",
|
||||
"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": "縦",
|
||||
@@ -306,6 +264,788 @@
|
||||
"Checking for updates...": "更新を確認中...",
|
||||
"Error checking for updates": "更新の確認中にエラーが発生しました",
|
||||
"Details": "詳細",
|
||||
"Cancel Selection": "選択をキャンセル",
|
||||
"File Size:": "ファイルサイズ:"
|
||||
"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": "重なりピクセル",
|
||||
"System Language": "システム言語",
|
||||
"Security": "セキュリティ",
|
||||
"Allow JavaScript": "JavaScriptを許可",
|
||||
"Enable only if you trust the file.": "ファイルを信頼する場合にのみ有効にしてください。",
|
||||
"Sort TOC by Page": "目次をページで並べ替え",
|
||||
"Search in {{count}} Book(s)..._other": "{{count}}冊の本を検索...",
|
||||
"No notes match your search": "ノートが見つかりません",
|
||||
"Search notes and excerpts...": "ノートを検索...",
|
||||
"Sign in to Sync": "同期するにはサインイン",
|
||||
"Synced at {{time}}": "{{time}} に同期済み",
|
||||
"Never synced": "まだ同期されていません",
|
||||
"Show Remaining Time": "残り時間を表示",
|
||||
"{{time}} min left in chapter": "{{time}}分残り",
|
||||
"Override Book Color": "書籍の色を上書き",
|
||||
"Login Required": "ログイン必須",
|
||||
"Quota Exceeded": "上限超過",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "日次翻訳文字数の使用量は{{percentage}}%です。",
|
||||
"Translation Characters": "翻訳文字数",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}}音声",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "何か問題が発生しました。心配しないでください。私たちのチームが通知を受けており、修正に取り組んでいます。",
|
||||
"Error Details:": "エラーの詳細:",
|
||||
"Try Again": "再試行",
|
||||
"Need help?": "ヘルプが必要ですか?",
|
||||
"Contact Support": "サポートに連絡",
|
||||
"Code Highlighting": "コードハイライト",
|
||||
"Enable Highlighting": "ハイライトを有効にする",
|
||||
"Code Language": "コード言語",
|
||||
"Top Margin (px)": "上マージン (px)",
|
||||
"Bottom Margin (px)": "下マージン (px)",
|
||||
"Right Margin (px)": "右マージン (px)",
|
||||
"Left Margin (px)": "左マージン (px)",
|
||||
"Column Gap (%)": "列間の間隔 (%)",
|
||||
"Always Show Status Bar": "ステータスバーを常に表示",
|
||||
"Custom Content CSS": "コンテンツCSS",
|
||||
"Enter CSS for book content styling...": "本のコンテンツ用CSSを入力...",
|
||||
"Custom Reader UI CSS": "リーダーUIのCSS",
|
||||
"Enter CSS for reader interface styling...": "リーダーインターフェース用CSSを入力...",
|
||||
"Crop": "トリミング",
|
||||
"Book Covers": "書籍の表紙",
|
||||
"Fit": "フィット",
|
||||
"Reset {{settings}}": "{{settings}}をリセット",
|
||||
"Reset Settings": "設定をリセット",
|
||||
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> ページ残り</1>",
|
||||
"Show Remaining Pages": "残りを表示",
|
||||
"Source Han Serif CN": "Source Han Serif",
|
||||
"Huiwen-MinchoGBK": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "サブスクリプションを管理",
|
||||
"Coming Soon": "近日公開",
|
||||
"Upgrade to {{plan}}": "{{plan}} にアップグレード",
|
||||
"Upgrade to Plus or Pro": "Plus または Pro にアップグレード",
|
||||
"Current Plan": "現在のプラン",
|
||||
"Plan Limits": "プランの上限",
|
||||
"Processing your payment...": "お支払いを処理中...",
|
||||
"Please wait while we confirm your subscription.": "サブスクリプションを確認しています。しばらくお待ちください。",
|
||||
"Payment Processing": "支払いを処理中",
|
||||
"Your payment is being processed. This usually takes a few moments.": "お支払いを処理しています。通常、数秒かかります。",
|
||||
"Payment Failed": "支払いに失敗しました",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "サブスクリプションを処理できませんでした。再度お試しいただくか、問題が続く場合はサポートにお問い合わせください。",
|
||||
"Back to Profile": "プロフィールに戻る",
|
||||
"Subscription Successful!": "サブスクリプションが完了しました!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "ご登録ありがとうございます!お支払いが正常に処理されました。",
|
||||
"Email:": "メール:",
|
||||
"Plan:": "プラン:",
|
||||
"Amount:": "金額:",
|
||||
"Go to Library": "ライブラリへ移動",
|
||||
"Need help? Contact our support team at support@readest.com": "サポートが必要ですか? support@readest.com までお問い合わせください。",
|
||||
"Free Plan": "無料プラン",
|
||||
"month": "月",
|
||||
"AI Translations (per day)": "AI翻訳(1日あたり)",
|
||||
"Plus Plan": "Plusプラン",
|
||||
"Includes All Free Plan Benefits": "無料プランの全特典を含む",
|
||||
"Pro Plan": "Proプラン",
|
||||
"More AI Translations": "さらに多くのAI翻訳",
|
||||
"Complete Your Subscription": "サブスクリプションを完了する",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "クラウド同期スペースの使用量は{{percentage}}%です。",
|
||||
"Cloud Sync Storage": "クラウド同期容量",
|
||||
"Disable": "無効にする",
|
||||
"Enable": "有効にする",
|
||||
"Upgrade to Readest Premium": "Readest Premiumにアップグレード",
|
||||
"Show Source Text": "原文を表示",
|
||||
"Cross-Platform Sync": "クロスプラットフォーム同期",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "ライブラリや進捗、メモを全デバイスで同期。",
|
||||
"Customizable Reading": "カスタマイズ可能な読書",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "フォントやレイアウト、テーマを自由に調整。",
|
||||
"AI Read Aloud": "AI音声読み上げ",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "自然なAI音声で本を聴く。",
|
||||
"AI Translations": "AI翻訳",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Google、Azure、DeepLで即時翻訳。",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "コミュニティで交流・サポートを利用。",
|
||||
"Unlimited AI Read Aloud Hours": "無制限のAI読み上げ",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "制限なくテキストを音声化。",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "高度な翻訳機能を利用可能に。",
|
||||
"DeepL Pro Access": "DeepL Pro利用",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "優先サポートを利用可能。",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "翻訳上限に達しました。アップグレードで継続可能です。",
|
||||
"Includes All Plus Plan Benefits": "Plusプランの全特典を含む",
|
||||
"Early Feature Access": "新機能への先行アクセス",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "新機能、アップデート、革新を誰よりも先に体験。",
|
||||
"Advanced AI Tools": "高度なAIツール",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "スマートな読書、翻訳、コンテンツ発見のための強力なAIツールを活用。",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "最も正確な翻訳エンジンで1日最大10万文字を翻訳。",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "最も正確な翻訳エンジンで1日最大50万文字を翻訳。",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "最大5GBの安全なクラウドストレージで読書コレクション全体を保存・アクセス。",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "最大20GBの安全なクラウドストレージで読書コレクション全体を保存・アクセス。",
|
||||
"Deleted cloud backup of the book: {{title}}": "書籍のクラウドバックアップを削除しました:{{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "選択した書籍のクラウドバックアップを削除してもよろしいですか?",
|
||||
"What's New in Readest": "Readestの新機能",
|
||||
"Enter book title": "本のタイトルを入力",
|
||||
"Subtitle": "サブタイトル",
|
||||
"Enter book subtitle": "本のサブタイトルを入力",
|
||||
"Enter author name": "著者名を入力",
|
||||
"Series": "シリーズ",
|
||||
"Enter series name": "シリーズ名を入力",
|
||||
"Series Index": "シリーズ番号",
|
||||
"Enter series index": "シリーズ番号を入力",
|
||||
"Total in Series": "シリーズ総数",
|
||||
"Enter total books in series": "シリーズの総冊数を入力",
|
||||
"Enter publisher": "出版社を入力",
|
||||
"Publication Date": "出版日",
|
||||
"Identifier": "識別子",
|
||||
"Enter book description": "本の説明を入力",
|
||||
"Change cover image": "表紙画像を変更",
|
||||
"Replace": "置き換え",
|
||||
"Unlock cover": "表紙のロックを解除",
|
||||
"Lock cover": "表紙をロック",
|
||||
"Auto-Retrieve Metadata": "メタデータを自動取得",
|
||||
"Auto-Retrieve": "自動取得",
|
||||
"Unlock all fields": "すべてのフィールドのロックを解除",
|
||||
"Unlock All": "すべてロック解除",
|
||||
"Lock all fields": "すべてのフィールドをロック",
|
||||
"Lock All": "すべてロック",
|
||||
"Reset": "リセット",
|
||||
"Edit Metadata": "メタデータを編集",
|
||||
"Locked": "ロック中",
|
||||
"Select Metadata Source": "メタデータソースを選択",
|
||||
"Keep manual input": "手動入力を保持",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "小説、科学、歴史",
|
||||
"Open Book in New Window": "新しいウィンドウで本を開く",
|
||||
"Voices for {{lang}}": "{{lang}}の音声",
|
||||
"Yandex Translate": "Yandex翻訳",
|
||||
"YYYY or YYYY-MM-DD": "YYYYまたはYYYY-MM-DD",
|
||||
"Restore Purchase": "購入を復元",
|
||||
"No purchases found to restore.": "復元する購入が見つかりません。",
|
||||
"Failed to restore purchases.": "購入の復元に失敗しました。",
|
||||
"Failed to manage subscription.": "サブスクリプションの管理に失敗しました。",
|
||||
"Failed to load subscription plans.": "サブスクリプションプランの読み込みに失敗しました。",
|
||||
"year": "年",
|
||||
"Failed to create checkout session": "チェックアウトセッションの作成に失敗しました",
|
||||
"Storage": "ストレージ",
|
||||
"Terms of Service": "利用規約",
|
||||
"Privacy Policy": "プライバシーポリシー",
|
||||
"Disable Double Click": "ダブルクリック無効",
|
||||
"TTS not supported for this document": "TTSはこのドキュメントではサポートされていません",
|
||||
"Reset Password": "パスワードをリセット",
|
||||
"Show Reading Progress": "読書進捗を表示",
|
||||
"Reading Progress Style": "読書進捗スタイル",
|
||||
"Page Number": "ページ番号",
|
||||
"Percentage": "パーセンテージ",
|
||||
"Deleted local copy of the book: {{title}}": "書籍のローカルコピーを削除しました:{{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "書籍のクラウドバックアップの削除に失敗しました:{{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "書籍のローカルコピーの削除に失敗しました:{{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "選択した書籍のローカルコピーを削除してもよろしいですか?",
|
||||
"Remove from Cloud & Device": "クラウドとデバイスから削除",
|
||||
"Remove from Cloud Only": "クラウドからのみ削除",
|
||||
"Remove from Device Only": "デバイスからのみ削除",
|
||||
"Disconnected": "切断されました",
|
||||
"KOReader Sync Settings": "KOReader Sync設定",
|
||||
"Sync Strategy": "同期戦略",
|
||||
"Ask on conflict": "競合時に確認",
|
||||
"Always use latest": "最新を常に使用",
|
||||
"Send changes only": "変更のみを送信",
|
||||
"Receive changes only": "変更のみを受信",
|
||||
"Checksum Method": "チェックサム方式",
|
||||
"File Content (recommended)": "ファイル内容(推奨)",
|
||||
"File Name": "ファイル名",
|
||||
"Device Name": "デバイス名",
|
||||
"Connect to your KOReader Sync server.": "KOReader Syncサーバーに接続します。",
|
||||
"Server URL": "サーバーURL",
|
||||
"Username": "ユーザー名",
|
||||
"Your Username": "あなたのユーザー名",
|
||||
"Password": "パスワード",
|
||||
"Connect": "接続",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "同期競合",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "{{deviceName}}から読書進捗を同期しますか?",
|
||||
"another device": "別のデバイス",
|
||||
"Local Progress": "ローカル進捗",
|
||||
"Remote Progress": "リモート進捗",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "ページ {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Current position": "現在の位置",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "おおよそページ {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "おおよそ {{percentage}}%",
|
||||
"Failed to connect": "接続に失敗しました",
|
||||
"Sync Server Connected": "同期サーバーに接続されました",
|
||||
"Sync as {{userDisplayName}}": "{{userDisplayName}} として同期",
|
||||
"Custom Fonts": "カスタムフォント",
|
||||
"Cancel Delete": "削除をキャンセル",
|
||||
"Import Font": "フォントをインポート",
|
||||
"Delete Font": "フォントを削除",
|
||||
"Tips": "ヒント",
|
||||
"Custom fonts can be selected from the Font Face menu": "カスタムフォントは書体メニューから選択できます",
|
||||
"Manage Custom Fonts": "カスタムフォント管理",
|
||||
"Select Files": "ファイルを選択",
|
||||
"Select Image": "画像を選択",
|
||||
"Select Video": "動画を選択",
|
||||
"Select Audio": "音声を選択",
|
||||
"Select Fonts": "フォントを選択",
|
||||
"Supported font formats: .ttf, .otf, .woff, .woff2": "サポートされているフォント形式:.ttf, .otf, .woff, .woff2",
|
||||
"Push Progress": "進捗をプッシュ",
|
||||
"Pull Progress": "進捗をプル",
|
||||
"Previous Paragraph": "前の段落",
|
||||
"Previous Sentence": "前の文",
|
||||
"Pause": "一時停止",
|
||||
"Play": "再生",
|
||||
"Next Sentence": "次の文",
|
||||
"Next Paragraph": "次の段落",
|
||||
"Separate Cover Page": "別の表紙ページ",
|
||||
"Resize Notebook": "ノートのサイズ変更",
|
||||
"Resize Sidebar": "サイドバーのサイズ変更",
|
||||
"Get Help from the Readest Community": "Readestコミュニティからヘルプを得る",
|
||||
"Remove cover image": "表紙画像を削除",
|
||||
"Bookshelf": "本棚",
|
||||
"View Menu": "メニューを表示",
|
||||
"Settings Menu": "設定メニュー",
|
||||
"View account details and quota": "アカウントの詳細とクォータを表示",
|
||||
"Library Header": "ライブラリヘッダー",
|
||||
"Book Content": "本の内容",
|
||||
"Footer Bar": "フッターバー",
|
||||
"Header Bar": "ヘッダーバー",
|
||||
"View Options": "表示オプション",
|
||||
"Book Menu": "本のメニュー",
|
||||
"Search Options": "検索オプション",
|
||||
"Close": "閉じる",
|
||||
"Delete Book Options": "本の削除オプション",
|
||||
"ON": "オン",
|
||||
"OFF": "オフ",
|
||||
"Reading Progress": "読書進捗",
|
||||
"Page Margin": "ページマージン",
|
||||
"Remove Bookmark": "ブックマークを削除",
|
||||
"Add Bookmark": "ブックマークを追加",
|
||||
"Books Content": "本の内容",
|
||||
"Jump to Location": "位置にジャンプ",
|
||||
"Unpin Notebook": "ノートブックのピン留めを解除",
|
||||
"Pin Notebook": "ノートブックをピン留め",
|
||||
"Hide Search Bar": "検索バーを隠す",
|
||||
"Show Search Bar": "検索バーを表示",
|
||||
"On {{current}} of {{total}} page": "ページ {{current}} / {{total}}",
|
||||
"Section Title": "セクションタイトル",
|
||||
"Decrease": "減少",
|
||||
"Increase": "増加",
|
||||
"Settings Panels": "設定パネル",
|
||||
"Settings": "設定",
|
||||
"Unpin Sidebar": "サイドバーのピン留めを解除",
|
||||
"Pin Sidebar": "サイドバーをピン留め",
|
||||
"Toggle Sidebar": "サイドバーの切り替え",
|
||||
"Toggle Translation": "翻訳の切り替え",
|
||||
"Translation Disabled": "翻訳が無効化されました",
|
||||
"Minimize": "最小化",
|
||||
"Maximize or Restore": "最大化または復元",
|
||||
"Exit Parallel Read": "並行読書を終了",
|
||||
"Enter Parallel Read": "並行読書に入る",
|
||||
"Zoom Level": "ズームレベル",
|
||||
"Zoom Out": "ズームアウト",
|
||||
"Reset Zoom": "ズームリセット",
|
||||
"Zoom In": "ズームイン",
|
||||
"Zoom Mode": "ズームモード",
|
||||
"Single Page": "シングルページ",
|
||||
"Auto Spread": "自動スプレッド",
|
||||
"Fit Page": "ページに合わせる",
|
||||
"Fit Width": "幅に合わせる",
|
||||
"Failed to select directory": "ディレクトリの選択に失敗しました",
|
||||
"The new data directory must be different from the current one.": "新しいデータディレクトリは現在のものと異なる必要があります。",
|
||||
"Migration failed: {{error}}": "移行に失敗しました: {{error}}",
|
||||
"Change Data Location": "データの場所を変更",
|
||||
"Current Data Location": "現在のデータの場所",
|
||||
"Total size: {{size}}": "合計サイズ: {{size}}",
|
||||
"Calculating file info...": "ファイル情報を計算中...",
|
||||
"New Data Location": "新しいデータの場所",
|
||||
"Choose Different Folder": "別のフォルダーを選択",
|
||||
"Choose New Folder": "新しいフォルダーを選択",
|
||||
"Migrating data...": "データを移行中...",
|
||||
"Copying: {{file}}": "コピー中: {{file}}",
|
||||
"{{current}} of {{total}} files": "{{current}} / {{total}} ファイル",
|
||||
"Migration completed successfully!": "移行が成功しました!",
|
||||
"Your data has been moved to the new location. Please restart the application to complete the process.": "データは新しい場所に移動されました。プロセスを完了するにはアプリケーションを再起動してください。",
|
||||
"Migration failed": "移行に失敗しました",
|
||||
"Important Notice": "重要なお知らせ",
|
||||
"This will move all your app data to the new location. Make sure the destination has enough free space.": "これにより、すべてのアプリデータが新しい場所に移動されます。宛先に十分な空き容量があることを確認してください。",
|
||||
"Restart App": "アプリを再起動",
|
||||
"Start Migration": "移行を開始",
|
||||
"Advanced Settings": "詳細設定",
|
||||
"File count: {{size}}": "ファイル数: {{size}}",
|
||||
"Background Read Aloud": "バックグラウンド読み上げ",
|
||||
"Ready to read aloud": "読み上げの準備ができました",
|
||||
"Read Aloud": "読み上げ",
|
||||
"Screen Brightness": "画面の明るさ",
|
||||
"Background Image": "背景画像",
|
||||
"Import Image": "画像をインポート",
|
||||
"Opacity": "不透明度",
|
||||
"Size": "サイズ",
|
||||
"Cover": "カバー",
|
||||
"Contain": "含む",
|
||||
"{{number}} pages left in chapter": "<1>章に</1><0>{{number}}</0><1>ページ残り</1>",
|
||||
"Device": "デバイス",
|
||||
"E-Ink Mode": "E-Inkモード",
|
||||
"Highlight Colors": "ハイライトカラー",
|
||||
"Pagination": "ページネーション",
|
||||
"Disable Double Tap": "ダブルタップを無効にする",
|
||||
"Tap to Paginate": "タップしてページを切り替える",
|
||||
"Click to Paginate": "クリックしてページを切り替える",
|
||||
"Tap Both Sides": "両側をタップ",
|
||||
"Click Both Sides": "両側をクリック",
|
||||
"Swap Tap Sides": "タップサイドを入れ替える",
|
||||
"Swap Click Sides": "クリックサイドを入れ替える",
|
||||
"Source and Translated": "原文と翻訳",
|
||||
"Translated Only": "翻訳のみ",
|
||||
"Source Only": "原文のみ",
|
||||
"TTS Text": "TTSテキスト",
|
||||
"The book file is corrupted": "本のファイルが破損しています",
|
||||
"The book file is empty": "本のファイルが空です",
|
||||
"Failed to open the book file": "本のファイルを開くことができませんでした",
|
||||
"On-Demand Purchase": "オンデマンド購入",
|
||||
"Full Customization": "フルカスタマイズ",
|
||||
"Lifetime Plan": "ライフタイムプラン",
|
||||
"One-Time Payment": "ワンタイムペイメント",
|
||||
"Make a single payment to enjoy lifetime access to specific features on all devices. Purchase specific features or services only when you need them.": "すべてのデバイスで特定の機能にライフタイムアクセスを楽しむために、1回の支払いを行います。必要なときにのみ特定の機能やサービスを購入します。",
|
||||
"Expand Cloud Sync Storage": "クラウド同期ストレージを拡張",
|
||||
"Expand your cloud storage forever with a one-time purchase. Each additional purchase adds more space.": "1回の購入でクラウドストレージを永遠に拡張します。追加の購入ごとに、さらに多くのスペースが追加されます。",
|
||||
"Unlock All Customization Options": "すべてのカスタマイズオプションを解除",
|
||||
"Unlock additional themes, fonts, layout options and read aloud, translators, cloud storage services.": "追加のテーマ、フォント、レイアウトオプション、読み上げ、翻訳者、クラウドストレージサービスを解除します。",
|
||||
"Purchase Successful!": "購入成功!",
|
||||
"lifetime": "ライフタイム",
|
||||
"Thank you for your purchase! Your payment has been processed successfully.": "ご購入ありがとうございます!お支払いが正常に処理されました。",
|
||||
"Order ID:": "購入ID:",
|
||||
"TTS Highlighting": "TTSハイライト",
|
||||
"Style": "スタイル",
|
||||
"Underline": "下線",
|
||||
"Strikethrough": "取り消し線",
|
||||
"Squiggly": "波線",
|
||||
"Outline": "アウトライン",
|
||||
"Save Current Color": "現在の色を保存",
|
||||
"Quick Colors": "クイックカラー",
|
||||
"Highlighter": "蛍光ペン",
|
||||
"Save Book Cover": "本のカバーを保存",
|
||||
"Auto-save last book cover": "最後の本のカバーを自動保存",
|
||||
"Back": "戻る",
|
||||
"Confirmation email sent! Please check your old and new email addresses to confirm the change.": "確認メールを送信しました。変更を確認するために、古いメールアドレスと新しいメールアドレスを確認してください。",
|
||||
"Failed to update email": "メールの更新に失敗しました",
|
||||
"New Email": "新しいメールアドレス",
|
||||
"Your new email": "あなたの新しいメールアドレス",
|
||||
"Updating email ...": "メールを更新しています ...",
|
||||
"Update email": "メールを更新",
|
||||
"Current email": "現在のメールアドレス",
|
||||
"Update Email": "メールを更新",
|
||||
"All": "すべて",
|
||||
"Unable to open book": "本を開くことができません",
|
||||
"Punctuation": "句読点",
|
||||
"Replace Quotation Marks": "引用符を置き換える",
|
||||
"Enabled only in vertical layout.": "縦書きレイアウトでのみ有効です。",
|
||||
"No Conversion": "変換なし",
|
||||
"Simplified to Traditional": "簡体字 → 繁体字",
|
||||
"Traditional to Simplified": "繁体字 → 簡体字",
|
||||
"Simplified to Traditional (Taiwan)": "簡体字 → 繁体字(台湾)",
|
||||
"Simplified to Traditional (Hong Kong)": "簡体字 → 繁体字(香港)",
|
||||
"Simplified to Traditional (Taiwan), with phrases": "簡体字 → 繁体字(台湾)• フレーズ",
|
||||
"Traditional (Taiwan) to Simplified": "繁体字(台湾) → 簡体字",
|
||||
"Traditional (Hong Kong) to Simplified": "繁体字(香港) → 簡体字",
|
||||
"Traditional (Taiwan) to Simplified, with phrases": "繁体字(台湾) → 簡体字 • フレーズ",
|
||||
"Convert Simplified and Traditional Chinese": "簡体字/繁体字変換",
|
||||
"Convert Mode": "変換モード",
|
||||
"Failed to auto-save book cover for lock screen: {{error}}": "ロック画面の本のカバーの自動保存に失敗しました:{{error}}",
|
||||
"Download from Cloud": "クラウドからダウンロード",
|
||||
"Upload to Cloud": "クラウドにアップロード",
|
||||
"Clear Custom Fonts": "カスタムフォントをクリア",
|
||||
"Columns": "列",
|
||||
"OPDS Catalogs": "OPDSカタログ",
|
||||
"Adding LAN addresses is not supported in the web app version.": "Webアプリ版ではLANアドレスの追加はサポートされていません。",
|
||||
"Invalid OPDS catalog. Please check the URL.": "無効なOPDSカタログです。URLを確認してください。",
|
||||
"Browse and download books from online catalogs": "オンラインカタログから本を閲覧・ダウンロード",
|
||||
"My Catalogs": "マイカタログ",
|
||||
"Add Catalog": "カタログを追加",
|
||||
"No catalogs yet": "まだカタログはありません",
|
||||
"Add your first OPDS catalog to start browsing books": "本を閲覧するには、最初のOPDSカタログを追加してください。",
|
||||
"Add Your First Catalog": "最初のカタログを追加",
|
||||
"Browse": "閲覧",
|
||||
"Popular Catalogs": "人気カタログ",
|
||||
"Add": "追加",
|
||||
"Add OPDS Catalog": "OPDSカタログを追加",
|
||||
"Catalog Name": "カタログ名",
|
||||
"My Calibre Library": "私のCalibreライブラリ",
|
||||
"OPDS URL": "OPDS URL",
|
||||
"Username (optional)": "ユーザー名(任意)",
|
||||
"Password (optional)": "パスワード(任意)",
|
||||
"Description (optional)": "説明(任意)",
|
||||
"A brief description of this catalog": "このカタログの簡単な説明",
|
||||
"Validating...": "検証中...",
|
||||
"View All": "すべて表示",
|
||||
"Forward": "進む",
|
||||
"Home": "ホーム",
|
||||
"{{count}} items_other": "{{count}} 件",
|
||||
"Download completed": "ダウンロード完了",
|
||||
"Download failed": "ダウンロード失敗",
|
||||
"Open Access": "オープンアクセス",
|
||||
"Borrow": "借りる",
|
||||
"Buy": "購入",
|
||||
"Subscribe": "購読",
|
||||
"Sample": "サンプル",
|
||||
"Download": "ダウンロード",
|
||||
"Open & Read": "開いて読む",
|
||||
"Tags": "タグ",
|
||||
"Tag": "タグ",
|
||||
"First": "最初",
|
||||
"Previous": "前へ",
|
||||
"Next": "次へ",
|
||||
"Last": "最後",
|
||||
"Cannot Load Page": "ページを読み込めません",
|
||||
"An error occurred": "エラーが発生しました",
|
||||
"Online Library": "オンラインライブラリ",
|
||||
"URL must start with http:// or https://": "URLはhttp://またはhttps://で始まる必要があります",
|
||||
"Title, Author, Tag, etc...": "タイトル、著者、タグなど...",
|
||||
"Query": "クエリ",
|
||||
"Subject": "件名",
|
||||
"Enter {{terms}}": "{{terms}}を入力してください",
|
||||
"No search results found": "検索結果が見つかりません",
|
||||
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDSフィードの読み込みに失敗しました: {{status}} {{statusText}}",
|
||||
"Search in {{title}}": "{{title}}内を検索",
|
||||
"Manage Storage": "ストレージ管理",
|
||||
"Failed to load files": "ファイルの読み込みに失敗しました",
|
||||
"Deleted {{count}} file(s)_other": "{{count}} 件のファイルを削除しました",
|
||||
"Failed to delete {{count}} file(s)_other": "{{count}} 件のファイルの削除に失敗しました",
|
||||
"Failed to delete files": "ファイルの削除に失敗しました",
|
||||
"Total Files": "ファイル合計",
|
||||
"Total Size": "合計サイズ",
|
||||
"Quota": "容量制限",
|
||||
"Used": "使用済み",
|
||||
"Files": "ファイル",
|
||||
"Search files...": "ファイルを検索...",
|
||||
"Newest First": "新しい順",
|
||||
"Oldest First": "古い順",
|
||||
"Largest First": "大きい順",
|
||||
"Smallest First": "小さい順",
|
||||
"Name A-Z": "名前 A-Z",
|
||||
"Name Z-A": "名前 Z-A",
|
||||
"{{count}} selected_other": "{{count}} 件選択済み",
|
||||
"Delete Selected": "選択した項目を削除",
|
||||
"Created": "作成日",
|
||||
"No files found": "ファイルが見つかりません",
|
||||
"No files uploaded yet": "まだファイルがアップロードされていません",
|
||||
"files": "ファイル",
|
||||
"Page {{current}} of {{total}}": "ページ {{current}} / {{total}}",
|
||||
"Are you sure to delete {{count}} selected file(s)?_other": "選択した {{count}} 件のファイルを削除してもよろしいですか?",
|
||||
"Cloud Storage Usage": "クラウドストレージ使用量",
|
||||
"Rename Group": "グループ名を変更",
|
||||
"From Directory": "ディレクトリから",
|
||||
"Successfully imported {{count}} book(s)_other": "成功裏に{{count}}冊の本をインポートしました",
|
||||
"Count": "件数",
|
||||
"Start Page": "開始ページ",
|
||||
"Search in OPDS Catalog...": "OPDSカタログ内を検索...",
|
||||
"Please log in to use advanced TTS features": "高度なTTS機能を使用するにはログインしてください",
|
||||
"Word limit of 30 words exceeded.": "30語の上限を超えています。",
|
||||
"Proofread": "校正",
|
||||
"Current selection": "現在の選択",
|
||||
"All occurrences in this book": "この本内のすべての出現箇所",
|
||||
"All occurrences in your library": "ライブラリ内のすべての出現箇所",
|
||||
"Selected text:": "選択されたテキスト:",
|
||||
"Replace with:": "置換後:",
|
||||
"Enter text...": "テキストを入力…",
|
||||
"Case sensitive:": "大文字と小文字を区別:",
|
||||
"Scope:": "適用範囲:",
|
||||
"Selection": "選択",
|
||||
"Library": "ライブラリ",
|
||||
"Yes": "はい",
|
||||
"No": "いいえ",
|
||||
"Proofread Replacement Rules": "校正置換ルール",
|
||||
"Selected Text Rules": "選択テキストのルール",
|
||||
"No selected text replacement rules": "選択されたテキストの置換ルールはありません",
|
||||
"Book Specific Rules": "書籍別ルール",
|
||||
"No book-level replacement rules": "書籍レベルの置換ルールはありません",
|
||||
"Disable Quick Action": "クイックアクションを無効にする",
|
||||
"Enable Quick Action on Selection": "選択時にクイックアクションを有効にする",
|
||||
"None": "なし",
|
||||
"Annotation Tools": "注釈ツール",
|
||||
"Enable Quick Actions": "クイックアクションを有効にする",
|
||||
"Quick Action": "クイックアクション",
|
||||
"Copy to Notebook": "ノートにコピー",
|
||||
"Copy text after selection": "選択後のテキストをコピー",
|
||||
"Highlight text after selection": "選択後のテキストをハイライト",
|
||||
"Annotate text after selection": "選択後のテキストに注釈を付ける",
|
||||
"Search text after selection": "選択後のテキストを検索",
|
||||
"Look up text in dictionary after selection": "選択後のテキストを辞書で調べる",
|
||||
"Look up text in Wikipedia after selection": "選択後のテキストをWikipediaで調べる",
|
||||
"Translate text after selection": "選択後のテキストを翻訳する",
|
||||
"Read text aloud after selection": "選択後のテキストを音読する",
|
||||
"Proofread text after selection": "選択後のテキストを校正する",
|
||||
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} 進行中、{{pendingCount}} 待機中",
|
||||
"{{failedCount}} failed": "{{failedCount}} 失敗",
|
||||
"Waiting...": "待機中...",
|
||||
"Failed": "失敗",
|
||||
"Completed": "完了",
|
||||
"Cancelled": "キャンセル",
|
||||
"Retry": "再試行",
|
||||
"Active": "進行中",
|
||||
"Transfer Queue": "転送キュー",
|
||||
"Upload All": "すべてアップロード",
|
||||
"Download All": "すべてダウンロード",
|
||||
"Resume Transfers": "転送を再開",
|
||||
"Pause Transfers": "転送を一時停止",
|
||||
"Pending": "待機中",
|
||||
"No transfers": "転送なし",
|
||||
"Retry All": "すべて再試行",
|
||||
"Clear Completed": "完了を消去",
|
||||
"Clear Failed": "失敗を消去",
|
||||
"Upload queued: {{title}}": "アップロードをキューに追加: {{title}}",
|
||||
"Download queued: {{title}}": "ダウンロードをキューに追加: {{title}}",
|
||||
"Book not found in library": "ライブラリに本が見つかりません",
|
||||
"Unknown error": "不明なエラー",
|
||||
"Please log in to continue": "続行するにはログインしてください",
|
||||
"Cloud File Transfers": "クラウドファイル転送",
|
||||
"Show Search Results": "検索結果を表示",
|
||||
"Search results for '{{term}}'": "「{{term}}」を含む結果",
|
||||
"Close Search": "検索を閉じる",
|
||||
"Previous Result": "前の結果",
|
||||
"Next Result": "次の結果",
|
||||
"Bookmarks": "ブックマーク",
|
||||
"Annotations": "注釈",
|
||||
"Show Results": "結果を表示",
|
||||
"Clear search": "検索をクリア",
|
||||
"Clear search history": "検索履歴をクリア",
|
||||
"Tap to Toggle Footer": "タップでフッターを切り替え",
|
||||
"Exported successfully": "エクスポート成功",
|
||||
"Book exported successfully.": "書籍のエクスポートに成功しました。",
|
||||
"Failed to export the book.": "書籍のエクスポートに失敗しました。",
|
||||
"Export Book": "書籍をエクスポート",
|
||||
"Whole word:": "単語全体:",
|
||||
"Error": "エラー",
|
||||
"Unable to load the article. Try searching directly on {{link}}.": "記事を読み込めません。{{link}}で直接検索してみてください。",
|
||||
"Unable to load the word. Try searching directly on {{link}}.": "単語を読み込めません。{{link}}で直接検索してみてください。",
|
||||
"Date Published": "出版日",
|
||||
"Only for TTS:": "TTSのみ:",
|
||||
"Uploaded": "アップロード済み",
|
||||
"Downloaded": "ダウンロード済み",
|
||||
"Deleted": "削除済み",
|
||||
"Note:": "ノート:",
|
||||
"Time:": "時刻:",
|
||||
"Format Options": "形式オプション",
|
||||
"Export Date": "エクスポート日",
|
||||
"Chapter Titles": "章タイトル",
|
||||
"Chapter Separator": "章区切り",
|
||||
"Highlights": "ハイライト",
|
||||
"Note Date": "ノート日付",
|
||||
"Advanced": "詳細設定",
|
||||
"Hide": "非表示",
|
||||
"Show": "表示",
|
||||
"Use Custom Template": "カスタムテンプレート使用",
|
||||
"Export Template": "エクスポートテンプレート",
|
||||
"Template Syntax:": "テンプレート構文:",
|
||||
"Insert value": "値を挿入",
|
||||
"Format date (locale)": "日付形式(ロケール)",
|
||||
"Format date (custom)": "日付形式(カスタム)",
|
||||
"Conditional": "条件分岐",
|
||||
"Loop": "ループ",
|
||||
"Available Variables:": "利用可能な変数:",
|
||||
"Book title": "本のタイトル",
|
||||
"Book author": "著者",
|
||||
"Export date": "エクスポート日",
|
||||
"Array of chapters": "章の配列",
|
||||
"Chapter title": "章タイトル",
|
||||
"Array of annotations": "注釈の配列",
|
||||
"Highlighted text": "ハイライトテキスト",
|
||||
"Annotation note": "注釈ノート",
|
||||
"Date Format Tokens:": "日付形式トークン:",
|
||||
"Year (4 digits)": "年(4桁)",
|
||||
"Month (01-12)": "月(01-12)",
|
||||
"Day (01-31)": "日(01-31)",
|
||||
"Hour (00-23)": "時(00-23)",
|
||||
"Minute (00-59)": "分(00-59)",
|
||||
"Second (00-59)": "秒(00-59)",
|
||||
"Show Source": "ソース表示",
|
||||
"No content to preview": "プレビューするコンテンツがありません",
|
||||
"Export": "エクスポート",
|
||||
"Set Timeout": "タイムアウト設定",
|
||||
"Select Voice": "音声選択",
|
||||
"Toggle Sticky Bottom TTS Bar": "TTSバー固定切替",
|
||||
"Display what I'm reading on Discord": "読書中の本をDiscordに表示",
|
||||
"Show on Discord": "Discordに表示",
|
||||
"Instant {{action}}": "インスタント{{action}}",
|
||||
"Instant {{action}} Disabled": "インスタント{{action}}を無効化",
|
||||
"Annotation": "注釈",
|
||||
"Reset Template": "テンプレートをリセット",
|
||||
"Annotation style": "注釈スタイル",
|
||||
"Annotation color": "注釈の色",
|
||||
"Annotation time": "注釈時間",
|
||||
"AI": "AI",
|
||||
"Are you sure you want to re-index this book?": "この本を再インデックスしてもよろしいですか?",
|
||||
"Enable AI in Settings": "設定でAIを有効にする",
|
||||
"Index This Book": "この本をインデックス",
|
||||
"Enable AI search and chat for this book": "この本のAI検索とチャットを有効にする",
|
||||
"Start Indexing": "インデックス開始",
|
||||
"Indexing book...": "本をインデックス中...",
|
||||
"Preparing...": "準備中...",
|
||||
"Delete this conversation?": "この会話を削除しますか?",
|
||||
"No conversations yet": "まだ会話がありません",
|
||||
"Start a new chat to ask questions about this book": "この本について質問するには新しいチャットを開始してください",
|
||||
"Rename": "名前を変更",
|
||||
"New Chat": "新しいチャット",
|
||||
"Chat": "チャット",
|
||||
"Please enter a model ID": "モデルIDを入力してください",
|
||||
"Model not available or invalid": "モデルが利用できないか無効です",
|
||||
"Failed to validate model": "モデルの検証に失敗しました",
|
||||
"Couldn't connect to Ollama. Is it running?": "Ollamaに接続できませんでした。実行中ですか?",
|
||||
"Invalid API key or connection failed": "APIキーが無効か接続に失敗しました",
|
||||
"Connection failed": "接続に失敗しました",
|
||||
"AI Assistant": "AIアシスタント",
|
||||
"Enable AI Assistant": "AIアシスタントを有効にする",
|
||||
"Provider": "プロバイダー",
|
||||
"Ollama (Local)": "Ollama(ローカル)",
|
||||
"AI Gateway (Cloud)": "AIゲートウェイ(クラウド)",
|
||||
"Ollama Configuration": "Ollama設定",
|
||||
"Refresh Models": "モデルを更新",
|
||||
"AI Model": "AIモデル",
|
||||
"No models detected": "モデルが検出されませんでした",
|
||||
"AI Gateway Configuration": "AIゲートウェイ設定",
|
||||
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "高品質で経済的なAIモデルからお選びください。下の「カスタムモデル」を選択して独自のモデルを使用することもできます。",
|
||||
"API Key": "APIキー",
|
||||
"Get Key": "キーを取得",
|
||||
"Model": "モデル",
|
||||
"Custom Model...": "カスタムモデル...",
|
||||
"Custom Model ID": "カスタムモデルID",
|
||||
"Validate": "検証",
|
||||
"Model available": "モデルが利用可能",
|
||||
"Connection": "接続",
|
||||
"Test Connection": "接続をテスト",
|
||||
"Connected": "接続済み",
|
||||
"Custom Colors": "カスタムカラー",
|
||||
"Color E-Ink Mode": "カラー電子ペーパーモード",
|
||||
"Reading Ruler": "リーディングルーラー",
|
||||
"Enable Reading Ruler": "リーディングルーラーを有効にする",
|
||||
"Lines to Highlight": "ハイライトする行数",
|
||||
"Ruler Color": "ルーラーの色",
|
||||
"Command Palette": "コマンドパレット",
|
||||
"Search settings and actions...": "設定とアクションを検索...",
|
||||
"No results found for": "の結果が見つかりませんでした",
|
||||
"Type to search settings and actions": "入力して設定とアクションを検索",
|
||||
"Recent": "最近使った項目",
|
||||
"navigate": "ナビゲート",
|
||||
"select": "選択",
|
||||
"close": "閉じる",
|
||||
"Search Settings": "設定を検索",
|
||||
"Page Margins": "ページ余白",
|
||||
"AI Provider": "AIプロバイダー",
|
||||
"Ollama URL": "OllamaのURL",
|
||||
"Ollama Model": "Ollamaモデル",
|
||||
"AI Gateway Model": "AI Gatewayモデル",
|
||||
"Actions": "アクション",
|
||||
"Navigation": "ナビゲーション",
|
||||
"Set status for {{count}} book(s)_other": "{{count}} 冊の本のステータスを設定",
|
||||
"Mark as Unread": "未読にする",
|
||||
"Mark as Finished": "読了にする",
|
||||
"Finished": "読了",
|
||||
"Unread": "未読",
|
||||
"Clear Status": "ステータスをクリア",
|
||||
"Status": "ステータス",
|
||||
"Loading": "読み込み中",
|
||||
"Exit Paragraph Mode": "段落モードを終了",
|
||||
"Paragraph Mode": "段落モード",
|
||||
"Embedding Model": "埋め込みモデル",
|
||||
"{{count}} book(s) synced_other": "{{count}} 冊の本が同期されました",
|
||||
"Unable to start RSVP": "RSVPを起動できません",
|
||||
"RSVP not supported for PDF": "PDFはRSVPに対応していません",
|
||||
"Select Chapter": "章を選択",
|
||||
"Context": "コンテキスト",
|
||||
"Ready": "準備完了",
|
||||
"Chapter Progress": "章の進捗",
|
||||
"words": "単語",
|
||||
"{{time}} left": "残り {{time}}",
|
||||
"Reading progress": "読書の進捗",
|
||||
"Click to seek": "クリックしてシーク",
|
||||
"Skip back 15 words": "15単語戻る",
|
||||
"Back 15 words (Shift+Left)": "15単語戻る (Shift+左)",
|
||||
"Pause (Space)": "一時停止 (Space)",
|
||||
"Play (Space)": "再生 (Space)",
|
||||
"Skip forward 15 words": "15単語進む",
|
||||
"Forward 15 words (Shift+Right)": "15単語進む (Shift+右)",
|
||||
"Pause:": "一時停止:",
|
||||
"Decrease speed": "速度を下げる",
|
||||
"Slower (Left/Down)": "低速 (左/下)",
|
||||
"Current speed": "現在の速度",
|
||||
"Increase speed": "速度を上げる",
|
||||
"Faster (Right/Up)": "高速 (右/上)",
|
||||
"Start RSVP Reading": "RSVP読書を開始",
|
||||
"Choose where to start reading": "読書を開始する場所を選択",
|
||||
"From Chapter Start": "章の最初から",
|
||||
"Start reading from the beginning of the chapter": "この章の最初から読み直す",
|
||||
"Resume": "再開",
|
||||
"Continue from where you left off": "中断した場所から再開する",
|
||||
"From Current Page": "現在のページから",
|
||||
"Start from where you are currently reading": "現在読んでいる場所から開始する",
|
||||
"From Selection": "選択範囲から",
|
||||
"Speed Reading Mode": "速読モード",
|
||||
"Scroll left": "左にスクロール",
|
||||
"Scroll right": "右にスクロール",
|
||||
"Library Sync Progress": "ライブラリ同期の進捗",
|
||||
"Back to library": "ライブラリに戻る",
|
||||
"Group by...": "グループ化...",
|
||||
"Export as Plain Text": "プレーンテキストとして書き出し",
|
||||
"Export as Markdown": "Markdownとして書き出し",
|
||||
"Show Page Navigation Buttons": "ナビゲーションボタンを表示",
|
||||
"Page {{number}}": "{{number}} ページ",
|
||||
"highlight": "ハイライト",
|
||||
"underline": "下線",
|
||||
"squiggly": "波線",
|
||||
"red": "赤",
|
||||
"violet": "すみれ色",
|
||||
"blue": "青",
|
||||
"green": "緑",
|
||||
"yellow": "黄色",
|
||||
"Select {{style}} style": "スタイル {{style}} を選択",
|
||||
"Select {{color}} color": "色 {{color}} を選択",
|
||||
"Close Book": "本を閉じる",
|
||||
"Speed Reading": "速読",
|
||||
"Close Speed Reading": "速読を閉じる",
|
||||
"Authors": "著者",
|
||||
"Books": "書籍",
|
||||
"Groups": "グループ",
|
||||
"Back to TTS Location": "TTSの位置に戻る",
|
||||
"Metadata": "メタデータ",
|
||||
"Image viewer": "画像ビューア",
|
||||
"Previous Image": "前の画像",
|
||||
"Next Image": "次の画像",
|
||||
"Zoomed": "ズーム済み",
|
||||
"Zoom level": "ズームレベル",
|
||||
"Table viewer": "テーブルビューア",
|
||||
"Unable to connect to Readwise. Please check your network connection.": "Readwise に接続できません。ネットワーク接続を確認してください。",
|
||||
"Invalid Readwise access token": "無効な Readwise アクセストークン",
|
||||
"Disconnected from Readwise": "Readwise から切断されました",
|
||||
"Never": "一度もなし",
|
||||
"Readwise Settings": "Readwise 設定",
|
||||
"Connected to Readwise": "Readwise に接続済み",
|
||||
"Last synced: {{time}}": "最終同期:{{time}}",
|
||||
"Sync Enabled": "同期有効",
|
||||
"Disconnect": "切断",
|
||||
"Connect your Readwise account to sync highlights.": "ハイライトを同期するには Readwise アカウントを接続してください。",
|
||||
"Get your access token at": "アクセストークンの取得先:",
|
||||
"Access Token": "アクセストークン",
|
||||
"Paste your Readwise access token": "Readwise アクセストークンを貼り付けてください",
|
||||
"Config": "設定",
|
||||
"Readwise Sync": "Readwise 同期",
|
||||
"Push Highlights": "ハイライトをプッシュ",
|
||||
"Highlights synced to Readwise": "ハイライトが Readwise に同期されました",
|
||||
"Readwise sync failed: no internet connection": "Readwise 同期失敗:インターネット接続がありません",
|
||||
"Readwise sync failed: {{error}}": "Readwise 同期失敗:{{error}}",
|
||||
"System Screen Brightness": "システムの画面の明るさ",
|
||||
"Page:": "ページ:",
|
||||
"Page: {{number}}": "ページ: {{number}}",
|
||||
"Annotation page number": "注釈のページ番号"
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
"Animation": "애니메이션",
|
||||
"Annotate": "주석",
|
||||
"Apply": "적용",
|
||||
"Are you sure to delete the selected books?": "선택한 책을 삭제하시겠습니까?",
|
||||
"Auto Mode": "자동 모드",
|
||||
"Behavior": "동작",
|
||||
"Book": "책",
|
||||
"Book Cover": "책 표지",
|
||||
"Bookmark": "북마크",
|
||||
"Cancel": "취소",
|
||||
"Chapter": "챕터",
|
||||
@@ -19,7 +17,6 @@
|
||||
"Confirm Deletion": "삭제 확인",
|
||||
"Copied to notebook": "노트북에 복사됨",
|
||||
"Copy": "복사",
|
||||
"Custom CSS": "사용자 정의 CSS",
|
||||
"Dark Mode": "다크 모드",
|
||||
"Default": "기본값",
|
||||
"Default Font": "기본 글꼴",
|
||||
@@ -29,7 +26,6 @@
|
||||
"Dictionary": "사전",
|
||||
"Download Readest": "Readest 다운로드",
|
||||
"Edit": "편집",
|
||||
"Enter your custom CSS here...": "여기에 사용자 정의 CSS를 입력하세요...",
|
||||
"Excerpts": "발췌",
|
||||
"Failed to import book(s): {{filenames}}": "책 가져오기 실패: {{filenames}}",
|
||||
"Fast": "빠름",
|
||||
@@ -42,8 +38,6 @@
|
||||
"Global Settings": "전역 설정",
|
||||
"Go Back": "뒤로",
|
||||
"Go Forward": "앞으로",
|
||||
"Go Left": "왼쪽으로",
|
||||
"Go Right": "오른쪽으로",
|
||||
"Grass": "잔디색",
|
||||
"Gray": "회색",
|
||||
"Gruvbox": "그러브박스",
|
||||
@@ -51,7 +45,6 @@
|
||||
"Horizontal Direction": "수평 방향",
|
||||
"Hyphenation": "하이픈 넣기",
|
||||
"Import Books": "책 가져오기",
|
||||
"Language:": "언어:",
|
||||
"Layout": "레이아웃",
|
||||
"Light Mode": "라이트 모드",
|
||||
"Loading...": "로딩 중...",
|
||||
@@ -62,7 +55,6 @@
|
||||
"Match Whole Words": "전체 단어 일치",
|
||||
"Maximum Number of Columns": "최대 열 수",
|
||||
"Minimum Font Size": "최소 글꼴 크기",
|
||||
"Misc": "기타",
|
||||
"Monospace Font": "고정폭 글꼴",
|
||||
"More Info": "추가 정보",
|
||||
"Nord": "노드",
|
||||
@@ -74,8 +66,8 @@
|
||||
"Paging Animation": "페이지 넘김 애니메이션",
|
||||
"Paragraph": "단락",
|
||||
"Parallel Read": "병렬 읽기",
|
||||
"Published:": "출판일:",
|
||||
"Publisher:": "출판사:",
|
||||
"Published": "출판일",
|
||||
"Publisher": "출판사",
|
||||
"Reading Progress Synced": "읽기 진행 상황 동기화됨",
|
||||
"Reload Page": "페이지 새로고침",
|
||||
"Reveal in File Explorer": "파일 탐색기에서 표시",
|
||||
@@ -89,7 +81,6 @@
|
||||
"Search...": "검색...",
|
||||
"Select Book": "책 선택",
|
||||
"Select Books": "책 선택",
|
||||
"Select Multiple Books": "여러 책 선택",
|
||||
"Sepia": "세피아",
|
||||
"Serif Font": "세리프체",
|
||||
"Show Book Details": "책 세부 정보 표시",
|
||||
@@ -100,16 +91,15 @@
|
||||
"Slow": "느림",
|
||||
"Solarized": "솔라라이즈드",
|
||||
"Speak": "말하기",
|
||||
"Subjects:": "주제:",
|
||||
"Subjects": "주제",
|
||||
"System Fonts": "시스템 글꼴",
|
||||
"TTS not supported in this device": "이 장치에서 TTS가 지원되지 않음",
|
||||
"Theme Color": "테마 색상",
|
||||
"Theme Mode": "테마 모드",
|
||||
"Translate": "번역",
|
||||
"Translated Text": "번역된 텍스트",
|
||||
"Unknown": "알 수 없음",
|
||||
"Untitled": "제목 없음",
|
||||
"Updated:": "업데이트일:",
|
||||
"Updated": "업데이트일",
|
||||
"Version {{version}}": "버전 {{version}}",
|
||||
"Vertical Direction": "수직 방향",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "내 서재에 오신 것을 환영합니다. 여기에서 책을 가져와 언제든지 읽을 수 있습니다.",
|
||||
@@ -118,8 +108,6 @@
|
||||
"Your Library": "내 서재",
|
||||
"TTS not supported for PDF": "PDF에서 TTS가 지원되지 않음",
|
||||
"Override Book Font": "책 글꼴 덮어쓰기",
|
||||
"Vertical Margins (px)": "수직 여백 (px)",
|
||||
"Horizontal Margins (%)": "수평 여백 (%)",
|
||||
"Apply to All Books": "모든 책에 적용",
|
||||
"Apply to This Book": "이 책에 적용",
|
||||
"Unable to fetch the translation. Try again later.": "번역을 가져올 수 없습니다. 나중에 다시 시도하세요.",
|
||||
@@ -128,14 +116,13 @@
|
||||
"Book Details": "책 세부 정보",
|
||||
"From Local File": "로컬 파일에서",
|
||||
"TOC": "목차",
|
||||
"Table of Contents": "목차",
|
||||
"Book uploaded: {{title}}": "책 업로드됨: {{title}}",
|
||||
"Failed to upload book: {{title}}": "책 업로드 실패: {{title}}",
|
||||
"Book downloaded: {{title}}": "책 다운로드됨: {{title}}",
|
||||
"Failed to download book: {{title}}": "책 다운로드 실패: {{title}}",
|
||||
"Upload Book": "책 업로드",
|
||||
"Auto Upload Books to Cloud": "책 자동으로 클라우드에 업로드",
|
||||
"{{percentage}}% of Cloud Storage Used.": "클라우드 저장소 사용량 {{percentage}}%.",
|
||||
"Storage": "저장소",
|
||||
"Book deleted: {{title}}": "책 삭제됨: {{title}}",
|
||||
"Failed to delete book: {{title}}": "책 삭제 실패: {{title}}",
|
||||
"Check Updates on Start": "시작 시 업데이트 확인",
|
||||
@@ -167,7 +154,7 @@
|
||||
"Create a Password": "비밀번호 만들기",
|
||||
"Sign up": "회원가입",
|
||||
"Signing up...": "회원가입 중...",
|
||||
"Don’t have an account? Sign up": "계정이 없습니까? 회원가입",
|
||||
"Don't have an account? Sign up": "계정이 없습니까? 회원가입",
|
||||
"Check your email for the confirmation link": "확인 링크를 위해 이메일을 확인하세요",
|
||||
"Signing in ...": "로그인 중 ...",
|
||||
"Send a magic link email": "마법의 링크 이메일 보내기",
|
||||
@@ -186,41 +173,15 @@
|
||||
"Token": "토큰",
|
||||
"Your OTP token": "당신의 OTP 토큰",
|
||||
"Verify token": "토큰 확인",
|
||||
"Sign in with Google": "Google로 로그인",
|
||||
"Sign in with Apple": "Apple로 로그인",
|
||||
"Sign in with GitHub": "GitHub로 로그인",
|
||||
"Account": "계정",
|
||||
"Cloud Storage": "클라우드 스토리지",
|
||||
"Failed to delete user. Please try again later.": "사용자 삭제에 실패했습니다. 나중에 다시 시도해 주세요.",
|
||||
"Unlimited Offline/Online Reading": "무제한 오프라인/온라인 읽기",
|
||||
"Unlimited Cloud Sync Devices": "무제한 클라우드 동기화 기기",
|
||||
"Essential Text-to-Speech Voices": "필수 텍스트 음성 변환 목소리",
|
||||
"DeepL Free Access": "DeepL 무료 접근",
|
||||
"Community Support": "커뮤니티 지원",
|
||||
"500 MB Cloud Sync Space": "500 MB 클라우드 동기화 공간",
|
||||
"Includes All Free Tier Benefits": "무료 등급의 모든 혜택 포함",
|
||||
"AI Summaries": "AI 요약",
|
||||
"AI Translations": "AI 번역",
|
||||
"Priority Support": "우선 지원",
|
||||
"DeepL Pro Access": "DeepL 프로 접근",
|
||||
"Expanded Text-to-Speech Voices": "확장된 텍스트 음성 변환 목소리",
|
||||
"2000 MB Cloud Sync Space": "2000 MB 클라우드 동기화 공간",
|
||||
"Includes All Plus Tier Benefits": "플러스 등급의 모든 혜택 포함",
|
||||
"Unlimited AI Summaries": "무제한 AI 요약",
|
||||
"Unlimited AI Translations": "무제한 AI 번역",
|
||||
"Unlimited DeepL Pro Access": "무제한 DeepL 프로 접근",
|
||||
"Advanced AI Tools": "고급 AI 도구",
|
||||
"Early Feature Access": "신기능 조기 접근",
|
||||
"10 GB Cloud Sync Space": "10 GB 클라우드 동기화 공간",
|
||||
"Loading profile...": "프로필 로딩 중...",
|
||||
"Features": "기능",
|
||||
"Delete Account": "계정 삭제",
|
||||
"Delete Your Account?": "계정을 삭제하시겠습니까?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "이 작업은 취소할 수 없습니다. 클라우드에 있는 모든 데이터가 영구적으로 삭제됩니다.",
|
||||
"Delete Permanently": "영구 삭제",
|
||||
"Free Tier": "무료 등급",
|
||||
"Plus Tier": "플러스 등급",
|
||||
"Pro Tier": "프로 등급",
|
||||
"RTL Direction": "RTL 방향",
|
||||
"Maximum Column Height": "최대 열 높이",
|
||||
"Maximum Column Width": "최대 열 너비",
|
||||
@@ -229,7 +190,7 @@
|
||||
"No supported files found. Supported formats: {{formats}}": "지원되는 파일이 없습니다. 지원되는 형식: {{formats}}",
|
||||
"Drop to Import Books": "책 가져오기 위해 드롭",
|
||||
"Custom": "사용자 정의",
|
||||
"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": "세상은 무대이며,\n그리고 모든 남자와 여자는 단지 배우일 뿐입니다;\n그들은 나가는 곳과 들어오는 곳이 있으며,\n그리고 한 남자는 그의 시간에 많은 역할을 합니다,\n그의 행동은 일곱 시대입니다.\n\n— 윌리엄 셰익스피어",
|
||||
"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": "세상은 무대이며,\n그리고 모든 남자와 여자는 단지 배우일 뿐입니다;\n그들은 나가는 곳과 들어오는 곳이 있으며,\n그리고 한 남자는 그의 시간에 많은 역할을 합니다,\n그의 행동은 일곱 시대입니다.\n\n— 윌리엄 셰익스피어",
|
||||
"Custom Theme": "사용자 정의 테마",
|
||||
"Theme Name": "테마 이름",
|
||||
"Text Color": "텍스트 색상",
|
||||
@@ -256,9 +217,9 @@
|
||||
"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.": "이 책 형식은 지원되지 않습니다.",
|
||||
"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": "항상 위에",
|
||||
@@ -271,13 +232,13 @@
|
||||
"Clear Search": "검색 지우기",
|
||||
"Header & Footer": "헤더 및 푸터",
|
||||
"Apply also in Scrolled Mode": "스크롤 모드에서도 적용",
|
||||
"A new version of Readest is Available!": "Readest의 새 버전이 있습니다!",
|
||||
"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:": "변경 로그:",
|
||||
"Changelog": "변경 로그",
|
||||
"Software Update": "소프트웨어 업데이트",
|
||||
"Title": "제목",
|
||||
"Date Read": "읽은 날짜",
|
||||
@@ -286,18 +247,15 @@
|
||||
"Ascending": "오름차순",
|
||||
"Descending": "내림차순",
|
||||
"Sort by...": "정렬 기준...",
|
||||
"Added:": "추가됨:",
|
||||
"Format:": "형식:",
|
||||
"Added": "추가됨",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "설명:",
|
||||
"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": "세로",
|
||||
@@ -306,6 +264,788 @@
|
||||
"Checking for updates...": "업데이트 확인 중...",
|
||||
"Error checking for updates": "업데이트 확인 중 오류 발생",
|
||||
"Details": "세부정보",
|
||||
"Cancel Selection": "선택 취소",
|
||||
"File Size:": "파일 크기:"
|
||||
"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": "겹치는 픽셀",
|
||||
"System Language": "시스템 언어",
|
||||
"Security": "보안",
|
||||
"Allow JavaScript": "JavaScript 허용",
|
||||
"Enable only if you trust the file.": "파일을 신뢰하는 경우에만 활성화하세요.",
|
||||
"Sort TOC by Page": "목차를 페이지별로 정렬",
|
||||
"Search in {{count}} Book(s)..._other": "{{count}}권의 책에서 검색...",
|
||||
"No notes match your search": "일치하는 노트가 없습니다",
|
||||
"Search notes and excerpts...": "노트 검색...",
|
||||
"Sign in to Sync": "동기화를 위해 로그인하세요",
|
||||
"Synced at {{time}}": "{{time}}에 동기화됨",
|
||||
"Never synced": "동기화된 적 없음",
|
||||
"Show Remaining Time": "남은 시간 표시",
|
||||
"{{time}} min left in chapter": "{{time}}분 남음",
|
||||
"Override Book Color": "책 색상 덮어쓰기",
|
||||
"Login Required": "로그인 필요",
|
||||
"Quota Exceeded": "한도 초과",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "일일 번역 문자 사용량 {{percentage}}%.",
|
||||
"Translation Characters": "번역 문자",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}}개의 음성",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "문제가 발생했습니다. 걱정하지 마세요, 저희 팀이 이미 알림을 받았으며 해결 작업을 진행 중입니다.",
|
||||
"Error Details:": "오류 세부사항:",
|
||||
"Try Again": "다시 시도",
|
||||
"Need help?": "도움이 필요하신가요?",
|
||||
"Contact Support": "지원팀에 문의",
|
||||
"Code Highlighting": "코드 하이라이팅",
|
||||
"Enable Highlighting": "하이라이팅 활성화",
|
||||
"Code Language": "코드 언어",
|
||||
"Top Margin (px)": "위쪽 여백 (px)",
|
||||
"Bottom Margin (px)": "아래쪽 여백 (px)",
|
||||
"Right Margin (px)": "오른쪽 여백 (px)",
|
||||
"Left Margin (px)": "왼쪽 여백 (px)",
|
||||
"Column Gap (%)": "열 간격 (%)",
|
||||
"Always Show Status Bar": "상태 표시줄 항상 표시",
|
||||
"Custom Content CSS": "콘텐츠 CSS",
|
||||
"Enter CSS for book content styling...": "도서 콘텐츠 스타일을 위한 CSS 입력...",
|
||||
"Custom Reader UI CSS": "리더 UI CSS",
|
||||
"Enter CSS for reader interface styling...": "리더 인터페이스 스타일용 CSS 입력...",
|
||||
"Crop": "자르기",
|
||||
"Book Covers": "책 표지",
|
||||
"Fit": "맞춤",
|
||||
"Reset {{settings}}": "{{settings}} 재설정",
|
||||
"Reset Settings": "설정 재설정",
|
||||
"{{count}} pages left in chapter_other": "<1>남은 페이지 </1><0>{{count}}</0><1>장</1>",
|
||||
"Show Remaining Pages": "남은 페이지 보기",
|
||||
"Source Han Serif CN": "Source Han Serif",
|
||||
"Huiwen-MinchoGBK": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "구독 관리",
|
||||
"Coming Soon": "곧 출시 예정",
|
||||
"Upgrade to {{plan}}": "{{plan}}로 업그레이드",
|
||||
"Upgrade to Plus or Pro": "Plus 또는 Pro로 업그레이드",
|
||||
"Current Plan": "현재 요금제",
|
||||
"Plan Limits": "요금제 한도",
|
||||
"Processing your payment...": "결제를 처리 중입니다...",
|
||||
"Please wait while we confirm your subscription.": "구독을 확인하는 중입니다. 잠시만 기다려주세요.",
|
||||
"Payment Processing": "결제 처리 중",
|
||||
"Your payment is being processed. This usually takes a few moments.": "결제가 처리되고 있습니다. 잠시만 기다려주세요.",
|
||||
"Payment Failed": "결제 실패",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "구독을 처리할 수 없습니다. 다시 시도하거나 문제가 계속되면 지원팀에 문의해주세요.",
|
||||
"Back to Profile": "프로필로 돌아가기",
|
||||
"Subscription Successful!": "구독이 완료되었습니다!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "구독해주셔서 감사합니다! 결제가 성공적으로 처리되었습니다.",
|
||||
"Email:": "이메일:",
|
||||
"Plan:": "요금제:",
|
||||
"Amount:": "금액:",
|
||||
"Go to Library": "라이브러리로 이동",
|
||||
"Need help? Contact our support team at support@readest.com": "도움이 필요하시면 support@readest.com으로 문의해주세요.",
|
||||
"Free Plan": "무료 요금제",
|
||||
"month": "월",
|
||||
"AI Translations (per day)": "AI 번역 (일 기준)",
|
||||
"Plus Plan": "Plus 요금제",
|
||||
"Includes All Free Plan Benefits": "무료 요금제의 모든 혜택 포함",
|
||||
"Pro Plan": "Pro 요금제",
|
||||
"More AI Translations": "더 많은 AI 번역",
|
||||
"Complete Your Subscription": "구독 완료",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}%의 클라우드 동기화 공간 사용됨.",
|
||||
"Cloud Sync Storage": "클라우드 저장 공간",
|
||||
"Disable": "비활성화",
|
||||
"Enable": "활성화",
|
||||
"Upgrade to Readest Premium": "Readest Premium으로 업그레이드",
|
||||
"Show Source Text": "원본 텍스트 표시",
|
||||
"Cross-Platform Sync": "크로스 플랫폼 동기화",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "라이브러리와 진행 상황을 모든 기기에서 동기화합니다.",
|
||||
"Customizable Reading": "맞춤형 읽기",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "폰트, 레이아웃, 테마를 자유롭게 조정하세요.",
|
||||
"AI Read Aloud": "AI 음성 읽기",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "자연스러운 AI 음성으로 책을 들어보세요.",
|
||||
"AI Translations": "AI 번역",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Google, Azure, DeepL로 즉시 번역합니다.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "커뮤니티에서 도움과 소통을 즐기세요.",
|
||||
"Unlimited AI Read Aloud Hours": "무제한 AI 음성 읽기",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "제한 없이 텍스트를 음성으로 변환하세요.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "고급 번역 기능을 더 많이 이용하세요.",
|
||||
"DeepL Pro Access": "DeepL Pro 이용",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "우선 지원을 이용하세요.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "일일 번역 한도에 도달했습니다. 업그레이드하여 계속 사용하세요.",
|
||||
"Includes All Plus Plan Benefits": "모든 플러스 플랜 혜택 포함",
|
||||
"Early Feature Access": "신기능 우선 액세스",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "새로운 기능, 업데이트, 혁신을 누구보다 먼저 체험하세요.",
|
||||
"Advanced AI Tools": "고급 AI 도구",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "스마트한 독서, 번역, 콘텐츠 발견을 위한 강력한 AI 도구를 활용하세요.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "가장 정확한 번역 엔진으로 매일 최대 10만 자를 번역하세요.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "가장 정확한 번역 엔진으로 매일 최대 50만 자를 번역하세요.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "최대 5GB의 안전한 클라우드 저장소로 전체 독서 컬렉션을 저장하고 액세스하세요.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "최대 20GB의 안전한 클라우드 저장소로 전체 독서 컬렉션을 저장하고 액세스하세요.",
|
||||
"Deleted cloud backup of the book: {{title}}": "책의 클라우드 백업이 삭제됨: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "선택한 책의 클라우드 백업을 삭제하시겠습니까?",
|
||||
"What's New in Readest": "Readest의 새로운 기능",
|
||||
"Enter book title": "도서 제목 입력",
|
||||
"Subtitle": "부제목",
|
||||
"Enter book subtitle": "도서 부제목 입력",
|
||||
"Enter author name": "저자명 입력",
|
||||
"Series": "시리즈",
|
||||
"Enter series name": "시리즈명 입력",
|
||||
"Series Index": "시리즈 번호",
|
||||
"Enter series index": "시리즈 번호 입력",
|
||||
"Total in Series": "시리즈 총권수",
|
||||
"Enter total books in series": "시리즈 총 도서 수 입력",
|
||||
"Enter publisher": "출판사 입력",
|
||||
"Publication Date": "출간일",
|
||||
"Identifier": "식별자",
|
||||
"Enter book description": "도서 설명 입력",
|
||||
"Change cover image": "표지 이미지 변경",
|
||||
"Replace": "교체",
|
||||
"Unlock cover": "표지 잠금 해제",
|
||||
"Lock cover": "표지 잠금",
|
||||
"Auto-Retrieve Metadata": "메타데이터 자동 검색",
|
||||
"Auto-Retrieve": "자동 검색",
|
||||
"Unlock all fields": "모든 필드 잠금 해제",
|
||||
"Unlock All": "모두 잠금 해제",
|
||||
"Lock all fields": "모든 필드 잠금",
|
||||
"Lock All": "모두 잠금",
|
||||
"Reset": "초기화",
|
||||
"Edit Metadata": "메타데이터 편집",
|
||||
"Locked": "잠김",
|
||||
"Select Metadata Source": "메타데이터 소스 선택",
|
||||
"Keep manual input": "수동 입력 유지",
|
||||
"Google Books": "구글 북스",
|
||||
"Open Library": "오픈 라이브러리",
|
||||
"Fiction, Science, History": "소설, 과학, 역사",
|
||||
"Open Book in New Window": "새 창에서 책 열기",
|
||||
"Voices for {{lang}}": "{{lang}}의 음성",
|
||||
"Yandex Translate": "Yandex 번역",
|
||||
"YYYY or YYYY-MM-DD": "YYYY 또는 YYYY-MM-DD",
|
||||
"Restore Purchase": "구매 복원",
|
||||
"No purchases found to restore.": "복원할 구매가 없습니다.",
|
||||
"Failed to restore purchases.": "구매 복원에 실패했습니다.",
|
||||
"Failed to manage subscription.": "구독 관리에 실패했습니다.",
|
||||
"Failed to load subscription plans.": "구독 플랜 로드에 실패했습니다.",
|
||||
"year": "년",
|
||||
"Failed to create checkout session": "체크아웃 세션 생성 실패",
|
||||
"Storage": "저장소",
|
||||
"Terms of Service": "서비스 약관",
|
||||
"Privacy Policy": "개인정보 보호정책",
|
||||
"Disable Double Click": "더블 클릭 비활성화",
|
||||
"TTS not supported for this document": "TTS가 이 문서에서 지원되지 않습니다",
|
||||
"Reset Password": "비밀번호 재설정",
|
||||
"Show Reading Progress": "읽기 진행 상황 표시",
|
||||
"Reading Progress Style": "읽기 진행 상황 스타일",
|
||||
"Page Number": "페이지 번호",
|
||||
"Percentage": "백분율",
|
||||
"Deleted local copy of the book: {{title}}": "책의 로컬 사본이 삭제됨: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "책의 클라우드 백업 삭제에 실패했습니다: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "책의 로컬 사본 삭제에 실패했습니다: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "선택한 책의 로컬 사본을 삭제하시겠습니까?",
|
||||
"Remove from Cloud & Device": "클라우드 및 장치에서 제거",
|
||||
"Remove from Cloud Only": "클라우드에서만 제거",
|
||||
"Remove from Device Only": "장치에서만 제거",
|
||||
"Disconnected": "연결 끊김",
|
||||
"KOReader Sync Settings": "KOReader Sync 설정",
|
||||
"Sync Strategy": "동기화 전략",
|
||||
"Ask on conflict": "충돌 시 묻기",
|
||||
"Always use latest": "항상 최신 버전 사용",
|
||||
"Send changes only": "변경 사항만 전송",
|
||||
"Receive changes only": "변경 사항만 수신",
|
||||
"Checksum Method": "체크섬 방법",
|
||||
"File Content (recommended)": "파일 내용 (권장)",
|
||||
"File Name": "파일 이름",
|
||||
"Device Name": "장치 이름",
|
||||
"Connect to your KOReader Sync server.": "KOReader Sync 서버에 연결합니다.",
|
||||
"Server URL": "서버 URL",
|
||||
"Username": "사용자 이름",
|
||||
"Your Username": "당신의 사용자 이름",
|
||||
"Password": "비밀번호",
|
||||
"Connect": "연결",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "동기화 충돌",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": " \"{{deviceName}}\"에서 읽기 진행 상황을 동기화하시겠습니까?",
|
||||
"another device": "다른 장치",
|
||||
"Local Progress": "로컬 진행 상황",
|
||||
"Remote Progress": "원격 진행 상황",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "페이지 {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Current position": "현재 위치",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "대략 페이지 {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "대략 {{percentage}}%",
|
||||
"Failed to connect": "연결 실패",
|
||||
"Sync Server Connected": "동기화 서버에 연결됨",
|
||||
"Sync as {{userDisplayName}}": "{{userDisplayName}}로 동기화",
|
||||
"Custom Fonts": "사용자 정의 글꼴",
|
||||
"Cancel Delete": "삭제 취소",
|
||||
"Import Font": "글꼴 가져오기",
|
||||
"Delete Font": "글꼴 삭제",
|
||||
"Tips": "팁",
|
||||
"Custom fonts can be selected from the Font Face menu": "사용자 정의 글꼴은 글꼴 메뉴에서 선택할 수 있습니다",
|
||||
"Manage Custom Fonts": "사용자 정의 글꼴 관리",
|
||||
"Select Files": "파일 선택",
|
||||
"Select Image": "이미지 선택",
|
||||
"Select Video": "동영상 선택",
|
||||
"Select Audio": "오디오 선택",
|
||||
"Select Fonts": "글꼴 선택",
|
||||
"Supported font formats: .ttf, .otf, .woff, .woff2": "지원되는 글꼴 형식: .ttf, .otf, .woff, .woff2",
|
||||
"Push Progress": "진행 상황 푸시",
|
||||
"Pull Progress": "진행 상황 끌어오기",
|
||||
"Previous Paragraph": "이전 단락",
|
||||
"Previous Sentence": "이전 문장",
|
||||
"Pause": "일시정지",
|
||||
"Play": "재생",
|
||||
"Next Sentence": "다음 문장",
|
||||
"Next Paragraph": "다음 단락",
|
||||
"Separate Cover Page": "별도의 표지 페이지",
|
||||
"Resize Notebook": "노트북 크기 조정",
|
||||
"Resize Sidebar": "사이드바 크기 조정",
|
||||
"Get Help from the Readest Community": "Readest 커뮤니티에서 도움 받기",
|
||||
"Remove cover image": "표지 이미지 제거",
|
||||
"Bookshelf": "책장",
|
||||
"View Menu": "메뉴 보기",
|
||||
"Settings Menu": "설정 메뉴",
|
||||
"View account details and quota": "계정 세부정보 및 할당량 보기",
|
||||
"Library Header": "라이브러리 헤더",
|
||||
"Book Content": "도서 내용",
|
||||
"Footer Bar": "푸터 바",
|
||||
"Header Bar": "헤더 바",
|
||||
"View Options": "보기 옵션",
|
||||
"Book Menu": "도서 메뉴",
|
||||
"Search Options": "검색 옵션",
|
||||
"Close": "닫기",
|
||||
"Delete Book Options": "도서 삭제 옵션",
|
||||
"ON": "켜기",
|
||||
"OFF": "끄기",
|
||||
"Reading Progress": "읽기 진행 상황",
|
||||
"Page Margin": "페이지 여백",
|
||||
"Remove Bookmark": "북마크 제거",
|
||||
"Add Bookmark": "북마크 추가",
|
||||
"Books Content": "책 내용",
|
||||
"Jump to Location": "위치로 이동",
|
||||
"Unpin Notebook": "노트북 고정 해제",
|
||||
"Pin Notebook": "노트북 고정",
|
||||
"Hide Search Bar": "검색 바 숨기기",
|
||||
"Show Search Bar": "검색 바 표시",
|
||||
"On {{current}} of {{total}} page": "페이지 {{current}} / {{total}}",
|
||||
"Section Title": "섹션 제목",
|
||||
"Decrease": "감소",
|
||||
"Increase": "증가",
|
||||
"Settings Panels": "설정 패널",
|
||||
"Settings": "설정",
|
||||
"Unpin Sidebar": "사이드바 고정 해제",
|
||||
"Pin Sidebar": "사이드바 고정",
|
||||
"Toggle Sidebar": "사이드바 전환",
|
||||
"Toggle Translation": "번역 전환",
|
||||
"Translation Disabled": "번역 비활성화",
|
||||
"Minimize": "최소화",
|
||||
"Maximize or Restore": "최대화 또는 복원",
|
||||
"Exit Parallel Read": "병렬 읽기 종료",
|
||||
"Enter Parallel Read": "병렬 읽기 시작",
|
||||
"Zoom Level": "확대/축소 수준",
|
||||
"Zoom Out": "축소",
|
||||
"Reset Zoom": "줌 재설정",
|
||||
"Zoom In": "확대",
|
||||
"Zoom Mode": "줌 모드",
|
||||
"Single Page": "단일 페이지",
|
||||
"Auto Spread": "자동 스프레드",
|
||||
"Fit Page": "페이지에 맞춤",
|
||||
"Fit Width": "너비에 맞춤",
|
||||
"Failed to select directory": "디렉토리 선택 실패",
|
||||
"The new data directory must be different from the current one.": "새 데이터 디렉토리는 현재 디렉토리와 달라야 합니다.",
|
||||
"Migration failed: {{error}}": "마이그레이션 실패: {{error}}",
|
||||
"Change Data Location": "데이터 위치 변경",
|
||||
"Current Data Location": "현재 데이터 위치",
|
||||
"Total size: {{size}}": "총 크기: {{size}}",
|
||||
"Calculating file info...": "파일 정보 계산 중...",
|
||||
"New Data Location": "새 데이터 위치",
|
||||
"Choose Different Folder": "다른 폴더 선택",
|
||||
"Choose New Folder": "새 폴더 선택",
|
||||
"Migrating data...": "데이터 마이그레이션 중...",
|
||||
"Copying: {{file}}": "복사 중: {{file}}",
|
||||
"{{current}} of {{total}} files": "{{current}} / {{total}} 파일",
|
||||
"Migration completed successfully!": "마이그레이션이 성공적으로 완료되었습니다!",
|
||||
"Your data has been moved to the new location. Please restart the application to complete the process.": "데이터가 새 위치로 이동되었습니다. 프로세스를 완료하려면 애플리케이션을 재시작하십시오.",
|
||||
"Migration failed": "마이그레이션 실패",
|
||||
"Important Notice": "중요한 공지",
|
||||
"This will move all your app data to the new location. Make sure the destination has enough free space.": "이 작업은 모든 앱 데이터를 새 위치로 이동합니다. 대상에 충분한 여유 공간이 있는지 확인하십시오.",
|
||||
"Restart App": "앱 재시작",
|
||||
"Start Migration": "마이그레이션 시작",
|
||||
"Advanced Settings": "고급 설정",
|
||||
"File count: {{size}}": "파일 수: {{size}}",
|
||||
"Background Read Aloud": "백그라운드 음성 읽기",
|
||||
"Ready to read aloud": "읽기 준비 완료",
|
||||
"Read Aloud": "음성 읽기",
|
||||
"Screen Brightness": "화면 밝기",
|
||||
"Background Image": "배경 이미지",
|
||||
"Import Image": "이미지 가져오기",
|
||||
"Opacity": "불투명도",
|
||||
"Size": "크기",
|
||||
"Cover": "표지",
|
||||
"Contain": "포함",
|
||||
"{{number}} pages left in chapter": "<1>남은 페이지 </1><0>{{number}}</0><1>장</1>",
|
||||
"Device": "장치",
|
||||
"E-Ink Mode": "E-Ink 모드",
|
||||
"Highlight Colors": "하이라이트 색상",
|
||||
"Pagination": "페이지 매김",
|
||||
"Disable Double Tap": "더블 탭 비활성화",
|
||||
"Tap to Paginate": "탭하여 페이지 매김",
|
||||
"Click to Paginate": "클릭하여 페이지 매김",
|
||||
"Tap Both Sides": "양쪽을 탭",
|
||||
"Click Both Sides": "양쪽을 클릭",
|
||||
"Swap Tap Sides": "탭 사이드 전환",
|
||||
"Swap Click Sides": "클릭 사이드 전환",
|
||||
"Source and Translated": "원본 및 번역",
|
||||
"Translated Only": "번역만",
|
||||
"Source Only": "원본만",
|
||||
"TTS Text": "TTS 텍스트",
|
||||
"The book file is corrupted": "책 파일이 손상되었습니다",
|
||||
"The book file is empty": "책 파일이 비어 있습니다",
|
||||
"Failed to open the book file": "책 파일을 열지 못했습니다",
|
||||
"On-Demand Purchase": "온디맨드 구매",
|
||||
"Full Customization": "풀 커스터마이징",
|
||||
"Lifetime Plan": "평생 플랜",
|
||||
"One-Time Payment": "일회성 결제",
|
||||
"Make a single payment to enjoy lifetime access to specific features on all devices. Purchase specific features or services only when you need them.": "모든 장치에서 특정 기능에 대한 평생 액세스를 즐기기 위해 단일 결제를 수행합니다. 필요할 때만 특정 기능이나 서비스를 구매합니다.",
|
||||
"Expand Cloud Sync Storage": "클라우드 동기화 저장소 확장",
|
||||
"Expand your cloud storage forever with a one-time purchase. Each additional purchase adds more space.": "1회 구매로 클라우드 저장소를 영구적으로 확장합니다. 추가 구매 시 더 많은 공간이 추가됩니다.",
|
||||
"Unlock All Customization Options": "모든 커스터마이징 옵션 잠금 해제",
|
||||
"Unlock additional themes, fonts, layout options and read aloud, translators, cloud storage services.": "추가 테마, 글꼴, 레이아웃 옵션 및 읽기, 번역기, 클라우드 스토리지 서비스를 잠금 해제합니다.",
|
||||
"Purchase Successful!": "구매 성공!",
|
||||
"lifetime": "평생",
|
||||
"Thank you for your purchase! Your payment has been processed successfully.": "구매해주셔서 감사합니다! 결제가 성공적으로 처리되었습니다.",
|
||||
"Order ID:": "주문 ID:",
|
||||
"TTS Highlighting": "TTS 하이라이팅",
|
||||
"Style": "스타일",
|
||||
"Underline": "밑줄",
|
||||
"Strikethrough": "취소선",
|
||||
"Squiggly": "물결선",
|
||||
"Outline": "윤곽선",
|
||||
"Save Current Color": "현재 색상 저장",
|
||||
"Quick Colors": "빠른 색상",
|
||||
"Highlighter": "형광펜",
|
||||
"Save Book Cover": "책 표지 저장",
|
||||
"Auto-save last book cover": "마지막 책 표지 자동 저장",
|
||||
"Back": "뒤로",
|
||||
"Confirmation email sent! Please check your old and new email addresses to confirm the change.": "확인 이메일이 전송되었습니다! 변경을 확인하려면 이전 이메일 주소와 새 이메일 주소를 확인하세요.",
|
||||
"Failed to update email": "이메일 업데이트 실패",
|
||||
"New Email": "새 이메일",
|
||||
"Your new email": "새 이메일 주소",
|
||||
"Updating email ...": "이메일 업데이트 중...",
|
||||
"Update email": "이메일 업데이트",
|
||||
"Current email": "현재 이메일",
|
||||
"Update Email": "이메일 업데이트",
|
||||
"All": "전체",
|
||||
"Unable to open book": "책을 열 수 없습니다",
|
||||
"Punctuation": "구두점",
|
||||
"Replace Quotation Marks": "인용 부호 바꾸기",
|
||||
"Enabled only in vertical layout.": "세로 레이아웃에서만 활성화됩니다.",
|
||||
"No Conversion": "변환 없음",
|
||||
"Simplified to Traditional": "간체 → 번체",
|
||||
"Traditional to Simplified": "번체 → 간체",
|
||||
"Simplified to Traditional (Taiwan)": "간체 → 번체 (대만)",
|
||||
"Simplified to Traditional (Hong Kong)": "간체 → 번체 (홍콩)",
|
||||
"Simplified to Traditional (Taiwan), with phrases": "간체 → 번체 (대만) • 문구",
|
||||
"Traditional (Taiwan) to Simplified": "번체 (대만) → 간체",
|
||||
"Traditional (Hong Kong) to Simplified": "번체 (홍콩) → 간체",
|
||||
"Traditional (Taiwan) to Simplified, with phrases": "번체 (대만) → 간체 • 문구",
|
||||
"Convert Simplified and Traditional Chinese": "간체/번체 변환",
|
||||
"Convert Mode": "변환 모드",
|
||||
"Failed to auto-save book cover for lock screen: {{error}}": "잠금 화면용 책 표지 자동 저장 실패: {{error}}",
|
||||
"Download from Cloud": "클라우드에서 다운로드",
|
||||
"Upload to Cloud": "클라우드에 업로드",
|
||||
"Clear Custom Fonts": "사용자 정의 글꼴 지우기",
|
||||
"Columns": "열",
|
||||
"OPDS Catalogs": "OPDS 카탈로그",
|
||||
"Adding LAN addresses is not supported in the web app version.": "웹 앱 버전에서는 LAN 주소 추가를 지원하지 않습니다.",
|
||||
"Invalid OPDS catalog. Please check the URL.": "잘못된 OPDS 카탈로그입니다. URL을 확인해주세요.",
|
||||
"Browse and download books from online catalogs": "온라인 카탈로그에서 책을 탐색하고 다운로드",
|
||||
"My Catalogs": "내 카탈로그",
|
||||
"Add Catalog": "카탈로그 추가",
|
||||
"No catalogs yet": "아직 카탈로그가 없습니다",
|
||||
"Add your first OPDS catalog to start browsing books": "책을 탐색하려면 첫 번째 OPDS 카탈로그를 추가하세요.",
|
||||
"Add Your First Catalog": "첫 번째 카탈로그 추가",
|
||||
"Browse": "탐색",
|
||||
"Popular Catalogs": "인기 카탈로그",
|
||||
"Add": "추가",
|
||||
"Add OPDS Catalog": "OPDS 카탈로그 추가",
|
||||
"Catalog Name": "카탈로그 이름",
|
||||
"My Calibre Library": "내 Calibre 라이브러리",
|
||||
"OPDS URL": "OPDS URL",
|
||||
"Username (optional)": "사용자 이름 (선택 사항)",
|
||||
"Password (optional)": "비밀번호 (선택 사항)",
|
||||
"Description (optional)": "설명 (선택 사항)",
|
||||
"A brief description of this catalog": "이 카탈로그의 간단한 설명",
|
||||
"Validating...": "검증 중...",
|
||||
"View All": "모두 보기",
|
||||
"Forward": "다음",
|
||||
"Home": "홈",
|
||||
"{{count}} items_other": "{{count}}개 항목",
|
||||
"Download completed": "다운로드 완료",
|
||||
"Download failed": "다운로드 실패",
|
||||
"Open Access": "오픈 액세스",
|
||||
"Borrow": "대출",
|
||||
"Buy": "구매",
|
||||
"Subscribe": "구독",
|
||||
"Sample": "샘플",
|
||||
"Download": "다운로드",
|
||||
"Open & Read": "열기 및 읽기",
|
||||
"Tags": "태그",
|
||||
"Tag": "태그",
|
||||
"First": "처음",
|
||||
"Previous": "이전",
|
||||
"Next": "다음",
|
||||
"Last": "마지막",
|
||||
"Cannot Load Page": "페이지를 불러올 수 없습니다",
|
||||
"An error occurred": "오류가 발생했습니다",
|
||||
"Online Library": "온라인 라이브러리",
|
||||
"URL must start with http:// or https://": "URL은 http:// 또는 https://로 시작해야 합니다",
|
||||
"Title, Author, Tag, etc...": "제목, 저자, 태그 등...",
|
||||
"Query": "쿼리",
|
||||
"Subject": "주제",
|
||||
"Enter {{terms}}": "{{terms}} 입력",
|
||||
"No search results found": "검색 결과가 없습니다",
|
||||
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDS 피드를 불러오지 못했습니다: {{status}} {{statusText}}",
|
||||
"Search in {{title}}": "{{title}}에서 검색",
|
||||
"Manage Storage": "저장소 관리",
|
||||
"Failed to load files": "파일 로드 실패",
|
||||
"Deleted {{count}} file(s)_other": "{{count}}개의 파일이 삭제되었습니다",
|
||||
"Failed to delete {{count}} file(s)_other": "{{count}}개의 파일 삭제 실패",
|
||||
"Failed to delete files": "파일 삭제 실패",
|
||||
"Total Files": "총 파일",
|
||||
"Total Size": "총 용량",
|
||||
"Quota": "쿼터",
|
||||
"Used": "사용됨",
|
||||
"Files": "파일",
|
||||
"Search files...": "파일 검색...",
|
||||
"Newest First": "최신순",
|
||||
"Oldest First": "오래된순",
|
||||
"Largest First": "큰 파일순",
|
||||
"Smallest First": "작은 파일순",
|
||||
"Name A-Z": "이름 A-Z",
|
||||
"Name Z-A": "이름 Z-A",
|
||||
"{{count}} selected_other": "{{count}}개 선택됨",
|
||||
"Delete Selected": "선택 삭제",
|
||||
"Created": "생성됨",
|
||||
"No files found": "파일이 없습니다",
|
||||
"No files uploaded yet": "아직 업로드된 파일이 없습니다",
|
||||
"files": "파일",
|
||||
"Page {{current}} of {{total}}": "페이지 {{current}} / {{total}}",
|
||||
"Are you sure to delete {{count}} selected file(s)?_other": "선택한 {{count}}개의 파일을 삭제하시겠습니까?",
|
||||
"Cloud Storage Usage": "클라우드 저장소 사용량",
|
||||
"Rename Group": "그룹 이름 바꾸기",
|
||||
"From Directory": "디렉토리에서",
|
||||
"Successfully imported {{count}} book(s)_other": "성공적으로 {{count}} 권의 책이 가져와졌습니다",
|
||||
"Count": "개수",
|
||||
"Start Page": "시작 페이지",
|
||||
"Search in OPDS Catalog...": "OPDS 카탈로그에서 검색...",
|
||||
"Please log in to use advanced TTS features": "고급 TTS 기능을 사용하려면 로그인하세요",
|
||||
"Word limit of 30 words exceeded.": "30단어 제한을 초과했습니다.",
|
||||
"Proofread": "교정",
|
||||
"Current selection": "현재 선택",
|
||||
"All occurrences in this book": "이 책의 모든 위치",
|
||||
"All occurrences in your library": "라이브러리의 모든 위치",
|
||||
"Selected text:": "선택한 텍스트:",
|
||||
"Replace with:": "다음으로 바꾸기:",
|
||||
"Enter text...": "텍스트 입력…",
|
||||
"Case sensitive:": "대소문자 구분:",
|
||||
"Scope:": "적용 범위:",
|
||||
"Selection": "선택",
|
||||
"Library": "라이브러리",
|
||||
"Yes": "예",
|
||||
"No": "아니요",
|
||||
"Proofread Replacement Rules": "교정 대체 규칙",
|
||||
"Selected Text Rules": "선택한 텍스트 규칙",
|
||||
"No selected text replacement rules": "선택한 텍스트에 대한 대체 규칙이 없습니다",
|
||||
"Book Specific Rules": "책별 규칙",
|
||||
"No book-level replacement rules": "책 수준의 대체 규칙이 없습니다",
|
||||
"Disable Quick Action": "빠른 작업 비활성화",
|
||||
"Enable Quick Action on Selection": "선택 시 빠른 작업 활성화",
|
||||
"None": "없음",
|
||||
"Annotation Tools": "주석 도구",
|
||||
"Enable Quick Actions": "빠른 작업 활성화",
|
||||
"Quick Action": "빠른 작업",
|
||||
"Copy to Notebook": "노트에 복사",
|
||||
"Copy text after selection": "선택 후 텍스트 복사",
|
||||
"Highlight text after selection": "선택 후 텍스트 강조",
|
||||
"Annotate text after selection": "선택 후 텍스트 주석 달기",
|
||||
"Search text after selection": "선택 후 텍스트 검색",
|
||||
"Look up text in dictionary after selection": "선택 후 텍스트를 사전에서 찾기",
|
||||
"Look up text in Wikipedia after selection": "선택 후 텍스트를 위키피디아에서 찾기",
|
||||
"Translate text after selection": "선택 후 텍스트 번역",
|
||||
"Read text aloud after selection": "선택 후 텍스트 음성 읽기",
|
||||
"Proofread text after selection": "선택 후 텍스트 교정",
|
||||
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} 진행 중, {{pendingCount}} 대기 중",
|
||||
"{{failedCount}} failed": "{{failedCount}} 실패",
|
||||
"Waiting...": "대기 중...",
|
||||
"Failed": "실패",
|
||||
"Completed": "완료됨",
|
||||
"Cancelled": "취소됨",
|
||||
"Retry": "재시도",
|
||||
"Active": "진행 중",
|
||||
"Transfer Queue": "전송 대기열",
|
||||
"Upload All": "모두 업로드",
|
||||
"Download All": "모두 다운로드",
|
||||
"Resume Transfers": "전송 재개",
|
||||
"Pause Transfers": "전송 일시정지",
|
||||
"Pending": "대기 중",
|
||||
"No transfers": "전송 없음",
|
||||
"Retry All": "모두 재시도",
|
||||
"Clear Completed": "완료된 항목 지우기",
|
||||
"Clear Failed": "실패한 항목 지우기",
|
||||
"Upload queued: {{title}}": "업로드 대기열에 추가됨: {{title}}",
|
||||
"Download queued: {{title}}": "다운로드 대기열에 추가됨: {{title}}",
|
||||
"Book not found in library": "라이브러리에서 책을 찾을 수 없음",
|
||||
"Unknown error": "알 수 없는 오류",
|
||||
"Please log in to continue": "계속하려면 로그인하세요",
|
||||
"Cloud File Transfers": "클라우드 파일 전송",
|
||||
"Show Search Results": "검색 결과 보기",
|
||||
"Search results for '{{term}}'": "'{{term}}' 검색 결과",
|
||||
"Close Search": "검색 닫기",
|
||||
"Previous Result": "이전 결과",
|
||||
"Next Result": "다음 결과",
|
||||
"Bookmarks": "북마크",
|
||||
"Annotations": "주석",
|
||||
"Show Results": "결과 표시",
|
||||
"Clear search": "검색 지우기",
|
||||
"Clear search history": "검색 기록 지우기",
|
||||
"Tap to Toggle Footer": "탭하여 바닥글 전환",
|
||||
"Exported successfully": "내보내기 성공",
|
||||
"Book exported successfully.": "책을 성공적으로 내보냈습니다.",
|
||||
"Failed to export the book.": "책 내보내기에 실패했습니다.",
|
||||
"Export Book": "책 내보내기",
|
||||
"Whole word:": "전체 단어:",
|
||||
"Error": "오류",
|
||||
"Unable to load the article. Try searching directly on {{link}}.": "문서를 로드할 수 없습니다. {{link}}에서 직접 검색해 보세요.",
|
||||
"Unable to load the word. Try searching directly on {{link}}.": "단어를 로드할 수 없습니다. {{link}}에서 직접 검색해 보세요.",
|
||||
"Date Published": "출판일",
|
||||
"Only for TTS:": "TTS 전용:",
|
||||
"Uploaded": "업로드됨",
|
||||
"Downloaded": "다운로드됨",
|
||||
"Deleted": "삭제됨",
|
||||
"Note:": "노트:",
|
||||
"Time:": "시간:",
|
||||
"Format Options": "형식 옵션",
|
||||
"Export Date": "내보내기 날짜",
|
||||
"Chapter Titles": "챕터 제목",
|
||||
"Chapter Separator": "챕터 구분 기호",
|
||||
"Highlights": "하이라이트",
|
||||
"Note Date": "노트 날짜",
|
||||
"Advanced": "고급",
|
||||
"Hide": "숨기기",
|
||||
"Show": "표시",
|
||||
"Use Custom Template": "사용자 지정 템플릿 사용",
|
||||
"Export Template": "내보내기 템플릿",
|
||||
"Template Syntax:": "템플릿 구문:",
|
||||
"Insert value": "값 삽입",
|
||||
"Format date (locale)": "날짜 형식 (로케일)",
|
||||
"Format date (custom)": "날짜 형식 (사용자 지정)",
|
||||
"Conditional": "조건부",
|
||||
"Loop": "반복",
|
||||
"Available Variables:": "사용 가능한 변수:",
|
||||
"Book title": "책 제목",
|
||||
"Book author": "책 저자",
|
||||
"Export date": "내보내기 날짜",
|
||||
"Array of chapters": "챕터 배열",
|
||||
"Chapter title": "챕터 제목",
|
||||
"Array of annotations": "주석 배열",
|
||||
"Highlighted text": "하이라이트된 텍스트",
|
||||
"Annotation note": "주석 노트",
|
||||
"Date Format Tokens:": "날짜 형식 토큰:",
|
||||
"Year (4 digits)": "연도 (4자리)",
|
||||
"Month (01-12)": "월 (01-12)",
|
||||
"Day (01-31)": "일 (01-31)",
|
||||
"Hour (00-23)": "시 (00-23)",
|
||||
"Minute (00-59)": "분 (00-59)",
|
||||
"Second (00-59)": "초 (00-59)",
|
||||
"Show Source": "소스 표시",
|
||||
"No content to preview": "미리 볼 내용 없음",
|
||||
"Export": "내보내기",
|
||||
"Set Timeout": "시간 제한 설정",
|
||||
"Select Voice": "음성 선택",
|
||||
"Toggle Sticky Bottom TTS Bar": "고정 TTS 바 전환",
|
||||
"Display what I'm reading on Discord": "Discord에 읽는 책 표시",
|
||||
"Show on Discord": "Discord에 표시",
|
||||
"Instant {{action}}": "즉시 {{action}}",
|
||||
"Instant {{action}} Disabled": "즉시 {{action}} 비활성화",
|
||||
"Annotation": "주석",
|
||||
"Reset Template": "템플릿 초기화",
|
||||
"Annotation style": "주석 스타일",
|
||||
"Annotation color": "주석 색상",
|
||||
"Annotation time": "주석 시간",
|
||||
"AI": "AI",
|
||||
"Are you sure you want to re-index this book?": "이 책을 다시 인덱싱하시겠습니까?",
|
||||
"Enable AI in Settings": "설정에서 AI 활성화",
|
||||
"Index This Book": "이 책 인덱싱",
|
||||
"Enable AI search and chat for this book": "이 책에 대한 AI 검색 및 채팅 활성화",
|
||||
"Start Indexing": "인덱싱 시작",
|
||||
"Indexing book...": "책 인덱싱 중...",
|
||||
"Preparing...": "준비 중...",
|
||||
"Delete this conversation?": "이 대화를 삭제하시겠습니까?",
|
||||
"No conversations yet": "아직 대화가 없습니다",
|
||||
"Start a new chat to ask questions about this book": "이 책에 대해 질문하려면 새 채팅을 시작하세요",
|
||||
"Rename": "이름 변경",
|
||||
"New Chat": "새 채팅",
|
||||
"Chat": "채팅",
|
||||
"Please enter a model ID": "모델 ID를 입력하세요",
|
||||
"Model not available or invalid": "모델을 사용할 수 없거나 유효하지 않습니다",
|
||||
"Failed to validate model": "모델 검증 실패",
|
||||
"Couldn't connect to Ollama. Is it running?": "Ollama에 연결할 수 없습니다. 실행 중인가요?",
|
||||
"Invalid API key or connection failed": "API 키가 유효하지 않거나 연결 실패",
|
||||
"Connection failed": "연결 실패",
|
||||
"AI Assistant": "AI 어시스턴트",
|
||||
"Enable AI Assistant": "AI 어시스턴트 활성화",
|
||||
"Provider": "제공자",
|
||||
"Ollama (Local)": "Ollama (로컬)",
|
||||
"AI Gateway (Cloud)": "AI 게이트웨이 (클라우드)",
|
||||
"Ollama Configuration": "Ollama 설정",
|
||||
"Refresh Models": "모델 새로고침",
|
||||
"AI Model": "AI 모델",
|
||||
"No models detected": "모델이 감지되지 않았습니다",
|
||||
"AI Gateway Configuration": "AI 게이트웨이 설정",
|
||||
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "고품질의 경제적인 AI 모델 중에서 선택하세요. 아래의 \"사용자 정의 모델\"을 선택하여 자신의 모델을 사용할 수도 있습니다.",
|
||||
"API Key": "API 키",
|
||||
"Get Key": "키 받기",
|
||||
"Model": "모델",
|
||||
"Custom Model...": "사용자 정의 모델...",
|
||||
"Custom Model ID": "사용자 정의 모델 ID",
|
||||
"Validate": "검증",
|
||||
"Model available": "모델 사용 가능",
|
||||
"Connection": "연결",
|
||||
"Test Connection": "연결 테스트",
|
||||
"Connected": "연결됨",
|
||||
"Custom Colors": "사용자 정의 색상",
|
||||
"Color E-Ink Mode": "컬러 E-Ink 모드",
|
||||
"Reading Ruler": "읽기 자",
|
||||
"Enable Reading Ruler": "읽기 자 활성화",
|
||||
"Lines to Highlight": "강조할 줄 수",
|
||||
"Ruler Color": "자 색상",
|
||||
"Command Palette": "명령 팔레트",
|
||||
"Search settings and actions...": "설정 및 작업 검색...",
|
||||
"No results found for": "에 대한 결과를 찾을 수 없음",
|
||||
"Type to search settings and actions": "입력하여 설정 및 작업 검색",
|
||||
"Recent": "최근",
|
||||
"navigate": "탐색",
|
||||
"select": "선택",
|
||||
"close": "닫기",
|
||||
"Search Settings": "설정 검색",
|
||||
"Page Margins": "페이지 여백",
|
||||
"AI Provider": "AI 제공처",
|
||||
"Ollama URL": "Ollama URL",
|
||||
"Ollama Model": "Ollama 모델",
|
||||
"AI Gateway Model": "AI 게이트웨이 모델",
|
||||
"Actions": "작업",
|
||||
"Navigation": "탐색",
|
||||
"Set status for {{count}} book(s)_other": "책 {{count}}권 상태 설정",
|
||||
"Mark as Unread": "읽지 않음으로 표시",
|
||||
"Mark as Finished": "완료로 표시",
|
||||
"Finished": "완료",
|
||||
"Unread": "읽지 않음",
|
||||
"Clear Status": "상태 지우기",
|
||||
"Status": "상태",
|
||||
"Loading": "로드 중...",
|
||||
"Exit Paragraph Mode": "단락 모드 종료",
|
||||
"Paragraph Mode": "단락 모드",
|
||||
"Embedding Model": "임베딩 모델",
|
||||
"{{count}} book(s) synced_other": "{{count}}권의 책이 동기화되었습니다",
|
||||
"Unable to start RSVP": "RSVP를 시작할 수 없습니다",
|
||||
"RSVP not supported for PDF": "PDF는 RSVP를 지원하지 않습니다",
|
||||
"Select Chapter": "챕터 선택",
|
||||
"Context": "문맥",
|
||||
"Ready": "준비됨",
|
||||
"Chapter Progress": "챕터 진행 상황",
|
||||
"words": "단어",
|
||||
"{{time}} left": "{{time}} 남음",
|
||||
"Reading progress": "독서 진행 상황",
|
||||
"Click to seek": "클릭하여 탐색",
|
||||
"Skip back 15 words": "15단어 뒤로",
|
||||
"Back 15 words (Shift+Left)": "15단어 뒤로 (Shift+왼쪽 화살표)",
|
||||
"Pause (Space)": "일시정지 (스페이스바)",
|
||||
"Play (Space)": "재생 (스페이스바)",
|
||||
"Skip forward 15 words": "15단어 앞으로",
|
||||
"Forward 15 words (Shift+Right)": "15단어 앞으로 (Shift+오른쪽 화살표)",
|
||||
"Pause:": "일시정지:",
|
||||
"Decrease speed": "속도 줄이기",
|
||||
"Slower (Left/Down)": "느리게 (왼쪽/아래 화살표)",
|
||||
"Current speed": "현재 속도",
|
||||
"Increase speed": "속도 높이기",
|
||||
"Faster (Right/Up)": "빠르게 (오른쪽/위 화살표)",
|
||||
"Start RSVP Reading": "RSVP 독서 시작",
|
||||
"Choose where to start reading": "독서를 시작할 위치 선택",
|
||||
"From Chapter Start": "챕터 시작부터",
|
||||
"Start reading from the beginning of the chapter": "챕터 처음부터 다시 시작",
|
||||
"Resume": "재개",
|
||||
"Continue from where you left off": "중단한 지점부터 계속 읽기",
|
||||
"From Current Page": "현재 페이지부터",
|
||||
"Start from where you are currently reading": "현재 읽고 있는 위치부터 시작",
|
||||
"From Selection": "선택 범위부터",
|
||||
"Speed Reading Mode": "속독 모드",
|
||||
"Scroll left": "왼쪽으로 스크롤",
|
||||
"Scroll right": "오른쪽으로 스크롤",
|
||||
"Library Sync Progress": "라이브러리 동기화 진행 상황",
|
||||
"Back to library": "라이브러리로 돌아가기",
|
||||
"Group by...": "그룹화 기준...",
|
||||
"Export as Plain Text": "일반 텍스트로 내보내기",
|
||||
"Export as Markdown": "Markdown으로 내보내기",
|
||||
"Show Page Navigation Buttons": "탐색 버튼 표시",
|
||||
"Page {{number}}": "{{number}} 페이지",
|
||||
"highlight": "하이라이트",
|
||||
"underline": "밑줄",
|
||||
"squiggly": "구불구불한 선",
|
||||
"red": "빨간색",
|
||||
"violet": "보라색",
|
||||
"blue": "파란색",
|
||||
"green": "초록색",
|
||||
"yellow": "노란색",
|
||||
"Select {{style}} style": "{{style}} 스타일 선택",
|
||||
"Select {{color}} color": "{{color}} 색상 선택",
|
||||
"Close Book": "책 닫기",
|
||||
"Speed Reading": "속독",
|
||||
"Close Speed Reading": "속독 닫기",
|
||||
"Authors": "저자",
|
||||
"Books": "도서",
|
||||
"Groups": "그룹",
|
||||
"Back to TTS Location": "TTS 위치로 돌아가기",
|
||||
"Metadata": "메타데이터",
|
||||
"Image viewer": "이미지 뷰어",
|
||||
"Previous Image": "이전 이미지",
|
||||
"Next Image": "다음 이미지",
|
||||
"Zoomed": "확대됨",
|
||||
"Zoom level": "확대 수준",
|
||||
"Table viewer": "테이블 뷰어",
|
||||
"Unable to connect to Readwise. Please check your network connection.": "Readwise에 연결할 수 없습니다. 네트워크 연결을 확인해 주세요.",
|
||||
"Invalid Readwise access token": "유효하지 않은 Readwise 액세스 토큰입니다",
|
||||
"Disconnected from Readwise": "Readwise와 연결이 끊어졌습니다",
|
||||
"Never": "안 함",
|
||||
"Readwise Settings": "Readwise 설정",
|
||||
"Connected to Readwise": "Readwise에 연결됨",
|
||||
"Last synced: {{time}}": "마지막 동기화: {{time}}",
|
||||
"Sync Enabled": "동기화 활성화됨",
|
||||
"Disconnect": "연결 해제",
|
||||
"Connect your Readwise account to sync highlights.": "하이라이트를 동기화하려면 Readwise 계정렬 연결하세요.",
|
||||
"Get your access token at": "액세스 토큰 받기:",
|
||||
"Access Token": "액세스 토큰",
|
||||
"Paste your Readwise access token": "Readwise 액세스 토큰을 붙여넣으세요",
|
||||
"Config": "구성",
|
||||
"Readwise Sync": "Readwise 동기화",
|
||||
"Push Highlights": "하이라이트 푸시",
|
||||
"Highlights synced to Readwise": "하이라이트가 Readwise에 동기화되었습니다",
|
||||
"Readwise sync failed: no internet connection": "Readwise 동기화 실패: 인터넷 연결 없음",
|
||||
"Readwise sync failed: {{error}}": "Readwise 동기화 실패: {{error}}",
|
||||
"System Screen Brightness": "시스템 화면 밝기",
|
||||
"Page:": "페이지:",
|
||||
"Page: {{number}}": "페이지: {{number}}",
|
||||
"Annotation page number": "주석 페이지 번호"
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
"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",
|
||||
"Book Cover": "Bìa sách",
|
||||
"Bookmark": "Đánh dấu",
|
||||
"Cancel": "Hủy",
|
||||
"Chapter": "Chương",
|
||||
@@ -19,7 +17,6 @@
|
||||
"Confirm Deletion": "Xác nhận xóa",
|
||||
"Copied to notebook": "Đã sao chép vào sổ ghi chú",
|
||||
"Copy": "Sao chép",
|
||||
"Custom CSS": "CSS tùy chỉnh",
|
||||
"Dark Mode": "Chế độ tối",
|
||||
"Default": "Mặc định",
|
||||
"Default Font": "Phông chữ mặc định",
|
||||
@@ -29,7 +26,6 @@
|
||||
"Dictionary": "Từ điển",
|
||||
"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...",
|
||||
"Excerpts": "Trích dẫn",
|
||||
"Failed to import book(s): {{filenames}}": "Không thể nhập sách: {{filenames}}",
|
||||
"Fast": "Nhanh",
|
||||
@@ -42,8 +38,6 @@
|
||||
"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",
|
||||
@@ -51,7 +45,6 @@
|
||||
"Horizontal Direction": "Hướng ngang",
|
||||
"Hyphenation": "Gạch nối từ",
|
||||
"Import Books": "Nhập sách",
|
||||
"Language:": "Ngôn ngữ:",
|
||||
"Layout": "Bố cục",
|
||||
"Light Mode": "Chế độ sáng",
|
||||
"Loading...": "Đang tải...",
|
||||
@@ -62,7 +55,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",
|
||||
@@ -74,8 +66,8 @@
|
||||
"Paging Animation": "Hiệu ứng lật trang",
|
||||
"Paragraph": "Đoạn văn",
|
||||
"Parallel Read": "Đọc song song",
|
||||
"Published:": "Xuất bản:",
|
||||
"Publisher:": "Nhà xuất bản:",
|
||||
"Published": "Xuất bản",
|
||||
"Publisher": "Nhà xuất bản",
|
||||
"Reading Progress Synced": "Tiến độ đọc đã được đồng bộ",
|
||||
"Reload Page": "Tải lại trang",
|
||||
"Reveal in File Explorer": "Hiển thị trong File Explorer",
|
||||
@@ -89,7 +81,6 @@
|
||||
"Search...": "Tìm kiếm...",
|
||||
"Select Book": "Chọn sách",
|
||||
"Select Books": "Chọn sách",
|
||||
"Select Multiple Books": "Chọn nhiều sách",
|
||||
"Sepia": "Nâu cổ",
|
||||
"Serif Font": "Phông chữ có chân",
|
||||
"Show Book Details": "Hiển thị chi tiết sách",
|
||||
@@ -100,16 +91,15 @@
|
||||
"Slow": "Chậm",
|
||||
"Solarized": "Solarized",
|
||||
"Speak": "Đọc",
|
||||
"Subjects:": "Chủ đề:",
|
||||
"Subjects": "Chủ đề",
|
||||
"System Fonts": "Phông chữ hệ thống",
|
||||
"TTS not supported in this device": "TTS không được hỗ trợ trên thiết bị này",
|
||||
"Theme Color": "Màu chủ đề",
|
||||
"Theme Mode": "Chế độ chủ đề",
|
||||
"Translate": "Dịch",
|
||||
"Translated Text": "Văn bản dịch",
|
||||
"Unknown": "Không xác định",
|
||||
"Untitled": "Không có tiêu đề",
|
||||
"Updated:": "Cập nhật:",
|
||||
"Updated": "Cập nhật",
|
||||
"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.",
|
||||
@@ -118,8 +108,6 @@
|
||||
"Your Library": "Thư viện của bạn",
|
||||
"TTS not supported for PDF": "TTS không được hỗ trợ cho PDF",
|
||||
"Override Book Font": "Ghi đè phông chữ sách",
|
||||
"Vertical Margins (px)": "Lề dọc (px)",
|
||||
"Horizontal Margins (%)": "Lề ngang (%)",
|
||||
"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.",
|
||||
@@ -128,14 +116,13 @@
|
||||
"Book Details": "Chi tiết sách",
|
||||
"From Local File": "Từ tệp địa phương",
|
||||
"TOC": "Mục lục",
|
||||
"Table of Contents": "Mục lục",
|
||||
"Book uploaded: {{title}}": "Sách đã tải lên: {{title}}",
|
||||
"Failed to upload book: {{title}}": "Không thể tải lên sách: {{title}}",
|
||||
"Book downloaded: {{title}}": "Sách đã tải về: {{title}}",
|
||||
"Failed to download book: {{title}}": "Không thể tải về sách: {{title}}",
|
||||
"Upload Book": "Tải lên sách",
|
||||
"Auto Upload Books to Cloud": "Tự động tải sách lên đám mây",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% dung lượng lưu trữ đám mây đã sử dụng.",
|
||||
"Storage": "Lưu trữ",
|
||||
"Book deleted: {{title}}": "Sách đã xóa: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Không thể xóa sách: {{title}}",
|
||||
"Check Updates on Start": "Kiểm tra cập nhật khi khởi động",
|
||||
@@ -167,7 +154,7 @@
|
||||
"Create a Password": "Tạo mật khẩu",
|
||||
"Sign up": "Đăng ký",
|
||||
"Signing up...": "Đang đăng ký...",
|
||||
"Don’t have an account? Sign up": "Chưa có tài khoản? Đăng ký",
|
||||
"Don't have an account? Sign up": "Chưa có tài khoản? Đăng ký",
|
||||
"Check your email for the confirmation link": "Kiểm tra email của bạn để nhận liên kết xác nhận",
|
||||
"Signing in ...": "Đang đăng nhập ...",
|
||||
"Send a magic link email": "Gửi email liên kết ma thuật",
|
||||
@@ -186,41 +173,15 @@
|
||||
"Token": "Mã token",
|
||||
"Your OTP token": "Mã OTP của bạn",
|
||||
"Verify token": "Xác minh mã token",
|
||||
"Sign in with Google": "Đăng nhập với Google",
|
||||
"Sign in with Apple": "Đăng nhập với Apple",
|
||||
"Sign in with GitHub": "Đăng nhập với GitHub",
|
||||
"Account": "Tài khoản",
|
||||
"Cloud Storage": "Lưu trữ đám mây",
|
||||
"Failed to delete user. Please try again later.": "Không thể xóa người dùng. Vui lòng thử lại sau.",
|
||||
"Unlimited Offline/Online Reading": "Đọc ngoại tuyến/trực tuyến không giới hạn",
|
||||
"Unlimited Cloud Sync Devices": "Đồng bộ hóa thiết bị đám mây không giới hạn",
|
||||
"Essential Text-to-Speech Voices": "Giọng nói chuyển văn bản thành giọng nói cơ bản",
|
||||
"DeepL Free Access": "Truy cập DeepL miễn phí",
|
||||
"Community Support": "Hỗ trợ cộng đồng",
|
||||
"500 MB Cloud Sync Space": "500 MB không gian đồng bộ hóa đám mây",
|
||||
"Includes All Free Tier Benefits": "Bao gồm tất cả lợi ích của gói miễn phí",
|
||||
"AI Summaries": "Tóm tắt AI",
|
||||
"AI Translations": "Dịch thuật AI",
|
||||
"Priority Support": "Hỗ trợ ưu tiên",
|
||||
"DeepL Pro Access": "Truy cập DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Giọng nói chuyển văn bản thành giọng nói mở rộng",
|
||||
"2000 MB Cloud Sync Space": "2000 MB không gian đồng bộ hóa đám mây",
|
||||
"Includes All Plus Tier Benefits": "Bao gồm tất cả lợi ích của gói Plus",
|
||||
"Unlimited AI Summaries": "Tóm tắt AI không giới hạn",
|
||||
"Unlimited AI Translations": "Dịch thuật AI không giới hạn",
|
||||
"Unlimited DeepL Pro Access": "Truy cập DeepL Pro không giới hạn",
|
||||
"Advanced AI Tools": "Công cụ AI nâng cao",
|
||||
"Early Feature Access": "Truy cập tính năng sớm",
|
||||
"10 GB Cloud Sync Space": "10 GB không gian đồng bộ hóa đám mây",
|
||||
"Loading profile...": "Đang tải hồ sơ...",
|
||||
"Features": "Tính năng",
|
||||
"Delete Account": "Xóa tài khoản",
|
||||
"Delete Your Account?": "Xóa tài khoản của bạn?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Hành động này không thể hoàn tác. Tất cả dữ liệu của bạn trên đám mây sẽ bị xóa vĩnh viễn.",
|
||||
"Delete Permanently": "Xóa vĩnh viễn",
|
||||
"Free Tier": "Gói miễn phí",
|
||||
"Plus Tier": "Gói Plus",
|
||||
"Pro Tier": "Gói Pro",
|
||||
"RTL Direction": "Hướng RTL",
|
||||
"Maximum Column Height": "Chiều cao cột tối đa",
|
||||
"Maximum Column Width": "Chiều rộng cột tối đa",
|
||||
@@ -229,7 +190,7 @@
|
||||
"No supported files found. Supported formats: {{formats}}": "Không tìm thấy tệp được hỗ trợ. Định dạng được hỗ trợ: {{formats}}",
|
||||
"Drop to Import Books": "Kéo và thả để nhập sách",
|
||||
"Custom": "Tùy chỉnh",
|
||||
"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": "Cả thế giới này là một sân khấu,\nVà tất cả đàn ông và phụ nữ chỉ là những diễn viên;\nHọ có cửa ra và cửa vào của mình,\nVà một người trong cuộc đời mình đóng nhiều vai,\nHành động của mình là bảy tuổi.\n\n— William Shakespeare",
|
||||
"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": "Cả thế giới này là một sân khấu,\nVà tất cả đàn ông và phụ nữ chỉ là những diễn viên;\nHọ có cửa ra và cửa vào của mình,\nVà một người trong cuộc đời mình đóng nhiều vai,\nHành động của mình là bảy tuổi.\n\n— William Shakespeare",
|
||||
"Custom Theme": "Chủ đề tùy chỉnh",
|
||||
"Theme Name": "Tên chủ đề",
|
||||
"Text Color": "Màu văn bản",
|
||||
@@ -256,9 +217,9 @@
|
||||
"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ợ.",
|
||||
"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",
|
||||
@@ -271,13 +232,13 @@
|
||||
"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!",
|
||||
"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:",
|
||||
"Changelog": "Thay đổi",
|
||||
"Software Update": "Cập nhật phần mềm",
|
||||
"Title": "Tiêu đề",
|
||||
"Date Read": "Ngày đã đọc",
|
||||
@@ -286,18 +247,15 @@
|
||||
"Ascending": "Tăng dần",
|
||||
"Descending": "Giảm dần",
|
||||
"Sort by...": "Sắp xếp theo...",
|
||||
"Added:": "Đã thêm:",
|
||||
"Format:": "Định dạng:",
|
||||
"Added": "Đã thêm",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Miêu 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",
|
||||
@@ -306,6 +264,788 @@
|
||||
"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",
|
||||
"Cancel Selection": "Hủy chọn",
|
||||
"File Size:": "Kích thước tệp:"
|
||||
"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",
|
||||
"System Language": "Ngôn ngữ hệ thống",
|
||||
"Security": "An ninh",
|
||||
"Allow JavaScript": "Cho phép JavaScript",
|
||||
"Enable only if you trust the file.": "Chỉ bật nếu bạn tin tưởng tệp.",
|
||||
"Sort TOC by Page": "Sort Mục lục theo Trang",
|
||||
"Search in {{count}} Book(s)..._other": "Tìm kiếm trong {{count}} cuốn sách...",
|
||||
"No notes match your search": "Không tìm thấy ghi chú nào",
|
||||
"Search notes and excerpts...": "Tìm ghi chú...",
|
||||
"Sign in to Sync": "Đăng nhập",
|
||||
"Synced at {{time}}": "Đã đồng bộ lúc {{time}}",
|
||||
"Never synced": "Chưa từng đồng bộ",
|
||||
"Show Remaining Time": "Hiển thị thời gian còn lại",
|
||||
"{{time}} min left in chapter": "{{time}} phút còn lại trong chương",
|
||||
"Override Book Color": "Đổi màu sách",
|
||||
"Login Required": "Cần đăng nhập",
|
||||
"Quota Exceeded": "Vượt hạn mức",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% số ký tự dịch hàng ngày đã sử dụng.",
|
||||
"Translation Characters": "Những ký tự dịch",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} giọng",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Đã xảy ra sự cố. Đừng lo, nhóm của chúng tôi đã được thông báo và chúng tôi đang làm việc để sửa chữa.",
|
||||
"Error Details:": "Chi tiết lỗi:",
|
||||
"Try Again": "Thử lại",
|
||||
"Need help?": "Bạn cần giúp đỡ?",
|
||||
"Contact Support": "Liên hệ hỗ trợ",
|
||||
"Code Highlighting": "Đánh dấu mã",
|
||||
"Enable Highlighting": "Cho phép đánh dấu",
|
||||
"Code Language": "Ngôn ngữ mã",
|
||||
"Top Margin (px)": "Lề trên (px)",
|
||||
"Bottom Margin (px)": "Lề dưới (px)",
|
||||
"Right Margin (px)": "Lề phải (px)",
|
||||
"Left Margin (px)": "Lề trái (px)",
|
||||
"Column Gap (%)": "Khoảng cách cột (%)",
|
||||
"Always Show Status Bar": "Luôn hiển thị thanh trạng thái",
|
||||
"Custom Content CSS": "CSS nội dung",
|
||||
"Enter CSS for book content styling...": "Nhập CSS cho phần nội dung sách...",
|
||||
"Custom Reader UI CSS": "CSS giao diện đọc",
|
||||
"Enter CSS for reader interface styling...": "Nhập CSS cho giao diện trình đọc...",
|
||||
"Crop": "Cắt",
|
||||
"Book Covers": "Bìa sách",
|
||||
"Fit": "Vừa vặn",
|
||||
"Reset {{settings}}": "Đặt lại {{settings}}",
|
||||
"Reset Settings": "Đặt lại cài đặt",
|
||||
"{{count}} pages left in chapter_other": "<1>Còn </1><0>{{count}}</0><1> trang trong chương</1>",
|
||||
"Show Remaining Pages": "Hiển thị trang còn lại",
|
||||
"Source Han Serif CN": "Source Han Serif",
|
||||
"Huiwen-MinchoGBK": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Quản lý đăng ký",
|
||||
"Coming Soon": "Sắp ra mắt",
|
||||
"Upgrade to {{plan}}": "Nâng cấp lên {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Nâng cấp lên Plus hoặc Pro",
|
||||
"Current Plan": "Gói hiện tại",
|
||||
"Plan Limits": "Giới hạn gói",
|
||||
"Processing your payment...": "Đang xử lý thanh toán...",
|
||||
"Please wait while we confirm your subscription.": "Vui lòng chờ trong khi chúng tôi xác nhận đăng ký của bạn.",
|
||||
"Payment Processing": "Đang xử lý thanh toán",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Thanh toán của bạn đang được xử lý. Thường mất vài giây.",
|
||||
"Payment Failed": "Thanh toán thất bại",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Chúng tôi không thể xử lý đăng ký của bạn. Vui lòng thử lại hoặc liên hệ hỗ trợ nếu sự cố tiếp tục.",
|
||||
"Back to Profile": "Quay lại hồ sơ",
|
||||
"Subscription Successful!": "Đăng ký thành công!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Cảm ơn bạn đã đăng ký! Thanh toán của bạn đã được xử lý thành công.",
|
||||
"Email:": "Email:",
|
||||
"Plan:": "Gói:",
|
||||
"Amount:": "Số tiền:",
|
||||
"Go to Library": "Đi đến Thư viện",
|
||||
"Need help? Contact our support team at support@readest.com": "Cần hỗ trợ? Liên hệ nhóm hỗ trợ tại support@readest.com",
|
||||
"Free Plan": "Gói miễn phí",
|
||||
"month": "tháng",
|
||||
"AI Translations (per day)": "Dịch AI (mỗi ngày)",
|
||||
"Plus Plan": "Gói Plus",
|
||||
"Includes All Free Plan Benefits": "Bao gồm tất cả lợi ích của gói miễn phí",
|
||||
"Pro Plan": "Gói Pro",
|
||||
"More AI Translations": "Thêm dịch AI",
|
||||
"Complete Your Subscription": "Hoàn tất đăng ký của bạn",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "Đã sử dụng {{percentage}}% không gian đồng bộ đám mây.",
|
||||
"Cloud Sync Storage": "Lưu trữ đồng bộ đám mây",
|
||||
"Disable": "Tắt",
|
||||
"Enable": "Bật",
|
||||
"Upgrade to Readest Premium": "Đăng ký Readest Premium",
|
||||
"Show Source Text": "Hiển thị văn bản gốc",
|
||||
"Cross-Platform Sync": "Đồng Bộ Đa Nền Tảng",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Đồng bộ liền mạch thư viện, tiến độ, đánh dấu và ghi chú trên tất cả thiết bị—không bao giờ mất vị trí đọc nữa.",
|
||||
"Customizable Reading": "Đọc Tùy Chỉnh",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Cá nhân hóa mọi chi tiết với phông chữ, bố cục, chủ đề có thể điều chỉnh và cài đặt hiển thị nâng cao cho trải nghiệm đọc hoàn hảo.",
|
||||
"AI Read Aloud": "AI Đọc To",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Thưởng thức việc đọc rảnh tay với giọng AI tự nhiên làm sống động sách của bạn.",
|
||||
"AI Translations": "Dịch Thuật AI",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Dịch bất kỳ văn bản nào ngay lập tức với sức mạnh của Google, Azure hoặc DeepL—hiểu nội dung bằng mọi ngôn ngữ.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Kết nối với độc giả khác và nhận trợ giúp nhanh chóng trong các kênh cộng đồng thân thiện.",
|
||||
"Unlimited AI Read Aloud Hours": "Giờ Đọc To AI Không Giới Hạn",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Nghe không giới hạn—chuyển đổi bao nhiêu văn bản bạn muốn thành âm thanh sống động.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Mở khóa khả năng dịch thuật nâng cao với nhiều lượt sử dụng hàng ngày và tùy chọn nâng cao.",
|
||||
"DeepL Pro Access": "Truy Cập DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Tận hưởng phản hồi nhanh hơn và hỗ trợ chuyên biệt khi bạn cần giúp đỡ.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Đã đạt hạn ngạch dịch thuật hàng ngày. Nâng cấp gói để tiếp tục sử dụng dịch thuật AI.",
|
||||
"Includes All Plus Plan Benefits": "Bao Gồm Tất Cả Lợi Ích Gói Plus",
|
||||
"Early Feature Access": "Truy Cập Tính Năng Sớm",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Khám phá tính năng mới, cập nhật và đổi mới trước người khác.",
|
||||
"Advanced AI Tools": "Công Cụ AI Nâng Cao",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Khai thác công cụ AI mạnh mẽ cho đọc thông minh, dịch thuật và khám phá nội dung.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Dịch tới 100.000 ký tự hàng ngày với công cụ dịch chính xác nhất hiện có.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Dịch tới 500.000 ký tự hàng ngày với công cụ dịch chính xác nhất hiện có.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Lưu trữ và truy cập an toàn toàn bộ bộ sưu tập đọc với tới 5 GB lưu trữ đám mây.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Lưu trữ và truy cập an toàn toàn bộ bộ sưu tập đọc với tới 20 GB lưu trữ đám mây.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Đã xóa bản sao lưu đám mây của sách: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Bạn có chắc chắn muốn xóa bản sao lưu đám mây của sách đã chọn không?",
|
||||
"What's New in Readest": "Những gì mới trong Readest",
|
||||
"Enter book title": "Nhập tiêu đề sách",
|
||||
"Subtitle": "Phụ đề",
|
||||
"Enter book subtitle": "Nhập phụ đề sách",
|
||||
"Enter author name": "Nhập tên tác giả",
|
||||
"Series": "Bộ sách",
|
||||
"Enter series name": "Nhập tên bộ sách",
|
||||
"Series Index": "Số thứ tự trong bộ",
|
||||
"Enter series index": "Nhập số thứ tự trong bộ",
|
||||
"Total in Series": "Tổng số trong bộ",
|
||||
"Enter total books in series": "Nhập tổng số sách trong bộ",
|
||||
"Enter publisher": "Nhập nhà xuất bản",
|
||||
"Publication Date": "Ngày xuất bản",
|
||||
"Identifier": "Định danh",
|
||||
"Enter book description": "Nhập mô tả sách",
|
||||
"Change cover image": "Thay đổi ảnh bìa",
|
||||
"Replace": "Thay thế",
|
||||
"Unlock cover": "Mở khóa bìa",
|
||||
"Lock cover": "Khóa bìa",
|
||||
"Auto-Retrieve Metadata": "Tự động lấy siêu dữ liệu",
|
||||
"Auto-Retrieve": "Tự động lấy",
|
||||
"Unlock all fields": "Mở khóa tất cả trường",
|
||||
"Unlock All": "Mở khóa tất cả",
|
||||
"Lock all fields": "Khóa tất cả trường",
|
||||
"Lock All": "Khóa tất cả",
|
||||
"Reset": "Đặt lại",
|
||||
"Edit Metadata": "Chỉnh sửa siêu dữ liệu",
|
||||
"Locked": "Đã khóa",
|
||||
"Select Metadata Source": "Chọn nguồn siêu dữ liệu",
|
||||
"Keep manual input": "Giữ nhập liệu thủ công",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Tiểu thuyết, Khoa học, Lịch sử",
|
||||
"Open Book in New Window": "Mở Sách trong Cửa sổ Mới",
|
||||
"Voices for {{lang}}": "Giọng nói cho {{lang}}",
|
||||
"Yandex Translate": "Yandex Dịch",
|
||||
"YYYY or YYYY-MM-DD": "YYYY hoặc YYYY-MM-DD",
|
||||
"Restore Purchase": "Khôi phục mua hàng",
|
||||
"No purchases found to restore.": "Không tìm thấy giao dịch mua nào để khôi phục.",
|
||||
"Failed to restore purchases.": "Khôi phục giao dịch mua không thành công.",
|
||||
"Failed to manage subscription.": "Quản lý đăng ký không thành công.",
|
||||
"Failed to load subscription plans.": "Tải kế hoạch đăng ký không thành công.",
|
||||
"year": "năm",
|
||||
"Failed to create checkout session": "Không thể tạo phiên làm việc thanh toán",
|
||||
"Storage": "Lưu trữ",
|
||||
"Terms of Service": "Điều khoản dịch vụ",
|
||||
"Privacy Policy": "Chính sách bảo mật",
|
||||
"Disable Double Click": "Tắt Nhấp Đôi",
|
||||
"TTS not supported for this document": "TTS không được hỗ trợ cho tài liệu này",
|
||||
"Reset Password": "Đặt lại mật khẩu",
|
||||
"Show Reading Progress": "Hiển thị tiến độ đọc",
|
||||
"Reading Progress Style": "Phong cách tiến độ đọc",
|
||||
"Page Number": "Số trang",
|
||||
"Percentage": "Phần trăm",
|
||||
"Deleted local copy of the book: {{title}}": "Đã xóa bản sao cục bộ của sách: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Không thể xóa bản sao lưu đám mây của sách: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Không thể xóa bản sao cục bộ của sách: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Bạn có chắc chắn muốn xóa bản sao cục bộ của cuốn sách đã chọn không?",
|
||||
"Remove from Cloud & Device": "Xóa khỏi Đám mây & Thiết bị",
|
||||
"Remove from Cloud Only": "Xóa chỉ khỏi Đám mây",
|
||||
"Remove from Device Only": "Xóa chỉ khỏi Thiết bị",
|
||||
"Disconnected": "Mất kết nối",
|
||||
"KOReader Sync Settings": "Cài đặt đồng bộ KOReader",
|
||||
"Sync Strategy": "Chiến lược đồng bộ",
|
||||
"Ask on conflict": "Hỏi khi có xung đột",
|
||||
"Always use latest": "Luôn sử dụng phiên bản mới nhất",
|
||||
"Send changes only": "Chỉ gửi thay đổi",
|
||||
"Receive changes only": "Chỉ nhận thay đổi",
|
||||
"Checksum Method": "Phương pháp kiểm tra",
|
||||
"File Content (recommended)": "Nội dung tệp (được khuyến nghị)",
|
||||
"File Name": "Tên tệp",
|
||||
"Device Name": "Tên thiết bị",
|
||||
"Connect to your KOReader Sync server.": "Kết nối với máy chủ đồng bộ KOReader của bạn.",
|
||||
"Server URL": "URL máy chủ",
|
||||
"Username": "Tên người dùng",
|
||||
"Your Username": "Tên người dùng của bạn",
|
||||
"Password": "Mật khẩu",
|
||||
"Connect": "Kết nối",
|
||||
"KOReader Sync": "Đồng bộ KOReader",
|
||||
"Sync Conflict": "Xung đột đồng bộ",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Đồng bộ tiến độ đọc từ \"{{deviceName}}\"?",
|
||||
"another device": "thiết bị khác",
|
||||
"Local Progress": "Tiến độ cục bộ",
|
||||
"Remote Progress": "Tiến độ từ xa",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Trang {{page}} của {{total}} ({{percentage}}%)",
|
||||
"Current position": "Vị trí hiện tại",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Khoảng trang {{page}} của {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Khoảng {{percentage}}%",
|
||||
"Failed to connect": "Không thể kết nối",
|
||||
"Sync Server Connected": "Máy chủ đồng bộ đã kết nối",
|
||||
"Sync as {{userDisplayName}}": "Đồng bộ dưới tên {{userDisplayName}}",
|
||||
"Custom Fonts": "Phông Chữ Tùy Chỉnh",
|
||||
"Cancel Delete": "Hủy Xóa",
|
||||
"Import Font": "Nhập Phông Chữ",
|
||||
"Delete Font": "Xóa Phông Chữ",
|
||||
"Tips": "Mẹo",
|
||||
"Custom fonts can be selected from the Font Face menu": "Phông chữ tùy chỉnh có thể được chọn từ menu Phông Chữ",
|
||||
"Manage Custom Fonts": "Quản Lý Phông Chữ Tùy Chỉnh",
|
||||
"Select Files": "Chọn Tệp",
|
||||
"Select Image": "Chọn Hình Ảnh",
|
||||
"Select Video": "Chọn Video",
|
||||
"Select Audio": "Chọn Âm Thanh",
|
||||
"Select Fonts": "Chọn Phông Chữ",
|
||||
"Supported font formats: .ttf, .otf, .woff, .woff2": "Định dạng phông chữ được hỗ trợ: .ttf, .otf, .woff, .woff2",
|
||||
"Push Progress": "Đẩy tiến độ",
|
||||
"Pull Progress": "Kéo tiến độ",
|
||||
"Previous Paragraph": "Đoạn trước",
|
||||
"Previous Sentence": "Câu trước",
|
||||
"Pause": "Tạm dừng",
|
||||
"Play": "Phát",
|
||||
"Next Sentence": "Câu tiếp theo",
|
||||
"Next Paragraph": "Đoạn tiếp theo",
|
||||
"Separate Cover Page": "Trang Bìa Riêng",
|
||||
"Resize Notebook": "Thay Đổi Kích Thước Sổ Ghi Chép",
|
||||
"Resize Sidebar": "Thay Đổi Kích Thước Thanh Bên",
|
||||
"Get Help from the Readest Community": "Nhận Trợ Giúp từ Cộng Đồng Readest",
|
||||
"Remove cover image": "Xóa ảnh bìa",
|
||||
"Bookshelf": "Kệ sách",
|
||||
"View Menu": "Xem menu",
|
||||
"Settings Menu": "Menu cài đặt",
|
||||
"View account details and quota": "Xem chi tiết tài khoản và hạn mức",
|
||||
"Library Header": "Tiêu đề thư viện",
|
||||
"Book Content": "Nội dung sách",
|
||||
"Footer Bar": "Thanh chân trang",
|
||||
"Header Bar": "Thanh đầu trang",
|
||||
"View Options": "Tùy chọn xem",
|
||||
"Book Menu": "Menu sách",
|
||||
"Search Options": "Tùy chọn tìm kiếm",
|
||||
"Close": "Đóng",
|
||||
"Delete Book Options": "Tùy chọn xóa sách",
|
||||
"ON": "BẬT",
|
||||
"OFF": "TẮT",
|
||||
"Reading Progress": "Tiến độ đọc",
|
||||
"Page Margin": "Lề trang",
|
||||
"Remove Bookmark": "Xóa đánh dấu",
|
||||
"Add Bookmark": "Thêm đánh dấu",
|
||||
"Books Content": "Nội dung sách",
|
||||
"Jump to Location": "Nhảy đến vị trí",
|
||||
"Unpin Notebook": "Bỏ ghim sổ tay",
|
||||
"Pin Notebook": "Ghim sổ tay",
|
||||
"Hide Search Bar": "Ẩn thanh tìm kiếm",
|
||||
"Show Search Bar": "Hiện thanh tìm kiếm",
|
||||
"On {{current}} of {{total}} page": "Trên {{current}} của {{total}} trang",
|
||||
"Section Title": "Tiêu đề phần",
|
||||
"Decrease": "Giảm",
|
||||
"Increase": "Tăng",
|
||||
"Settings Panels": "Các bảng điều khiển cài đặt",
|
||||
"Settings": "Cài đặt",
|
||||
"Unpin Sidebar": "Bỏ ghim thanh bên",
|
||||
"Pin Sidebar": "Ghim thanh bên",
|
||||
"Toggle Sidebar": "Chuyển đổi thanh bên",
|
||||
"Toggle Translation": "Chuyển đổi dịch thuật",
|
||||
"Translation Disabled": "Dịch thuật đã tắt",
|
||||
"Minimize": "Thu nhỏ",
|
||||
"Maximize or Restore": "Tối đa hóa hoặc Khôi phục",
|
||||
"Exit Parallel Read": "Thoát Đọc Song Song",
|
||||
"Enter Parallel Read": "Vào Đọc Song Song",
|
||||
"Zoom Level": "Độ phóng to",
|
||||
"Zoom Out": "Thu nhỏ",
|
||||
"Reset Zoom": "Đặt lại độ phóng to",
|
||||
"Zoom In": "Phóng to",
|
||||
"Zoom Mode": "Chế độ phóng to",
|
||||
"Single Page": "Một trang",
|
||||
"Auto Spread": "Tự động trải rộng",
|
||||
"Fit Page": "Vừa trang",
|
||||
"Fit Width": "Vừa chiều rộng",
|
||||
"Failed to select directory": "Không thể chọn thư mục",
|
||||
"The new data directory must be different from the current one.": "Thư mục dữ liệu mới phải khác với thư mục hiện tại.",
|
||||
"Migration failed: {{error}}": "Di chuyển không thành công: {{error}}",
|
||||
"Change Data Location": "Thay đổi vị trí dữ liệu",
|
||||
"Current Data Location": "Vị trí dữ liệu hiện tại",
|
||||
"Total size: {{size}}": "Tổng kích thước: {{size}}",
|
||||
"Calculating file info...": "Đang tính toán thông tin tệp...",
|
||||
"New Data Location": "Vị trí dữ liệu mới",
|
||||
"Choose Different Folder": "Chọn thư mục khác",
|
||||
"Choose New Folder": "Chọn thư mục mới",
|
||||
"Migrating data...": "Đang di chuyển dữ liệu...",
|
||||
"Copying: {{file}}": "Đang sao chép: {{file}}",
|
||||
"{{current}} of {{total}} files": "{{current}} của {{total}} tệp",
|
||||
"Migration completed successfully!": "Di chuyển hoàn tất thành công!",
|
||||
"Your data has been moved to the new location. Please restart the application to complete the process.": "Dữ liệu của bạn đã được chuyển đến vị trí mới. Vui lòng khởi động lại ứng dụng để hoàn tất quá trình.",
|
||||
"Migration failed": "Di chuyển không thành công",
|
||||
"Important Notice": "Thông báo quan trọng",
|
||||
"This will move all your app data to the new location. Make sure the destination has enough free space.": "Điều này sẽ di chuyển tất cả dữ liệu ứng dụng của bạn đến vị trí mới. Hãy chắc chắn rằng đích đến có đủ không gian trống.",
|
||||
"Restart App": "Khởi động lại ứng dụng",
|
||||
"Start Migration": "Bắt đầu di chuyển",
|
||||
"Advanced Settings": "Cài đặt nâng cao",
|
||||
"File count: {{size}}": "Số lượng tệp: {{size}}",
|
||||
"Background Read Aloud": "Đọc To Nền",
|
||||
"Ready to read aloud": "Sẵn sàng để đọc to",
|
||||
"Read Aloud": "Đọc To",
|
||||
"Screen Brightness": "Độ sáng màn hình",
|
||||
"Background Image": "Ảnh nền",
|
||||
"Import Image": "Nhập ảnh",
|
||||
"Opacity": "Độ mờ",
|
||||
"Size": "Kích thước",
|
||||
"Cover": "Bìa",
|
||||
"Contain": "Chứa",
|
||||
"{{number}} pages left in chapter": "<1>Còn </1><0>{{number}}</0><1> trang trong chương</1>",
|
||||
"Device": "Thiết bị",
|
||||
"E-Ink Mode": "Chế độ E-Ink",
|
||||
"Highlight Colors": "Màu nổi bật",
|
||||
"Pagination": "Phân trang",
|
||||
"Disable Double Tap": "Vô hiệu hóa chạm hai lần",
|
||||
"Tap to Paginate": "Chạm để phân trang",
|
||||
"Click to Paginate": "Nhấp để phân trang",
|
||||
"Tap Both Sides": "Chạm vào cả hai bên",
|
||||
"Click Both Sides": "Nhấp vào cả hai bên",
|
||||
"Swap Tap Sides": "Đổi bên chạm",
|
||||
"Swap Click Sides": "Đổi bên nhấp",
|
||||
"Source and Translated": "Nguồn và Đã dịch",
|
||||
"Translated Only": "Chỉ Đã dịch",
|
||||
"Source Only": "Chỉ Nguồn",
|
||||
"TTS Text": "Văn bản TTS",
|
||||
"The book file is corrupted": "Tệp sách bị hỏng",
|
||||
"The book file is empty": "Tệp sách trống",
|
||||
"Failed to open the book file": "Không thể mở tệp sách",
|
||||
"On-Demand Purchase": "Mua theo yêu cầu",
|
||||
"Full Customization": "Tùy chỉnh đầy đủ",
|
||||
"Lifetime Plan": "Kế hoạch trọn đời",
|
||||
"One-Time Payment": "Thanh toán một lần",
|
||||
"Make a single payment to enjoy lifetime access to specific features on all devices. Purchase specific features or services only when you need them.": "Thực hiện một khoản thanh toán duy nhất để tận hưởng quyền truy cập trọn đời vào các tính năng cụ thể trên tất cả các thiết bị. Mua các tính năng hoặc dịch vụ cụ thể chỉ khi bạn cần chúng.",
|
||||
"Expand Cloud Sync Storage": "Mở rộng dung lượng lưu trữ đồng bộ hóa đám mây",
|
||||
"Expand your cloud storage forever with a one-time purchase. Each additional purchase adds more space.": "Mở rộng dung lượng lưu trữ đám mây của bạn mãi mãi với một lần mua. Mỗi lần mua thêm sẽ thêm không gian.",
|
||||
"Unlock All Customization Options": "Mở khóa tất cả tùy chọn tùy chỉnh",
|
||||
"Unlock additional themes, fonts, layout options and read aloud, translators, cloud storage services.": "Mở khóa các chủ đề, phông chữ, tùy chọn bố cục và đọc to, dịch giả, dịch vụ lưu trữ đám mây bổ sung.",
|
||||
"Purchase Successful!": "Mua hàng thành công!",
|
||||
"lifetime": "trọn đời",
|
||||
"Thank you for your purchase! Your payment has been processed successfully.": "Cảm ơn bạn đã mua hàng! Thanh toán của bạn đã được xử lý thành công.",
|
||||
"Order ID:": "Mã đơn hàng:",
|
||||
"TTS Highlighting": "Đánh dấu TTS",
|
||||
"Style": "Kiểu",
|
||||
"Underline": "Gạch chân",
|
||||
"Strikethrough": "Gạch ngang",
|
||||
"Squiggly": "Gạch ngoằn ngoèo",
|
||||
"Outline": "Đường viền",
|
||||
"Save Current Color": "Lưu màu hiện tại",
|
||||
"Quick Colors": "Màu nhanh",
|
||||
"Highlighter": "Bút đánh dấu",
|
||||
"Save Book Cover": "Lưu bìa sách",
|
||||
"Auto-save last book cover": "Tự động lưu bìa sách cuối cùng",
|
||||
"Back": "Quay lại",
|
||||
"Confirmation email sent! Please check your old and new email addresses to confirm the change.": "Email xác nhận đã được gửi! Vui lòng kiểm tra địa chỉ email cũ và mới của bạn để xác nhận thay đổi.",
|
||||
"Failed to update email": "Cập nhật email thất bại",
|
||||
"New Email": "Email mới",
|
||||
"Your new email": "Email mới của bạn",
|
||||
"Updating email ...": "Đang cập nhật email ...",
|
||||
"Update email": "Cập nhật email",
|
||||
"Current email": "Email hiện tại",
|
||||
"Update Email": "Cập nhật email",
|
||||
"All": "Tất cả",
|
||||
"Unable to open book": "Không thể mở sách",
|
||||
"Punctuation": "Dấu câu",
|
||||
"Replace Quotation Marks": "Thay thế dấu ngoặc kép",
|
||||
"Enabled only in vertical layout.": "Chỉ bật trong bố cục dọc.",
|
||||
"No Conversion": "Không chuyển đổi",
|
||||
"Simplified to Traditional": "Giản thể → Phồn thể",
|
||||
"Traditional to Simplified": "Phồn thể → Giản thể",
|
||||
"Simplified to Traditional (Taiwan)": "Giản thể → Phồn thể (Đài Loan)",
|
||||
"Simplified to Traditional (Hong Kong)": "Giản thể → Phồn thể (Hồng Kông)",
|
||||
"Simplified to Traditional (Taiwan), with phrases": "Giản thể → Phồn thể (Đài Loan • cụm từ)",
|
||||
"Traditional (Taiwan) to Simplified": "Phồn thể (Đài Loan) → Giản thể",
|
||||
"Traditional (Hong Kong) to Simplified": "Phồn thể (Hồng Kông) → Giản thể",
|
||||
"Traditional (Taiwan) to Simplified, with phrases": "Phồn thể (Đài Loan • cụm từ) → Giản thể",
|
||||
"Convert Simplified and Traditional Chinese": "Chuyển đổi Trung giản/Phồn thể",
|
||||
"Convert Mode": "Chế độ chuyển đổi",
|
||||
"Failed to auto-save book cover for lock screen: {{error}}": "Không thể tự động lưu bìa sách cho màn hình khóa: {{error}}",
|
||||
"Download from Cloud": "Tải xuống từ đám mây",
|
||||
"Upload to Cloud": "Tải lên đám mây",
|
||||
"Clear Custom Fonts": "Xóa phông chữ tùy chỉnh",
|
||||
"Columns": "Cột",
|
||||
"OPDS Catalogs": "Danh mục OPDS",
|
||||
"Adding LAN addresses is not supported in the web app version.": "Việc thêm địa chỉ LAN không được hỗ trợ trong phiên bản web.",
|
||||
"Invalid OPDS catalog. Please check the URL.": "Danh mục OPDS không hợp lệ. Vui lòng kiểm tra URL.",
|
||||
"Browse and download books from online catalogs": "Duyệt và tải sách từ các danh mục trực tuyến",
|
||||
"My Catalogs": "Danh mục của tôi",
|
||||
"Add Catalog": "Thêm danh mục",
|
||||
"No catalogs yet": "Chưa có danh mục nào",
|
||||
"Add your first OPDS catalog to start browsing books": "Thêm danh mục OPDS đầu tiên để bắt đầu duyệt sách",
|
||||
"Add Your First Catalog": "Thêm danh mục đầu tiên",
|
||||
"Browse": "Duyệt",
|
||||
"Popular Catalogs": "Danh mục phổ biến",
|
||||
"Add": "Thêm",
|
||||
"Add OPDS Catalog": "Thêm danh mục OPDS",
|
||||
"Catalog Name": "Tên danh mục",
|
||||
"My Calibre Library": "Thư viện Calibre của tôi",
|
||||
"OPDS URL": "URL OPDS",
|
||||
"Username (optional)": "Tên đăng nhập (tùy chọn)",
|
||||
"Password (optional)": "Mật khẩu (tùy chọn)",
|
||||
"Description (optional)": "Mô tả (tùy chọn)",
|
||||
"A brief description of this catalog": "Mô tả ngắn gọn về danh mục này",
|
||||
"Validating...": "Đang xác thực...",
|
||||
"View All": "Xem tất cả",
|
||||
"Forward": "Tiếp",
|
||||
"Home": "Trang chủ",
|
||||
"{{count}} items_other": "{{count}} mục",
|
||||
"Download completed": "Tải xuống hoàn tất",
|
||||
"Download failed": "Tải xuống thất bại",
|
||||
"Open Access": "Truy cập mở",
|
||||
"Borrow": "Mượn",
|
||||
"Buy": "Mua",
|
||||
"Subscribe": "Đăng ký",
|
||||
"Sample": "Mẫu",
|
||||
"Download": "Tải xuống",
|
||||
"Open & Read": "Mở & Đọc",
|
||||
"Tags": "Thẻ",
|
||||
"Tag": "Thẻ",
|
||||
"First": "Đầu tiên",
|
||||
"Previous": "Trước",
|
||||
"Next": "Tiếp theo",
|
||||
"Last": "Cuối cùng",
|
||||
"Cannot Load Page": "Không thể tải trang",
|
||||
"An error occurred": "Đã xảy ra lỗi",
|
||||
"Online Library": "Thư viện trực tuyến",
|
||||
"URL must start with http:// or https://": "URL phải bắt đầu bằng http:// hoặc https://",
|
||||
"Title, Author, Tag, etc...": "Tiêu đề, Tác giả, Thẻ, v.v...",
|
||||
"Query": "Truy vấn",
|
||||
"Subject": "Chủ đề",
|
||||
"Enter {{terms}}": "Nhập {{terms}}",
|
||||
"No search results found": "Không tìm thấy kết quả tìm kiếm",
|
||||
"Failed to load OPDS feed: {{status}} {{statusText}}": "Không tải được nguồn cấp OPDS: {{status}} {{statusText}}",
|
||||
"Search in {{title}}": "Tìm kiếm trong {{title}}",
|
||||
"Manage Storage": "Quản lý bộ nhớ",
|
||||
"Failed to load files": "Không tải được tệp",
|
||||
"Deleted {{count}} file(s)_other": "Đã xóa {{count}} tệp",
|
||||
"Failed to delete {{count}} file(s)_other": "Không xóa được {{count}} tệp",
|
||||
"Failed to delete files": "Không xóa được tệp",
|
||||
"Total Files": "Tổng số tệp",
|
||||
"Total Size": "Tổng dung lượng",
|
||||
"Quota": "Dung lượng cho phép",
|
||||
"Used": "Đã sử dụng",
|
||||
"Files": "Tệp",
|
||||
"Search files...": "Tìm tệp...",
|
||||
"Newest First": "Mới nhất trước",
|
||||
"Oldest First": "Cũ nhất trước",
|
||||
"Largest First": "Lớn nhất trước",
|
||||
"Smallest First": "Nhỏ nhất trước",
|
||||
"Name A-Z": "Tên A-Z",
|
||||
"Name Z-A": "Tên Z-A",
|
||||
"{{count}} selected_other": "Đã chọn {{count}} tệp",
|
||||
"Delete Selected": "Xóa các mục đã chọn",
|
||||
"Created": "Đã tạo",
|
||||
"No files found": "Không tìm thấy tệp",
|
||||
"No files uploaded yet": "Chưa tải lên tệp nào",
|
||||
"files": "tệp",
|
||||
"Page {{current}} of {{total}}": "Trang {{current}} trên {{total}}",
|
||||
"Are you sure to delete {{count}} selected file(s)?_other": "Bạn có chắc muốn xóa {{count}} tệp đã chọn không?",
|
||||
"Cloud Storage Usage": "Sử dụng lưu trữ đám mây",
|
||||
"Rename Group": "Đổi tên nhóm",
|
||||
"From Directory": "Từ thư mục",
|
||||
"Successfully imported {{count}} book(s)_other": "Đã nhập thành công {{count}} sách",
|
||||
"Count": "Số lượng",
|
||||
"Start Page": "Trang bắt đầu",
|
||||
"Search in OPDS Catalog...": "Tìm kiếm trong danh mục OPDS...",
|
||||
"Please log in to use advanced TTS features": "Vui lòng đăng nhập để sử dụng các tính năng TTS nâng cao",
|
||||
"Word limit of 30 words exceeded.": "Đã vượt quá giới hạn 30 từ.",
|
||||
"Proofread": "Hiệu đính",
|
||||
"Current selection": "Vùng chọn hiện tại",
|
||||
"All occurrences in this book": "Tất cả các lần xuất hiện trong sách này",
|
||||
"All occurrences in your library": "Tất cả các lần xuất hiện trong thư viện của bạn",
|
||||
"Selected text:": "Văn bản đã chọn:",
|
||||
"Replace with:": "Thay thế bằng:",
|
||||
"Enter text...": "Nhập văn bản…",
|
||||
"Case sensitive:": "Phân biệt chữ hoa/thường:",
|
||||
"Scope:": "Phạm vi:",
|
||||
"Selection": "Vùng chọn",
|
||||
"Library": "Thư viện",
|
||||
"Yes": "Có",
|
||||
"No": "Không",
|
||||
"Proofread Replacement Rules": "Quy tắc thay thế hiệu đính",
|
||||
"Selected Text Rules": "Quy tắc cho văn bản đã chọn",
|
||||
"No selected text replacement rules": "Không có quy tắc thay thế cho văn bản đã chọn",
|
||||
"Book Specific Rules": "Quy tắc dành riêng cho sách",
|
||||
"No book-level replacement rules": "Không có quy tắc thay thế ở cấp sách",
|
||||
"Disable Quick Action": "Vô hiệu hóa Hành động Nhanh",
|
||||
"Enable Quick Action on Selection": "Bật Hành động Nhanh khi chọn",
|
||||
"None": "Không có",
|
||||
"Annotation Tools": "Công cụ chú thích",
|
||||
"Enable Quick Actions": "Bật Hành động Nhanh",
|
||||
"Quick Action": "Hành động Nhanh",
|
||||
"Copy to Notebook": "Sao chép vào Sổ tay",
|
||||
"Copy text after selection": "Sao chép văn bản sau khi chọn",
|
||||
"Highlight text after selection": "Đánh dấu văn bản sau khi chọn",
|
||||
"Annotate text after selection": "Chú thích văn bản sau khi chọn",
|
||||
"Search text after selection": "Tìm kiếm văn bản sau khi chọn",
|
||||
"Look up text in dictionary after selection": "Tra cứu văn bản trong từ điển sau khi chọn",
|
||||
"Look up text in Wikipedia after selection": "Tra cứu văn bản trong Wikipedia sau khi chọn",
|
||||
"Translate text after selection": "Dịch văn bản sau khi chọn",
|
||||
"Read text aloud after selection": "Đọc to văn bản sau khi chọn",
|
||||
"Proofread text after selection": "Hiệu đính văn bản sau khi chọn",
|
||||
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} đang hoạt động, {{pendingCount}} đang chờ",
|
||||
"{{failedCount}} failed": "{{failedCount}} thất bại",
|
||||
"Waiting...": "Đang chờ...",
|
||||
"Failed": "Thất bại",
|
||||
"Completed": "Hoàn thành",
|
||||
"Cancelled": "Đã hủy",
|
||||
"Retry": "Thử lại",
|
||||
"Active": "Đang hoạt động",
|
||||
"Transfer Queue": "Hàng đợi truyền",
|
||||
"Upload All": "Tải lên tất cả",
|
||||
"Download All": "Tải xuống tất cả",
|
||||
"Resume Transfers": "Tiếp tục truyền",
|
||||
"Pause Transfers": "Tạm dừng truyền",
|
||||
"Pending": "Đang chờ",
|
||||
"No transfers": "Không có truyền",
|
||||
"Retry All": "Thử lại tất cả",
|
||||
"Clear Completed": "Xóa đã hoàn thành",
|
||||
"Clear Failed": "Xóa thất bại",
|
||||
"Upload queued: {{title}}": "Tải lên trong hàng đợi: {{title}}",
|
||||
"Download queued: {{title}}": "Tải xuống trong hàng đợi: {{title}}",
|
||||
"Book not found in library": "Không tìm thấy sách trong thư viện",
|
||||
"Unknown error": "Lỗi không xác định",
|
||||
"Please log in to continue": "Vui lòng đăng nhập để tiếp tục",
|
||||
"Cloud File Transfers": "Truyền tệp đám mây",
|
||||
"Show Search Results": "Hiển thị kết quả tìm kiếm",
|
||||
"Search results for '{{term}}'": "Kết quả cho '{{term}}'",
|
||||
"Close Search": "Đóng tìm kiếm",
|
||||
"Previous Result": "Kết quả trước",
|
||||
"Next Result": "Kết quả tiếp theo",
|
||||
"Bookmarks": "Dấu trang",
|
||||
"Annotations": "Chú thích",
|
||||
"Show Results": "Hiển thị kết quả",
|
||||
"Clear search": "Xóa tìm kiếm",
|
||||
"Clear search history": "Xóa lịch sử tìm kiếm",
|
||||
"Tap to Toggle Footer": "Nhấn để bật/tắt chân trang",
|
||||
"Exported successfully": "Xuất thành công",
|
||||
"Book exported successfully.": "Sách đã được xuất thành công.",
|
||||
"Failed to export the book.": "Xuất sách thất bại.",
|
||||
"Export Book": "Xuất sách",
|
||||
"Whole word:": "Toàn bộ từ:",
|
||||
"Error": "Lỗi",
|
||||
"Unable to load the article. Try searching directly on {{link}}.": "Không thể tải bài viết. Hãy thử tìm kiếm trực tiếp trên {{link}}.",
|
||||
"Unable to load the word. Try searching directly on {{link}}.": "Không thể tải từ. Hãy thử tìm kiếm trực tiếp trên {{link}}.",
|
||||
"Date Published": "Ngày xuất bản",
|
||||
"Only for TTS:": "Chỉ dành cho TTS:",
|
||||
"Uploaded": "Đã tải lên",
|
||||
"Downloaded": "Đã tải xuống",
|
||||
"Deleted": "Đã xóa",
|
||||
"Note:": "Ghi chú:",
|
||||
"Time:": "Thời gian:",
|
||||
"Format Options": "Tùy chọn định dạng",
|
||||
"Export Date": "Ngày xuất",
|
||||
"Chapter Titles": "Tiêu đề chương",
|
||||
"Chapter Separator": "Dấu phân cách chương",
|
||||
"Highlights": "Đánh dấu",
|
||||
"Note Date": "Ngày ghi chú",
|
||||
"Advanced": "Nâng cao",
|
||||
"Hide": "Ẩn",
|
||||
"Show": "Hiển thị",
|
||||
"Use Custom Template": "Sử dụng mẫu tùy chỉnh",
|
||||
"Export Template": "Mẫu xuất",
|
||||
"Template Syntax:": "Cú pháp mẫu:",
|
||||
"Insert value": "Chèn giá trị",
|
||||
"Format date (locale)": "Định dạng ngày (địa phương)",
|
||||
"Format date (custom)": "Định dạng ngày (tùy chỉnh)",
|
||||
"Conditional": "Điều kiện",
|
||||
"Loop": "Vòng lặp",
|
||||
"Available Variables:": "Biến có sẵn:",
|
||||
"Book title": "Tiêu đề sách",
|
||||
"Book author": "Tác giả sách",
|
||||
"Export date": "Ngày xuất",
|
||||
"Array of chapters": "Danh sách chương",
|
||||
"Chapter title": "Tiêu đề chương",
|
||||
"Array of annotations": "Danh sách chú thích",
|
||||
"Highlighted text": "Văn bản được đánh dấu",
|
||||
"Annotation note": "Ghi chú chú thích",
|
||||
"Date Format Tokens:": "Token định dạng ngày:",
|
||||
"Year (4 digits)": "Năm (4 chữ số)",
|
||||
"Month (01-12)": "Tháng (01-12)",
|
||||
"Day (01-31)": "Ngày (01-31)",
|
||||
"Hour (00-23)": "Giờ (00-23)",
|
||||
"Minute (00-59)": "Phút (00-59)",
|
||||
"Second (00-59)": "Giây (00-59)",
|
||||
"Show Source": "Hiển thị nguồn",
|
||||
"No content to preview": "Không có nội dung để xem trước",
|
||||
"Export": "Xuất",
|
||||
"Set Timeout": "Đặt thời gian chờ",
|
||||
"Select Voice": "Chọn giọng nói",
|
||||
"Toggle Sticky Bottom TTS Bar": "Bật/tắt thanh TTS cố định",
|
||||
"Display what I'm reading on Discord": "Hiển thị sách đang đọc trên Discord",
|
||||
"Show on Discord": "Hiện trên Discord",
|
||||
"Instant {{action}}": "{{action}} tức thì",
|
||||
"Instant {{action}} Disabled": "{{action}} tức thì đã tắt",
|
||||
"Annotation": "Chú thích",
|
||||
"Reset Template": "Đặt lại mẫu",
|
||||
"Annotation style": "Kiểu chú thích",
|
||||
"Annotation color": "Màu chú thích",
|
||||
"Annotation time": "Thời gian chú thích",
|
||||
"AI": "AI",
|
||||
"Are you sure you want to re-index this book?": "Bạn có chắc muốn lập chỉ mục lại cuốn sách này?",
|
||||
"Enable AI in Settings": "Bật AI trong Cài đặt",
|
||||
"Index This Book": "Lập chỉ mục sách này",
|
||||
"Enable AI search and chat for this book": "Bật tìm kiếm AI và trò chuyện cho cuốn sách này",
|
||||
"Start Indexing": "Bắt đầu lập chỉ mục",
|
||||
"Indexing book...": "Đang lập chỉ mục sách...",
|
||||
"Preparing...": "Đang chuẩn bị...",
|
||||
"Delete this conversation?": "Xóa cuộc trò chuyện này?",
|
||||
"No conversations yet": "Chưa có cuộc trò chuyện nào",
|
||||
"Start a new chat to ask questions about this book": "Bắt đầu cuộc trò chuyện mới để hỏi về cuốn sách này",
|
||||
"Rename": "Đổi tên",
|
||||
"New Chat": "Cuộc trò chuyện mới",
|
||||
"Chat": "Trò chuyện",
|
||||
"Please enter a model ID": "Vui lòng nhập ID mô hình",
|
||||
"Model not available or invalid": "Mô hình không khả dụng hoặc không hợp lệ",
|
||||
"Failed to validate model": "Xác thực mô hình thất bại",
|
||||
"Couldn't connect to Ollama. Is it running?": "Không thể kết nối với Ollama. Nó có đang chạy không?",
|
||||
"Invalid API key or connection failed": "Khóa API không hợp lệ hoặc kết nối thất bại",
|
||||
"Connection failed": "Kết nối thất bại",
|
||||
"AI Assistant": "Trợ lý AI",
|
||||
"Enable AI Assistant": "Bật trợ lý AI",
|
||||
"Provider": "Nhà cung cấp",
|
||||
"Ollama (Local)": "Ollama (Cục bộ)",
|
||||
"AI Gateway (Cloud)": "Cổng AI (Đám mây)",
|
||||
"Ollama Configuration": "Cấu hình Ollama",
|
||||
"Refresh Models": "Làm mới mô hình",
|
||||
"AI Model": "Mô hình AI",
|
||||
"No models detected": "Không phát hiện mô hình nào",
|
||||
"AI Gateway Configuration": "Cấu hình cổng AI",
|
||||
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Chọn từ các mô hình AI chất lượng cao, tiết kiệm. Bạn cũng có thể sử dụng mô hình riêng bằng cách chọn \"Mô hình tùy chỉnh\" bên dưới.",
|
||||
"API Key": "Khóa API",
|
||||
"Get Key": "Lấy khóa",
|
||||
"Model": "Mô hình",
|
||||
"Custom Model...": "Mô hình tùy chỉnh...",
|
||||
"Custom Model ID": "ID mô hình tùy chỉnh",
|
||||
"Validate": "Xác thực",
|
||||
"Model available": "Mô hình khả dụng",
|
||||
"Connection": "Kết nối",
|
||||
"Test Connection": "Kiểm tra kết nối",
|
||||
"Connected": "Đã kết nối",
|
||||
"Custom Colors": "Màu tùy chỉnh",
|
||||
"Color E-Ink Mode": "Chế độ E-Ink màu",
|
||||
"Reading Ruler": "Thước đọc",
|
||||
"Enable Reading Ruler": "Bật thước đọc",
|
||||
"Lines to Highlight": "Số dòng cần làm nổi bật",
|
||||
"Ruler Color": "Màu thước",
|
||||
"Command Palette": "Bảng lệnh",
|
||||
"Search settings and actions...": "Tìm kiếm cài đặt và hành động...",
|
||||
"No results found for": "Không tìm thấy kết quả cho",
|
||||
"Type to search settings and actions": "Nhập để tìm kiếm cài đặt và hành động",
|
||||
"Recent": "Gần đây",
|
||||
"navigate": "điều hướng",
|
||||
"select": "chọn",
|
||||
"close": "đóng",
|
||||
"Search Settings": "Tìm kiếm cài đặt",
|
||||
"Page Margins": "Lề trang",
|
||||
"AI Provider": "Nhà cung cấp AI",
|
||||
"Ollama URL": "URL Ollama",
|
||||
"Ollama Model": "Mô hình Ollama",
|
||||
"AI Gateway Model": "Mô hình AI Gateway",
|
||||
"Actions": "Hành động",
|
||||
"Navigation": "Điều hướng",
|
||||
"Set status for {{count}} book(s)_other": "Đặt trạng thái cho {{count}} cuốn sách",
|
||||
"Mark as Unread": "Đánh dấu là chưa đọc",
|
||||
"Mark as Finished": "Đánh dấu là đã hoàn thành",
|
||||
"Finished": "Đã hoàn thành",
|
||||
"Unread": "Chưa đọc",
|
||||
"Clear Status": "Xóa trạng thái",
|
||||
"Status": "Trạng thái",
|
||||
"Loading": "Đang tải...",
|
||||
"Exit Paragraph Mode": "Thoát chế độ đoạn văn",
|
||||
"Paragraph Mode": "Chế độ đoạn văn",
|
||||
"Embedding Model": "Mô hình nhúng",
|
||||
"{{count}} book(s) synced_other": "Đã đồng bộ {{count}} cuốn sách",
|
||||
"Unable to start RSVP": "Không thể bắt đầu RSVP",
|
||||
"RSVP not supported for PDF": "RSVP không được hỗ trợ cho PDF",
|
||||
"Select Chapter": "Chọn chương",
|
||||
"Context": "Ngữ cảnh",
|
||||
"Ready": "Sẵn sàng",
|
||||
"Chapter Progress": "Tiến độ chương",
|
||||
"words": "từ",
|
||||
"{{time}} left": "còn lại {{time}}",
|
||||
"Reading progress": "Tiến độ đọc",
|
||||
"Click to seek": "Nhấp để tua",
|
||||
"Skip back 15 words": "Lùi lại 15 từ",
|
||||
"Back 15 words (Shift+Left)": "Lùi lại 15 từ (Shift+Trái)",
|
||||
"Pause (Space)": "Tạm dừng (Khoảng trắng)",
|
||||
"Play (Space)": "Phát (Khoảng trắng)",
|
||||
"Skip forward 15 words": "Tiến tới 15 từ",
|
||||
"Forward 15 words (Shift+Right)": "Tiến tới 15 từ (Shift+Phải)",
|
||||
"Pause:": "Tạm dừng:",
|
||||
"Decrease speed": "Giảm tốc độ",
|
||||
"Slower (Left/Down)": "Chậm hơn (Trái/Xuống)",
|
||||
"Current speed": "Tốc độ hiện tại",
|
||||
"Increase speed": "Tăng tốc độ",
|
||||
"Faster (Right/Up)": "Nhanh hơn (Phải/Lên)",
|
||||
"Start RSVP Reading": "Bắt đầu đọc RSVP",
|
||||
"Choose where to start reading": "Chọn nơi để bắt đầu đọc",
|
||||
"From Chapter Start": "Từ đầu chương",
|
||||
"Start reading from the beginning of the chapter": "Bắt đầu đọc lại từ đầu chương này",
|
||||
"Resume": "Tiếp tục",
|
||||
"Continue from where you left off": "Tiếp tục từ nơi bạn đã dừng lại",
|
||||
"From Current Page": "Từ trang hiện tại",
|
||||
"Start from where you are currently reading": "Bắt đầu từ nơi bạn hiện đang đọc",
|
||||
"From Selection": "Từ phần đã chọn",
|
||||
"Speed Reading Mode": "Chế độ đọc nhanh",
|
||||
"Scroll left": "Cuộn sang trái",
|
||||
"Scroll right": "Cuộn sang phải",
|
||||
"Library Sync Progress": "Tiến trình đồng bộ hóa thư viện",
|
||||
"Back to library": "Quay lại thư viện",
|
||||
"Group by...": "Nhóm theo...",
|
||||
"Export as Plain Text": "Xuất dưới dạng văn bản thuần túy",
|
||||
"Export as Markdown": "Xuất dưới dạng Markdown",
|
||||
"Show Page Navigation Buttons": "Nút điều hướng trang",
|
||||
"Page {{number}}": "Trang {{number}}",
|
||||
"highlight": "làm nổi bật",
|
||||
"underline": "gạch chân",
|
||||
"squiggly": "ngoằn ngoèo",
|
||||
"red": "đỏ",
|
||||
"violet": "tím",
|
||||
"blue": "xanh lam",
|
||||
"green": "xanh lá",
|
||||
"yellow": "vàng",
|
||||
"Select {{style}} style": "Chọn kiểu {{style}}",
|
||||
"Select {{color}} color": "Chọn màu {{color}}",
|
||||
"Close Book": "Đóng sách",
|
||||
"Speed Reading": "Đọc nhanh",
|
||||
"Close Speed Reading": "Đóng đọc nhanh",
|
||||
"Authors": "Tác giả",
|
||||
"Books": "Sách",
|
||||
"Groups": "Nhóm",
|
||||
"Back to TTS Location": "Quay lại vị trí TTS",
|
||||
"Metadata": "Siêu dữ liệu",
|
||||
"Image viewer": "Trình xem ảnh",
|
||||
"Previous Image": "Ảnh trước",
|
||||
"Next Image": "Ảnh sau",
|
||||
"Zoomed": "Đã thu phóng",
|
||||
"Zoom level": "Mức thu phóng",
|
||||
"Table viewer": "Trình xem bảng",
|
||||
"Unable to connect to Readwise. Please check your network connection.": "Không thể kết nối với Readwise. Vui lòng kiểm tra kết nối mạng của bạn.",
|
||||
"Invalid Readwise access token": "Mã xác thực Readwise không hợp lệ",
|
||||
"Disconnected from Readwise": "Đã ngắt kết nối với Readwise",
|
||||
"Never": "Không bao giờ",
|
||||
"Readwise Settings": "Cài đặt Readwise",
|
||||
"Connected to Readwise": "Đã kết nối với Readwise",
|
||||
"Last synced: {{time}}": "Lần đồng bộ cuối: {{time}}",
|
||||
"Sync Enabled": "Đã bật đồng bộ",
|
||||
"Disconnect": "Ngắt kết nối",
|
||||
"Connect your Readwise account to sync highlights.": "Kết nối tài khoản Readwise của bạn để đồng bộ các phần đánh dấu.",
|
||||
"Get your access token at": "Lấy mã xác thực tại",
|
||||
"Access Token": "Mã xác thực",
|
||||
"Paste your Readwise access token": "Dán mã xác thực Readwise của bạn vào đây",
|
||||
"Config": "Cấu hình",
|
||||
"Readwise Sync": "Đồng bộ Readwise",
|
||||
"Push Highlights": "Đẩy các phần đánh dấu",
|
||||
"Highlights synced to Readwise": "Các phần đánh dấu đã được đồng bộ với Readwise",
|
||||
"Readwise sync failed: no internet connection": "Đồng bộ Readwise thất bại: không có kết nối internet",
|
||||
"Readwise sync failed: {{error}}": "Đồng bộ Readwise thất bại: {{error}}",
|
||||
"System Screen Brightness": "Độ sáng màn hình hệ thống",
|
||||
"Page:": "Trang:",
|
||||
"Page: {{number}}": "Trang: {{number}}",
|
||||
"Annotation page number": "Số trang chú thích"
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
"Animation": "動畫",
|
||||
"Annotate": "筆記",
|
||||
"Apply": "應用",
|
||||
"Are you sure to delete the selected books?": "您確定要刪除所選書籍嗎?",
|
||||
"Auto Mode": "自動主題",
|
||||
"Behavior": "行為",
|
||||
"Book": "書籍",
|
||||
"Book Cover": "書籍封面",
|
||||
"Bookmark": "書籤",
|
||||
"Cancel": "取消",
|
||||
"Chapter": "章節",
|
||||
@@ -19,7 +17,6 @@
|
||||
"Confirm Deletion": "確認刪除",
|
||||
"Copied to notebook": "已複製到筆記本",
|
||||
"Copy": "複製",
|
||||
"Custom CSS": "自定義 CSS",
|
||||
"Dark Mode": "深色主題",
|
||||
"Default": "預設",
|
||||
"Default Font": "預設字體",
|
||||
@@ -29,7 +26,6 @@
|
||||
"Dictionary": "詞典",
|
||||
"Download Readest": "下載 Readest",
|
||||
"Edit": "編輯",
|
||||
"Enter your custom CSS here...": "在此處輸入您的自定義 CSS...",
|
||||
"Excerpts": "摘錄",
|
||||
"Failed to import book(s): {{filenames}}": "導入書籍失敗:{{filenames}}",
|
||||
"Fast": "快",
|
||||
@@ -42,8 +38,6 @@
|
||||
"Global Settings": "全局設置",
|
||||
"Go Back": "返回",
|
||||
"Go Forward": "前進",
|
||||
"Go Left": "向左",
|
||||
"Go Right": "向右",
|
||||
"Grass": "青草",
|
||||
"Gray": "素雅",
|
||||
"Gruvbox": "暖橘",
|
||||
@@ -51,7 +45,6 @@
|
||||
"Horizontal Direction": "橫排",
|
||||
"Hyphenation": "斷字",
|
||||
"Import Books": "導入書籍",
|
||||
"Language:": "語言",
|
||||
"Layout": "版面",
|
||||
"Light Mode": "淺色主題",
|
||||
"Loading...": "載入中...",
|
||||
@@ -62,7 +55,6 @@
|
||||
"Match Whole Words": "匹配整個單詞",
|
||||
"Maximum Number of Columns": "分欄數",
|
||||
"Minimum Font Size": "最小字號",
|
||||
"Misc": "雜項",
|
||||
"Monospace Font": "等寬字體",
|
||||
"More Info": "更多信息",
|
||||
"Nord": "極光",
|
||||
@@ -74,8 +66,8 @@
|
||||
"Paging Animation": "翻頁動畫",
|
||||
"Paragraph": "段落",
|
||||
"Parallel Read": "並排閱讀",
|
||||
"Published:": "出版日期",
|
||||
"Publisher:": "出版商",
|
||||
"Published": "出版日期",
|
||||
"Publisher": "出版商",
|
||||
"Reading Progress Synced": "閱讀進度已同步",
|
||||
"Reload Page": "重新載入頁面",
|
||||
"Reveal in File Explorer": "在檔案管理器中顯示",
|
||||
@@ -89,7 +81,6 @@
|
||||
"Search...": "搜索...",
|
||||
"Select Book": "選擇書籍",
|
||||
"Select Books": "選擇書籍",
|
||||
"Select Multiple Books": "選擇多本書籍",
|
||||
"Sepia": "舊韻",
|
||||
"Serif Font": "襯線字體",
|
||||
"Show Book Details": "顯示書籍詳情",
|
||||
@@ -100,16 +91,15 @@
|
||||
"Slow": "慢",
|
||||
"Solarized": "日暉",
|
||||
"Speak": "朗讀",
|
||||
"Subjects:": "主題",
|
||||
"Subjects": "主題",
|
||||
"System Fonts": "系統字體",
|
||||
"TTS not supported in this device": "此設備不支持 TTS",
|
||||
"Theme Color": "主題顏色",
|
||||
"Theme Mode": "主題模式",
|
||||
"Translate": "翻譯",
|
||||
"Translated Text": "譯文",
|
||||
"Unknown": "未知",
|
||||
"Untitled": "無標題",
|
||||
"Updated:": "更新日期",
|
||||
"Updated": "更新日期",
|
||||
"Version {{version}}": "版本 {{version}}",
|
||||
"Vertical Direction": "豎排",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "書庫空空如也,您可以導入您的書籍並隨時閱讀。",
|
||||
@@ -118,8 +108,6 @@
|
||||
"Your Library": "書庫",
|
||||
"TTS not supported for PDF": "PDF 文檔暫不支持 TTS",
|
||||
"Override Book Font": "覆蓋書籍字體",
|
||||
"Vertical Margins (px)": "垂直邊距(px)",
|
||||
"Horizontal Margins (%)": "水平邊距(%)",
|
||||
"Apply to All Books": "應用於所有書籍",
|
||||
"Apply to This Book": "應用於此書籍",
|
||||
"Unable to fetch the translation. Try again later.": "無法獲取翻譯,請稍後再試。",
|
||||
@@ -128,14 +116,13 @@
|
||||
"Book Details": "書籍詳情",
|
||||
"From Local File": "從本地文件導入",
|
||||
"TOC": "目錄",
|
||||
"Table of Contents": "目錄",
|
||||
"Book uploaded: {{title}}": "書籍已上傳:{{title}}",
|
||||
"Failed to upload book: {{title}}": "書籍上傳失敗:{{title}}",
|
||||
"Book downloaded: {{title}}": "書籍已下載:{{title}}",
|
||||
"Failed to download book: {{title}}": "書籍下載失敗:{{title}}",
|
||||
"Upload Book": "上傳書籍",
|
||||
"Auto Upload Books to Cloud": "自動上傳書籍到雲端",
|
||||
"{{percentage}}% of Cloud Storage Used.": "已使用 {{percentage}}% 雲存儲空間",
|
||||
"Storage": "存儲空間",
|
||||
"Book deleted: {{title}}": "書籍已刪除:{{title}}",
|
||||
"Failed to delete book: {{title}}": "書籍刪除失敗:{{title}}",
|
||||
"Check Updates on Start": "啟動時檢查更新",
|
||||
@@ -167,7 +154,7 @@
|
||||
"Create a Password": "創建密碼",
|
||||
"Sign up": "註冊",
|
||||
"Signing up...": "正在註冊...",
|
||||
"Don’t have an account? Sign up": "沒有帳號?註冊",
|
||||
"Don't have an account? Sign up": "沒有帳號?註冊",
|
||||
"Check your email for the confirmation link": "檢查您的電子郵件以獲取確認連結",
|
||||
"Signing in ...": "正在登入...",
|
||||
"Send a magic link email": "發送魔法連結郵件",
|
||||
@@ -186,41 +173,15 @@
|
||||
"Token": "令牌",
|
||||
"Your OTP token": "您的 OTP 令牌",
|
||||
"Verify token": "驗證令牌",
|
||||
"Sign in with Google": "使用 Google 登入",
|
||||
"Sign in with Apple": "使用 Apple 登入",
|
||||
"Sign in with GitHub": "使用 GitHub 登入",
|
||||
"Account": "帳戶",
|
||||
"Cloud Storage": "雲端儲存空間",
|
||||
"Failed to delete user. Please try again later.": "刪除使用者失敗。請稍後再試。",
|
||||
"Unlimited Offline/Online Reading": "無限離線/線上閱讀",
|
||||
"Unlimited Cloud Sync Devices": "無限雲端同步裝置",
|
||||
"Essential Text-to-Speech Voices": "基本文字轉語音聲音",
|
||||
"DeepL Free Access": "DeepL免費存取",
|
||||
"Community Support": "社群支援",
|
||||
"500 MB Cloud Sync Space": "500 MB雲端同步空間",
|
||||
"Includes All Free Tier Benefits": "包含所有免費方案權益",
|
||||
"AI Summaries": "AI摘要",
|
||||
"AI Translations": "AI翻譯",
|
||||
"Priority Support": "優先支援",
|
||||
"DeepL Pro Access": "DeepL專業版存取",
|
||||
"Expanded Text-to-Speech Voices": "擴展文字轉語音聲音",
|
||||
"2000 MB Cloud Sync Space": "2000 MB雲端同步空間",
|
||||
"Includes All Plus Tier Benefits": "包含所有Plus方案權益",
|
||||
"Unlimited AI Summaries": "無限AI摘要",
|
||||
"Unlimited AI Translations": "無限AI翻譯",
|
||||
"Unlimited DeepL Pro Access": "無限DeepL專業版存取",
|
||||
"Advanced AI Tools": "進階AI工具",
|
||||
"Early Feature Access": "搶先體驗新功能",
|
||||
"10 GB Cloud Sync Space": "10 GB雲端同步空間",
|
||||
"Loading profile...": "載入個人資料中...",
|
||||
"Features": "功能",
|
||||
"Delete Account": "刪除帳戶",
|
||||
"Delete Your Account?": "刪除您的帳戶?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "此操作無法撤銷。您在雲端的所有資料將被永久刪除。",
|
||||
"Delete Permanently": "永久刪除",
|
||||
"Free Tier": "免費方案",
|
||||
"Plus Tier": "Plus方案",
|
||||
"Pro Tier": "專業方案",
|
||||
"RTL Direction": "從右向左",
|
||||
"Maximum Column Height": "最大列高",
|
||||
"Maximum Column Width": "最大列寬",
|
||||
@@ -229,7 +190,7 @@
|
||||
"No supported files found. Supported formats: {{formats}}": "未找到支援的檔案。支援的格式:{{formats}}",
|
||||
"Drop to Import Books": "拖放導入書籍",
|
||||
"Custom": "自定義",
|
||||
"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": "世界是一個舞台,\n所有的男人和女人都只是演員:\n他們有出口,也有入口;\n一個人在他的時代扮演許多角色。 \n\n——威廉·莎士比亞",
|
||||
"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": "世界是一個舞台,\n所有的男人和女人都只是演員:\n他們有出口,也有入口;\n一個人在他的時代扮演許多角色。 \n\n——威廉·莎士比亞",
|
||||
"Custom Theme": "自定義主題",
|
||||
"Theme Name": "主題名稱",
|
||||
"Text Color": "文字顏色",
|
||||
@@ -256,9 +217,9 @@
|
||||
"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.": "不支援此書籍格式。",
|
||||
"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": "視窗置頂",
|
||||
@@ -271,13 +232,13 @@
|
||||
"Clear Search": "清除搜索",
|
||||
"Header & Footer": "頁眉頁腳",
|
||||
"Apply also in Scrolled Mode": "滾動模式同樣適用",
|
||||
"A new version of Readest is Available!": "有新版本的 Readest 可更新!",
|
||||
"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:": "更新日誌:",
|
||||
"Changelog": "更新日誌:",
|
||||
"Software Update": "軟體更新",
|
||||
"Title": "書名",
|
||||
"Date Read": "閱讀日期",
|
||||
@@ -286,18 +247,15 @@
|
||||
"Ascending": "升序",
|
||||
"Descending": "降序",
|
||||
"Sort by...": "排序方式...",
|
||||
"Added:": "添加日期",
|
||||
"Format:": "格式",
|
||||
"Added": "添加日期",
|
||||
"GuanKiapTsingKhai-T": "原俠正楷-T",
|
||||
"Description:": "簡介",
|
||||
"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": "豎屏",
|
||||
@@ -306,6 +264,788 @@
|
||||
"Checking for updates...": "檢查更新中...",
|
||||
"Error checking for updates": "檢查更新時出錯",
|
||||
"Details": "詳情",
|
||||
"Cancel Selection": "取消選擇",
|
||||
"File Size:": "檔案大小"
|
||||
"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": "重疊像素",
|
||||
"System Language": "系統語言",
|
||||
"Security": "安全",
|
||||
"Allow JavaScript": "允許 JavaScript",
|
||||
"Enable only if you trust the file.": "建議僅在您信任該檔案時啟用",
|
||||
"Sort TOC by Page": "依頁碼排序目錄",
|
||||
"Search in {{count}} Book(s)..._other": "在 {{count}} 本書中搜索...",
|
||||
"No notes match your search": "找不到符合的筆記",
|
||||
"Search notes and excerpts...": "搜尋筆記...",
|
||||
"Sign in to Sync": "登入以同步",
|
||||
"Synced at {{time}}": "同步於:{{time}}",
|
||||
"Never synced": "從未同步",
|
||||
"Show Remaining Time": "顯示剩餘時間",
|
||||
"{{time}} min left in chapter": "本章剩餘 {{time}} 分鐘",
|
||||
"Override Book Color": "覆蓋書籍顏色",
|
||||
"Login Required": "需登入",
|
||||
"Quota Exceeded": "額度不足",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "已使用 {{percentage}}% 的每日翻譯字數",
|
||||
"Translation Characters": "翻譯字數",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} 種語音",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "出了點問題。別擔心,我們的團隊已經收到通知,正在努力修復。",
|
||||
"Error Details:": "錯誤詳情:",
|
||||
"Try Again": "再試一次",
|
||||
"Need help?": "需要幫助嗎?",
|
||||
"Contact Support": "聯繫支援",
|
||||
"Code Highlighting": "程式碼高亮",
|
||||
"Enable Highlighting": "啟用高亮",
|
||||
"Code Language": "程式語言",
|
||||
"Top Margin (px)": "上邊距",
|
||||
"Bottom Margin (px)": "下邊距",
|
||||
"Right Margin (px)": "右邊距",
|
||||
"Left Margin (px)": "左邊距",
|
||||
"Column Gap (%)": "欄間距",
|
||||
"Always Show Status Bar": "始終顯示狀態欄",
|
||||
"Custom Content CSS": "自訂內容 CSS",
|
||||
"Enter CSS for book content styling...": "輸入書籍內容樣式的CSS…",
|
||||
"Custom Reader UI CSS": "自訂介面 CSS",
|
||||
"Enter CSS for reader interface styling...": "輸入閱讀器介面樣式的CSS…",
|
||||
"Crop": "裁剪",
|
||||
"Book Covers": "書籍封面",
|
||||
"Fit": "適應",
|
||||
"Reset {{settings}}": "重置{{settings}}",
|
||||
"Reset Settings": "重置設置",
|
||||
"{{count}} pages left in chapter_other": "<1>本章剩餘 </1><0>{{count}}</0><1> 頁</1>",
|
||||
"Show Remaining Pages": "顯示剩餘頁數",
|
||||
"Source Han Serif CN": "思源宋體",
|
||||
"Huiwen-MinchoGBK": "匯文明朝體",
|
||||
"KingHwa_OldSong": "京華老宋體",
|
||||
"Manage Subscription": "管理訂閱",
|
||||
"Coming Soon": "即將推出",
|
||||
"Upgrade to {{plan}}": "升級到 {{plan}}",
|
||||
"Upgrade to Plus or Pro": "升級到 Plus 或 Pro",
|
||||
"Current Plan": "當前方案",
|
||||
"Plan Limits": "方案限制",
|
||||
"Processing your payment...": "正在處理付款...",
|
||||
"Please wait while we confirm your subscription.": "請稍候,我們正在確認您的訂閱。",
|
||||
"Payment Processing": "付款處理中",
|
||||
"Your payment is being processed. This usually takes a few moments.": "您的付款正在處理中,通常需要幾秒鐘。",
|
||||
"Payment Failed": "付款失敗",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "我們無法處理您的訂閱。請重試,或若問題持續,請聯絡客服。",
|
||||
"Back to Profile": "返回個人資料",
|
||||
"Subscription Successful!": "訂閱成功!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "感謝您的訂閱!您的付款已成功完成。",
|
||||
"Email:": "電子郵件:",
|
||||
"Plan:": "方案:",
|
||||
"Amount:": "金額:",
|
||||
"Go to Library": "前往書庫",
|
||||
"Need help? Contact our support team at support@readest.com": "需要協助?請聯絡支援團隊:support@readest.com",
|
||||
"Free Plan": "免費方案",
|
||||
"month": "月",
|
||||
"AI Translations (per day)": "AI 翻譯(每日)",
|
||||
"Plus Plan": "Plus 方案",
|
||||
"Includes All Free Plan Benefits": "包含所有免費方案功能",
|
||||
"Pro Plan": "Pro 方案",
|
||||
"More AI Translations": "更多 AI 翻譯",
|
||||
"Complete Your Subscription": "完成您的訂閱",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "已使用 {{percentage}}% 的雲同步空間。",
|
||||
"Cloud Sync Storage": "雲同步空間",
|
||||
"Disable": "禁用",
|
||||
"Enable": "啟用",
|
||||
"Upgrade to Readest Premium": "升級到 Readest Premium",
|
||||
"Show Source Text": "顯示原文",
|
||||
"Cross-Platform Sync": "跨平台同步",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "在所有裝置間無縫同步您的書庫、閱讀進度、標註和筆記——再也不會遺失閱讀位置。",
|
||||
"Customizable Reading": "個人化閱讀",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "透過可調節的字體、版面配置、主題和進階顯示設定個人化每個細節,獲得完美閱讀體驗。",
|
||||
"AI Read Aloud": "AI 朗讀",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "享受免手持的閱讀體驗,透過自然流暢的 AI 聲音讓書籍栩栩如生。",
|
||||
"AI Translations": "AI 翻譯",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "借助 Google、Azure 或 DeepL 的強大功能即時翻譯任何文字——理解任何語言的內容。",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "與其他讀者交流,在我們友善的社群頻道中快速獲得幫助。",
|
||||
"Unlimited AI Read Aloud Hours": "無限 AI 朗讀時長",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "無限制聆聽——將任意數量的文字轉換為沉浸式音訊。",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "解鎖增強翻譯功能,享受更多翻譯用量和進階選項。",
|
||||
"DeepL Pro Access": "DeepL Pro 存取權限",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "享受更快回應和專屬幫助,隨時獲得所需支援。",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "已達每日翻譯配額。升級方案以繼續使用 AI 翻譯。",
|
||||
"Includes All Plus Plan Benefits": "包含所有 Plus 方案優惠",
|
||||
"Early Feature Access": "搶先體驗新功能",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "搶先體驗最新功能、更新與創新成果。",
|
||||
"Advanced AI Tools": "進階 AI 工具",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "運用強大的 AI 工具實現智慧閱讀、翻譯和內容探索。",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "使用最精準的翻譯引擎每天翻譯多達10萬字元。",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "使用最精準的翻譯引擎每天翻譯多達50萬字元。",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "使用多達 5GB 的安全雲端儲存保存和存取您的整個閱讀收藏。",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "使用多達 20GB 的安全雲端儲存保存和存取您的整個閱讀收藏。",
|
||||
"Deleted cloud backup of the book: {{title}}": "已刪除書籍的雲端備份:{{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "您確定要刪除所選書籍的雲端備份嗎?",
|
||||
"What's New in Readest": "Readest 更新功能",
|
||||
"Enter book title": "輸入書名",
|
||||
"Subtitle": "副標題",
|
||||
"Enter book subtitle": "輸入書籍副標題",
|
||||
"Enter author name": "輸入作者姓名",
|
||||
"Series": "系列",
|
||||
"Enter series name": "輸入系列名稱",
|
||||
"Series Index": "系列編號",
|
||||
"Enter series index": "輸入系列編號",
|
||||
"Total in Series": "系列總數",
|
||||
"Enter total books in series": "輸入系列中書籍總數",
|
||||
"Enter publisher": "輸入出版社",
|
||||
"Publication Date": "出版日期",
|
||||
"Identifier": "識別碼",
|
||||
"Enter book description": "輸入書籍描述",
|
||||
"Change cover image": "替換封面圖片",
|
||||
"Replace": "替換",
|
||||
"Unlock cover": "解鎖封面",
|
||||
"Lock cover": "鎖定封面",
|
||||
"Auto-Retrieve Metadata": "自動擷取中繼資料",
|
||||
"Auto-Retrieve": "自動擷取",
|
||||
"Unlock all fields": "解鎖所有欄位",
|
||||
"Unlock All": "全部解鎖",
|
||||
"Lock all fields": "鎖定所有欄位",
|
||||
"Lock All": "全部鎖定",
|
||||
"Reset": "重設",
|
||||
"Edit Metadata": "編輯中繼資料",
|
||||
"Locked": "已鎖定",
|
||||
"Select Metadata Source": "選擇中繼資料來源",
|
||||
"Keep manual input": "保留手動輸入",
|
||||
"Google Books": "Google 圖書",
|
||||
"Open Library": "開放圖書館",
|
||||
"Fiction, Science, History": "小說,科學,歷史",
|
||||
"Open Book in New Window": "在新視窗中打開書籍",
|
||||
"Voices for {{lang}}": "{{lang}} 的語音",
|
||||
"Yandex Translate": "Yandex 翻譯",
|
||||
"YYYY or YYYY-MM-DD": "YYYY 或 YYYY-MM-DD",
|
||||
"Restore Purchase": "恢復購買",
|
||||
"No purchases found to restore.": "未找到可恢復的購買。",
|
||||
"Failed to restore purchases.": "恢復購買失敗。",
|
||||
"Failed to manage subscription.": "管理訂閱失敗。",
|
||||
"Failed to load subscription plans.": "加載訂閱計劃失敗。",
|
||||
"year": "年",
|
||||
"Failed to create checkout session": "創建結帳會話失敗",
|
||||
"Storage": "雲同步空間",
|
||||
"Terms of Service": "服務條款",
|
||||
"Privacy Policy": "隱私政策",
|
||||
"Disable Double Click": "禁用雙擊",
|
||||
"TTS not supported for this document": "TTS 不支援此文件",
|
||||
"Reset Password": "重設密碼",
|
||||
"Show Reading Progress": "顯示閱讀進度",
|
||||
"Reading Progress Style": "閱讀進度樣式",
|
||||
"Page Number": "頁碼",
|
||||
"Percentage": "百分比",
|
||||
"Deleted local copy of the book: {{title}}": "已刪除書籍的本地副本:{{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "未能刪除書籍的雲端備份:{{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "未能刪除書籍的本地副本:{{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "您確定要刪除所選書籍的本地副本嗎?",
|
||||
"Remove from Cloud & Device": "從雲端和設備中移除",
|
||||
"Remove from Cloud Only": "僅從雲端移除",
|
||||
"Remove from Device Only": "僅從設備中移除",
|
||||
"Disconnected": "已斷開連接",
|
||||
"KOReader Sync Settings": "KOReader 同步設置",
|
||||
"Sync Strategy": "同步策略",
|
||||
"Ask on conflict": "發生衝突時詢問",
|
||||
"Always use latest": "始終使用最新",
|
||||
"Send changes only": "僅發送更改",
|
||||
"Receive changes only": "僅接收更改",
|
||||
"Checksum Method": "校驗和方法",
|
||||
"File Content (recommended)": "文件內容(推薦)",
|
||||
"File Name": "文件名",
|
||||
"Device Name": "設備名稱",
|
||||
"Connect to your KOReader Sync server.": "連接到您的 KOReader 同步伺服器。",
|
||||
"Server URL": "伺服器 URL",
|
||||
"Username": "用戶名",
|
||||
"Your Username": "您的用戶名",
|
||||
"Password": "密碼",
|
||||
"Connect": "連接",
|
||||
"KOReader Sync": "KOReader 同步",
|
||||
"Sync Conflict": "同步衝突",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "從 \"{{deviceName}}\" 同步閱讀進度?",
|
||||
"another device": "另一個設備",
|
||||
"Local Progress": "本地進度",
|
||||
"Remote Progress": "遠程進度",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "第 {{page}} 頁,共 {{total}} 頁 ({{percentage}}%)",
|
||||
"Current position": "當前位置",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "大約第 {{page}} 頁,共 {{total}} 頁 ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "大約 {{percentage}}%",
|
||||
"Failed to connect": "連接失敗",
|
||||
"Sync Server Connected": "同步伺服器已連接",
|
||||
"Sync as {{userDisplayName}}": "以 {{userDisplayName}} 身份同步",
|
||||
"Custom Fonts": "自訂字型",
|
||||
"Cancel Delete": "取消刪除",
|
||||
"Import Font": "匯入字型",
|
||||
"Delete Font": "刪除字型",
|
||||
"Tips": "提示",
|
||||
"Custom fonts can be selected from the Font Face menu": "可以從字型選單中選擇自訂字型",
|
||||
"Manage Custom Fonts": "管理自訂字型",
|
||||
"Select Files": "選擇檔案",
|
||||
"Select Image": "選擇圖片",
|
||||
"Select Video": "選擇影片",
|
||||
"Select Audio": "選擇音訊",
|
||||
"Select Fonts": "選擇字型",
|
||||
"Supported font formats: .ttf, .otf, .woff, .woff2": "支援的字型格式:.ttf, .otf, .woff, .woff2",
|
||||
"Push Progress": "推送閱讀進度",
|
||||
"Pull Progress": "拉取閱讀進度",
|
||||
"Previous Paragraph": "上一段",
|
||||
"Previous Sentence": "上一句",
|
||||
"Pause": "暫停",
|
||||
"Play": "播放",
|
||||
"Next Sentence": "下一句",
|
||||
"Next Paragraph": "下一段",
|
||||
"Separate Cover Page": "單獨封面頁",
|
||||
"Resize Notebook": "調整筆記本大小",
|
||||
"Resize Sidebar": "調整側邊欄大小",
|
||||
"Get Help from the Readest Community": "從 Readest 社群獲取幫助",
|
||||
"Remove cover image": "移除封面圖片",
|
||||
"Bookshelf": "書架",
|
||||
"View Menu": "檢視選單",
|
||||
"Settings Menu": "設定選單",
|
||||
"View account details and quota": "檢視帳戶詳細資料與配額",
|
||||
"Library Header": "圖書館標題",
|
||||
"Book Content": "書籍內容",
|
||||
"Footer Bar": "頁腳欄",
|
||||
"Header Bar": "標頭欄",
|
||||
"View Options": "檢視選項",
|
||||
"Book Menu": "書籍選單",
|
||||
"Search Options": "搜尋選項",
|
||||
"Close": "關閉",
|
||||
"Delete Book Options": "刪除書籍選項",
|
||||
"ON": "開啟",
|
||||
"OFF": "關閉",
|
||||
"Reading Progress": "閱讀進度",
|
||||
"Page Margin": "頁邊距",
|
||||
"Remove Bookmark": "移除書籤",
|
||||
"Add Bookmark": "添加書籤",
|
||||
"Books Content": "書籍內容",
|
||||
"Jump to Location": "跳轉到位置",
|
||||
"Unpin Notebook": "取消固定筆記本",
|
||||
"Pin Notebook": "固定筆記本",
|
||||
"Hide Search Bar": "隱藏搜索欄",
|
||||
"Show Search Bar": "顯示搜索欄",
|
||||
"On {{current}} of {{total}} page": "在第 {{current}} 頁,共 {{total}} 頁",
|
||||
"Section Title": "章節標題",
|
||||
"Decrease": "減少",
|
||||
"Increase": "增加",
|
||||
"Settings Panels": "設定面板",
|
||||
"Settings": "設定",
|
||||
"Unpin Sidebar": "取消固定側邊欄",
|
||||
"Pin Sidebar": "固定側邊欄",
|
||||
"Toggle Sidebar": "切換側邊欄",
|
||||
"Toggle Translation": "切換翻譯",
|
||||
"Translation Disabled": "翻譯已禁用",
|
||||
"Minimize": "最小化",
|
||||
"Maximize or Restore": "最大化或還原",
|
||||
"Exit Parallel Read": "退出並排閱讀",
|
||||
"Enter Parallel Read": "進入並排閱讀",
|
||||
"Zoom Level": "縮放級別",
|
||||
"Zoom Out": "縮小",
|
||||
"Reset Zoom": "重置縮放",
|
||||
"Zoom In": "放大",
|
||||
"Zoom Mode": "縮放模式",
|
||||
"Single Page": "單頁",
|
||||
"Auto Spread": "自動展開",
|
||||
"Fit Page": "適應頁面",
|
||||
"Fit Width": "適應寬度",
|
||||
"Failed to select directory": "選擇目錄失敗",
|
||||
"The new data directory must be different from the current one.": "新的數據目錄必須與當前目錄不同。",
|
||||
"Migration failed: {{error}}": "遷移失敗: {{error}}",
|
||||
"Change Data Location": "更改數據位置",
|
||||
"Current Data Location": "當前數據位置",
|
||||
"Total size: {{size}}": "總大小: {{size}}",
|
||||
"Calculating file info...": "正在計算文件信息...",
|
||||
"New Data Location": "新的數據位置",
|
||||
"Choose Different Folder": "選擇不同的文件夾",
|
||||
"Choose New Folder": "選擇新文件夾",
|
||||
"Migrating data...": "正在遷移數據...",
|
||||
"Copying: {{file}}": "正在複製: {{file}}",
|
||||
"{{current}} of {{total}} files": "{{current}} / {{total}} 個文件",
|
||||
"Migration completed successfully!": "遷移成功完成!",
|
||||
"Your data has been moved to the new location. Please restart the application to complete the process.": "您的數據已移動到新位置。請重新啟動應用程序以完成此過程。",
|
||||
"Migration failed": "遷移失敗",
|
||||
"Important Notice": "重要通知",
|
||||
"This will move all your app data to the new location. Make sure the destination has enough free space.": "這將把您所有的應用數據移動到新位置。請確保目標位置有足夠的可用空間。",
|
||||
"Restart App": "重新啟動應用",
|
||||
"Start Migration": "開始遷移",
|
||||
"Advanced Settings": "高級設置",
|
||||
"File count: {{size}}": "文件數量: {{size}}",
|
||||
"Background Read Aloud": "背景朗讀",
|
||||
"Ready to read aloud": "朗讀準備就緒",
|
||||
"Read Aloud": "朗讀",
|
||||
"Screen Brightness": "螢幕亮度",
|
||||
"Background Image": "背景圖片",
|
||||
"Import Image": "匯入圖片",
|
||||
"Opacity": "不透明度",
|
||||
"Size": "大小",
|
||||
"Cover": "覆蓋",
|
||||
"Contain": "適應",
|
||||
"{{number}} pages left in chapter": "<1>本章剩餘</1><0>{{number}}</0><1>頁</1>",
|
||||
"Device": "設備",
|
||||
"E-Ink Mode": "E-Ink 模式",
|
||||
"Highlight Colors": "高亮顏色",
|
||||
"Pagination": "翻頁",
|
||||
"Disable Double Tap": "禁用雙擊",
|
||||
"Tap to Paginate": "點擊翻頁",
|
||||
"Click to Paginate": "點擊翻頁",
|
||||
"Tap Both Sides": "點擊兩側翻頁",
|
||||
"Click Both Sides": "點擊兩側翻頁",
|
||||
"Swap Tap Sides": "交換點擊區域",
|
||||
"Swap Click Sides": "交換點擊區域",
|
||||
"Source and Translated": "原文和翻譯",
|
||||
"Translated Only": "僅翻譯",
|
||||
"Source Only": "僅原文",
|
||||
"TTS Text": "TTS 文字",
|
||||
"The book file is corrupted": "書籍文件已損壞",
|
||||
"The book file is empty": "書籍文件為空",
|
||||
"Failed to open the book file": "無法打開書籍文件",
|
||||
"On-Demand Purchase": "按需購買",
|
||||
"Full Customization": "完全自定義",
|
||||
"Lifetime Plan": "終身方案",
|
||||
"One-Time Payment": "一次性付款",
|
||||
"Make a single payment to enjoy lifetime access to specific features on all devices. Purchase specific features or services only when you need them.": "進行一次性付款以享受在所有設備上對特定功能的終身訪問。僅在需要時購買特定功能或服務。",
|
||||
"Expand Cloud Sync Storage": "擴展雲同步存儲",
|
||||
"Expand your cloud storage forever with a one-time purchase. Each additional purchase adds more space.": "通過一次性購買,永久擴展您的雲存儲。每次額外購買都會增加更多空間。",
|
||||
"Unlock All Customization Options": "解鎖所有自定義選項",
|
||||
"Unlock additional themes, fonts, layout options and read aloud, translators, cloud storage services.": "解鎖其他主題、字體、佈局選項以及朗讀、翻譯、雲存儲服務。",
|
||||
"Purchase Successful!": "購買成功!",
|
||||
"lifetime": "永久",
|
||||
"Thank you for your purchase! Your payment has been processed successfully.": "感謝您的購買!您的付款已成功處理。",
|
||||
"Order ID:": "訂單編號:",
|
||||
"TTS Highlighting": "TTS 高亮",
|
||||
"Style": "樣式",
|
||||
"Underline": "底線",
|
||||
"Strikethrough": "刪除線",
|
||||
"Squiggly": "波浪線",
|
||||
"Outline": "輪廓",
|
||||
"Save Current Color": "儲存目前顏色",
|
||||
"Quick Colors": "快速顏色",
|
||||
"Highlighter": "螢光筆",
|
||||
"Save Book Cover": "保存書籍封面",
|
||||
"Auto-save last book cover": "自動保存最後一本書的封面",
|
||||
"Back": "返回",
|
||||
"Confirmation email sent! Please check your old and new email addresses to confirm the change.": "確認郵件已發送!請檢查您的舊電子郵件和新電子郵件以確認更改。",
|
||||
"Failed to update email": "更新電子郵件失敗",
|
||||
"New Email": "新電子郵件",
|
||||
"Your new email": "您的新電子郵件",
|
||||
"Updating email ...": "正在更新電子郵件 ...",
|
||||
"Update email": "更新電子郵件",
|
||||
"Current email": "當前電子郵件",
|
||||
"Update Email": "更新電子郵件",
|
||||
"All": "全部",
|
||||
"Unable to open book": "無法打開書籍",
|
||||
"Punctuation": "標點符號",
|
||||
"Replace Quotation Marks": "替換引號",
|
||||
"Enabled only in vertical layout.": "僅在直排佈局中啟用",
|
||||
"No Conversion": "不轉換",
|
||||
"Simplified to Traditional": "簡體 → 繁體",
|
||||
"Traditional to Simplified": "繁體 → 簡體",
|
||||
"Simplified to Traditional (Taiwan)": "簡體 → 繁體(台灣)",
|
||||
"Simplified to Traditional (Hong Kong)": "簡體 → 繁體(香港)",
|
||||
"Simplified to Traditional (Taiwan), with phrases": "簡體 → 繁體(台灣)慣用語",
|
||||
"Traditional (Taiwan) to Simplified": "繁體(台灣) → 簡體",
|
||||
"Traditional (Hong Kong) to Simplified": "繁體(香港) → 簡體",
|
||||
"Traditional (Taiwan) to Simplified, with phrases": "繁體(台灣) → 簡體慣用語",
|
||||
"Convert Simplified and Traditional Chinese": "簡繁中文轉換",
|
||||
"Convert Mode": "轉換模式",
|
||||
"Failed to auto-save book cover for lock screen: {{error}}": "自動保存鎖定螢幕書籍封面失敗:{{error}}",
|
||||
"Download from Cloud": "從雲端下載",
|
||||
"Upload to Cloud": "上傳到雲端",
|
||||
"Clear Custom Fonts": "清除自訂字型",
|
||||
"Columns": "欄數",
|
||||
"OPDS Catalogs": "OPDS 目錄",
|
||||
"Adding LAN addresses is not supported in the web app version.": "網頁版不支援新增區域網路位址。",
|
||||
"Invalid OPDS catalog. Please check the URL.": "無效的 OPDS 目錄。請檢查 URL。",
|
||||
"Browse and download books from online catalogs": "瀏覽並下載線上目錄的書籍",
|
||||
"My Catalogs": "我的目錄",
|
||||
"Add Catalog": "新增目錄",
|
||||
"No catalogs yet": "尚無目錄",
|
||||
"Add your first OPDS catalog to start browsing books": "新增第一個 OPDS 目錄以開始瀏覽書籍",
|
||||
"Add Your First Catalog": "新增第一個目錄",
|
||||
"Browse": "瀏覽",
|
||||
"Popular Catalogs": "熱門目錄",
|
||||
"Add": "新增",
|
||||
"Add OPDS Catalog": "新增 OPDS 目錄",
|
||||
"Catalog Name": "目錄名稱",
|
||||
"My Calibre Library": "我的 Calibre 圖書館",
|
||||
"OPDS URL": "OPDS URL",
|
||||
"Username (optional)": "使用者名稱(可選)",
|
||||
"Password (optional)": "密碼(可選)",
|
||||
"Description (optional)": "描述(可選)",
|
||||
"A brief description of this catalog": "此目錄的簡短描述",
|
||||
"Validating...": "驗證中...",
|
||||
"View All": "檢視全部",
|
||||
"Forward": "前往",
|
||||
"Home": "首頁",
|
||||
"{{count}} items_other": "{{count}} 項",
|
||||
"Download completed": "下載完成",
|
||||
"Download failed": "下載失敗",
|
||||
"Open Access": "開放存取",
|
||||
"Borrow": "借閱",
|
||||
"Buy": "購買",
|
||||
"Subscribe": "訂閱",
|
||||
"Sample": "試閱",
|
||||
"Download": "下載",
|
||||
"Open & Read": "開啟並閱讀",
|
||||
"Tags": "標籤",
|
||||
"Tag": "標籤",
|
||||
"First": "第一頁",
|
||||
"Previous": "上一頁",
|
||||
"Next": "下一頁",
|
||||
"Last": "最後一頁",
|
||||
"Cannot Load Page": "無法載入頁面",
|
||||
"An error occurred": "發生錯誤",
|
||||
"Online Library": "線上書庫",
|
||||
"URL must start with http:// or https://": "URL 必須以 http:// 或 https:// 開頭",
|
||||
"Title, Author, Tag, etc...": "標題、作者、標籤等...",
|
||||
"Query": "查詢",
|
||||
"Subject": "主題",
|
||||
"Enter {{terms}}": "輸入 {{terms}}",
|
||||
"No search results found": "未找到搜尋結果",
|
||||
"Failed to load OPDS feed: {{status}} {{statusText}}": "載入 OPDS 資料源失敗:{{status}} {{statusText}}",
|
||||
"Search in {{title}}": "在 {{title}} 中搜尋",
|
||||
"Manage Storage": "管理儲存",
|
||||
"Failed to load files": "載入檔案失敗",
|
||||
"Deleted {{count}} file(s)_other": "已刪除 {{count}} 個檔案",
|
||||
"Failed to delete {{count}} file(s)_other": "刪除 {{count}} 個檔案失敗",
|
||||
"Failed to delete files": "刪除檔案失敗",
|
||||
"Total Files": "檔案總數",
|
||||
"Total Size": "總大小",
|
||||
"Quota": "配額",
|
||||
"Used": "已使用",
|
||||
"Files": "檔案",
|
||||
"Search files...": "搜尋檔案...",
|
||||
"Newest First": "最新優先",
|
||||
"Oldest First": "最舊優先",
|
||||
"Largest First": "最大優先",
|
||||
"Smallest First": "最小優先",
|
||||
"Name A-Z": "名稱 A-Z",
|
||||
"Name Z-A": "名稱 Z-A",
|
||||
"{{count}} selected_other": "已選擇 {{count}} 個檔案",
|
||||
"Delete Selected": "刪除所選",
|
||||
"Created": "建立時間",
|
||||
"No files found": "找不到檔案",
|
||||
"No files uploaded yet": "尚未上傳檔案",
|
||||
"files": "檔案",
|
||||
"Page {{current}} of {{total}}": "第 {{current}} 頁,共 {{total}} 頁",
|
||||
"Are you sure to delete {{count}} selected file(s)?_other": "確定要刪除已選擇的 {{count}} 個檔案嗎?",
|
||||
"Cloud Storage Usage": "雲端儲存使用情況",
|
||||
"Rename Group": "重新命名",
|
||||
"From Directory": "從目錄導入",
|
||||
"Successfully imported {{count}} book(s)_other": "成功導入 {{count}} 本書",
|
||||
"Count": "數量",
|
||||
"Start Page": "起始頁",
|
||||
"Search in OPDS Catalog...": "在 OPDS 目錄中搜尋...",
|
||||
"Please log in to use advanced TTS features": "請登入以使用進階 TTS 功能",
|
||||
"Word limit of 30 words exceeded.": "已超過 30 個詞的限制。",
|
||||
"Proofread": "校對",
|
||||
"Current selection": "目前選取內容",
|
||||
"All occurrences in this book": "本書中的所有出現位置",
|
||||
"All occurrences in your library": "整個書庫中的所有出現位置",
|
||||
"Selected text:": "已選取文字:",
|
||||
"Replace with:": "取代為:",
|
||||
"Enter text...": "請輸入文字…",
|
||||
"Case sensitive:": "區分大小寫:",
|
||||
"Scope:": "適用範圍:",
|
||||
"Selection": "選取內容",
|
||||
"Library": "書庫",
|
||||
"Yes": "是",
|
||||
"No": "否",
|
||||
"Proofread Replacement Rules": "校對取代規則",
|
||||
"Selected Text Rules": "已選取文字規則",
|
||||
"No selected text replacement rules": "沒有已選取文字的取代規則",
|
||||
"Book Specific Rules": "書籍層級規則",
|
||||
"No book-level replacement rules": "沒有書籍層級的取代規則",
|
||||
"Disable Quick Action": "停用快速操作",
|
||||
"Enable Quick Action on Selection": "選取時啟用快速操作",
|
||||
"None": "無",
|
||||
"Annotation Tools": "註解工具",
|
||||
"Enable Quick Actions": "啟用快速操作",
|
||||
"Quick Action": "快速操作",
|
||||
"Copy to Notebook": "複製到筆記本",
|
||||
"Copy text after selection": "複製選取後的文字",
|
||||
"Highlight text after selection": "劃線選取後的文字",
|
||||
"Annotate text after selection": "註解選取後的文字",
|
||||
"Search text after selection": "搜尋選取後的文字",
|
||||
"Look up text in dictionary after selection": "在字典中查找選取後的文字",
|
||||
"Look up text in Wikipedia after selection": "在維基百科中查找選取後的文字",
|
||||
"Translate text after selection": "翻譯選取後的文字",
|
||||
"Read text aloud after selection": "朗讀選取後的文字",
|
||||
"Proofread text after selection": "校對選取後的文字",
|
||||
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} 進行中,{{pendingCount}} 等待中",
|
||||
"{{failedCount}} failed": "{{failedCount}} 失敗",
|
||||
"Waiting...": "等待中...",
|
||||
"Failed": "失敗",
|
||||
"Completed": "已完成",
|
||||
"Cancelled": "已取消",
|
||||
"Retry": "重試",
|
||||
"Active": "進行中",
|
||||
"Transfer Queue": "傳輸佇列",
|
||||
"Upload All": "全部上傳",
|
||||
"Download All": "全部下載",
|
||||
"Resume Transfers": "繼續傳輸",
|
||||
"Pause Transfers": "暫停傳輸",
|
||||
"Pending": "等待中",
|
||||
"No transfers": "沒有傳輸",
|
||||
"Retry All": "全部重試",
|
||||
"Clear Completed": "清除已完成",
|
||||
"Clear Failed": "清除失敗",
|
||||
"Upload queued: {{title}}": "上傳已排隊:{{title}}",
|
||||
"Download queued: {{title}}": "下載已排隊:{{title}}",
|
||||
"Book not found in library": "在書庫中找不到書籍",
|
||||
"Unknown error": "未知錯誤",
|
||||
"Please log in to continue": "請登入以繼續",
|
||||
"Cloud File Transfers": "雲端檔案傳輸",
|
||||
"Show Search Results": "顯示搜尋結果",
|
||||
"Search results for '{{term}}'": "包含「{{term}}」的結果",
|
||||
"Close Search": "關閉搜尋",
|
||||
"Previous Result": "上一個結果",
|
||||
"Next Result": "下一個結果",
|
||||
"Bookmarks": "書籤",
|
||||
"Annotations": "註解",
|
||||
"Show Results": "顯示結果",
|
||||
"Clear search": "清除搜尋",
|
||||
"Clear search history": "清除搜尋歷史",
|
||||
"Tap to Toggle Footer": "點擊切換頁尾",
|
||||
"Exported successfully": "匯出成功",
|
||||
"Book exported successfully.": "書籍匯出成功。",
|
||||
"Failed to export the book.": "書籍匯出失敗。",
|
||||
"Export Book": "匯出書籍",
|
||||
"Whole word:": "全詞匹配:",
|
||||
"Error": "錯誤",
|
||||
"Unable to load the article. Try searching directly on {{link}}.": "無法載入文章。請直接在 {{link}} 上搜尋。",
|
||||
"Unable to load the word. Try searching directly on {{link}}.": "無法載入詞彙。請直接在 {{link}} 上搜尋。",
|
||||
"Date Published": "出版日期",
|
||||
"Only for TTS:": "僅用於TTS:",
|
||||
"Uploaded": "已上傳",
|
||||
"Downloaded": "已下載",
|
||||
"Deleted": "已刪除",
|
||||
"Note:": "筆記:",
|
||||
"Time:": "時間:",
|
||||
"Format Options": "格式選項",
|
||||
"Export Date": "匯出日期",
|
||||
"Chapter Titles": "章節標題",
|
||||
"Chapter Separator": "章節分隔符",
|
||||
"Highlights": "標記",
|
||||
"Note Date": "筆記日期",
|
||||
"Advanced": "進階",
|
||||
"Hide": "隱藏",
|
||||
"Show": "顯示",
|
||||
"Use Custom Template": "使用自訂模板",
|
||||
"Export Template": "匯出模板",
|
||||
"Template Syntax:": "模板語法:",
|
||||
"Insert value": "插入值",
|
||||
"Format date (locale)": "格式化日期(區域)",
|
||||
"Format date (custom)": "格式化日期(自訂)",
|
||||
"Conditional": "條件",
|
||||
"Loop": "迴圈",
|
||||
"Available Variables:": "可用變數:",
|
||||
"Book title": "書名",
|
||||
"Book author": "作者",
|
||||
"Export date": "匯出日期",
|
||||
"Array of chapters": "章節陣列",
|
||||
"Chapter title": "章節標題",
|
||||
"Array of annotations": "標註陣列",
|
||||
"Highlighted text": "標記文字",
|
||||
"Annotation note": "標註筆記",
|
||||
"Date Format Tokens:": "日期格式標記:",
|
||||
"Year (4 digits)": "年(4位)",
|
||||
"Month (01-12)": "月(01-12)",
|
||||
"Day (01-31)": "日(01-31)",
|
||||
"Hour (00-23)": "時(00-23)",
|
||||
"Minute (00-59)": "分(00-59)",
|
||||
"Second (00-59)": "秒(00-59)",
|
||||
"Show Source": "顯示原始碼",
|
||||
"No content to preview": "無內容可預覽",
|
||||
"Export": "匯出",
|
||||
"Set Timeout": "設定逾時",
|
||||
"Select Voice": "選擇語音",
|
||||
"Toggle Sticky Bottom TTS Bar": "切換固定TTS欄",
|
||||
"Display what I'm reading on Discord": "在Discord顯示閱讀狀態",
|
||||
"Show on Discord": "在Discord顯示",
|
||||
"Instant {{action}}": "即時{{action}}",
|
||||
"Instant {{action}} Disabled": "即時{{action}}已停用",
|
||||
"Annotation": "註解",
|
||||
"Reset Template": "重置範本",
|
||||
"Annotation style": "標註樣式",
|
||||
"Annotation color": "標註顏色",
|
||||
"Annotation time": "標註時間",
|
||||
"AI": "AI",
|
||||
"Are you sure you want to re-index this book?": "確定要重新索引這本書嗎?",
|
||||
"Enable AI in Settings": "在設定中啟用 AI",
|
||||
"Index This Book": "索引這本書",
|
||||
"Enable AI search and chat for this book": "為這本書啟用 AI 搜尋和聊天",
|
||||
"Start Indexing": "開始索引",
|
||||
"Indexing book...": "正在索引書籍...",
|
||||
"Preparing...": "準備中...",
|
||||
"Delete this conversation?": "刪除此對話?",
|
||||
"No conversations yet": "尚無對話",
|
||||
"Start a new chat to ask questions about this book": "開始新對話來詢問關於這本書的問題",
|
||||
"Rename": "重新命名",
|
||||
"New Chat": "新對話",
|
||||
"Chat": "聊天",
|
||||
"Please enter a model ID": "請輸入模型 ID",
|
||||
"Model not available or invalid": "模型不可用或無效",
|
||||
"Failed to validate model": "模型驗證失敗",
|
||||
"Couldn't connect to Ollama. Is it running?": "無法連線到 Ollama。它是否正在執行?",
|
||||
"Invalid API key or connection failed": "API 金鑰無效或連線失敗",
|
||||
"Connection failed": "連線失敗",
|
||||
"AI Assistant": "AI 助手",
|
||||
"Enable AI Assistant": "啟用 AI 助手",
|
||||
"Provider": "提供者",
|
||||
"Ollama (Local)": "Ollama(本機)",
|
||||
"AI Gateway (Cloud)": "AI 閘道(雲端)",
|
||||
"Ollama Configuration": "Ollama 設定",
|
||||
"Refresh Models": "重新整理模型",
|
||||
"AI Model": "AI 模型",
|
||||
"No models detected": "未偵測到模型",
|
||||
"AI Gateway Configuration": "AI 閘道設定",
|
||||
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "從一系列高品質、經濟實惠的 AI 模型中選擇。您也可以透過下方的「自訂模型」使用自己的模型。",
|
||||
"API Key": "API 金鑰",
|
||||
"Get Key": "取得金鑰",
|
||||
"Model": "模型",
|
||||
"Custom Model...": "自訂模型...",
|
||||
"Custom Model ID": "自訂模型 ID",
|
||||
"Validate": "驗證",
|
||||
"Model available": "模型可用",
|
||||
"Connection": "連線",
|
||||
"Test Connection": "測試連線",
|
||||
"Connected": "已連線",
|
||||
"Custom Colors": "自定義顏色",
|
||||
"Color E-Ink Mode": "彩色墨水屏模式",
|
||||
"Reading Ruler": "閱讀尺",
|
||||
"Enable Reading Ruler": "啟用閱讀尺",
|
||||
"Lines to Highlight": "突出顯示行數",
|
||||
"Ruler Color": "尺子顏色",
|
||||
"Command Palette": "命令面板",
|
||||
"Search settings and actions...": "搜尋設定和操作...",
|
||||
"No results found for": "未找到結果:",
|
||||
"Type to search settings and actions": "輸入以搜尋設定和操作",
|
||||
"Recent": "最近",
|
||||
"navigate": "導覽",
|
||||
"select": "選擇",
|
||||
"close": "關閉",
|
||||
"Search Settings": "搜尋設定",
|
||||
"Page Margins": "頁邊距",
|
||||
"AI Provider": "AI 服務商",
|
||||
"Ollama URL": "Ollama URL",
|
||||
"Ollama Model": "Ollama 模型",
|
||||
"AI Gateway Model": "AI Gateway 模型",
|
||||
"Actions": "操作",
|
||||
"Navigation": "導覽",
|
||||
"Set status for {{count}} book(s)_other": "設定 {{count}} 本書的狀態",
|
||||
"Mark as Unread": "標示為未讀",
|
||||
"Mark as Finished": "標示為已讀",
|
||||
"Finished": "已讀",
|
||||
"Unread": "未讀",
|
||||
"Clear Status": "清除狀態",
|
||||
"Status": "狀態",
|
||||
"Loading": "正在載入",
|
||||
"Exit Paragraph Mode": "退出段落模式",
|
||||
"Paragraph Mode": "段落模式",
|
||||
"Embedding Model": "嵌入模型",
|
||||
"{{count}} book(s) synced_other": "{{count}} 本書已同步",
|
||||
"Unable to start RSVP": "無法啟動 RSVP",
|
||||
"RSVP not supported for PDF": "PDF 不支援 RSVP",
|
||||
"Select Chapter": "選擇章節",
|
||||
"Context": "上下文",
|
||||
"Ready": "準備就緒",
|
||||
"Chapter Progress": "章節進度",
|
||||
"words": "詞",
|
||||
"{{time}} left": "剩餘 {{time}}",
|
||||
"Reading progress": "閱讀進度",
|
||||
"Click to seek": "點擊跳轉",
|
||||
"Skip back 15 words": "回退 15 詞",
|
||||
"Back 15 words (Shift+Left)": "回退 15 詞 (Shift+Left)",
|
||||
"Pause (Space)": "暫停 (空格)",
|
||||
"Play (Space)": "播放 (空格)",
|
||||
"Skip forward 15 words": "前進 15 詞",
|
||||
"Forward 15 words (Shift+Right)": "前進 15 詞 (Shift+Right)",
|
||||
"Pause:": "暫停:",
|
||||
"Decrease speed": "減速",
|
||||
"Slower (Left/Down)": "較慢 (左/下)",
|
||||
"Current speed": "當前速度",
|
||||
"Increase speed": "加速",
|
||||
"Faster (Right/Up)": "較快 (右/上)",
|
||||
"Start RSVP Reading": "開始 RSVP 閱讀",
|
||||
"Choose where to start reading": "選擇閱讀起始位置",
|
||||
"From Chapter Start": "從章節開始",
|
||||
"Start reading from the beginning of the chapter": "重新從本章節開始閱讀",
|
||||
"Resume": "繼續",
|
||||
"Continue from where you left off": "從上次離開的地方繼續",
|
||||
"From Current Page": "從當前頁",
|
||||
"Start from where you are currently reading": "從當前正在閱讀的位置開始",
|
||||
"From Selection": "從選取內容",
|
||||
"Speed Reading Mode": "快讀模式",
|
||||
"Scroll left": "向左捲動",
|
||||
"Scroll right": "向右捲動",
|
||||
"Library Sync Progress": "資料庫同步進度",
|
||||
"Back to library": "返回書庫",
|
||||
"Group by...": "分組方式...",
|
||||
"Export as Plain Text": "匯出為純文字",
|
||||
"Export as Markdown": "匯出為 Markdown",
|
||||
"Show Page Navigation Buttons": "顯示翻頁按鈕",
|
||||
"Page {{number}}": "第 {{number}} 頁",
|
||||
"highlight": "高亮",
|
||||
"underline": "底線",
|
||||
"squiggly": "波浪線",
|
||||
"red": "紅色",
|
||||
"violet": "紫色",
|
||||
"blue": "藍色",
|
||||
"green": "綠色",
|
||||
"yellow": "黃色",
|
||||
"Select {{style}} style": "選擇 {{style}} 樣式",
|
||||
"Select {{color}} color": "選擇 {{color}} 顏色",
|
||||
"Close Book": "關閉書籍",
|
||||
"Speed Reading": "快速閱讀",
|
||||
"Close Speed Reading": "關閉快速閱讀",
|
||||
"Authors": "作者",
|
||||
"Books": "書籍",
|
||||
"Groups": "分組",
|
||||
"Back to TTS Location": "回到朗讀位置",
|
||||
"Metadata": "元數據",
|
||||
"Image viewer": "圖片檢視器",
|
||||
"Previous Image": "上一張圖片",
|
||||
"Next Image": "下一張圖片",
|
||||
"Zoomed": "已縮放",
|
||||
"Zoom level": "縮放級別",
|
||||
"Table viewer": "表格檢視器",
|
||||
"Unable to connect to Readwise. Please check your network connection.": "無法連接到 Readwise。請檢查您的網路連線。",
|
||||
"Invalid Readwise access token": "無效的 Readwise 存取權杖",
|
||||
"Disconnected from Readwise": "已斷開與 Readwise 的連線",
|
||||
"Never": "從不",
|
||||
"Readwise Settings": "Readwise 設定",
|
||||
"Connected to Readwise": "已連接到 Readwise",
|
||||
"Last synced: {{time}}": "上次同步時間:{{time}}",
|
||||
"Sync Enabled": "同步已啟用",
|
||||
"Disconnect": "斷開連線",
|
||||
"Connect your Readwise account to sync highlights.": "連接您的 Readwise 帳戶以同步高亮內容。",
|
||||
"Get your access token at": "獲取您的存取權杖:",
|
||||
"Access Token": "存取權杖",
|
||||
"Paste your Readwise access token": "貼上您的 Readwise 存取權杖",
|
||||
"Config": "配置",
|
||||
"Readwise Sync": "Readwise 同步",
|
||||
"Push Highlights": "推送高亮內容",
|
||||
"Highlights synced to Readwise": "高亮內容已同步到 Readwise",
|
||||
"Readwise sync failed: no internet connection": "Readwise 同步失敗:無網路連線",
|
||||
"Readwise sync failed: {{error}}": "Readwise 同步失敗:{{error}}",
|
||||
"System Screen Brightness": "系統螢幕亮度",
|
||||
"Page:": "頁面:",
|
||||
"Page: {{number}}": "頁面: {{number}}",
|
||||
"Annotation page number": "註解頁碼"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,586 @@
|
||||
{
|
||||
"releases": {
|
||||
"0.9.101": {
|
||||
"date": "2026-02-28",
|
||||
"notes": [
|
||||
"Text-to-Speech: Fixed an issue where TTS could not continue to the next chapter",
|
||||
"Reading: Fixed an issue importing very large TXT files on iOS",
|
||||
"Reading: Fixed the layout of the reading ruler in scrolled mode",
|
||||
"Reading: Smoother page-turn animations for a more fluid experience",
|
||||
"Reading: Added a gallery mode to zoom and browse images within books",
|
||||
"Annotations: Page numbers are now displayed and exported with highlights and notes",
|
||||
"Annotations: Added support for exporting highlights to Readwise",
|
||||
"OPDS: Fixed missing book descriptions in OPDS feeds",
|
||||
"Android: Improved compatibility with older WebView versions on Android"
|
||||
]
|
||||
},
|
||||
"0.9.100": {
|
||||
"date": "2026-02-13",
|
||||
"notes": [
|
||||
"Text-to-Speech: Automatically moves to the next chapter when the current chapter finishes",
|
||||
"Reading: Fixed the reading ruler on Android",
|
||||
"Reading: Improved RSVP layout and stability",
|
||||
"Reading: Fixed an issue where some EPUB books could not adjust font size",
|
||||
"Annotations: Added a magnifier (loupe) when adjusting text selection",
|
||||
"Annotations: Text selection now snaps to word boundaries for better accuracy",
|
||||
"Annotations: Custom highlight colors now apply correctly in the sidebar",
|
||||
"Dictionary: Added a back button for easier navigation",
|
||||
"Dictionary: Fixed an issue where Dictionary and Wikipedia could not open on Android",
|
||||
"Metadata: Added collapsible sections in Book Details",
|
||||
"Metadata: Added support for parsing series information from Calibre-exported EPUB files",
|
||||
"Eink: Improved component layout and colors in E-ink mode",
|
||||
"Layout: Sidebar toggle button position is now persistent"
|
||||
]
|
||||
},
|
||||
"0.9.99": {
|
||||
"date": "2026-02-07",
|
||||
"notes": [
|
||||
"Reading: Added a reading ruler (line highlight) to help guide your eyes while reading",
|
||||
"Reading: Added paragraph-by-paragraph reading mode for more focused navigation",
|
||||
"Reading: Added a speed reading mode to support faster reading workflows",
|
||||
"Reading: Added support for gamepad input to turn pages",
|
||||
"Accessibility: Improved TalkBack support in dropdown menus for smoother navigation",
|
||||
"Accessibility: Page navigation buttons are now always visible to screen readers",
|
||||
"Accessibility: Reading progress is now updated correctly when navigating with screen readers",
|
||||
"Annotations: Expanded highlight customization with more color options",
|
||||
"Annotations: Added an option to export annotations as plain text",
|
||||
"Library: Added grouping by author and series for better library organization",
|
||||
"Library: Added support of marking books as finished",
|
||||
"Search: Added fuzzy search to quickly find settings even with partial keywords",
|
||||
"Table of Contents: Added page numbers for nested TOC entries",
|
||||
"Text-to-Speech: Added navigation controls to return to the current reading position",
|
||||
"Linux: Added an in-app updater for AppImage builds",
|
||||
"E-ink: Improved color support for color E-ink devices to display richer highlights"
|
||||
]
|
||||
},
|
||||
"0.9.98": {
|
||||
"date": "2026-01-21",
|
||||
"notes": [
|
||||
"Bookshelf: Added the ability to export original book files directly from the book details dialog",
|
||||
"Bookshelf: Added a new sorting option to organize books by publish date",
|
||||
"Bookshelf: Improved performance for large library with faster and more stable imports",
|
||||
"OPDS: Improved download reliability by correctly detecting filenames for PDF and CBZ files",
|
||||
"Proofreading: Added support for whole-word replacement to make text corrections more precise",
|
||||
"Notes: Added the ability to export notes using a customizable template",
|
||||
"PDF: Added support for panning within PDF documents for easier navigation",
|
||||
"PDF: Improved performance by pre-rendering the next page for smoother page transitions",
|
||||
"Text-to-Speech: Added support for word replacement to improve pronunciation during playback",
|
||||
"Text-to-Speech: Fixed an issue where playback speed settings were not applied correctly on Linux",
|
||||
"E-ink: Improved reading stability by preventing unwanted scrolling when animations are disabled or on e-ink devices",
|
||||
"Discord: Added Discord Rich Presence to display your current reading status on Desktop platforms",
|
||||
"Android: Fixed an issue that could cause occasional crashes during app startup",
|
||||
"Fonts: Improved web font loading for faster and more stable text rendering"
|
||||
]
|
||||
},
|
||||
"0.9.97": {
|
||||
"date": "2026-01-10",
|
||||
"notes": [
|
||||
"Annotations: Added annotation bubble icons directly on text with instant note popups",
|
||||
"Annotations: Enabled quick highlighting when selecting text",
|
||||
"Annotations: Added the ability to edit the text range of existing highlights",
|
||||
"Annotations: Added an annotation navigation bar for easier browsing",
|
||||
"OPDS: Fixed an issue where CBZ files could not be downloaded from OPDS catalogs",
|
||||
"OPDS: Improved compatibility with Komga OPDS servers",
|
||||
"OPDS: Added support for OPDS servers using self-signed SSL certificates",
|
||||
"PDF: Fixed an issue where some PDF files failed to open",
|
||||
"E-ink: Optimized colors and layout for improved e-ink device readability",
|
||||
"Search: Added search term history for quicker repeated searches",
|
||||
"Text-to-Speech: Reduced excessive pauses between sentences for smoother playback",
|
||||
"File Transfer: Introduced a background transfer queue for uploading and downloading books",
|
||||
"Proofreading: Added a proofread tool that can replace text directly within books"
|
||||
]
|
||||
},
|
||||
"0.9.96": {
|
||||
"date": "2025-12-19",
|
||||
"notes": [
|
||||
"TTS: Resolved an issue where Edge TTS was blocked in certain regions",
|
||||
"OPDS: Improved compatibility with older versions of Calibre Web",
|
||||
"OPDS: Added an instant search bar for faster browsing in OPDS catalogs",
|
||||
"OPDS: Added a curated book catalog from Standard Ebooks",
|
||||
"Bookshelf: Added a “Group Books” action to the context menu",
|
||||
"Comics: Fixed layout issues to improve comic book reading",
|
||||
"Layout: Footnote popups now adapt correctly to different screen sizes",
|
||||
"Layout: Fixed an issue where the maximum inline width was not applied to EPUBs",
|
||||
"Sync: Improved file downloading by correctly handling special characters in filenames",
|
||||
"UI: Added an option to temporarily dismiss the reading progress bar",
|
||||
"Settings: Screen brightness adjustments now apply only to the reader view",
|
||||
"iOS: You can now open files directly in Readest from the Files app",
|
||||
"macOS: Added a global menu option to open files with Readest"
|
||||
]
|
||||
},
|
||||
"0.9.95": {
|
||||
"date": "2025-12-08",
|
||||
"notes": [
|
||||
"OPDS: You can now search, browse, and download ebooks directly from OPDS catalogs",
|
||||
"OPDS: Improved compatibility with Calibre Web for reliable downloads",
|
||||
"OPDS: Fixed an issue where book covers from self-hosted Calibre OPDS servers did not display correctly",
|
||||
"Cloud Storage: You can view and delete files stored in your cloud storage directly from the app",
|
||||
"Bookshelf: Added support for importing books from a folder recursively",
|
||||
"Bookshelf: You can now rename your bookshelf groups",
|
||||
"EPUB: Added support for SVG covers from Standard Ebooks",
|
||||
"Annotations: Keyboard shortcuts no longer copy selected text into the notebook",
|
||||
"Footnotes: Footnote popups now scale properly on small screens",
|
||||
"Touch/Styli: Removed the context menu for smoother interaction on touch and stylus devices",
|
||||
"Layout: Devices with unfoldable screens now automatically switch to a two-column layout",
|
||||
"PDF: Improved zoomed-in navigation and hand-tool behavior for smoother page navigation",
|
||||
"Android: Fixed highlighting of the current sentence when using native Android text-to-speech",
|
||||
"Android: Back button handling now works correctly on Android 15 and above",
|
||||
"Android: You can now open files shared from other apps"
|
||||
]
|
||||
},
|
||||
"0.9.94": {
|
||||
"date": "2025-12-02",
|
||||
"notes": [
|
||||
"OPDS: You can now browse and download ebooks directly from OPDS catalogs",
|
||||
"PDF: Added a hand-tool mode that makes navigating PDF pages easier",
|
||||
"Covers: You can now choose a folder to save the latest book cover image",
|
||||
"CJK: Added one-tap conversion between Simplified and Traditional Chinese",
|
||||
"Shortcuts: Use Ctrl + mouse wheel to quickly zoom in or out",
|
||||
"Library: Added a new option to adjust cover sizes in grid view",
|
||||
"Windows: Added Ebook thumbnails with book covers in Windows Explorer",
|
||||
"TTS: Fixed an issue where the page would scroll when showing the annotator",
|
||||
"Annotations: Resolved layout shifts when selecting text in paginated mode on Android",
|
||||
"Files: Improved backup handling to prevent file corruption",
|
||||
"Layout: Fixed text clipping caused by negative indents",
|
||||
"E-ink: Improved sidebar and notebook background colors for e-ink devices",
|
||||
"Android: The Back button now works properly in menus, dialogs, and alerts",
|
||||
"iOS: Fixed occasional failures when opening books on older iOS versions (16.4 and below)"
|
||||
]
|
||||
},
|
||||
"0.9.93": {
|
||||
"date": "2025-11-20",
|
||||
"notes": [
|
||||
"Sync: Improved reliability for new accounts so your books sync correctly from the start",
|
||||
"Library: Fixed an occasional issue where returning to the library could get stuck",
|
||||
"Layout: Improved image display in paginated mode so images won't exceed the available height",
|
||||
"TXT: Improved performance and stability when opening and parsing TXT files",
|
||||
"Fonts: Fixed an issue where monospace fonts were not applied correctly in code blocks"
|
||||
]
|
||||
},
|
||||
"0.9.92": {
|
||||
"date": "2025-11-18",
|
||||
"notes": [
|
||||
"Bookshelf: Added support for nested groups to organize your books more flexibly",
|
||||
"Sync: Improved reliability by automatically retrying failed book syncs",
|
||||
"Sync: Improved token refresh so login sessions stay active longer on KOReader",
|
||||
"E-Ink: Enhanced readability on the library page for E-Ink devices",
|
||||
"UI: Storage capacity is now displayed with precise values in the user profile",
|
||||
"Reader: You can now import files directly into the currently selected book group",
|
||||
"Account: You can now update the email address linked to your Readest account",
|
||||
"Footnotes: Added compatibility for more footnote formats"
|
||||
]
|
||||
},
|
||||
"0.9.91": {
|
||||
"date": "2025-10-29",
|
||||
"notes": [
|
||||
"TTS: Fixed an issue where system voices were not working on Android",
|
||||
"TTS: Added options to customize the highlight style during read-aloud",
|
||||
"E-Ink: Disabled all shadows to improve display clarity",
|
||||
"E-Ink: Added an option to save the last book cover on Android",
|
||||
"Android: Fixed a crash that occurred when toggling 'Allow Script'",
|
||||
"Android: Adjusted launcher icon size for better compatibility across devices"
|
||||
]
|
||||
},
|
||||
"0.9.90": {
|
||||
"date": "2025-10-28",
|
||||
"notes": [
|
||||
"Sync: Faster library syncing and support for in-app one-time purchases of extra cloud storage",
|
||||
"TTS: You can now choose the target language for read-aloud on translated books",
|
||||
"Reader: New keyboard shortcuts make it easier to move between sections",
|
||||
"Reader: Smoother scrolling for a more comfortable reading experience",
|
||||
"Reader: Footnotes in definition lists are now fully supported",
|
||||
"Reader: Improved search accuracy and display of search results",
|
||||
"Reader: Clearer error messages when importing books",
|
||||
"Reader: Improved safety when loading book content",
|
||||
"Settings: You can now choose your own highlight colors",
|
||||
"Settings: Added option to adjust screen brightness automatically",
|
||||
"E-Ink: Better readability and clearer progress display with underlined links",
|
||||
"Android: You can now store app data on an external SD card",
|
||||
"iOS: Restores your last reading page if the app was closed in the background",
|
||||
"Localization: Added Persian (Farsi) language support"
|
||||
]
|
||||
},
|
||||
"0.9.88": {
|
||||
"date": "2025-10-18",
|
||||
"notes": [
|
||||
"TTS: Fixed issue where the read-aloud indicator disappeared too quickly",
|
||||
"Layout: Overriding book layout no longer affects explicitly set text alignment",
|
||||
"Reader: Added localized number display for reading progress in vertical layout",
|
||||
"Reader: Enhanced swipe responsiveness for smoother page transitions",
|
||||
"Settings: Added an option to disable animations when using E-Ink mode"
|
||||
]
|
||||
},
|
||||
"0.9.87": {
|
||||
"date": "2025-10-16",
|
||||
"notes": [
|
||||
"Reader: Improved chapter detection with better chapter title recognition",
|
||||
"Reader: Enhanced compatibility for parsing more footnote formats",
|
||||
"Theme: Fixed auto theme switching in new reader windows",
|
||||
"TTS: Improved scrolling accuracy to highlighted text during read-aloud",
|
||||
"TTS: Fixed crash issues on certain Android systems"
|
||||
]
|
||||
},
|
||||
"0.9.86": {
|
||||
"date": "2025-10-14",
|
||||
"notes": [
|
||||
"Reader: Added support for per-book background image customization",
|
||||
"Reader: Added option to override book language metadata for improved hyphenation accuracy",
|
||||
"Sync: Readest sync in KOReader can now be triggered using gesture controls",
|
||||
"Sync: Fixed a crash in KOReader that occurred when syncing progress without an open book",
|
||||
"Sync: Improved upload performance with concurrent upload support"
|
||||
]
|
||||
},
|
||||
"0.9.85": {
|
||||
"date": "2025-10-14",
|
||||
"notes": [
|
||||
"TTS: Improved media session control compatibility across more Android systems",
|
||||
"TTS: Made the TTS icon less distracting during read-aloud mode",
|
||||
"Settings: Added an option to adjust screen brightness while reading",
|
||||
"Settings: Added support for custom background images",
|
||||
"PDF: Fixed an issue where scrolling and panning didn’t work correctly when zoomed in",
|
||||
"Android: Added adaptive launcher icon for better system integration",
|
||||
"Reader: Added keyboard shortcuts for toggling bookmarks",
|
||||
"Reader: Annotations are now rendered instantly when opening files"
|
||||
]
|
||||
},
|
||||
"0.9.82": {
|
||||
"date": "2025-10-01",
|
||||
"notes": [
|
||||
"Sync: More reliable syncing between KOReader and Readest",
|
||||
"MOBI: Fixed an issue where some footnotes in MOBI/AZW files were not parsed correctly",
|
||||
"Storage: Added support for changing data location on Windows, macOS, Linux, and Android",
|
||||
"Windows: Portable EXE version now stores all reading data in the directory of the executable",
|
||||
"Android: Added background TTS support with media session controls",
|
||||
"WebView: Extended compatibility down to version 92",
|
||||
"Settings: Added global settings menu to the library page",
|
||||
"iOS: Fixed an issue where custom theme colors were not handled correctly on older Safari versions",
|
||||
"TTS: Added more languages for Edge TTS"
|
||||
]
|
||||
},
|
||||
"0.9.81": {
|
||||
"date": "2025-09-19",
|
||||
"notes": [
|
||||
"TTS: Added desktop common media control support when TTS is playing",
|
||||
"PDF: Disabled swipe-up gesture for toggling the action bar when zoomed in",
|
||||
"Fonts: Fixed an issue where embedded fonts in ebooks were occasionally not applied",
|
||||
"Reader: Fixed an issue where newly created highlights sometimes would not appear",
|
||||
"Reader: Enabled scrolling in the menu when the menu is too long"
|
||||
]
|
||||
},
|
||||
"0.9.80": {
|
||||
"date": "2025-09-19",
|
||||
"notes": [
|
||||
"Accessibility: Added support for popular screen readers on all platforms, making books accessible to readers with visual impairments",
|
||||
"Sync: Progress is now synced across different versions of the same book by aggregated metadata",
|
||||
"Library: Groups are now displayed in list view for easier organization",
|
||||
"Library: Added delete confirmation when removing books from the context menu",
|
||||
"Fonts: Added support for fonts with subfamilies when importing TTF/OTF font files",
|
||||
"Layout: Prevented layout shift in Android when the virtual keyboard appears"
|
||||
]
|
||||
},
|
||||
"0.9.78": {
|
||||
"date": "2025-09-06",
|
||||
"notes": [
|
||||
"TTS: Added option to keep a persistent TTS bar at the bottom of the screen",
|
||||
"TTS: Added previous/next sentence navigation controls in the TTS bar",
|
||||
"TTS: Added media control support on iOS lock screen and AirPods",
|
||||
"TTS: Fixed an issue where headings could not be highlighted for TTS",
|
||||
"Sync: Fixed occasional sync failures between KOReader and Readest",
|
||||
"Fonts: Grouped font styles into custom font families for easier selection",
|
||||
"Reader: Improved text selection sensitivity when using a stylus",
|
||||
"Reader: Books can now be properly closed using the system back button on Android",
|
||||
"EPUB: Fixed parsing to correctly detect the front cover in some EPUB files",
|
||||
"PDF: Added zoom level, zoom mode, and spread mode settings for PDFs",
|
||||
"Reader: Bookmarks can now be edited directly in the sidebar",
|
||||
"Layout: Prevented layout shift when the virtual keyboard appears on Android",
|
||||
"TXT: Improved chapter detection and added support for nested table of contents in TXT files"
|
||||
]
|
||||
},
|
||||
"0.9.76": {
|
||||
"date": "2025-08-30",
|
||||
"notes": [
|
||||
"Sync: Moved KOReader sync controls into the reader sidebar with a manual push/pull option",
|
||||
"Sync: Improved tolerance and reliability for KOReader sync operations",
|
||||
"Sync: Added support for more KOReader server implementations",
|
||||
"Fonts: Added support for importing multiple styles within a font family",
|
||||
"Reader: Fixed an issue where footnotes are empty in some EPUB files",
|
||||
"Theme: Applied theme background color for PDFs on macOS",
|
||||
"Reader: Added support for importing AZW3 files on Android",
|
||||
"Reader: Reduced screen flash when launching the app and opening books",
|
||||
"Layout: Fixed an issue where the grouping modal would not appear on Linux"
|
||||
]
|
||||
},
|
||||
"0.9.75": {
|
||||
"date": "2025-08-22",
|
||||
"notes": [
|
||||
"Fonts: Added support for importing local TTF and ODF fonts",
|
||||
"TTS: Reduced the pause between sentences when using Edge TTS",
|
||||
"TTS: Fixed an issue where voices list could not be opened on older versions of iOS",
|
||||
"TOC: Fixed an issue where nested TOC items would not collapse properly in some books",
|
||||
"Translator: Fixed an issue where translations in the popup did not refresh correctly",
|
||||
"Library: Added option to sort authors with last name first",
|
||||
"Layout: Fixed mismatch in close button position between the reader and library pages",
|
||||
"Layout: Book title now remains centered in the available space",
|
||||
"Layout: Fixed footer bar layout in landscape mode"
|
||||
]
|
||||
},
|
||||
"0.9.72": {
|
||||
"date": "2025-08-18",
|
||||
"notes": [
|
||||
"Fixed Edge TTS voice playback for EPUBs",
|
||||
"Reduced accidental page flips when toggling toolbars",
|
||||
"Fixed images with background colors not displaying correctly in some books",
|
||||
"Added support for custom KOReader Sync Servers on your local network (LAN)"
|
||||
]
|
||||
},
|
||||
"0.9.71": {
|
||||
"date": "2025-08-13",
|
||||
"notes": [
|
||||
"Sync: Added two ways to sync reading progress with KOReader devices",
|
||||
"EPUB: Applied monospace font settings",
|
||||
"EPUB: Fixed initial text alignment for some EPUB files",
|
||||
"PDF: Fixed issue opening large PDF files on Android",
|
||||
"TTS: Improved book language detection when language code is invalid",
|
||||
"iOS: Fixed applying system color scheme in auto theme mode",
|
||||
"Config: Added option to choose reading progress display style (percentage or page number)",
|
||||
"Library: Added option to delete only the local copy of a book",
|
||||
"Account: Added 'Reset Password' button in the account page"
|
||||
]
|
||||
},
|
||||
"0.9.69": {
|
||||
"date": "2025-08-02",
|
||||
"notes": [
|
||||
"Updater: Fixed permission issue when updating in standalone reader window",
|
||||
"Config: Added option to disable double‑click actions",
|
||||
"EPUB: Fixed missing images in some EPUB files",
|
||||
"EPUB: Corrected formatting for book subjects",
|
||||
"TXT: Improved language detection when parsing TXT files",
|
||||
"CSS: Resolved issue where custom font colors could not be applied",
|
||||
"Layout: Corrected cover image width display in 'Fit' mode for list view",
|
||||
"Layout: Fixed unintended scrolling in reader view when highlighting text"
|
||||
]
|
||||
},
|
||||
"0.9.68": {
|
||||
"date": "2025-07-30",
|
||||
"notes": [
|
||||
"Reader: Files now open in a new window by default on Desktop",
|
||||
"Translation: Skips translating content inside <pre>, <code>, and <math> tags",
|
||||
"Performance: Improved multi-part downloading for large books",
|
||||
"Sync: Resolved issue preventing some PDF files from syncing",
|
||||
"CSS: Resolved issue with fixed font color in some EPUB files",
|
||||
"TXT: Enhanced TXT parsing for better compatibility"
|
||||
]
|
||||
},
|
||||
"0.9.67": {
|
||||
"date": "2025-07-22",
|
||||
"notes": [
|
||||
"Reader: Fixed file opening issue when filenames contain commas",
|
||||
"Translation: Added Yandex Translator as a new translation provider",
|
||||
"Layout: Hovering over header now reveals macOS traffic light window controls",
|
||||
"Theme: Reader background color is now customizable for EPUB and PDF",
|
||||
"Library: Search now includes book format, group names, and descriptions",
|
||||
"Library: Various improvements to metadata editor and bookshelf management",
|
||||
"Linux: Fixed social login failure on Linux systems"
|
||||
]
|
||||
},
|
||||
"0.9.66": {
|
||||
"date": "2025-07-21",
|
||||
"notes": [
|
||||
"Layout: Fixed nested table of contents items not expanding in long TOC lists",
|
||||
"Reader: Opening files from the OS now takes highest priority",
|
||||
"TTS: Improved voice filtering for some language codes",
|
||||
"App: Fixed loading issues on older browsers",
|
||||
"App: Fixed wrong MIME types generated on Linux"
|
||||
]
|
||||
},
|
||||
"0.9.65": {
|
||||
"date": "2025-07-19",
|
||||
"notes": [
|
||||
"Reader: Added support for opening multiple reader windows on desktop",
|
||||
"Layout: Added visible window borders on Linux",
|
||||
"TTS: Improved handling of invalid book language",
|
||||
"TTS: Skipped reading footnote anchor links",
|
||||
"Translation: Fixed an issue where table of contents entries were not translated",
|
||||
"Sync: Edited book metadata is now included in sync across devices",
|
||||
"iOS: Disabled context menu when long-pressing on book covers"
|
||||
]
|
||||
},
|
||||
"0.9.64": {
|
||||
"date": "2025-07-16",
|
||||
"notes": [
|
||||
"Library: Added support to edit book metadata",
|
||||
"Layout: Fixed bookmark icon placement when the sidebar is pinned",
|
||||
"Reader: Fixed an issue where links to other chapters in EPUB files did not work",
|
||||
"Reader: Lightened the highlight color in dark mode for better contrast",
|
||||
"Reader: Pull-to-refresh gesture is now smoother and more responsive",
|
||||
"Reader: Removed unintended indentation on images",
|
||||
"Search: Improved responsiveness of full-text search for faster results"
|
||||
]
|
||||
},
|
||||
"0.9.63": {
|
||||
"date": "2025-07-07",
|
||||
"notes": [
|
||||
"Reader: Fixed links not working in some MOBI books",
|
||||
"TTS: Fixed an issue where some text was skipped during reading",
|
||||
"Library: You can now delete only the cloud backup of a book without removing the local copy",
|
||||
"Performance: Fonts in the font list now load only when displayed for faster startup",
|
||||
"Performance: Opening large MOBI books is now significantly faster",
|
||||
"Layout: Fixed overlay scrollbar display in longer TOC items list",
|
||||
"Layout: Improved grid cover image height in crop mode",
|
||||
"Layout: Removed unsupported screen orientation lock option on iPad",
|
||||
"Layout: System context menu no longer appears when selecting text on iPad",
|
||||
"Translation: Added Thai (th-TH) language support"
|
||||
]
|
||||
},
|
||||
"0.9.62": {
|
||||
"date": "2025-07-02",
|
||||
"notes": [
|
||||
"Subscription: You can now upgrade to Readest Premium and manage your subscription",
|
||||
"Reader: Added an option to toggle Parallel Reading when viewing multiple books",
|
||||
"Translation: Added an option to show or hide the original text in translations",
|
||||
"Layout: TOC on Android now uses an overlay scrollbar for easier navigation",
|
||||
"Layout: Custom CSS can now also be applied to the library page",
|
||||
"Fonts: Added three new CJK fonts to choose from",
|
||||
"Shortcuts: Press ESC to quickly close the search bar"
|
||||
]
|
||||
},
|
||||
"0.9.61": {
|
||||
"date": "2025-06-26",
|
||||
"notes": [
|
||||
"Layout: Fixed view menu width on smaller screens",
|
||||
"Layout: Added option to crop or fit book covers in library view",
|
||||
"Reader: You can now apply custom CSS to the reading interface (UI)",
|
||||
"Reader: Added an option to show remaining pages in the current chapter",
|
||||
"Settings: Added options to reset settings from the config dialog",
|
||||
"Import: Fixed encoded filenames when importing TXT files on iOS",
|
||||
"Linux: Fixed missing app icon on some Linux distributions",
|
||||
"GitHub: Added donation link to support Readest development"
|
||||
]
|
||||
},
|
||||
"0.9.60": {
|
||||
"date": "2025-06-22",
|
||||
"notes": [
|
||||
"Layout has been improved for iPad devices",
|
||||
"Screen orientation changes are now smoother on iOS",
|
||||
"Importing books on iOS during the first launch is now more reliable",
|
||||
"Font size no longer changes unexpectedly in scrolled mode on iOS",
|
||||
"Top and bottom safe insets are now ignored when no header or footer is present",
|
||||
"Interactive books now load correctly on the Desktop platform",
|
||||
"Translation is now disabled when the primary language is undefined",
|
||||
"Android: the task list background color has been updated with current theme color"
|
||||
]
|
||||
},
|
||||
"0.9.59": {
|
||||
"date": "2025-06-19",
|
||||
"notes": [
|
||||
"Layout: Image size now adjusts more accurately by converting screen units to pixels properly",
|
||||
"Layout: Improved header layout on iOS landscape mode — extra spacing has been removed",
|
||||
"Settings: Top and bottom margin settings are now more intuitive and better constrained"
|
||||
]
|
||||
},
|
||||
"0.9.58": {
|
||||
"date": "2025-06-18",
|
||||
"notes": [
|
||||
"TTS: Translation now works even when TTS is playing in the background",
|
||||
"TTS: Press T on your keyboard to quickly toggle TTS on or off",
|
||||
"TTS: Each open book now has its own independent TTS controller",
|
||||
"Annotations: Annotation tools now function properly while TTS is active",
|
||||
"Settings: Margins for top, bottom, left, and right can now be adjusted individually",
|
||||
"Settings: New option to always show the status bar while reading",
|
||||
"Layout: Reader page now adapts better to device safe areas for a more comfortable layout",
|
||||
"UI: Added creation timestamps to annotations and made various interface improvements",
|
||||
"iPad: Multiple columns are now supported in portrait mode",
|
||||
"iPad: Fixed issue where sidebars couldn't be resized by dragging",
|
||||
"iPad: Added support for split-screen mode"
|
||||
]
|
||||
},
|
||||
"0.9.57": {
|
||||
"date": "2025-06-14",
|
||||
"notes": ["TTS: Fixed an issue to detect custom TTS engines on some Android devices"]
|
||||
},
|
||||
"0.9.56": {
|
||||
"date": "2025-06-13",
|
||||
"notes": [
|
||||
"Performance: Improved TOC loading for books with very large chapter lists",
|
||||
"TTS: Added support for native Android TTS engines (e.g., MultiTTS)",
|
||||
"TTS: Fixed an issue where the next chapter would not load properly in the background",
|
||||
"Translation: Improved accuracy and clarity of some Arabic translations",
|
||||
"Reading: Added support for code syntax highlighting in books with code snippets"
|
||||
]
|
||||
},
|
||||
"0.9.55": {
|
||||
"date": "2025-06-07",
|
||||
"notes": [
|
||||
"Sync: Notes deleted on other devices now correctly sync across all platforms",
|
||||
"Layout: Now text justification can be unset in the layout settings",
|
||||
"Translation: Added notification when daily translation quota is exceeded",
|
||||
"Performance: Reduced unnecessary animations on Android to improve efficiency"
|
||||
]
|
||||
},
|
||||
"0.9.53": {
|
||||
"date": "2025-06-05",
|
||||
"notes": [
|
||||
"Reader: New option to customize book foreground and background colors",
|
||||
"Reader: Fixed an issue where images appeared too small in some EPUBs",
|
||||
"EPUB: Improved cover image detection for certain EPUB files",
|
||||
"TTS: Improved background playback stability on iOS devices",
|
||||
"Translation: Introduced a daily DeepL translation quota to ensure long-term availability"
|
||||
]
|
||||
},
|
||||
"0.9.52": {
|
||||
"date": "2025-06-03",
|
||||
"notes": [
|
||||
"Library: The search bar now shows the number of books currently displayed",
|
||||
"Book Details: Book descriptions now support rich HTML formatting",
|
||||
"Notebook: Added support for writing notes using Markdown",
|
||||
"Notebook: You can now search within your notes",
|
||||
"Sync: A sync status item has been added to the View menu",
|
||||
"TOC: You can now sort the table of contents by page number",
|
||||
"Reader: Option added to show remaining time in the current chapter"
|
||||
]
|
||||
},
|
||||
"0.9.51": {
|
||||
"date": "2025-06-01",
|
||||
"notes": [
|
||||
"Translator: Table of contents is now included in translations",
|
||||
"Library Theme: Added light, dark, and system theme options to the library page",
|
||||
"TTS: Now shows the current sentence and chapter in the media session",
|
||||
"TTS: Text-to-speech continues from the last read sentence",
|
||||
"Pagination Fix: Taking notes no longer breaks page turning",
|
||||
"EPUB Scripts: Added a setting to allow JavaScript in EPUB files"
|
||||
]
|
||||
},
|
||||
"0.9.50": {
|
||||
"date": "2025-05-28",
|
||||
"notes": [
|
||||
"Bilingual TTS: You can now choose voices for each language in bilingual text-to-speech mode",
|
||||
"Settings Layout: Improved header layout in the settings dialog for different screen sizes",
|
||||
"CSS Fixes: Corrected anchor text color and fixed image dimensions within paragraphs"
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
@@ -118,7 +699,7 @@
|
||||
"0.9.30": {
|
||||
"date": "2025-04-05",
|
||||
"notes": [
|
||||
"Added fixed storage quata when running the app in self-hosted mode",
|
||||
"Added fixed storage quota 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,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=$(jq -r '.version' package.json)
|
||||
MANIFEST="./src-tauri/gen/android/app/src/main/AndroidManifest.xml"
|
||||
INSTALL_PERMISSION_LINE='<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>'
|
||||
STORAGE_PERMISSION_LINE='<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>'
|
||||
|
||||
ised() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
|
||||
return $?
|
||||
}
|
||||
|
||||
# --- REMOVE PERMISSION BEFORE BUILD ---
|
||||
if grep -q 'REQUEST_INSTALL_PACKAGES' "$MANIFEST"; then
|
||||
echo "🧹 Removing REQUEST_INSTALL_PACKAGES from AndroidManifest.xml"
|
||||
if ised "/REQUEST_INSTALL_PACKAGES/d" "$MANIFEST"; then
|
||||
echo "✅ Successfully removed REQUEST_INSTALL_PACKAGES"
|
||||
else
|
||||
echo "❌ Failed to remove REQUEST_INSTALL_PACKAGES" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if grep -q 'MANAGE_EXTERNAL_STORAGE' "$MANIFEST"; then
|
||||
echo "🧹 Removing MANAGE_EXTERNAL_STORAGE from AndroidManifest.xml"
|
||||
if ised "/MANAGE_EXTERNAL_STORAGE/d" "$MANIFEST"; then
|
||||
echo "✅ Successfully removed MANAGE_EXTERNAL_STORAGE"
|
||||
else
|
||||
echo "❌ Failed to remove MANAGE_EXTERNAL_STORAGE" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "🚀 Running: pnpm tauri android build"
|
||||
pnpm tauri android build --config src-tauri/tauri.playstore.conf.json
|
||||
|
||||
# --- ADD PERMISSION BACK AFTER BUILD ---
|
||||
if ! grep -q 'REQUEST_INSTALL_PACKAGES' "$MANIFEST"; then
|
||||
echo "♻️ Restoring REQUEST_INSTALL_PACKAGES in AndroidManifest.xml"
|
||||
ised "/android.permission.INTERNET/a\\
|
||||
$INSTALL_PERMISSION_LINE
|
||||
" "$MANIFEST"
|
||||
fi
|
||||
|
||||
if ! grep -q 'MANAGE_EXTERNAL_STORAGE' "$MANIFEST"; then
|
||||
echo "♻️ Restoring MANAGE_EXTERNAL_STORAGE in AndroidManifest.xml"
|
||||
ised "/android.permission.WRITE_EXTERNAL_STORAGE/a\\
|
||||
$STORAGE_PERMISSION_LINE
|
||||
" "$MANIFEST"
|
||||
fi
|
||||
|
||||
source .env.google-play.local
|
||||
if [[ -z "$GOOGLE_PLAY_JSON_KEY_FILE" ]]; then
|
||||
echo "❌ GOOGLE_PLAY_JSON_KEY_FILE is not set"
|
||||
exit 1
|
||||
fi
|
||||
cd ../../
|
||||
|
||||
fastlane android upload_production
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Sync release notes from JSON to XML AppData file
|
||||
# Usage: ./sync_release_notes.sh [json_file] [xml_file]
|
||||
#
|
||||
# Description:
|
||||
# Extracts release information from a JSON file and updates the <releases>
|
||||
# section in an AppData XML file, then calculates the SHA256 checksum.
|
||||
#
|
||||
# Arguments:
|
||||
# json_file: Path to JSON file (default: releases.json)
|
||||
# xml_file: Path to XML file (default: appdata.xml)
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Helper functions
|
||||
log_info() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}✗${NC} $1" >&2
|
||||
}
|
||||
|
||||
# Check if required commands are available
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
log_error "jq is required but not installed."
|
||||
echo "Install it with: apt-get install jq (Debian/Ubuntu) or brew install jq (macOS)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse arguments
|
||||
JSON_FILE="${1:-releases.json}"
|
||||
XML_FILE="${2:-appdata.xml}"
|
||||
|
||||
# Validate input files
|
||||
if [ ! -f "$JSON_FILE" ]; then
|
||||
log_error "JSON file '$JSON_FILE' not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$XML_FILE" ]; then
|
||||
log_error "XML file '$XML_FILE' not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate JSON format
|
||||
if ! jq empty "$JSON_FILE" 2>/dev/null; then
|
||||
log_error "Invalid JSON format in '$JSON_FILE'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if releases key exists
|
||||
if ! jq -e '.releases' "$JSON_FILE" >/dev/null 2>&1; then
|
||||
log_error "No 'releases' key found in JSON file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "================================================"
|
||||
echo "Release Notes Sync Tool"
|
||||
echo "================================================"
|
||||
echo "Source: $JSON_FILE"
|
||||
echo "Target: $XML_FILE"
|
||||
echo ""
|
||||
|
||||
# Create backup
|
||||
BACKUP_FILE="${XML_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
cp "$XML_FILE" "$BACKUP_FILE"
|
||||
log_info "Created backup: $BACKUP_FILE"
|
||||
|
||||
# Create temporary files
|
||||
TEMP_XML=$(mktemp)
|
||||
TEMP_RELEASES=$(mktemp)
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
rm -f "$TEMP_XML" "$TEMP_RELEASES"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Extract releases from JSON and convert to XML format
|
||||
log_info "Extracting releases from JSON..."
|
||||
RELEASE_COUNT=$(jq '.releases | length' "$JSON_FILE")
|
||||
echo " Found $RELEASE_COUNT releases"
|
||||
|
||||
jq -r '
|
||||
.releases | to_entries | .[0:10] | .[] |
|
||||
" <release version=\"\(.key)\" date=\"\(.value.date)\">
|
||||
<description>
|
||||
<ul>
|
||||
" +
|
||||
((.value.notes | map(" <li>\(.)</li>")) | join("\n")) +
|
||||
"
|
||||
</ul>
|
||||
</description>
|
||||
</release>"
|
||||
' "$JSON_FILE" > "$TEMP_RELEASES"
|
||||
|
||||
# Find the releases section in XML and replace it
|
||||
log_info "Updating XML file..."
|
||||
awk -v releases="$TEMP_RELEASES" '
|
||||
BEGIN { in_releases = 0; printed_releases = 0 }
|
||||
/<releases>/ {
|
||||
print $0
|
||||
if (printed_releases == 0) {
|
||||
while ((getline line < releases) > 0) {
|
||||
print line
|
||||
}
|
||||
close(releases)
|
||||
printed_releases = 1
|
||||
}
|
||||
in_releases = 1
|
||||
next
|
||||
}
|
||||
/<\/releases>/ {
|
||||
in_releases = 0
|
||||
print $0
|
||||
next
|
||||
}
|
||||
in_releases { next }
|
||||
{ print }
|
||||
' "$XML_FILE" > "$TEMP_XML"
|
||||
|
||||
# Validate the generated XML
|
||||
if ! grep -q "<releases>" "$TEMP_XML" || ! grep -q "</releases>" "$TEMP_XML"; then
|
||||
log_error "Failed to generate valid XML structure"
|
||||
log_warn "Restoring from backup..."
|
||||
mv "$BACKUP_FILE" "$XML_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove backup if everything is fine
|
||||
rm "$BACKUP_FILE"
|
||||
|
||||
# Replace original file
|
||||
mv "$TEMP_XML" "$XML_FILE"
|
||||
log_info "XML file updated successfully"
|
||||
|
||||
# Calculate SHA256 checksum
|
||||
echo ""
|
||||
log_info "Calculating SHA256 checksum..."
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
CHECKSUM=$(sha256sum "$XML_FILE" | awk '{print $1}')
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
CHECKSUM=$(shasum -a 256 "$XML_FILE" | awk '{print $1}')
|
||||
else
|
||||
log_error "Neither sha256sum nor shasum found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Save checksum to file
|
||||
CHECKSUM_FILE="${XML_FILE}.sha256"
|
||||
echo "$CHECKSUM $XML_FILE" > "$CHECKSUM_FILE"
|
||||
|
||||
echo ""
|
||||
echo "================================================"
|
||||
echo "Summary"
|
||||
echo "================================================"
|
||||
echo " Updated file: $XML_FILE"
|
||||
echo " Backup file: $BACKUP_FILE"
|
||||
echo " Checksum file: $CHECKSUM_FILE"
|
||||
echo " SHA256: $CHECKSUM"
|
||||
echo " Releases: $RELEASE_COUNT"
|
||||
echo ""
|
||||
log_info "Sync completed successfully!"
|
||||
echo ""
|
||||
echo "To verify the checksum, run:"
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
echo " sha256sum -c $CHECKSUM_FILE"
|
||||
else
|
||||
echo " shasum -a 256 -c $CHECKSUM_FILE"
|
||||
fi
|
||||
@@ -14,38 +14,47 @@ rust-version = "1.77.2"
|
||||
name = "readestlib"
|
||||
crate-type = ["staticlib", "cdylib", "lib"]
|
||||
|
||||
[features]
|
||||
# Internal feature to suppress warnings from old objc crate
|
||||
cargo-clippy = []
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.1.1", features = [] }
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
log = "0.4"
|
||||
thiserror = "2"
|
||||
walkdir = "2"
|
||||
tokio = { version = "1", features = ["fs"] }
|
||||
tokio-util = { version = "0.7", features = ["codec"] }
|
||||
futures-util = "0.3"
|
||||
futures = "0.3.31"
|
||||
read-progress-stream = "1.0.0"
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"json",
|
||||
"stream",
|
||||
] }
|
||||
# FIXME: remove the devtools feature in production
|
||||
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 = { version = "2", features = [ "protocol-asset" ] }
|
||||
tauri-build = "2"
|
||||
tauri-plugin-log = "2"
|
||||
tauri-plugin-fs = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-os = "2"
|
||||
tauri-plugin-http = { version = "2", features = ["dangerous-settings"] }
|
||||
tauri-plugin-shell = "2"
|
||||
tauri-plugin-process = "2"
|
||||
tauri-plugin-oauth = "2"
|
||||
tauri-plugin-opener = "2.2.6"
|
||||
tauri-plugin-deep-link = "2.2.1"
|
||||
tauri-plugin-opener = "2"
|
||||
tauri-plugin-deep-link = "2"
|
||||
tauri-plugin-sign-in-with-apple = "1.0.2"
|
||||
tauri-plugin-haptics = "2.2.4"
|
||||
tauri-plugin-haptics = "2"
|
||||
tauri-plugin-persisted-scope = "2"
|
||||
tauri-plugin-native-bridge = { path = "./plugins/tauri-plugin-native-bridge" }
|
||||
tauri-plugin-native-tts = { path = "./plugins/tauri-plugin-native-tts" }
|
||||
tauri-plugin-websocket = "2"
|
||||
tauri-plugin-sharekit = "0.3"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
rand = "0.8"
|
||||
@@ -60,6 +69,7 @@ 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.3"
|
||||
tauri-plugin-updater = "2.7.0"
|
||||
tauri-plugin-window-state = "2.2.2"
|
||||
tauri-plugin-single-instance = "2"
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-window-state = "2"
|
||||
discord-rich-presence = "1.0.0"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>UIHomeIndicatorAutoHidden</key>
|
||||
<true/>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -19,6 +19,8 @@
|
||||
<string>EPUB Document</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.idpf.epub-container</string>
|
||||
@@ -30,6 +32,8 @@
|
||||
<string>PDF Document</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.adobe.pdf</string>
|
||||
@@ -41,6 +45,8 @@
|
||||
<string>FB2 Document</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.readest.fb2</string>
|
||||
@@ -52,6 +58,8 @@
|
||||
<string>CBZ Archive</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.readest.cbz</string>
|
||||
@@ -63,6 +71,8 @@
|
||||
<string>MOBI Document</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.mobipocket.mobi</string>
|
||||
@@ -74,6 +84,8 @@
|
||||
<string>AZW Document</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.amazon.azw</string>
|
||||
@@ -86,6 +98,8 @@
|
||||
<string>Text File</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.plain-text</string>
|
||||
@@ -93,6 +107,153 @@
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<key>UTImportedTypeDeclarations</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.composite-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.adobe.pdf</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>PDF Document</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.composite-content</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>pdf</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<string>application/pdf</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>
|
||||
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>public.plain-text</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Text File</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>txt</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<string>text/plain</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -198,4 +359,4 @@
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
|
||||
@@ -1,3 +1,65 @@
|
||||
use std::{env, fs, path::PathBuf, process::Command};
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../extensions/windows-thumbnail/src");
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
||||
if target_os == "windows" {
|
||||
build_windows_thumbnail();
|
||||
}
|
||||
|
||||
tauri_build::build()
|
||||
}
|
||||
|
||||
fn build_windows_thumbnail() {
|
||||
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let dll_crate_dir = manifest_dir
|
||||
.join("..")
|
||||
.join("extensions")
|
||||
.join("windows-thumbnail");
|
||||
let dll_crate_manifest = dll_crate_dir.join("Cargo.toml");
|
||||
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".into());
|
||||
|
||||
let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into()));
|
||||
cmd.arg("build")
|
||||
.arg("--package")
|
||||
.arg("windows_thumbnail")
|
||||
.arg("--manifest-path")
|
||||
.arg(&dll_crate_manifest);
|
||||
|
||||
if profile == "release" {
|
||||
cmd.arg("--release");
|
||||
}
|
||||
|
||||
let target_triple = env::var("TARGET").unwrap_or_default();
|
||||
let host_triple = env::var("HOST").unwrap_or_default();
|
||||
if !target_triple.is_empty() && target_triple != host_triple {
|
||||
cmd.arg("--target").arg(&target_triple);
|
||||
}
|
||||
|
||||
let status = cmd
|
||||
.status()
|
||||
.expect("Failed to run cargo build for windows_thumbnail");
|
||||
if !status.success() {
|
||||
panic!("Failed to build windows_thumbnail DLL");
|
||||
}
|
||||
|
||||
let dll_name = "windows_thumbnail.dll";
|
||||
let candidate_paths = [
|
||||
dll_crate_dir.join("target").join(&profile).join(dll_name),
|
||||
dll_crate_dir
|
||||
.join("target")
|
||||
.join(&target_triple)
|
||||
.join(&profile)
|
||||
.join(dll_name),
|
||||
];
|
||||
|
||||
let dll_src = candidate_paths
|
||||
.iter()
|
||||
.find(|p| p.exists())
|
||||
.expect("Failed to find built windows_thumbnail DLL");
|
||||
|
||||
let dll_dest = &dll_crate_dir.join("target").join(dll_name);
|
||||
|
||||
fs::copy(dll_src, dll_dest).expect("Failed to copy windows_thumbnail DLL");
|
||||
println!("cargo:rerun-if-changed={}", dll_dest.display());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "enables the default permissions",
|
||||
"windows": ["main", "updater"],
|
||||
"windows": ["main", "updater", "reader-*"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"fs:default",
|
||||
@@ -43,27 +43,80 @@
|
||||
"identifier": "fs:allow-cache-read",
|
||||
"allow": [
|
||||
{
|
||||
"path": "$CACHEDIR/**/*"
|
||||
"path": "$APPCACHE/**/*"
|
||||
},
|
||||
{
|
||||
"path": "/private/var/mobile/Containers/Data/Application/**/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-cache-write",
|
||||
"allow": [
|
||||
{
|
||||
"path": "$APPCACHE/**/*"
|
||||
},
|
||||
{
|
||||
"path": "/private/var/mobile/Containers/Data/Application/**/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:scope",
|
||||
"allow": [
|
||||
{
|
||||
"path": "**/Readest"
|
||||
},
|
||||
{
|
||||
"path": "**/Readest/**/*"
|
||||
},
|
||||
{
|
||||
"path": "**/last-book-cover.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "https://*.deepl.com"
|
||||
"url": "https://*.readest.com"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/readest/*"
|
||||
},
|
||||
{
|
||||
"url": "https://*.deepl.com"
|
||||
},
|
||||
{
|
||||
"url": "https://*.cloudflarestorage.com"
|
||||
},
|
||||
{
|
||||
"url": "https://edge.microsoft.com"
|
||||
},
|
||||
{
|
||||
"url": "https://translate.toil.cc"
|
||||
},
|
||||
{
|
||||
"url": "https://*.microsofttranslator.com"
|
||||
},
|
||||
{
|
||||
"url": "https://translate.googleapis.com"
|
||||
},
|
||||
{
|
||||
"url": "http://*:*"
|
||||
},
|
||||
{
|
||||
"url": "https://*:*"
|
||||
},
|
||||
{
|
||||
"url": "http://*"
|
||||
},
|
||||
{
|
||||
"url": "https://*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"websocket:default",
|
||||
"dialog:default",
|
||||
"os:default",
|
||||
"core:window:default",
|
||||
@@ -79,6 +132,7 @@
|
||||
"core:window:allow-toggle-maximize",
|
||||
"core:window:allow-set-fullscreen",
|
||||
"core:window:allow-set-always-on-top",
|
||||
"core:window:allow-destroy",
|
||||
"core:webview:allow-create-webview-window",
|
||||
"core:path:allow-resolve-directory",
|
||||
"log:default",
|
||||
@@ -90,11 +144,41 @@
|
||||
"oauth:allow-cancel",
|
||||
"sign-in-with-apple:default",
|
||||
"opener:default",
|
||||
{
|
||||
"identifier": "opener:allow-open-url",
|
||||
"allow": [
|
||||
{
|
||||
"url": "alipays:*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "shell:allow-spawn",
|
||||
"allow": [
|
||||
{
|
||||
"name": "start-readest",
|
||||
"cmd": "cmd",
|
||||
"args": ["/C", "start", "", { "validator": "^.*Readest(.*)\\.exe$" }]
|
||||
},
|
||||
{
|
||||
"name": "chmod-appimage",
|
||||
"cmd": "chmod",
|
||||
"args": ["+x", { "validator": "^.*Readest(.*)\\.AppImage$" }]
|
||||
},
|
||||
{
|
||||
"name": "launch-appimage",
|
||||
"cmd": "setsid",
|
||||
"args": [{ "validator": "^.*Readest(.*)\\.AppImage$" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
"haptics:allow-vibrate",
|
||||
"haptics:allow-impact-feedback",
|
||||
"haptics:allow-notification-feedback",
|
||||
"haptics:allow-selection-feedback",
|
||||
"native-bridge:default",
|
||||
"deep-link:default"
|
||||
"deep-link:default",
|
||||
"sharekit:default",
|
||||
"native-tts:default",
|
||||
"native-bridge:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "desktop-capability",
|
||||
"windows": ["main", "updater"],
|
||||
"windows": ["main", "updater", "reader-*"],
|
||||
"platforms": ["linux", "macOS", "windows"],
|
||||
"permissions": ["updater:default", "cli:default"]
|
||||
}
|
||||
|
||||
@@ -15,28 +15,29 @@ val tauriProperties = Properties().apply {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 34
|
||||
compileSdk = 36
|
||||
namespace = "com.bilingify.readest"
|
||||
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
val keystoreProperties = Properties()
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
defaultConfig {
|
||||
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||
applicationId = "com.bilingify.readest"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
targetSdk = 36
|
||||
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
|
||||
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
|
||||
}
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
val keystoreProperties = Properties()
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
create("signing") {
|
||||
keyAlias = keystoreProperties["keyAlias"] as String
|
||||
keyPassword = keystoreProperties["password"] as String
|
||||
storeFile = file(keystoreProperties["storeFile"] as String)
|
||||
storePassword = keystoreProperties["password"] as String
|
||||
}
|
||||
|
||||
keyAlias = keystoreProperties["keyAlias"] as String
|
||||
keyPassword = keystoreProperties["password"] as String
|
||||
storeFile = file(keystoreProperties["storeFile"] as String)
|
||||
storePassword = keystoreProperties["password"] as String
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
@@ -45,14 +46,26 @@ android {
|
||||
isDebuggable = true
|
||||
isJniDebuggable = true
|
||||
isMinifyEnabled = false
|
||||
packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
signingConfig = signingConfigs.getByName("signing")
|
||||
}
|
||||
packaging {
|
||||
jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/x86/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
|
||||
}
|
||||
}
|
||||
getByName("release") {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
isMinifyEnabled = true
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
signingConfig = signingConfigs.getByName("signing")
|
||||
}
|
||||
proguardFiles(
|
||||
*fileTree(".") { include("**/*.pro") }
|
||||
.plus(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||
.toList().toTypedArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
@@ -68,13 +81,13 @@ rust {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.webkit:webkit:1.6.1")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.activity:activity-ktx:1.9.3")
|
||||
implementation("com.google.android.material:material:1.8.0")
|
||||
implementation("androidx.webkit:webkit:1.14.0")
|
||||
implementation("androidx.appcompat:appcompat:1.7.1")
|
||||
implementation("androidx.activity:activity-ktx:1.10.1")
|
||||
implementation("com.google.android.material:material:1.12.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.4")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
|
||||
}
|
||||
|
||||
apply(from = "tauri.build.gradle.kts")
|
||||
apply(from = "tauri.build.gradle.kts")
|
||||
@@ -1,23 +1,32 @@
|
||||
<?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.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!-- AndroidTV support -->
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:largeHeap="true"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.readest"
|
||||
android:hardwareAccelerated="true"
|
||||
android:usesCleartextTraffic="${usesCleartextTraffic}">
|
||||
<activity
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/main_activity_title"
|
||||
android:hardwareAccelerated="true"
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
|
||||
@@ -28,35 +37,64 @@
|
||||
|
||||
<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/mobi"/>
|
||||
<data android:mimeType="application/azw"/>
|
||||
<data android:mimeType="application/azw3"/>
|
||||
<data android:mimeType="application/x-mobipocket-ebook" />
|
||||
<data android:mimeType="application/vnd.amazon.ebook" />
|
||||
<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:mimeType="application/octet-stream"/>
|
||||
<data android:mimeType="application/x-font-ttf"/>
|
||||
<data android:mimeType="application/x-font-otf"/>
|
||||
</intent-filter>
|
||||
|
||||
<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="*/*" />
|
||||
<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=".*\\.azw3" />
|
||||
<data android:pathPattern=".*\\.cbz" />
|
||||
<data android:pathPattern=".*\\.zip" />
|
||||
<data android:pathPattern=".*\\.txt" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="*/*"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="*/*"/>
|
||||
</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" />
|
||||
@@ -64,18 +102,38 @@
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
<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:scheme="http" />
|
||||
<data android:host="web.readest.com" />
|
||||
|
||||
|
||||
|
||||
|
||||
</intent-filter>
|
||||
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
|
||||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name="androidx.media.session.MediaButtonReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="com.readest.native_tts.MediaPlaybackService"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
package com.bilingify.readest
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.webkit.WebView
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.app.ActivityManager
|
||||
import android.content.res.Configuration
|
||||
import android.window.OnBackInvokedCallback
|
||||
import android.window.OnBackInvokedDispatcher
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import app.tauri.plugin.JSArray
|
||||
import app.tauri.plugin.JSObject
|
||||
import com.readest.native_bridge.KeyDownInterceptor
|
||||
import com.readest.native_bridge.NativeBridgePlugin
|
||||
|
||||
class MainActivity : TauriActivity(), KeyDownInterceptor {
|
||||
private lateinit var wv: WebView
|
||||
private var wv: WebView? = null
|
||||
private var interceptVolumeKeysEnabled = false
|
||||
private var interceptBackKeyEnabled = false
|
||||
|
||||
@@ -35,10 +48,78 @@ class MainActivity : TauriActivity(), KeyDownInterceptor {
|
||||
interceptBackKeyEnabled = enabled
|
||||
}
|
||||
|
||||
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||
val action = when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> "touchstart"
|
||||
MotionEvent.ACTION_UP -> "touchend"
|
||||
MotionEvent.ACTION_CANCEL -> "touchcancel"
|
||||
MotionEvent.ACTION_POINTER_DOWN -> "touchstart"
|
||||
MotionEvent.ACTION_POINTER_UP -> "touchend"
|
||||
else -> null
|
||||
}
|
||||
|
||||
action?.let { eventType ->
|
||||
val pointerIndex = event.actionIndex
|
||||
val pointerId = event.getPointerId(pointerIndex)
|
||||
val x = event.getX(pointerIndex)
|
||||
val y = event.getY(pointerIndex)
|
||||
val pressure = event.getPressure(pointerIndex)
|
||||
|
||||
wv?.evaluateJavascript(
|
||||
"""
|
||||
try {
|
||||
if (window.onNativeTouch) {
|
||||
window.onNativeTouch({
|
||||
type: "$eventType",
|
||||
pointerId: $pointerId,
|
||||
x: $x,
|
||||
y: $y,
|
||||
pressure: $pressure,
|
||||
pointerCount: ${event.pointerCount},
|
||||
timestamp: ${event.eventTime}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Native touch error:', err);
|
||||
}
|
||||
""".trimIndent(),
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
return super.dispatchTouchEvent(event)
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||
val keyCode = event.keyCode
|
||||
val keyName = keyEventMap[keyCode]
|
||||
|
||||
if (keyName != null) {
|
||||
val shouldIntercept = when (keyCode) {
|
||||
KeyEvent.KEYCODE_BACK -> interceptBackKeyEnabled
|
||||
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> interceptVolumeKeysEnabled
|
||||
else -> false
|
||||
}
|
||||
|
||||
if (shouldIntercept) {
|
||||
wv?.evaluateJavascript(
|
||||
"""
|
||||
try { window.onNativeKeyDown("$keyName", $keyCode); } catch (_) {}
|
||||
""".trimIndent(),
|
||||
null
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
val keyName = keyEventMap[keyCode]
|
||||
if (keyName != null) {
|
||||
wv.evaluateJavascript(
|
||||
wv?.evaluateJavascript(
|
||||
"""
|
||||
try {
|
||||
window.onNativeKeyDown("$keyName", $keyCode)
|
||||
@@ -77,5 +158,102 @@ class MainActivity : TauriActivity(), KeyDownInterceptor {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
handleIncomingIntent(intent)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setTaskDescription(
|
||||
ActivityManager.TaskDescription(
|
||||
getString(R.string.app_name),
|
||||
null,
|
||||
Color.TRANSPARENT
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
onBackInvokedDispatcher.registerOnBackInvokedCallback(
|
||||
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
||||
OnBackInvokedCallback {
|
||||
Log.d("MainActivity", "Back invoked callback triggered ${interceptBackKeyEnabled}")
|
||||
if (interceptBackKeyEnabled) {
|
||||
Log.d("MainActivity", "Back intercepted (OnBackInvokedCallback)")
|
||||
wv?.evaluateJavascript(
|
||||
"""window.onNativeKeyDown("Back", ${KeyEvent.KEYCODE_BACK});""",
|
||||
null
|
||||
)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(this,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
if (interceptBackKeyEnabled) {
|
||||
Log.d("MainActivity", "Back intercepted (OnBackPressedDispatcher)")
|
||||
wv?.evaluateJavascript(
|
||||
"""window.onNativeKeyDown("Back", ${KeyEvent.KEYCODE_BACK});""",
|
||||
null
|
||||
)
|
||||
} else {
|
||||
isEnabled = false
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
NativeBridgePlugin.getInstance()?.handleActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
intent?.let { handleIncomingIntent(it) }
|
||||
}
|
||||
|
||||
private fun handleIncomingIntent(intent: Intent) {
|
||||
when (intent.action) {
|
||||
Intent.ACTION_SEND -> {
|
||||
if (intent.type != null) {
|
||||
handleSingleFile(intent)
|
||||
}
|
||||
}
|
||||
Intent.ACTION_SEND_MULTIPLE -> {
|
||||
if (intent.type != null) {
|
||||
handleMultipleFiles(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSingleFile(intent: Intent) {
|
||||
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
uri?.let { fileUri ->
|
||||
val payload = JSObject().apply {
|
||||
var urls = JSArray()
|
||||
urls.put(fileUri.toString())
|
||||
put("urls", urls)
|
||||
}
|
||||
NativeBridgePlugin.getInstance()?.triggerEvent("shared-intent", payload)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleMultipleFiles(intent: Intent) {
|
||||
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
uris?.let { fileUris ->
|
||||
val payload = JSObject().apply {
|
||||
var urls = JSArray()
|
||||
fileUris.forEach { urls.put(it.toString()) }
|
||||
put("urls", urls)
|
||||
}
|
||||
NativeBridgePlugin.getInstance()?.triggerEvent("shared-intent", payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@@ -0,0 +1,8 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<color android:color="#FF323130" />
|
||||
</item>
|
||||
<item android:gravity="center" android:width="120dp" android:height="120dp">
|
||||
<bitmap android:src="@drawable/splash_icon" />
|
||||
</item>
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 170 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@mipmap/ic_launcher_foreground"
|
||||
android:inset="22%"/>
|
||||
</foreground>
|
||||
</adaptive-icon>
|
||||
@@ -2,6 +2,7 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.readest" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowBackground">@drawable/splash_background</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |