1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-27 20:29:45 +02:00

Compare commits

...

215 Commits

Author SHA1 Message Date
Laurent Cozic
ed0f6d165c Android release v1.0.107 2018-03-16 08:10:32 +00:00
Laurent Cozic
8e22d38eb3 Electron release v1.0.73 2018-03-16 08:08:10 +00:00
Laurent Cozic
2599c425c3 Updated translations 2018-03-15 18:15:33 +00:00
Laurent Cozic
0e15821a81 Merge branch 'master' of github.com:laurent22/joplin 2018-03-15 18:10:11 +00:00
Laurent Cozic
1532b6d159 All: Made WebDAV options dynamics so that changing username or password doesn't require restarting the app 2018-03-15 17:57:11 +00:00
Laurent Cozic
4fe495675b Merge pull request #301 from fmrtn/master
Updated Spanish translation
2018-03-15 14:26:28 +00:00
Fernando
7828eef2ad Updated Spanish translation 2018-03-15 15:24:37 +01:00
Laurent Cozic
694f81b75f Merge pull request #300 from rtmkrlv/master
Updated Russian translation
2018-03-15 13:15:09 +00:00
rtmkrlv
8364b6e08d Updated Russian translation 2018-03-15 11:14:04 +02:00
rtmkrlv
3f4328ce9d Merge pull request #5 from laurent22/master
Update fork from original repository
2018-03-15 10:41:44 +02:00
Laurent Cozic
9e0bf1acb2 Mobile: Fixes #299: App freezes when connecting to OneDrive login page while internet connection is down 2018-03-14 23:17:02 +00:00
Laurent Cozic
26331f61e1 Electron: Resolves #298: Removed extraneous first characters from auto-title 2018-03-14 17:37:47 +00:00
Laurent Cozic
694672859a Updated translations 2018-03-14 17:32:05 +00:00
Laurent Cozic
858ead40b9 /bin/bash: qa: command not found 2018-03-14 17:29:13 +00:00
Laurent Cozic
b07fe5cc34 CLI: Fixes #288: Don't log commands in release versions 2018-03-14 17:28:41 +00:00
Laurent Cozic
0317171097 Electron: Fixes #292: Removed buggy electron-updater and implemented custom check 2018-03-14 17:23:19 +00:00
Laurent Cozic
9741a3a53d Merge pull request #283 from jcgerhard/master
Updated german translation de_DE
2018-03-14 16:44:54 +00:00
Laurent Cozic
7937fab5ff Electron: Fixes #291: Crash with empty backtick 2018-03-13 17:54:40 +00:00
Laurent Cozic
eef106c99b Electron: Resolves #237: Export to PDF and print option 2018-03-12 18:01:47 +00:00
Laurent Cozic
dbe1833f92 Made easier to add export options 2018-03-12 08:30:10 +00:00
Jan C. Gerhard
520dc0ae21 Updated german translation de_DE 2018-03-12 08:08:14 +01:00
Laurent Cozic
c9be287f4a Electron release v1.0.72 2018-03-11 19:34:07 +00:00
Laurent Cozic
711f5dcaba Android release v1.0.106 2018-03-11 19:32:41 +00:00
Laurent Cozic
ebc0aa9809 Electron release v1.0.71 2018-03-11 19:31:07 +00:00
Laurent Cozic
dcaaf50a5a All: Fixes #271: Sort by created time was not respected 2018-03-10 15:34:55 +00:00
Laurent Cozic
3370b57134 Finished removing prettier 2018-03-10 15:34:29 +00:00
Laurent Cozic
55c5ddedf4 Revert "Applied prettier to code base"
This reverts commit c4f19465a6.
2018-03-09 20:59:12 +00:00
Laurent Cozic
5e8b09f5af All: Display icon next to resources and allow downloading them from Electron client 2018-03-09 20:46:28 +00:00
Laurent Cozic
1acffce62d All: Display last sync error unless it's a timeout or network error 2018-03-09 19:51:01 +00:00
Laurent Cozic
8555ecce87 Merge branch 'master' of github.com:laurent22/joplin 2018-03-09 19:07:47 +00:00
Laurent Cozic
4df5f668dc All: Improved sync when dealing with many items, in particular when using Nextcloud 2018-03-09 19:07:38 +00:00
Laurent Cozic
cceebeebef Update CONTRIBUTING.md 2018-03-09 17:59:41 +00:00
Laurent Cozic
c4f19465a6 Applied prettier to code base 2018-03-09 17:49:35 +00:00
Laurent Cozic
e868102c98 Revert "Adding prettier to all projects"
This reverts commit d6a4436313.
2018-03-09 17:45:42 +00:00
Laurent Cozic
0d4a1837f5 Merge branch 'master' of github.com:laurent22/joplin 2018-03-09 17:32:26 +00:00
Laurent Cozic
d6a4436313 Adding prettier to all projects 2018-03-09 17:32:10 +00:00
Laurent Cozic
03b5c6aa5e Added prettier options to ST project 2018-03-09 17:48:08 +00:00
Laurent Cozic
250cd47e02 Added prettier config file 2018-03-09 17:41:34 +00:00
Laurent Cozic
943fef32e7 Merge pull request #281 from fmrtn/master
Updated Spanish translation
2018-03-09 16:59:55 +00:00
Fernando
408634671c Updated Spanish translation 2018-03-09 17:28:39 +01:00
Laurent Cozic
570b5856ba Updated translations 2018-03-09 09:11:13 +00:00
Laurent Cozic
d114d14e87 Added donation links 2018-03-09 09:09:13 +00:00
Laurent Cozic
32791f502e More consistent links 2018-03-09 08:57:34 +00:00
Laurent Cozic
083ab0c788 Updated website 2018-03-09 08:53:26 +00:00
Laurent Cozic
003c4c4e26 Moved readme files to own folder 2018-03-09 08:47:21 +00:00
Laurent Cozic
f08f89ebd4 Added donate page 2018-03-09 08:30:36 +00:00
Laurent Cozic
3c973144c4 Added image 2018-03-09 08:18:02 +00:00
Laurent Cozic
82e99ca658 All: Better handling of startup errors 2018-03-07 19:11:55 +00:00
Laurent Cozic
b04d750cec Merge branch 'master' of github.com:laurent22/joplin 2018-03-07 17:40:00 +00:00
Laurent Cozic
c804e9f541 CLI: Fixes #268: Improve error message for invalid flags 2018-03-07 17:39:45 +00:00
Laurent Cozic
7753f3f842 Minor tweaks 2018-03-07 17:35:11 +00:00
Laurent Cozic
c985b7c682 Merge pull request #277 from fmrtn/master
Updated Spanish translation
2018-03-07 17:30:48 +00:00
Fernando
4509919c22 Updated Spanish translation 2018-03-07 17:04:10 +01:00
Laurent Cozic
89b164c7ca Merge pull request #272 from alexdevero/windows-build-instructions
Add build instructions for Windows
2018-03-06 23:16:32 +00:00
Alex Devero
e52d17b39a Add build instructions for Windows 2018-03-06 08:42:29 +01:00
Laurent Cozic
5014914dc9 Android release v1.0.104 2018-03-05 18:23:11 +00:00
Laurent Cozic
122ab83a84 Android: Fix when downloading many encrypted items 2018-03-05 18:21:42 +00:00
Laurent Cozic
7a985c2c8a Android release v1.0.103 2018-03-02 18:15:09 +00:00
Laurent Cozic
b11ad30a31 Updated translations 2018-03-02 18:12:58 +00:00
Laurent Cozic
5914fc97df Merge branch 'master' of github.com:laurent22/joplin 2018-03-02 18:03:28 +00:00
Laurent Cozic
e41ae1832d Minor tweaks 2018-03-02 18:24:02 +00:00
Laurent Cozic
89b50909ed Electron: Resolves #266: Allow setting text editor font family 2018-03-02 18:16:48 +00:00
Laurent Cozic
edccd7412f Merge pull request #265 from jaredcrowe/docs-correct-checkbox-syntax
correct the documentation about adding checkboxes
2018-03-02 17:59:33 +00:00
Jared Crowe
c76beae057 correct the documentation about adding checkboxes 2018-03-02 09:30:27 +11:00
Laurent Cozic
23c5934a7d Electron: Allow exporting only selected notes or notebook 2018-03-01 20:14:06 +00:00
Laurent Cozic
a078947d6d Allow importing and exporting single notes and notebooks 2018-03-01 18:35:17 +00:00
rtmkrlv
0faaf660b4 Merge pull request #4 from laurent22/master
Update fork from original repository
2018-03-01 15:31:51 +02:00
Laurent Cozic
5ba98b4200 CLI v1.0.100 2018-02-28 21:18:27 +00:00
Laurent Cozic
c36513b99d Updated translation 2018-02-28 20:46:15 +00:00
Laurent Cozic
97814531fa Update readme downloads 2018-02-28 20:06:06 +00:00
Laurent Cozic
fd3e335a02 Electron release v1.0.70 2018-02-28 18:57:10 +00:00
Laurent Cozic
e676fa2b57 Trying to fix Electron builder 2018-02-28 18:56:57 +00:00
Laurent Cozic
122cbbf673 Electron release v1.0.69 2018-02-28 18:43:12 +00:00
Laurent Cozic
271793b324 Trying to upgrade Electron Builder 2018-02-28 18:43:03 +00:00
Laurent Cozic
134b31933b Electron release v1.0.68 2018-02-28 18:13:02 +00:00
Laurent Cozic
0ec5518a62 Android release v1.0.102 2018-02-28 18:10:33 +00:00
Laurent Cozic
76931370d7 Updated readme for import/export 2018-02-27 22:15:20 +00:00
Laurent Cozic
8cf0e4517a Merge branch 'master' of github.com:laurent22/joplin 2018-02-28 17:27:06 +00:00
Laurent Cozic
e75c62bf0f Electron: Fix logic of hidding app in macOS 2018-02-27 21:54:40 +00:00
Laurent Cozic
058285e0b9 All: Made scheduled sync delay slightly shorter 2018-02-27 21:19:11 +00:00
Laurent Cozic
795568d8c2 Electron: Fixed note sorting bug 2018-02-27 21:11:53 +00:00
Laurent Cozic
df4933fddd All: Prevent export of encrypted items 2018-02-27 20:51:07 +00:00
Laurent Cozic
4046a51472 Electron: Handle import export 2018-02-27 20:04:38 +00:00
Laurent Cozic
45845f645d Refactored Interop service to make export/import code more modular 2018-02-26 19:25:54 +00:00
Laurent Cozic
d7fd8944f7 Moved Enex import to InteropService 2018-02-26 19:16:01 +00:00
Laurent Cozic
3cee671f25 Done MD importer 2018-02-26 18:43:50 +00:00
Laurent Cozic
8f2e5faff3 Support importing JEX and raw data 2018-02-25 17:01:16 +00:00
Laurent Cozic
39ddd934f6 Changed export format extension to JEX and made it a non-compressed tar file 2018-02-25 21:08:40 +00:00
Laurent Cozic
9f8a46b9d9 All: Added more export options including jpz format 2018-02-23 19:32:19 +00:00
Laurent Cozic
c6698eaea6 Electron: Fixes #256: Check that no other instance of Joplin is running before launching a new one 2018-02-23 17:51:23 +00:00
Laurent Cozic
8a96cf3434 All: Allow sorting notes by various fields 2018-02-22 18:58:15 +00:00
Laurent Cozic
74d255c056 Updated readme 2018-02-21 18:19:22 +00:00
Laurent Cozic
71aa841265 Mobile: Fixes #244: When accessing configuration or encrypt configuration option while making a note and the going back, the note gets erased 2018-02-21 22:08:34 +00:00
Laurent Cozic
14a93a9f26 All: Fixed sync interval sorting order 2018-02-21 19:58:28 +00:00
Laurent Cozic
e1fd9c6922 Clean up 2018-02-21 19:20:33 +00:00
Laurent Cozic
b9db747b5c Minor refactoring to make function purpose clearer 2018-02-21 18:36:29 +00:00
Laurent Cozic
4a56c76901 Electron: Fixes #247: Unreadable error messages when checking for updates 2018-02-20 18:07:31 +00:00
Laurent Cozic
6bb3184a72 macOS: Resolves #243: Added black and white tray icon for macOS 2018-02-20 00:41:52 +00:00
Laurent Cozic
7fb8fbd450 iOS: Added compatibility with iPad 2018-02-19 23:41:53 +00:00
Laurent Cozic
9d5bba472e iOS v1.0.13 2018-02-19 23:41:12 +00:00
Laurent Cozic
e6d821a45f CLI v1.0.99 2018-02-19 22:59:09 +00:00
Laurent Cozic
72f0027e21 Electron release v1.0.67 2018-02-19 21:05:43 +00:00
Laurent Cozic
29a13a9943 Android release v1.0.101 2018-02-19 21:04:27 +00:00
Laurent Cozic
3691ae4d13 Electron: Fixes #217: Display a message when the note has no content and only the note viewer is visible 2018-02-19 18:56:56 +00:00
Laurent Cozic
4dda397c29 Removed es_CR translation as it's unfortunately too incomplete and there's already a 100% Spanish translation 2018-02-19 18:44:41 +00:00
Laurent Cozic
b4b058998d Updated French and Español translation 2018-02-19 18:41:39 +00:00
Laurent Cozic
10919e415e Merge branch 'master' of github.com:laurent22/joplin 2018-02-19 18:38:56 +00:00
Laurent Cozic
4966d74864 All: Fixes #240: Tags should be handled in a case-insensitive way 2018-02-19 18:38:27 +00:00
Laurent Cozic
c70ecb30a5 All: Fixes #241: Ignore response for certain calls. Also re-enabled If-None-Match fix. 2018-02-19 18:37:44 +00:00
Laurent Cozic
acc0d17e0f Minor changes 2018-02-19 18:36:31 +00:00
Laurent Cozic
b509b878bf Merge pull request #238 from fmrtn/master
Improved Spanish translations
2018-02-19 18:02:58 +00:00
Laurent Cozic
322ec2efa1 Android release v1.0.100 2018-02-18 22:58:37 +00:00
Laurent Cozic
1232661b1e Electron release v1.0.66 2018-02-18 22:56:57 +00:00
Laurent Cozic
c46d123503 All: Fixed: Local items were no longer being deleted via sync. Also fixed Nextcloud sync target. 2018-02-18 21:52:07 +00:00
Laurent Cozic
8f4060999f Merge branch 'master' of github.com:laurent22/joplin 2018-02-18 21:16:26 +00:00
Laurent Cozic
0addd86069 iOS: Improved compatbility with some WebDAV services (Seafile) 2018-02-18 21:16:18 +00:00
Fernando
760086307b Improved Spanish translations 2018-02-18 17:15:18 +01:00
Laurent Cozic
fc6558a64c Electron release v1.0.65 2018-02-17 19:29:26 +00:00
Laurent Cozic
eca500880d All: Convert new lines in tables to BR tag, and added support for HTML tags in Markdown viewers 2018-02-17 19:29:10 +00:00
Laurent Cozic
90bcd7c977 Merge pull request #226 from fmrtn/master
Nextcloud typos and Spanish update
2018-02-17 11:05:25 +00:00
Fernando
cca0c6eaf3 Updated Spanish translation 2018-02-17 01:09:32 +01:00
Fernando
b0736002be Fixed several Nextcloud typos 2018-02-17 01:09:00 +01:00
Laurent Cozic
51fc2d8e51 CLI v1.0.98 2018-02-16 23:49:22 +00:00
Laurent Cozic
d87c192ff1 Updated readme and translation 2018-02-16 23:26:31 +00:00
Laurent Cozic
52ccf398a6 Update readme 2018-02-16 23:17:55 +00:00
Laurent Cozic
344d0e2687 Update readme 2018-02-16 23:14:26 +00:00
Laurent Cozic
1bc4d6b423 CLI v1.0.97 2018-02-16 23:08:57 +00:00
Laurent Cozic
baa9ca7ea3 Updated terminal readme 2018-02-16 23:08:04 +00:00
Laurent Cozic
e4d477fb4c CLI: Resolve #63: Allow customizing shortcuts 2018-02-16 22:53:53 +00:00
Laurent Cozic
71319eee28 Also add link to po file in translation table 2018-02-16 21:45:28 +00:00
Laurent Cozic
68b31526f8 Updated translations 2018-02-16 21:39:54 +00:00
Laurent Cozic
0b2b7324d9 Merge pull request #224 from fmrtn/master
Fixed and updated Spanish translation
2018-02-16 20:35:46 +00:00
Fernando
43512cf27b Fixed and updated Spanish translation 2018-02-16 20:36:58 +01:00
Laurent Cozic
4218b65969 Electron: Added several keyboard shortcuts 2018-02-16 18:08:02 +00:00
Laurent Cozic
7244e12b78 Electron: Fixed: Confirmation message boxes, and release notes text 2018-02-16 18:06:02 +00:00
Laurent Cozic
a796ef5c66 All: Fixed: Notify DecryptionWorker when item added due to conflict, to make sure it is decrypted as early as possible 2018-02-16 17:55:50 +00:00
Laurent Cozic
9474a05aaa Update readme 2018-02-16 00:17:05 +00:00
Laurent Cozic
41df355a7e CLI v1.0.96 2018-02-15 23:51:00 +00:00
Laurent Cozic
4f3ab87914 Electron release v1.0.64 2018-02-15 23:21:47 +00:00
Laurent Cozic
5d1a08707c Android release v1.0.98 2018-02-15 23:20:28 +00:00
Laurent Cozic
4f822df80e Merge branch 'master' of github.com:laurent22/joplin 2018-02-15 23:05:34 +00:00
Laurent Cozic
951be5cbf6 Electron: Fixes #201, Fixes #216: Make sure only one update check can run at a time, and improved modal dialog boxes 2018-02-15 23:05:04 +00:00
Laurent Cozic
b6c2341542 Merge pull request #219 from fmrtn/patch-1
Update README.md
2018-02-15 18:33:48 +00:00
Laurent Cozic
a6e6b49a9d Merge branch 'master' of github.com:laurent22/joplin 2018-02-15 18:33:21 +00:00
Laurent Cozic
3a4bbd571e All: Provide Content-Length header for WebDAV for better compatibility with more servers 2018-02-15 18:33:08 +00:00
Laurent Cozic
feccc6150e Merge branch 'master' of github.com:laurent22/joplin 2018-02-15 18:19:11 +00:00
Laurent Cozic
a37b599a6b macOS: Allow copy and paste from config and encryption screen 2018-02-15 18:19:00 +00:00
Laurent Cozic
9347683fe3 All: Fixed Nextcloud sync target ID (which was incorrectly set to WebDAV sync ID) 2018-02-15 18:01:05 +00:00
Laurent Cozic
3551c26e28 Fixed race condition when testing with memory driver and fixed encoding issue 2018-02-15 17:12:09 +00:00
Fernando
cfca0107eb Update README.md 2018-02-15 13:09:59 +01:00
rtmkrlv
dfbe37fdaf Merge pull request #3 from laurent22/master
Update fork from original repository
2018-02-15 10:37:59 +02:00
Laurent Cozic
81bc975193 Update readme 2018-02-14 21:54:34 +00:00
Laurent Cozic
7908fda451 Android release v1.0.97 2018-02-14 19:11:35 +00:00
Laurent Cozic
cdbb7c4b0d Merge branch 'master' of github.com:laurent22/joplin 2018-02-14 19:10:42 +00:00
Laurent Cozic
414e57ec55 Electron release v1.0.63 2018-02-14 19:08:24 +00:00
Laurent Cozic
1871123066 All: Improved WebDAV driver compatibility with some services (eg. Seafile) 2018-02-14 19:08:07 +00:00
Laurent Cozic
87bc08bef5 iOS: Fixed resource decryption issue, log page crash and text input rendering issue 2018-02-14 15:28:56 +00:00
Laurent Cozic
214a39c3d3 All: Improved the way settings are changed. Should also fixed issue with sync context being accidentally broken. 2018-02-13 18:26:33 +00:00
Laurent Cozic
ef0cc5e33e Update readme 2018-02-12 20:23:16 +00:00
Laurent Cozic
3a1fa583ab CLI v1.0.95 2018-02-12 18:07:20 +00:00
Laurent Cozic
c1161ae017 Android release v1.0.95 2018-02-12 17:58:55 +00:00
Laurent Cozic
1023ec6206 Electron release v1.0.62 2018-02-12 17:56:42 +00:00
Laurent Cozic
7841421c0d All: Fixes #209: Items with non-ASCII characters end up truncated on Nextcloud 2018-02-12 18:15:22 +00:00
Laurent Cozic
995d8c35dd Updated readme 2018-02-11 20:49:28 +00:00
Laurent Cozic
b179471eff Electron: Fixes #205: Importing Evernote notes while on import page re-imports previous import 2018-02-11 20:31:26 +00:00
Laurent Cozic
19a126ebfe Android release v1.0.94 2018-02-11 17:29:09 +00:00
Laurent Cozic
7e56e5b587 Mobile: Fixes #207: Crash when changing dropdown value in config screen 2018-02-11 17:26:20 +00:00
Laurent Cozic
acf0c79341 Graduated E2EE and WebDAV from beta, and moved to v1.0 2018-02-11 13:50:59 +00:00
Laurent Cozic
9fe7e23ffe Android release v0.10.92 2018-02-11 13:20:32 +00:00
Laurent Cozic
c94cc93971 Fixed translator names 2018-02-10 13:03:01 +00:00
Laurent Cozic
b26094eba8 Fixed Basque flag 2018-02-10 12:52:57 +00:00
Laurent Cozic
89a5ccdf93 Added Basque translation, fixed issue with handling invalid translations. Updated translation FR. 2018-02-10 12:43:45 +00:00
Laurent Cozic
ce2da0e6dc Update readme 2018-02-09 16:03:43 +00:00
Laurent Cozic
f49d644b6a Electron release v0.10.61 2018-02-08 18:15:49 +00:00
Laurent Cozic
02ac0b8593 Removed uneeded created_time property 2018-02-07 20:42:52 +00:00
Laurent Cozic
78e5eaf1e2 Electron: Toolbar button to set tags 2018-02-07 20:35:11 +00:00
Laurent Cozic
fc0d227396 Electron: Allowing opening and saving resource images 2018-02-07 20:23:17 +00:00
Laurent Cozic
f91c52cdf7 Mobile: Update time when app is activated 2018-02-07 19:51:58 +00:00
Laurent Cozic
3f14878d0f All: Improved request repeating mechanism 2018-02-07 19:46:07 +00:00
Laurent Cozic
69fd32e7c6 All: Use mutex when saving model to avoid race conditions when decrypting and syncing at the same time 2018-02-07 19:02:07 +00:00
Laurent Cozic
80801cedf0 Android release v0.10.91 2018-02-07 17:57:08 +00:00
Laurent Cozic
480e4fa94b Merge branch 'master' of github.com:laurent22/joplin 2018-02-07 17:56:36 +00:00
Laurent Cozic
717c789836 Android release v0.10.90 2018-02-07 17:55:54 +00:00
Laurent Cozic
f099376446 Update README.md 2018-02-07 17:55:17 +00:00
Laurent Cozic
41fa9d093e Android release v0.10.89 2018-02-07 17:54:06 +00:00
Laurent Cozic
e2f3f81eb6 Android release v0.10.88 2018-02-07 17:51:31 +00:00
Laurent Cozic
5cab7aeb55 Fixed: Make sure alarms and resources are attached to right note when creating new note 2018-02-06 19:31:22 +00:00
Laurent Cozic
fa5f418c22 All: Added sync config check to config screens 2018-02-06 18:59:36 +00:00
Laurent Cozic
a25fcacace Electron: Display message when creating new note or to-do 2018-02-06 18:12:43 +00:00
Laurent Cozic
727ba7300e All: Also support $ as delimiter for Katex expressoins 2018-02-06 17:58:54 +00:00
Laurent Cozic
d25d9b3f44 CLI v0.10.93 2018-02-06 13:17:10 +00:00
Laurent Cozic
9d762a4319 Android release v0.10.86 2018-02-06 13:01:20 +00:00
Laurent Cozic
18d94c7585 Electron release v0.10.60 2018-02-06 12:57:40 +00:00
Laurent Cozic
af82345eb8 Fixed tray icon and update issue 2018-02-06 13:11:59 +00:00
Laurent Cozic
1e94a22986 Merge branch 'master' of github.com:laurent22/joplin 2018-02-06 13:06:59 +00:00
Laurent Cozic
e19a8a99ff macOS: Fixed startup crash 2018-02-06 13:05:36 +00:00
Laurent Cozic
f975009e24 Update readme 2018-02-05 19:02:11 +00:00
Laurent Cozic
90640fafc7 Merge branch 'master' of github.com:laurent22/joplin 2018-02-05 18:49:58 +00:00
Laurent Cozic
42e0e1e5a5 Updated build instructions 2018-02-06 09:42:20 +00:00
Laurent Cozic
61f64fa933 Added Markdown doc 2018-02-05 22:53:10 +00:00
Laurent Cozic
0d0ffd6d27 Added Markdown doc 2018-02-05 22:50:17 +00:00
Laurent Cozic
023ccffd2e Electron release v0.10.59 2018-02-05 22:20:31 +00:00
Laurent Cozic
bc26098c7d Android release v0.10.85 2018-02-05 22:19:21 +00:00
Laurent Cozic
7257a71a18 Mobile: Fixes #159: Make sure text fields aren't hidden by keyboard on iOS 2018-02-05 18:32:59 +00:00
Laurent Cozic
8ad8b73585 Better error handling for Katex and handling of code blocks and inline 2018-02-05 17:55:35 +00:00
Laurent Cozic
9a06815db9 Electron release v0.10.58 2018-02-05 17:34:39 +00:00
Laurent Cozic
66947d4954 Fixing appveyor script 2018-02-05 17:34:31 +00:00
Laurent Cozic
3ec22185d5 Electron release v0.10.57 2018-02-05 17:32:54 +00:00
Laurent Cozic
0f05c23e26 Fixing deployment script 2018-02-05 17:32:48 +00:00
Laurent Cozic
74493fece0 Android release v0.10.83 2018-02-05 17:32:00 +00:00
Laurent Cozic
557a96e814 Electron release v0.10.56 2018-02-05 17:27:38 +00:00
rtmkrlv
37e7ea0b52 Merge pull request #2 from laurent22/master
Update fork from original repository
2018-02-01 14:43:34 +02:00
rtmkrlv
44bf518244 Revert "Updated Russian translation"
This reverts commit 63cb9b4968.
2018-01-06 14:36:37 +02:00
rtmkrlv
63cb9b4968 Updated Russian translation
Added translation of new strings and small corrections
2018-01-06 14:33:32 +02:00
rtmkrlv
a6cecc103c Merge pull request #1 from laurent22/master
Update
2018-01-06 13:43:28 +02:00
195 changed files with 11914 additions and 5427 deletions

View File

@@ -46,6 +46,8 @@ before_install:
script:
- |
cd ElectronClient/app
cd Tools
npm install
cd ../ElectronClient/app
rsync -aP --delete ../../ReactNativeClient/lib/ lib/
npm install && yarn dist

View File

@@ -8,7 +8,7 @@
brew install yarn node
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
## Linux and Windows (WSL) dependencies
@@ -17,6 +17,15 @@ If you get a node-gyp related error you might need to manually install it: `npm
- Install node v8.x (check with `node --version`) - https://nodejs.org/en/
- If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
# Building the tools
Before building any of the applications, you need to build the tools:
```
cd Tools
npm install
```
# Building the Electron application
```
@@ -34,6 +43,17 @@ That will create the executable file in the `dist` directory.
From `/ElectronClient` you can also run `run.sh` to run the app for testing.
## Building Electron application on Windows
```
cd Tools
npm install
cd ..\ElectronClient\app
xcopy /C /I /H /R /Y /S ..\..\ReactNativeClient\lib lib
npm install
yarn dist
```
# Building the Mobile application
First you need to setup React Native to build projects with native code. For this, follow the instructions on the [Get Started](https://facebook.github.io/react-native/docs/getting-started.html) tutorial, in the "Building Projects with Native Code" tab.

View File

@@ -1,6 +1,6 @@
# Reporting a bug
Please check first that it [has not already been reported](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). Also consider [enabling debug mode](https://github.com/laurent22/joplin/blob/master/README_debugging.md) before reporting the issue so that you can provide as much details as possible to help fix it.
Please check first that it [has not already been reported](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). Also consider [enabling debug mode](https://github.com/laurent22/joplin/blob/master/readme/debugging.md) before reporting the issue so that you can provide as much details as possible to help fix it.
If possible, **please provide a screenshot**. A screenshot showing the problem is often more useful than a paragraph describing it as it can make it immediately clear what the issue is.
@@ -16,5 +16,4 @@ Building the apps is relatively easy - please [see the build instructions](https
# Coding style
- Only use tabs for indentation, not spaces.
- Do not remove or add optional characters from other lines (such as colons or new line characters) as it can make the commit needlessly big, and create conflicts with other changes.
See the [prettier config](https://github.com/laurent22/joplin/blob/master/prettier.config.js).

View File

@@ -35,38 +35,55 @@ const ConsoleWidget = require('./gui/ConsoleWidget.js');
class AppGui {
constructor(app, store) {
this.app_ = app;
this.store_ = store;
constructor(app, store, keymap) {
try {
this.app_ = app;
this.store_ = store;
BaseWidget.setLogger(app.logger());
BaseWidget.setLogger(app.logger());
this.term_ = new TermWrapper(tk.terminal);
this.term_ = new TermWrapper(tk.terminal);
this.renderer_ = null;
this.logger_ = new Logger();
this.buildUi();
// Some keys are directly handled by the tkwidget framework
// so they need to be remapped in a different way.
this.tkWidgetKeys_ = {
'focus_next': 'TAB',
'focus_previous': 'SHIFT_TAB',
'move_up': 'UP',
'move_down': 'DOWN',
'page_down': 'PAGE_DOWN',
'page_up': 'PAGE_UP',
};
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
this.renderer_ = null;
this.logger_ = new Logger();
this.buildUi();
this.app_.on('modelAction', async (event) => {
await this.handleModelAction(event.action);
});
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
this.shortcuts_ = this.setupShortcuts();
this.app_.on('modelAction', async (event) => {
await this.handleModelAction(event.action);
});
this.inputMode_ = AppGui.INPUT_MODE_NORMAL;
this.keymap_ = this.setupKeymap(keymap);
this.commandCancelCalled_ = false;
this.inputMode_ = AppGui.INPUT_MODE_NORMAL;
this.currentShortcutKeys_ = [];
this.lastShortcutKeyTime_ = 0;
this.commandCancelCalled_ = false;
// Recurrent sync is setup only when the GUI is started. In
// a regular command it's not necessary since the process
// exits right away.
reg.setupRecurrentSync();
DecryptionWorker.instance().scheduleStart();
this.currentShortcutKeys_ = [];
this.lastShortcutKeyTime_ = 0;
// Recurrent sync is setup only when the GUI is started. In
// a regular command it's not necessary since the process
// exits right away.
reg.setupRecurrentSync();
DecryptionWorker.instance().scheduleStart();
} catch (error) {
this.fullScreen(false);
console.error(error);
process.exit(1);
}
}
store() {
@@ -105,6 +122,7 @@ class AppGui {
buildUi() {
this.rootWidget_ = new ReduxRootWidget(this.store_);
this.rootWidget_.name = 'root';
this.rootWidget_.autoShortcutsEnabled = false;
const folderList = new FolderListWidget();
folderList.style = {
@@ -269,155 +287,31 @@ class AppGui {
addCommandToConsole(cmd) {
if (!cmd) return;
const isConfigPassword = cmd.indexOf('config ') >= 0 && cmd.indexOf('password') >= 0;
if (isConfigPassword) return;
this.stdout(chalk.cyan.bold('> ' + cmd));
}
setupShortcuts() {
const shortcuts = {};
setupKeymap(keymap) {
const output = [];
shortcuts['TAB'] = {
friendlyName: 'Tab',
description: () => _('Give focus to next pane'),
isDocOnly: true,
}
for (let i = 0; i < keymap.length; i++) {
const item = Object.assign({}, keymap[i]);
shortcuts['SHIFT_TAB'] = {
friendlyName: 'Shift+Tab',
description: () => _('Give focus to previous pane'),
isDocOnly: true,
}
if (!item.command) throw new Error('Missing command for keymap item: ' + JSON.stringify(item));
shortcuts[':'] = {
description: () => _('Enter command line mode'),
action: async () => {
const cmd = await this.widget('statusBar').prompt();
if (!cmd) return;
this.addCommandToConsole(cmd);
await this.processCommand(cmd);
},
};
if (!('type' in item)) item.type = 'exec';
shortcuts['ESC'] = { // Built into terminal-kit inputField
description: () => _('Exit command line mode'),
isDocOnly: true,
};
shortcuts['ENTER'] = {
description: () => _('Edit the selected note'),
action: () => {
const w = this.widget('mainWindow').focusedWidget;
if (w.name === 'folderList') {
this.widget('noteList').focus();
} else if (w.name === 'noteList' || w.name === 'noteText') {
this.processCommand('edit $n');
}
},
}
shortcuts['CTRL_C'] = {
description: () => _('Cancel the current command.'),
friendlyName: 'Ctrl+C',
isDocOnly: true,
}
shortcuts['CTRL_D'] = {
description: () => _('Exit the application.'),
friendlyName: 'Ctrl+D',
isDocOnly: true,
}
shortcuts['DELETE'] = {
description: () => _('Delete the currently selected note or notebook.'),
action: async () => {
if (this.widget('folderList').hasFocus) {
const item = this.widget('folderList').selectedJoplinItem;
if (!item) return;
if (item.type_ === BaseModel.TYPE_FOLDER) {
await this.processCommand('rmbook ' + item.id);
} else if (item.type_ === BaseModel.TYPE_TAG) {
this.stdout(_('To delete a tag, untag the associated notes.'));
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
this.store().dispatch({
type: 'SEARCH_DELETE',
id: item.id,
});
}
} else if (this.widget('noteList').hasFocus) {
await this.processCommand('rmnote $n');
} else {
this.stdout(_('Please select the note or notebook to be deleted first.'));
}
if (item.command in this.tkWidgetKeys_) {
item.type = 'tkwidgets';
}
};
shortcuts['BACKSPACE'] = {
alias: 'DELETE',
};
item.canRunAlongOtherCommands = item.type === 'function' && ['toggle_metadata', 'toggle_console'].indexOf(item.command) >= 0;
shortcuts[' '] = {
friendlyName: 'SPACE',
description: () => _('Set a to-do as completed / not completed'),
action: 'todo toggle $n',
output.push(item);
}
shortcuts['tc'] = {
description: () => _('[t]oggle [c]onsole between maximized/minimized/hidden/visible.'),
action: () => {
if (!this.consoleIsShown()) {
this.showConsole();
this.minimizeConsole();
} else {
if (this.consoleIsMaximized()) {
this.hideConsole();
} else {
this.maximizeConsole();
}
}
},
canRunAlongOtherCommands: true,
}
shortcuts['/'] = {
description: () => _('Search'),
action: { type: 'prompt', initialText: 'search ""', cursorPosition: -2 },
};
shortcuts['tm'] = {
description: () => _('[t]oggle note [m]etadata.'),
action: () => {
this.toggleNoteMetadata();
},
canRunAlongOtherCommands: true,
}
shortcuts['mn'] = {
description: () => _('[M]ake a new [n]ote'),
action: { type: 'prompt', initialText: 'mknote ""', cursorPosition: -2 },
}
shortcuts['mt'] = {
description: () => _('[M]ake a new [t]odo'),
action: { type: 'prompt', initialText: 'mktodo ""', cursorPosition: -2 },
}
shortcuts['mb'] = {
description: () => _('[M]ake a new note[b]ook'),
action: { type: 'prompt', initialText: 'mkbook ""', cursorPosition: -2 },
}
shortcuts['yn'] = {
description: () => _('Copy ([Y]ank) the [n]ote to a notebook.'),
action: { type: 'prompt', initialText: 'cp $n ""', cursorPosition: -2 },
}
shortcuts['dn'] = {
description: () => _('Move the note to a notebook.'),
action: { type: 'prompt', initialText: 'mv $n ""', cursorPosition: -2 },
}
return shortcuts;
return output;
}
toggleConsole() {
@@ -492,8 +386,16 @@ class AppGui {
return this.logger_;
}
shortcuts() {
return this.shortcuts_;
keymap() {
return this.keymap_;
}
keymapItemByKey(key) {
for (let i = 0; i < this.keymap_.length; i++) {
const item = this.keymap_[i];
if (item.keys.indexOf(key) >= 0) return item;
}
return null;
}
term() {
@@ -524,17 +426,77 @@ class AppGui {
}
}
async processCommand(cmd) {
async processFunctionCommand(cmd) {
if (cmd === 'activate') {
const w = this.widget('mainWindow').focusedWidget;
if (w.name === 'folderList') {
this.widget('noteList').focus();
} else if (w.name === 'noteList' || w.name === 'noteText') {
this.processPromptCommand('edit $n');
}
} else if (cmd === 'delete') {
if (this.widget('folderList').hasFocus) {
const item = this.widget('folderList').selectedJoplinItem;
if (!item) return;
if (item.type_ === BaseModel.TYPE_FOLDER) {
await this.processPromptCommand('rmbook ' + item.id);
} else if (item.type_ === BaseModel.TYPE_TAG) {
this.stdout(_('To delete a tag, untag the associated notes.'));
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
this.store().dispatch({
type: 'SEARCH_DELETE',
id: item.id,
});
}
} else if (this.widget('noteList').hasFocus) {
await this.processPromptCommand('rmnote $n');
} else {
this.stdout(_('Please select the note or notebook to be deleted first.'));
}
} else if (cmd === 'toggle_console') {
if (!this.consoleIsShown()) {
this.showConsole();
this.minimizeConsole();
} else {
if (this.consoleIsMaximized()) {
this.hideConsole();
} else {
this.maximizeConsole();
}
}
} else if (cmd === 'toggle_metadata') {
this.toggleNoteMetadata();
} else if (cmd === 'enter_command_line_mode') {
const cmd = await this.widget('statusBar').prompt();
if (!cmd) return;
this.addCommandToConsole(cmd);
await this.processPromptCommand(cmd);
} else {
throw new Error('Unknown command: ' + cmd);
}
}
async processPromptCommand(cmd) {
if (!cmd) return;
cmd = cmd.trim();
if (!cmd.length) return;
this.logger().info('Got command: ' + cmd);
if (cmd === 'q' || cmd === 'wq' || cmd === 'qa') { // Vim bonus
await this.app().exit();
return;
}
// this.logger().debug('Got command: ' + cmd);
try {
let note = this.widget('noteList').currentItem;
@@ -786,35 +748,34 @@ class AppGui {
// -------------------------------------------------------------------------
const shortcutKey = this.currentShortcutKeys_.join('');
let cmd = shortcutKey in this.shortcuts_ ? this.shortcuts_[shortcutKey] : null;
let keymapItem = this.keymapItemByKey(shortcutKey);
// If this command is an alias to another command, resolve to the actual command
if (cmd && cmd.alias) cmd = this.shortcuts_[cmd.alias];
let processShortcutKeys = !this.app().currentCommand() && cmd;
if (cmd && cmd.canRunAlongOtherCommands) processShortcutKeys = true;
let processShortcutKeys = !this.app().currentCommand() && keymapItem;
if (keymapItem && keymapItem.canRunAlongOtherCommands) processShortcutKeys = true;
if (statusBar.promptActive) processShortcutKeys = false;
if (cmd && cmd.isDocOnly) processShortcutKeys = false;
if (processShortcutKeys) {
this.logger().info('Shortcut:', shortcutKey, cmd.description());
this.logger().debug('Shortcut:', shortcutKey, keymapItem);
this.currentShortcutKeys_ = [];
if (typeof cmd.action === 'function') {
await cmd.action();
} else if (typeof cmd.action === 'object') {
if (cmd.action.type === 'prompt') {
let promptOptions = {};
if ('cursorPosition' in cmd.action) promptOptions.cursorPosition = cmd.action.cursorPosition;
const commandString = await statusBar.prompt(cmd.action.initialText ? cmd.action.initialText : '', null, promptOptions);
this.addCommandToConsole(commandString);
await this.processCommand(commandString);
} else {
throw new Error('Unknown command: ' + JSON.stringify(cmd.action));
}
} else { // String
this.stdout(cmd.action);
await this.processCommand(cmd.action);
if (keymapItem.type === 'function') {
this.processFunctionCommand(keymapItem.command);
} else if (keymapItem.type === 'prompt') {
let promptOptions = {};
if ('cursorPosition' in keymapItem) promptOptions.cursorPosition = keymapItem.cursorPosition;
const commandString = await statusBar.prompt(keymapItem.command ? keymapItem.command : '', null, promptOptions);
this.addCommandToConsole(commandString);
await this.processPromptCommand(commandString);
} else if (keymapItem.type === 'exec') {
this.stdout(keymapItem.command);
await this.processPromptCommand(keymapItem.command);
} else if (keymapItem.type === 'tkwidgets') {
this.widget('root').handleKey(this.tkWidgetKeys_[keymapItem.command]);
} else {
throw new Error('Unknown command type: ' + JSON.stringify(keymapItem));
}
}

View File

@@ -283,7 +283,7 @@ class Application extends BaseApplication {
exit: () => {},
showModalOverlay: (text) => {},
hideModalOverlay: () => {},
stdoutMaxWidth: () => { return 78; },
stdoutMaxWidth: () => { return 100; },
forceRender: () => {},
termSaveState: () => {},
termRestoreState: (state) => {},
@@ -292,7 +292,7 @@ class Application extends BaseApplication {
async execCommand(argv) {
if (!argv.length) return this.execCommand(['help']);
reg.logger().info('execCommand()', argv);
// reg.logger().debug('execCommand()', argv);
const commandName = argv[0];
this.activeCommand_ = this.findCommandByName(commandName);
@@ -312,6 +312,63 @@ class Application extends BaseApplication {
return this.activeCommand_;
}
async loadKeymaps() {
const defaultKeyMap = [
{ "keys": [":"], "type": "function", "command": "enter_command_line_mode" },
{ "keys": ["TAB"], "type": "function", "command": "focus_next" },
{ "keys": ["SHIFT_TAB"], "type": "function", "command": "focus_previous" },
{ "keys": ["UP"], "type": "function", "command": "move_up" },
{ "keys": ["DOWN"], "type": "function", "command": "move_down" },
{ "keys": ["PAGE_UP"], "type": "function", "command": "page_up" },
{ "keys": ["PAGE_DOWN"], "type": "function", "command": "page_down" },
{ "keys": ["ENTER"], "type": "function", "command": "activate" },
{ "keys": ["DELETE", "BACKSPACE"], "type": "function", "command": "delete" },
{ "keys": [" "], "command": "todo toggle $n" },
{ "keys": ["tc"], "type": "function", "command": "toggle_console" },
{ "keys": ["tm"], "type": "function", "command": "toggle_metadata" },
{ "keys": ["/"], "type": "prompt", "command": "search \"\"", "cursorPosition": -2 },
{ "keys": ["mn"], "type": "prompt", "command": "mknote \"\"", "cursorPosition": -2 },
{ "keys": ["mt"], "type": "prompt", "command": "mktodo \"\"", "cursorPosition": -2 },
{ "keys": ["mb"], "type": "prompt", "command": "mkbook \"\"", "cursorPosition": -2 },
{ "keys": ["yn"], "type": "prompt", "command": "cp $n \"\"", "cursorPosition": -2 },
{ "keys": ["dn"], "type": "prompt", "command": "mv $n \"\"", "cursorPosition": -2 }
];
// Filter the keymap item by command so that items in keymap.json can override
// the default ones.
const itemsByCommand = {};
for (let i = 0; i < defaultKeyMap.length; i++) {
itemsByCommand[defaultKeyMap[i].command] = defaultKeyMap[i]
}
const filePath = Setting.value('profileDir') + '/keymap.json';
if (await fs.pathExists(filePath)) {
try {
let configString = await fs.readFile(filePath, 'utf-8');
configString = configString.replace(/^\s*\/\/.*/, ''); // Strip off comments
const keymap = JSON.parse(configString);
for (let keymapIndex = 0; keymapIndex < keymap.length; keymapIndex++) {
const item = keymap[keymapIndex];
itemsByCommand[item.command] = item;
}
} catch (error) {
let msg = error.message ? error.message : '';
msg = 'Could not load keymap ' + filePath + '\n' + msg;
error.message = msg;
throw error;
}
}
const output = [];
for (let n in itemsByCommand) {
if (!itemsByCommand.hasOwnProperty(n)) continue;
output.push(itemsByCommand[n]);
}
return output;
}
async start(argv) {
argv = await super.start(argv);
@@ -330,16 +387,19 @@ class Application extends BaseApplication {
await this.execCommand(argv);
} catch (error) {
if (this.showStackTraces_) {
console.info(error);
console.error(error);
} else {
console.info(error.message);
}
process.exit(1);
}
} else { // Otherwise open the GUI
this.initRedux();
const keymap = await this.loadKeymaps();
const AppGui = require('./app-gui.js');
this.gui_ = new AppGui(this, this.store());
this.gui_ = new AppGui(this, this.store(), keymap);
this.gui_.setLogger(this.logger_);
await this.gui_.start();

View File

@@ -32,10 +32,6 @@ class BaseCommand {
return this.compatibleUis().indexOf(ui) >= 0;
}
aliases() {
return [];
}
options() {
return [];
}

View File

@@ -1,5 +1,5 @@
const { BaseCommand } = require('./base-command.js');
const { Exporter } = require('lib/services/exporter.js');
const InteropService = require('lib/services/InteropService.js');
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
const { reg } = require('lib/registry.js');
@@ -10,15 +10,21 @@ const fs = require('fs-extra');
class Command extends BaseCommand {
usage() {
return 'export <directory>';
return 'export <path>';
}
description() {
return _('Exports Joplin data to the given directory. By default, it will export the complete database including notebooks, notes, tags and resources.');
return _('Exports Joplin data to the given path. By default, it will export the complete database including notebooks, notes, tags and resources.');
}
options() {
const service = new InteropService();
const formats = service.modules()
.filter(m => m.type === 'exporter')
.map(m => m.format + (m.description ? ' (' + m.description + ')' : ''));
return [
['--format <format>', _('Destination format: %s', formats.join(', '))],
['--note <note>', _('Exports only the given note.')],
['--notebook <notebook>', _('Exports only the given notebook.')],
];
@@ -26,13 +32,9 @@ class Command extends BaseCommand {
async action(args) {
let exportOptions = {};
exportOptions.destDir = args.directory;
exportOptions.writeFile = (filePath, data) => {
return fs.writeFile(filePath, data);
};
exportOptions.copyFile = (source, dest) => {
return fs.copy(source, dest, { overwrite: true });
};
exportOptions.path = args.path;
exportOptions.format = args.options.format ? args.options.format : 'jex';
if (args.options.note) {
@@ -48,10 +50,10 @@ class Command extends BaseCommand {
}
const exporter = new Exporter();
const result = await exporter.export(exportOptions);
const service = new InteropService();
const result = await service.export(exportOptions);
reg.logger().info('Export result: ', result);
result.warnings.map((w) => this.stdout(w));
}
}

View File

@@ -36,21 +36,22 @@ class Command extends BaseCommand {
async action(args) {
const stdoutWidth = app().commandStdoutMaxWidth();
if (args.command === 'shortcuts') {
if (args.command === 'shortcuts' || args.command === 'keymap') {
this.stdout(_('For information on how to customise the shortcuts please visit %s', 'http://joplin.cozic.net/terminal/#shortcuts'));
this.stdout('');
if (app().gui().isDummy()) {
throw new Error(_('Shortcuts are not available in CLI mode.'));
}
const shortcuts = app().gui().shortcuts();
const keymap = app().gui().keymap();
let rows = [];
for (let n in shortcuts) {
if (!shortcuts.hasOwnProperty(n)) continue;
const shortcut = shortcuts[n];
if (!shortcut.description) continue;
n = shortcut.friendlyName ? shortcut.friendlyName : n;
rows.push([n, shortcut.description()]);
for (let i = 0; i < keymap.length; i++) {
const item = keymap[i];
const keys = item.keys.map((k) => k === ' ' ? '(SPACE)' : k);
rows.push([keys.join(', '), item.command]);
}
cliUtils.printArray(this.stdout.bind(this), rows);
@@ -78,7 +79,7 @@ class Command extends BaseCommand {
this.stdout(_('To maximise/minimise the console, press "TC".'));
this.stdout(_('To enter command line mode, press ":"'));
this.stdout(_('To exit command line mode, press ESCAPE'));
this.stdout(_('For the complete list of available keyboard shortcuts, type `help shortcuts`'));
this.stdout(_('For the list of keyboard shortcuts and config options, type `help keymap`'));
}
app().gui().showConsole();

View File

@@ -1,68 +0,0 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const Folder = require('lib/models/Folder.js');
const { importEnex } = require('lib/import-enex');
const { filename, basename } = require('lib/path-utils.js');
const { cliUtils } = require('./cli-utils.js');
class Command extends BaseCommand {
usage() {
return 'import-enex <file> [notebook]';
}
description() {
return _('Imports an Evernote notebook file (.enex file).');
}
options() {
return [
['-f, --force', _('Do not ask for confirmation.')],
];
}
async action(args) {
let filePath = args.file;
let folder = null;
let folderTitle = args['notebook'];
let force = args.options.force === true;
if (!folderTitle) folderTitle = filename(filePath);
folder = await Folder.loadByField('title', folderTitle);
const msg = folder ? _('File "%s" will be imported into existing notebook "%s". Continue?', basename(filePath), folderTitle) : _('New notebook "%s" will be created and file "%s" will be imported into it. Continue?', folderTitle, basename(filePath));
const ok = force ? true : await this.prompt(msg);
if (!ok) return;
let lastProgress = '';
let options = {
onProgress: (progressState) => {
let line = [];
line.push(_('Found: %d.', progressState.loaded));
line.push(_('Created: %d.', progressState.created));
if (progressState.updated) line.push(_('Updated: %d.', progressState.updated));
if (progressState.skipped) line.push(_('Skipped: %d.', progressState.skipped));
if (progressState.resourcesCreated) line.push(_('Resources: %d.', progressState.resourcesCreated));
if (progressState.notesTagged) line.push(_('Tagged: %d.', progressState.notesTagged));
lastProgress = line.join(' ');
cliUtils.redraw(lastProgress);
},
onError: (error) => {
let s = error.trace ? error.trace : error.toString();
this.stdout(s);
},
}
folder = !folder ? await Folder.save({ title: folderTitle }) : folder;
app().gui().showConsole();
this.stdout(_('Importing notes...'));
await importEnex(folder.id, filePath, options);
cliUtils.redrawDone();
this.stdout(_('The notes have been imported: %s', lastProgress));
}
}
module.exports = Command;

View File

@@ -0,0 +1,75 @@
const { BaseCommand } = require('./base-command.js');
const InteropService = require('lib/services/InteropService.js');
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
const { filename, basename, fileExtension } = require('lib/path-utils.js');
const { importEnex } = require('lib/import-enex');
const { cliUtils } = require('./cli-utils.js');
const { reg } = require('lib/registry.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const fs = require('fs-extra');
class Command extends BaseCommand {
usage() {
return 'import <path> [notebook]';
}
description() {
return _('Imports data into Joplin.');
}
options() {
const service = new InteropService();
const formats = service.modules().filter(m => m.type === 'importer').map(m => m.format);
return [
['--format <format>', _('Source format: %s', (['auto'].concat(formats)).join(', '))],
['-f, --force', _('Do not ask for confirmation.')],
];
}
async action(args) {
let folder = await app().loadItem(BaseModel.TYPE_FOLDER, args.notebook);
if (args.notebook && !folder) throw new Error(_('Cannot find "%s".', args.notebook));
const importOptions = {};
importOptions.path = args.path;
importOptions.format = args.options.format ? args.options.format : 'auto';
importOptions.destinationFolderId = folder ? folder.id : null;
let lastProgress = '';
// onProgress/onError supported by Enex import only
importOptions.onProgress = (progressState) => {
let line = [];
line.push(_('Found: %d.', progressState.loaded));
line.push(_('Created: %d.', progressState.created));
if (progressState.updated) line.push(_('Updated: %d.', progressState.updated));
if (progressState.skipped) line.push(_('Skipped: %d.', progressState.skipped));
if (progressState.resourcesCreated) line.push(_('Resources: %d.', progressState.resourcesCreated));
if (progressState.notesTagged) line.push(_('Tagged: %d.', progressState.notesTagged));
lastProgress = line.join(' ');
cliUtils.redraw(lastProgress);
};
importOptions.onError = (error) => {
let s = error.trace ? error.trace : error.toString();
this.stdout(s);
};
app().gui().showConsole();
this.stdout(_('Importing notes...'));
const service = new InteropService();
const result = await service.import(importOptions);
result.warnings.map((w) => this.stdout(w));
cliUtils.redrawDone();
if (lastProgress) this.stdout(_('The notes have been imported: %s', lastProgress));
}
}
module.exports = Command;

View File

@@ -14,10 +14,6 @@ class Command extends BaseCommand {
return _('Creates a new notebook.');
}
aliases() {
return ['mkdir'];
}
async action(args) {
let folder = await Folder.save({ title: args['new-notebook'] }, { userSideValidation: true });
app().switchCurrentFolder(folder);

View File

@@ -10,7 +10,6 @@ const { cliUtils } = require('./cli-utils.js');
const md5 = require('md5');
const locker = require('proper-lockfile');
const fs = require('fs-extra');
const osTmpdir = require('os-tmpdir');
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
class Command extends BaseCommand {
@@ -101,7 +100,7 @@ class Command extends BaseCommand {
this.releaseLockFn_ = null;
// Lock is unique per profile/database
const lockFilePath = osTmpdir() + '/synclock_' + md5(escape(Setting.value('profileDir'))); // https://github.com/pvorb/node-md5/issues/41
const lockFilePath = require('os').tmpdir() + '/synclock_' + md5(escape(Setting.value('profileDir'))); // https://github.com/pvorb/node-md5/issues/41
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, 'synclock');
try {

View File

@@ -133,7 +133,8 @@ class StatusBarWidget extends BaseWidget {
resolveResult = input ? input.trim() : input;
// Add the command to history but only if it's longer than one character.
// Below that it's usually an answer like "y"/"n", etc.
if (!isSecurePrompt && input && input.length > 1) this.history_.push(input);
const isConfigPassword = input.indexOf('config ') >= 0 && input.indexOf('password') >= 0;
if (!isSecurePrompt && input && input.length > 1 && !isConfigPassword) this.history_.push(input);
}
}

View File

@@ -53,9 +53,8 @@ function renderCommandHelp(cmd, width = null) {
desc.push(label);
}
if (md.description) {
desc.push(md.description());
}
const description = Setting.keyDescription(md.key, 'cli');
if (description) desc.push(description);
desc.push(_('Type: %s.', md.isEnum ? _('Enum') : Setting.typeToString(md.type)));
if (md.isEnum) desc.push(_('Possible values: %s.', Setting.enumOptionsDoc(md.key, '%s (%s)')));

View File

@@ -71,37 +71,10 @@ process.stdout.on('error', function( err ) {
// async function main() {
// const WebDavApi = require('lib/WebDavApi');
// const api = new WebDavApi('http://nextcloud.local/remote.php/dav/files/admin/Joplin', { username: 'admin', password: '1234567' });
// const { FileApiDriverWebDav } = new require('lib/file-api-driver-webdav');
// const driver = new FileApiDriverWebDav(api);
// const stat = await driver.stat('');
// console.info(stat);
// // const stat = await driver.stat('testing.txt');
// // console.info(stat);
// // const content = await driver.get('testing.txta');
// // console.info(content);
// // const content = await driver.get('testing.txta', { target: 'file', path: '/var/www/joplin/CliClient/testing-file.txt' });
// // console.info(content);
// // const content = await driver.mkdir('newdir5');
// // console.info(content);
// //await driver.put('myfile4.md', 'this is my content');
// // await driver.put('testimg.jpg', null, { source: 'file', path: '/mnt/d/test.jpg' });
// // await driver.delete('myfile4.md');
// // const deltaResult = await driver.delta('', {
// // allItemIdsHandler: () => { return []; }
// // });
// // console.info(deltaResult);
// const InteropService = require('lib/services/InteropService');
// const service = new InteropService();
// console.info(service.moduleByFormat('importer', 'enex'));
// //await service.modules();
// }
// main().catch((error) => { console.error(error); });
@@ -114,6 +87,13 @@ process.stdout.on('error', function( err ) {
application.start(process.argv).catch((error) => {
console.error(_('Fatal error:'));
console.error(error);
if (error.code == 'flagError') {
console.error(error.message);
console.error(_('Type `joplin help` for usage information.'));
} else {
console.error(_('Fatal error:'));
console.error(error);
}
process.exit(1);
});

View File

@@ -16,82 +16,29 @@ msgstr ""
"X-Generator: Poedit 2.0.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Give focus to next pane"
msgstr "Das nächste Fenster fokussieren"
msgid "Give focus to previous pane"
msgstr "Das vorherige Fenster fokussieren"
msgid "Enter command line mode"
msgstr "Zum Terminal-Modus wechseln"
msgid "Exit command line mode"
msgstr "Den Terminal-Modus verlassen"
msgid "Edit the selected note"
msgstr "Die ausgewählte Notiz bearbeiten"
msgid "Cancel the current command."
msgstr "Den momentanen Befehl abbrechen."
msgid "Exit the application."
msgstr "Das Programm verlassen."
msgid "Delete the currently selected note or notebook."
msgstr "Die/das momentan ausgewählte Notiz(-buch) löschen."
msgid "To delete a tag, untag the associated notes."
msgstr ""
"Hebe die Markierungen zugehöriger Notizen auf, um eine Markierung zu löschen."
"Um eine Markierung zu löschen, entferne diese bei allen damit verbundenen "
"Notizen."
msgid "Please select the note or notebook to be deleted first."
msgstr ""
"Wähle bitte zuerst eine Notiz oder ein Notizbuch aus, das gelöscht werden "
"soll."
msgid "Set a to-do as completed / not completed"
msgstr "Ein To-Do als abgeschlossen / nicht abgeschlossen markieren"
#, fuzzy
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr ""
"Schal[t]e das Terminal zwischen maximiert/minimiert/versteckt/sichtbar um."
msgid "Search"
msgstr "Suchen"
msgid "[t]oggle note [m]etadata."
msgstr "Notiz-[M]etadata einschal[t]en."
msgid "[M]ake a new [n]ote"
msgstr "Eine neue [N]otiz [m]achen"
msgid "[M]ake a new [t]odo"
msgstr "Ein neues [T]o-Do [m]achen"
msgid "[M]ake a new note[b]ook"
msgstr "Ein neues Notiz[b]uch [m]achen"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "Die Notiz zu einem Notizbuch kopieren."
msgid "Move the note to a notebook."
msgstr "Die Notiz zu einem Notizbuch verschieben."
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Drücke Strg+D oder tippe \"exit\", um das Programm zu verlassen"
msgstr "Drücke Strg+D oder tippe \"exit\" um das Programm zu verlassen"
#, javascript-format
msgid "More than one item match \"%s\". Please narrow down your query."
msgstr ""
"Mehr als eine Notiz stimmt mit \"%s\" überein. Bitte schränke deine Suche "
"ein."
"Mehr als eine Notiz stimmt mit \"%s\" überein. Bitte die Suche einschränken."
msgid "No notebook selected."
msgstr "Kein Notizbuch ausgewählt."
msgid "No notebook has been specified."
msgstr "Kein Notizbuch wurde angegeben."
msgstr "Kein Notizbuch wurde ausgewählt."
msgid "Y"
msgstr "J"
@@ -121,7 +68,7 @@ msgstr "Kann verschlüsseltes Objekt nicht ändern"
#, javascript-format
msgid "Missing required argument: %s"
msgstr "Fehlendes benötigtes Argument: %s"
msgstr "Fehlendes erforderliches Argument: %s"
#, javascript-format
msgid "%s: %s"
@@ -157,8 +104,7 @@ msgstr ""
"gegeben sind, wird eine Liste der momentanen Konfiguration angezeigt."
msgid "Also displays unset and hidden config variables."
msgstr ""
"Zeigt auch nicht angegebene oder versteckte Konfigurationsvariablen an."
msgstr "Zeigt auch nicht gesetzte und versteckte Konfigurationsvariablen an."
#, javascript-format
msgid "%s = %s (%s)"
@@ -172,12 +118,12 @@ msgid ""
"Duplicates the notes matching <note> to [notebook]. If no notebook is "
"specified the note is duplicated in the current notebook."
msgstr ""
"Dupliziert die Notizen die mit <note> übereinstimmen zu [Notizbuch]. Wenn "
"kein Notizbuch angegeben ist, wird die Notiz in das momentane Notizbuch "
"Dupliziert die Notizen die mit <note> übereinstimmen in [Notizbuch]. Wenn "
"kein Notizbuch angegeben ist, wird die Notiz in das aktuelle Notizbuch "
"kopiert."
msgid "Marks a to-do as done."
msgstr "Markiert ein To-Do als abgeschlossen."
msgstr "Markiert ein To-Do als erledigt."
#, javascript-format
msgid "Note is not a to-do: \"%s\""
@@ -187,7 +133,7 @@ msgid ""
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, "
"`status` and `target-status`."
msgstr ""
"Verwaltet die E2EE-Konfiguration. Die Befehle sind `enable`, `disable`, "
"Verwaltet die E2EE-Konfiguration. Die Befehle lauten `enable`, `disable`, "
"`decrypt`, `status` und `target-status`."
msgid "Enter master password:"
@@ -200,8 +146,8 @@ msgid ""
"Starting decryption... Please wait as it may take several minutes depending "
"on how much there is to decrypt."
msgstr ""
"Entschlüsselung starten.... Warte bitte, da es einige Minuten dauern kann, "
"je nachdem, wie viel es zu entschlüsseln gibt."
"Starte Entschlüsselung.... Bitte warten, da dies je nach Anzahl der "
"betreffenden Objekte einige Minuten dauern kann."
msgid "Completed decryption."
msgstr "Entschlüsselung abgeschlossen."
@@ -214,7 +160,7 @@ msgstr "Deaktiviert"
#, javascript-format
msgid "Encryption is: %s"
msgstr "Die Verschlüsselung ist: %s"
msgstr "Verschlüsselung ist: %s"
msgid "Edit note."
msgstr "Notiz bearbeiten."
@@ -222,8 +168,8 @@ msgstr "Notiz bearbeiten."
msgid ""
"No text editor is defined. Please set it using `config editor <editor-path>`"
msgstr ""
"Kein Textverarbeitungsprogramm angegeben. Bitte lege eines mit `config "
"editor <Pfad-Zum-Textverarbeitungsprogramm>` fest"
"Kein Texteditor definiert. Bitte lege einen mit `config editor <Pfad-Zum-"
"Texteditor>` fest"
msgid "No active notebook."
msgstr "Kein aktives Notizbuch."
@@ -245,16 +191,20 @@ msgid "Note has been saved."
msgstr "Die Notiz wurde gespeichert."
msgid "Exits the application."
msgstr "Schließt das Programm."
msgstr "Beendet das Programm."
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Exportiert Joplins Dateien zu dem angegebenen Pfad. Standardmäßig wird die "
"Exportiert Joplin Dateien in den angegebenen Pfad. Standardmäßig wird die "
"komplette Datenbank inklusive Notizbüchern, Notizen, Markierungen und "
"Anhängen exportiert."
#, javascript-format
msgid "Destination format: %s"
msgstr "Zielformat: %s"
msgid "Exports only the given note."
msgstr "Exportiert nur die angegebene Notiz."
@@ -267,6 +217,12 @@ msgstr "Zeigt die Standort-URL der Notiz an."
msgid "Displays usage information."
msgstr "Zeigt die Nutzungsstatistik an."
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
"Für weitere Informationen über die Anpassung von Tastenkürzel besuche bitte "
"%s"
msgid "Shortcuts are not available in CLI mode."
msgstr "Tastenkürzel sind im CLI Modus nicht verfügbar."
@@ -274,11 +230,12 @@ msgid ""
"Type `help [command]` for more information about a command; or type `help "
"all` for the complete usage information."
msgstr ""
"Tippe `help [Befehl]` für weitere Informationen über einen Befehl; oder "
"tippe `help all` für die vollständigen Informationen zur Befehlsverwendung."
"Tippe `help [Befehl]` um weitere Informationen über einen Befehl zu erhalten "
"oder tippe `help all` für die vollständigen Informationen zur "
"Befehlsverwendung."
msgid "The possible commands are:"
msgstr "Mögliche Befehle sind:"
msgstr "Mögliche Befehle lauten:"
msgid ""
"In any command, a note or notebook can be refered to by title or ID, or "
@@ -287,13 +244,13 @@ msgid ""
msgstr ""
"In jedem Befehl können Notizen oder Notizbücher durch ihren Titel oder ihre "
"ID spezifiziert werden, oder durch die Abkürzung `$n` oder `$b` um entweder "
"das momentan ausgewählte Notizbuch oder die momentan ausgewählte Notiz zu "
"wählen. `$c` kann benutzt werden, um auf die momentane Auswahl zu verweisen."
"das momentan ausgewählte Notizbuch oder die momentan ausgewählte Notiz "
"auszuwählen. `$c` kann benutzt werden, um auf die momentane Auswahl zu "
"verweisen."
msgid "To move from one pane to another, press Tab or Shift+Tab."
msgstr ""
"Um ein von einem Fenster zu einem anderen zu wechseln, drücke Tab oder Shift"
"+Tab."
"Um von einem Fenster zu einem anderen zu wechseln, drücke Tab oder Shift+Tab."
msgid ""
"Use the arrows and page up/down to scroll the lists and text areas "
@@ -312,31 +269,21 @@ msgid "To exit command line mode, press ESCAPE"
msgstr "Um den Kommandozeilen Modus zu beenden, drücke ESCAPE"
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
"Um die komplette Liste von verfügbaren Tastenkürzeln anzuzeigen, tippe `help "
"shortcuts` ein"
"Um die komplette Liste aller verfügbaren Tastenkürzeln anzuzeigen, tippe "
"`help keymap` ein"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Importiert eine Evernote Notizbuch-Datei (.enex Datei)."
msgid "Imports data into Joplin."
msgstr "Importiert Daten in Joplin."
#, javascript-format
msgid "Source format: %s"
msgstr "Quellformat: %s"
msgid "Do not ask for confirmation."
msgstr "Nicht nach einer Bestätigung fragen."
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
"Datei \"%s\" wird in das existierende Notizbuch \"%s\" importiert. "
"Fortfahren?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"Neues Notizbuch \"%s\" wird erstellt und die Datei \"%s\" wird hinein "
"importiert. Fortfahren?"
#, javascript-format
msgid "Found: %d."
msgstr "Gefunden: %d."
@@ -511,7 +458,7 @@ msgid "Starting synchronisation..."
msgstr "Starte Synchronisation..."
msgid "Cancelling... Please wait."
msgstr "Abbrechen... Bitte warten."
msgstr "Abbrechen Bitte warten."
msgid ""
"<tag-command> can be \"add\", \"remove\" or \"list\" to assign or remove "
@@ -573,6 +520,10 @@ msgstr "Standard: %s"
msgid "Possible keys/values:"
msgstr "Mögliche Werte:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Zeigt die Nutzungsstatistik an."
msgid "Fatal error:"
msgstr "Schwerwiegender Fehler:"
@@ -627,6 +578,18 @@ msgstr ""
"Wenn du das Passwort bereits eingegeben hast, werden die verschlüsselten "
"Objekte im Hintergrund entschlüsselt und stehen in Kürze zur Verfügung."
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "Exportiere „%s“ ins „%s“ Format. Bitte warten..."
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "Importiere „%s“ ins „%s“ Format. Bitte warten…"
#, fuzzy
msgid "PDF File"
msgstr "Datei"
msgid "File"
msgstr "Datei"
@@ -639,11 +602,18 @@ msgstr "Neues To-Do"
msgid "New notebook"
msgstr "Neues Notizbuch"
msgid "Import Evernote notes"
msgstr "Evernote Notizen importieren"
msgid "Import"
msgstr "Importieren"
msgid "Evernote Export Files"
msgstr "Evernote Export Dateien"
msgid "Export"
msgstr "Exportieren"
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr "%s ausblenden"
msgid "Quit"
msgstr "Verlassen"
@@ -663,6 +633,12 @@ msgstr "Einfügen"
msgid "Search in all the notes"
msgstr "Alle Notizen durchsuchen"
msgid "View"
msgstr "Ansicht"
msgid "Toggle editor layout"
msgstr "Editor Layout umschalten"
msgid "Tools"
msgstr "Werkzeuge"
@@ -670,7 +646,7 @@ msgid "Synchronisation status"
msgstr "Status der Synchronisation"
msgid "Encryption options"
msgstr "Verschlüsselungsoptionen"
msgstr "Verschlüsselung"
msgid "General Options"
msgstr "Allgemeine Einstellungen"
@@ -681,8 +657,12 @@ msgstr "Hilfe"
msgid "Website and documentation"
msgstr "Webseite und Dokumentation"
#, fuzzy
msgid "Make a donation"
msgstr "Webseite und Dokumentation"
msgid "Check for updates..."
msgstr ""
msgstr "Überprüfe auf Updates..."
msgid "About Joplin"
msgstr "Über Joplin"
@@ -691,12 +671,12 @@ msgstr "Über Joplin"
msgid "%s %s (%s, %s)"
msgstr "%s %s (%s, %s)"
#, fuzzy, javascript-format
#, javascript-format
msgid "Open %s"
msgstr "Auf %s: %s"
msgstr "Öffne %s"
msgid "Exit"
msgstr ""
msgstr "Verlassen"
msgid "OK"
msgstr "OK"
@@ -704,36 +684,24 @@ msgstr "OK"
msgid "Cancel"
msgstr "Abbrechen"
msgid "Error"
msgstr ""
msgid "Current version is up-to-date."
msgstr "Die aktuelle Version ist up-to-date."
msgid "An update is available, do you want to update now?"
msgstr ""
msgid "An update is available, do you want to download it now?"
msgstr "Es ist ein Update verfügbar! Soll dies jetzt heruntergeladen werden?"
msgid "Yes"
msgstr ""
msgstr "Ja"
#, fuzzy
msgid "No"
msgstr "N"
msgstr "Nein"
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Überprüfen der Synchronisationseinstellungen"
#, javascript-format
msgid "Notes and settings are stored in: %s"
msgstr "Notizen und Einstellungen gespeichert in: %s"
msgstr "Notizen und Einstellungen werden gespeichert in: %s"
msgid "Save"
msgstr "Speichern"
@@ -799,15 +767,14 @@ msgstr ""
"verwendet werden, abhängig davon, wie die jeweiligen Notizen oder "
"Notizbücher ursprünglich verschlüsselt wurden."
#, fuzzy
msgid "Missing Master Keys"
msgstr "Hauptschlüssel"
msgstr "Fehlender Master-Key"
msgid ""
"The master keys with these IDs are used to encrypt some of your items, "
"however the application does not currently have access to them. It is likely "
"they will eventually be downloaded via synchronisation."
msgstr ""
msgstr "Die Master-Keas dieser IDs werden für die Verschlüsselung einiger ..."
msgid "Status"
msgstr "Status"
@@ -822,7 +789,7 @@ msgstr "Zurück"
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into it"
msgstr ""
"Neues Notizbuch \"%s\" wird erstellt und die Datei \"%s\" wird hinein "
"Neues Notizbuch \"%s\" wird erstellt und die Datei \"%s\" wird dort hinein "
"importiert"
msgid "Please create a notebook first."
@@ -841,11 +808,14 @@ msgid "Separate each tag by a comma."
msgstr "Trenne jede Markierung mit einem Komma."
msgid "Rename notebook:"
msgstr "Benne Notizbuch um:"
msgstr "Notizbuch umbenennen:"
msgid "Set alarm:"
msgstr "Alarm erstellen:"
msgid "Search"
msgstr "Suchen"
msgid "Layout"
msgstr "Layout"
@@ -884,6 +854,12 @@ msgstr ""
"Momentan existieren noch keine Notizbücher. Erstelle eines, indem du auf den "
"(+) Knopf drückst."
msgid "Open..."
msgstr "Öffne..."
msgid "Save as..."
msgstr "Sichern unter..."
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Nicht unterstützter Link oder Nachricht: %s"
@@ -891,9 +867,30 @@ msgstr "Nicht unterstützter Link oder Nachricht: %s"
msgid "Attach file"
msgstr "Datei anhängen"
msgid "Tags"
msgstr "Markierungen"
msgid "Set alarm"
msgstr "Alarm erstellen"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
"Diese Notiz hat keinen Inhalt. Klicke auf „%s“ um den Editor zu aktivieren "
"und die Notiz zu bearbeiten."
msgid "to-do"
msgstr "To-Do"
msgid "note"
msgstr "Notiz"
#, javascript-format
msgid "Creating new %s..."
msgstr "Erstelle neue %s..."
msgid "Refresh"
msgstr "Aktualisieren"
@@ -903,9 +900,6 @@ msgstr "Leeren"
msgid "OneDrive Login"
msgstr "OneDrive Login"
msgid "Import"
msgstr "Importieren"
msgid "Options"
msgstr "Optionen"
@@ -930,9 +924,6 @@ msgstr "Synchronisieren"
msgid "Notebooks"
msgstr "Notizbücher"
msgid "Tags"
msgstr "Markierungen"
msgid "Searches"
msgstr "Suchen"
@@ -951,8 +942,8 @@ msgstr "Unbekanntes Argument: %s"
msgid "File system"
msgstr "Dateisystem"
msgid "Nextcloud (Beta)"
msgstr "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr "Nextcloud"
msgid "OneDrive"
msgstr "OneDrive"
@@ -960,8 +951,8 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (Nur für Tests)"
msgid "WebDAV (Beta)"
msgstr ""
msgid "WebDAV"
msgstr "WebDAV"
#, javascript-format
msgid "Unknown log level: %s"
@@ -1036,6 +1027,16 @@ msgstr "Abbrechen..."
msgid "Completed: %s"
msgstr "Abgeschlossen: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Schwerwiegender Fehler:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "Synchronisation ist bereits im Gange. Status: %s"
@@ -1044,7 +1045,7 @@ msgid "Encrypted"
msgstr "Verschlüsselt"
msgid "Encrypted items cannot be modified"
msgstr "Verschlüsselte Objekte können nicht verändert werden."
msgstr "Verschlüsselte Objekte können nicht verändert werden"
msgid "Conflicts"
msgstr "Konflikte"
@@ -1101,32 +1102,45 @@ msgstr "Hell"
msgid "Dark"
msgstr "Dunkel"
msgid "Show uncompleted todos on top of the lists"
msgstr "Zeige unvollständige To-Dos oben in der Liste"
msgid "Uncompleted to-dos on top"
msgstr "Zeige unvollständige To-Dos an oberster Stelle"
msgid "Sort notes by"
msgstr "Sortiere Notizen nach"
msgid "Reverse sort order"
msgstr "Sortierreihenfolge umdrehen"
msgid "Save geo-location with notes"
msgstr "Momentanen Standort zusammen mit Notizen speichern"
#, fuzzy
msgid "When creating a new to-do:"
msgstr "Erstellt ein neues To-Do."
msgstr "Wenn eine neue To-Do erstellt wird:"
#, fuzzy
msgid "Focus title"
msgstr "Notiz Titel:"
msgstr "Fokussiere Titel"
msgid "Focus body"
msgstr ""
msgstr "Fokussiere Inhalt"
#, fuzzy
msgid "When creating a new note:"
msgstr "Erstellt eine neue Notiz."
msgstr "Wenn eine neue Notiz erstellt wird:"
msgid "Show tray icon"
msgstr ""
msgstr "Zeige Tray Icon"
msgid "Set application zoom percentage"
msgstr "Einstellen des Anwendungszooms"
msgid "Global zoom percentage"
msgstr "Zoomstufe der Benutzeroberfläche"
msgid "Editor font family"
msgstr "Editor Schriftenfamilie"
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
"Der Name der Schrift wird nicht überprüft. Ist dieser inkorrekt oder leer "
"wird eine generische Monospace Schrift verwendet."
msgid "Automatically update the application"
msgstr "Die Applikation automatisch aktualisieren"
@@ -1170,31 +1184,66 @@ msgstr ""
"Der Pfad, mit dem synchronisiert werden soll, wenn die Dateisystem-"
"Synchronisation aktiviert ist. Siehe `sync.target`."
msgid "Nexcloud WebDAV URL"
msgstr "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr "Nextcloud WebDAV URL"
msgid "Nexcloud username"
msgstr "Nexcloud Benutzername"
msgid "Nextcloud username"
msgstr "Nextcloud Benutzername"
msgid "Nexcloud password"
msgstr "Nexcloud Passwort"
msgid "Nextcloud password"
msgstr "Nextcloud Passwort"
#, fuzzy
msgid "WebDAV URL"
msgstr "Nexcloud WebDAV URL"
msgstr "WebDAV URL"
#, fuzzy
msgid "WebDAV username"
msgstr "Nexcloud Benutzername"
msgstr "WebDAV Benutzername"
#, fuzzy
msgid "WebDAV password"
msgstr "Setze ein Passwort"
msgstr "WebDAV Passwort"
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Ungültiger Optionswert: \"%s\". Mögliche Werte sind: %s."
msgid "Joplin Export File"
msgstr "Joplin Export Datei"
msgid "Markdown"
msgstr "Markdown"
msgid "Joplin Export Directory"
msgstr "Joplin Export Verzeichnis"
msgid "Evernote Export File"
msgstr "Evernote Export Datei"
msgid "Directory"
msgstr "Verzeichnis"
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "Das Modul „%s“ für das Format „%s“ kann nicht geladen werden"
#, javascript-format
msgid "Please specify import format for %s"
msgstr "Bitte das Exportformat für %s angeben"
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
"Dieses Objekt ist zur Zeit verschlüsselt: %s „%s“. Bitte warten bis alle "
"Objekte entschlüsselt wurden und versuche es dann erneut."
msgid "There is no data to export."
msgstr "Keine Daten für den Export vorhanden."
msgid "Please specify the notebook where the notes should be imported to."
msgstr ""
"Bitte wähle aus, wohin der Synchronisations-Status exportiert werden soll."
msgid "Items that cannot be synchronised"
msgstr "Objekte können nicht synchronisiert werden"
@@ -1283,6 +1332,9 @@ msgstr "Bestätigen"
msgid "Cancel synchronisation"
msgstr "Synchronisation abbrechen"
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr "Hauptschlüssel %s"
@@ -1308,10 +1360,10 @@ msgid "Edit notebook"
msgstr "Notizbuch bearbeiten"
msgid "Show all"
msgstr ""
msgstr "Zeige Alles"
msgid "Errors only"
msgstr ""
msgstr "Nur Fehler"
msgid "This note has been modified:"
msgstr "Diese Notiz wurde verändert:"
@@ -1369,6 +1421,84 @@ msgstr ""
msgid "Welcome"
msgstr "Willkommen"
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr ""
#~ "Versionshinweise:\n"
#~ "\n"
#~ "%s"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importiert eine Evernote Notizbuch-Datei (.enex Datei)."
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "Datei \"%s\" wird in das existierende Notizbuch \"%s\" importiert. "
#~ "Fortfahren?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "Neues Notizbuch \"%s\" wird erstellt und die Datei \"%s\" wird hinein "
#~ "importiert. Fortfahren?"
#~ msgid "Import Evernote notes"
#~ msgstr "Evernote Notizen importieren"
#~ msgid "Give focus to next pane"
#~ msgstr "Das nächste Fenster fokussieren"
#~ msgid "Give focus to previous pane"
#~ msgstr "Das vorherige Fenster fokussieren"
#~ msgid "Enter command line mode"
#~ msgstr "Zum Terminal-Modus wechseln"
#~ msgid "Exit command line mode"
#~ msgstr "Den Terminal-Modus verlassen"
#~ msgid "Edit the selected note"
#~ msgstr "Die ausgewählte Notiz bearbeiten"
#~ msgid "Cancel the current command."
#~ msgstr "Den momentanen Befehl abbrechen."
#~ msgid "Exit the application."
#~ msgstr "Das Programm verlassen."
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "Die/das momentan ausgewählte Notiz(-buch) löschen."
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "Ein To-Do als abgeschlossen / nicht abgeschlossen markieren"
#, fuzzy
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr ""
#~ "Schal[t]e das Terminal zwischen maximiert/minimiert/versteckt/sichtbar um."
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "Notiz-[M]etadata einschal[t]en."
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "Eine neue [N]otiz [m]achen"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "Ein neues [T]o-Do [m]achen"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "Ein neues Notiz[b]uch [m]achen"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "Die Notiz zu einem Notizbuch kopieren."
#~ msgid "Move the note to a notebook."
#~ msgstr "Die Notiz zu einem Notizbuch verschieben."
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -15,63 +15,12 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Give focus to next pane"
msgstr ""
msgid "Give focus to previous pane"
msgstr ""
msgid "Enter command line mode"
msgstr ""
msgid "Exit command line mode"
msgstr ""
msgid "Edit the selected note"
msgstr ""
msgid "Cancel the current command."
msgstr ""
msgid "Exit the application."
msgstr ""
msgid "Delete the currently selected note or notebook."
msgstr ""
msgid "To delete a tag, untag the associated notes."
msgstr ""
msgid "Please select the note or notebook to be deleted first."
msgstr ""
msgid "Set a to-do as completed / not completed"
msgstr ""
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr ""
msgid "Search"
msgstr ""
msgid "[t]oggle note [m]etadata."
msgstr ""
msgid "[M]ake a new [n]ote"
msgstr ""
msgid "[M]ake a new [t]odo"
msgstr ""
msgid "[M]ake a new note[b]ook"
msgstr ""
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr ""
msgid "Move the note to a notebook."
msgstr ""
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr ""
@@ -225,10 +174,14 @@ msgid "Exits the application."
msgstr ""
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
#, javascript-format
msgid "Destination format: %s"
msgstr ""
msgid "Exports only the given note."
msgstr ""
@@ -241,6 +194,10 @@ msgstr ""
msgid "Displays usage information."
msgstr ""
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
msgid "Shortcuts are not available in CLI mode."
msgstr ""
@@ -276,25 +233,19 @@ msgid "To exit command line mode, press ESCAPE"
msgstr ""
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
msgid "Imports an Evernote notebook file (.enex file)."
msgid "Imports data into Joplin."
msgstr ""
#, javascript-format
msgid "Source format: %s"
msgstr ""
msgid "Do not ask for confirmation."
msgstr ""
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
#, javascript-format
msgid "Found: %d."
msgstr ""
@@ -498,6 +449,9 @@ msgstr ""
msgid "Possible keys/values:"
msgstr ""
msgid "Type `joplin help` for usage information."
msgstr ""
msgid "Fatal error:"
msgstr ""
@@ -535,6 +489,17 @@ msgid ""
"background and will be available soon."
msgstr ""
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr ""
msgid "PDF File"
msgstr ""
msgid "File"
msgstr ""
@@ -547,10 +512,17 @@ msgstr ""
msgid "New notebook"
msgstr ""
msgid "Import Evernote notes"
msgid "Import"
msgstr ""
msgid "Evernote Export Files"
msgid "Export"
msgstr ""
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr ""
msgid "Quit"
@@ -571,6 +543,12 @@ msgstr ""
msgid "Search in all the notes"
msgstr ""
msgid "View"
msgstr ""
msgid "Toggle editor layout"
msgstr ""
msgid "Tools"
msgstr ""
@@ -589,6 +567,9 @@ msgstr ""
msgid "Website and documentation"
msgstr ""
msgid "Make a donation"
msgstr ""
msgid "Check for updates..."
msgstr ""
@@ -612,10 +593,10 @@ msgstr ""
msgid "Cancel"
msgstr ""
msgid "Error"
msgid "Current version is up-to-date."
msgstr ""
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
msgid "Yes"
@@ -624,18 +605,7 @@ msgstr ""
msgid "No"
msgstr ""
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgid "Check synchronisation configuration"
msgstr ""
#, javascript-format
@@ -738,6 +708,9 @@ msgstr ""
msgid "Set alarm:"
msgstr ""
msgid "Search"
msgstr ""
msgid "Layout"
msgstr ""
@@ -772,6 +745,12 @@ msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr ""
msgid "Open..."
msgstr ""
msgid "Save as..."
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr ""
@@ -779,9 +758,28 @@ msgstr ""
msgid "Attach file"
msgstr ""
msgid "Tags"
msgstr ""
msgid "Set alarm"
msgstr ""
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
msgid "to-do"
msgstr ""
msgid "note"
msgstr ""
#, javascript-format
msgid "Creating new %s..."
msgstr ""
msgid "Refresh"
msgstr ""
@@ -791,9 +789,6 @@ msgstr ""
msgid "OneDrive Login"
msgstr ""
msgid "Import"
msgstr ""
msgid "Options"
msgstr ""
@@ -818,9 +813,6 @@ msgstr ""
msgid "Notebooks"
msgstr ""
msgid "Tags"
msgstr ""
msgid "Searches"
msgstr ""
@@ -838,7 +830,7 @@ msgstr ""
msgid "File system"
msgstr ""
msgid "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr ""
msgid "OneDrive"
@@ -847,7 +839,7 @@ msgstr ""
msgid "OneDrive Dev (For testing only)"
msgstr ""
msgid "WebDAV (Beta)"
msgid "WebDAV"
msgstr ""
#, javascript-format
@@ -915,6 +907,16 @@ msgstr ""
msgid "Completed: %s"
msgstr ""
#, javascript-format
msgid "Last error: %s"
msgstr ""
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr ""
@@ -976,7 +978,13 @@ msgstr ""
msgid "Dark"
msgstr ""
msgid "Show uncompleted todos on top of the lists"
msgid "Uncompleted to-dos on top"
msgstr ""
msgid "Sort notes by"
msgstr ""
msgid "Reverse sort order"
msgstr ""
msgid "Save geo-location with notes"
@@ -997,7 +1005,15 @@ msgstr ""
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr ""
msgid "Editor font family"
msgstr ""
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
msgid "Automatically update the application"
@@ -1037,13 +1053,13 @@ msgid ""
"See `sync.target`."
msgstr ""
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr ""
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr ""
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr ""
msgid "WebDAV URL"
@@ -1059,6 +1075,41 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""
msgid "Joplin Export File"
msgstr ""
msgid "Markdown"
msgstr ""
msgid "Joplin Export Directory"
msgstr ""
msgid "Evernote Export File"
msgstr ""
msgid "Directory"
msgstr ""
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr ""
#, javascript-format
msgid "Please specify import format for %s"
msgstr ""
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
msgid "There is no data to export."
msgstr ""
msgid "Please specify the notebook where the notes should be imported to."
msgstr ""
msgid "Items that cannot be synchronised"
msgstr ""
@@ -1142,6 +1193,9 @@ msgstr ""
msgid "Cancel synchronisation"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1479
CliClient/locales/eu.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -14,32 +14,6 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.3\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
msgid "Give focus to next pane"
msgstr "Activer le volet suivant"
msgid "Give focus to previous pane"
msgstr "Activer le volet précédent"
msgid "Enter command line mode"
msgstr "Démarrer le mode de ligne de commande"
msgid "Exit command line mode"
msgstr "Sortir du mode de ligne de commande"
msgid "Edit the selected note"
msgstr "Éditer la note sélectionnée"
msgid "Cancel the current command."
msgstr "Annuler la commande en cours."
msgid "Exit the application."
msgstr "Quitter le logiciel."
msgid "Delete the currently selected note or notebook."
msgstr "Supprimer la note ou carnet sélectionné."
msgid "To delete a tag, untag the associated notes."
msgstr "Pour supprimer une vignette, enlever là des notes associées."
@@ -47,33 +21,6 @@ msgstr "Pour supprimer une vignette, enlever là des notes associées."
msgid "Please select the note or notebook to be deleted first."
msgstr "Veuillez d'abord sélectionner un carnet."
msgid "Set a to-do as completed / not completed"
msgstr "Marquer une tâches comme complétée / non-complétée"
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr "Maximiser, minimiser, cacher ou rendre visible la console."
msgid "Search"
msgstr "Chercher"
msgid "[t]oggle note [m]etadata."
msgstr "Afficher/Cacher les métadonnées des notes."
msgid "[M]ake a new [n]ote"
msgstr "Créer une nouvelle note"
msgid "[M]ake a new [t]odo"
msgstr "Créer une nouvelle tâche"
msgid "[M]ake a new note[b]ook"
msgstr "Créer un nouveau carnet"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "Copier la note dans un autre carnet."
msgid "Move the note to a notebook."
msgstr "Déplacer la note vers un carnet."
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Appuyez sur Ctrl+D ou tapez \"exit\" pour sortir du logiciel"
@@ -148,8 +95,8 @@ msgid ""
"current configuration."
msgstr ""
"Obtient ou modifie une valeur de configuration. Si la [valeur] n'est pas "
"fournie, la valeur de [nom] est affichée. Si ni le [nom] ni la [valeur] ne "
"sont fournis, la configuration complète est affichée."
"fournie, la valeur de [nom] sera affichée. Si ni le [nom] ni la [valeur] ne "
"sont fournis, la configuration complète sera affichée."
msgid "Also displays unset and hidden config variables."
msgstr "Afficher également les variables cachées."
@@ -241,12 +188,15 @@ msgid "Exits the application."
msgstr "Quitter le logiciel."
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Exporter les données de Joplin vers le dossier fourni. Par défaut, la base "
"de donnée complète sera exportée, y compris les carnets, notes, tags et "
"resources."
"Exporter les données de Joplin. Par défaut, la base de donnée complète sera "
"exportée, y compris les carnets, notes, tags et ressources."
#, javascript-format
msgid "Destination format: %s"
msgstr "Format de la destination : %s"
msgid "Exports only the given note."
msgstr "Exporter uniquement la note spécifiée."
@@ -260,6 +210,11 @@ msgstr "Afficher l'URL de l'emplacement de la note."
msgid "Displays usage information."
msgstr "Affiche les informations d'utilisation."
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
"Pour personnaliser les raccourcis veuillez consulter la documentation à %s"
msgid "Shortcuts are not available in CLI mode."
msgstr "Les raccourcis ne sont pas disponible en mode de ligne de commande."
@@ -303,29 +258,19 @@ msgid "To exit command line mode, press ESCAPE"
msgstr "Pour sortir du mode ligne de commande, pressez ECHAP"
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
msgstr ""
"Pour la liste complète des raccourcis disponibles, tapez `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr "Pour la liste complète des raccourcis disponibles, tapez `help keymap`"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Importer un carnet Evernote (fichier .enex)."
msgid "Imports data into Joplin."
msgstr "Importer des données dans Joplin."
#, javascript-format
msgid "Source format: %s"
msgstr "Format de la source : %s"
msgid "Do not ask for confirmation."
msgstr "Ne pas demander de confirmation."
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
"Le fichier \"%s\" va être importé dans le carnet existant \"%s\". Continuer ?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"Un nouveau carnet \"%s\" va être créé et le fichier \"%s\" va être importé "
"dedans. Continuer ?"
#, javascript-format
msgid "Found: %d."
msgstr "Trouvés : %d."
@@ -559,6 +504,9 @@ msgstr "Défaut : %s"
msgid "Possible keys/values:"
msgstr "Clefs/Valeurs possibles :"
msgid "Type `joplin help` for usage information."
msgstr "Tapez `Joplin help` pour afficher l'aide."
msgid "Fatal error:"
msgstr "Erreur fatale :"
@@ -612,6 +560,17 @@ msgstr ""
"decrypt`. Si vous avez déjà fourni ce mot de passe, les objets cryptés vont "
"être décrypté en tâche de fond et seront disponible prochainement."
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "Exporter vers \"%s\" au format \"%s\". Veuillez patienter..."
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "Importer depuis \"%s\" au format \"%s\". Veuillez patienter..."
msgid "PDF File"
msgstr "Fichier PDF"
msgid "File"
msgstr "Fichier"
@@ -624,11 +583,18 @@ msgstr "Nouvelle tâche"
msgid "New notebook"
msgstr "Nouveau carnet"
msgid "Import Evernote notes"
msgstr "Importer notes d'Evernote"
msgid "Import"
msgstr "Importer"
msgid "Evernote Export Files"
msgstr "Fichiers d'export Evernote"
msgid "Export"
msgstr "Exporter"
msgid "Print"
msgstr "Imprimer"
#, javascript-format
msgid "Hide %s"
msgstr "Cacher %s"
msgid "Quit"
msgstr "Quitter"
@@ -648,6 +614,12 @@ msgstr "Coller"
msgid "Search in all the notes"
msgstr "Chercher dans toutes les notes"
msgid "View"
msgstr "Affichage"
msgid "Toggle editor layout"
msgstr "Basculer l'agencement de l'éditeur"
msgid "Tools"
msgstr "Outils"
@@ -666,6 +638,9 @@ msgstr "Aide"
msgid "Website and documentation"
msgstr "Documentation en ligne"
msgid "Make a donation"
msgstr "Faire un don"
msgid "Check for updates..."
msgstr "Vérifier les mises à jour..."
@@ -689,10 +664,10 @@ msgstr "OK"
msgid "Cancel"
msgstr "Annuler"
msgid "Error"
msgstr "Erreur"
msgid "Current version is up-to-date."
msgstr "La version actuelle est à jour."
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
"Une mise à jour est disponible, souhaitez vous la télécharger maintenant ?"
@@ -702,21 +677,8 @@ msgstr "Oui"
msgid "No"
msgstr "Non"
#, javascript-format
msgid "Could not download the update: %s"
msgstr "Impossible de télécharger la mise à jour : %s"
msgid "Current version is up-to-date."
msgstr "La version actuelle est à jour."
msgid "New version downloaded - application will quit now and update..."
msgstr ""
"La nouvelle version a été téléchargée - le programme va se fermer et se "
"mettre à jour..."
#, javascript-format
msgid "Could not install the update: %s"
msgstr "Impossible d'installer la mise à jour : %s"
msgid "Check synchronisation configuration"
msgstr "Vérifier config synchronisation"
#, javascript-format
msgid "Notes and settings are stored in: %s"
@@ -835,6 +797,9 @@ msgstr "Renommer le carnet :"
msgid "Set alarm:"
msgstr "Régler alarme :"
msgid "Search"
msgstr "Chercher"
msgid "Layout"
msgstr "Disposition"
@@ -872,6 +837,12 @@ msgstr ""
"Il n'y a pour l'instant aucun carnet. Créez-en un en cliquant sur \"Nouveau "
"carnet\"."
msgid "Open..."
msgstr "Ouvrir..."
msgid "Save as..."
msgstr "Enregistrer sous..."
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Lien ou message non géré : %s"
@@ -879,9 +850,30 @@ msgstr "Lien ou message non géré : %s"
msgid "Attach file"
msgstr "Attacher un fichier"
msgid "Tags"
msgstr "Étiquettes"
msgid "Set alarm"
msgstr "Régler alarme"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
"Cette note n'a pas de contenu. Cliquer sur \"%s\" pour basculer vers "
"l'éditeur et éditer cette note."
msgid "to-do"
msgstr "tâche"
msgid "note"
msgstr "note"
#, javascript-format
msgid "Creating new %s..."
msgstr "Création de %s..."
msgid "Refresh"
msgstr "Rafraîchir"
@@ -891,9 +883,6 @@ msgstr "Supprimer"
msgid "OneDrive Login"
msgstr "Connexion OneDrive"
msgid "Import"
msgstr "Importer"
msgid "Options"
msgstr "Options"
@@ -918,9 +907,6 @@ msgstr "Synchroniser"
msgid "Notebooks"
msgstr "Carnets"
msgid "Tags"
msgstr "Étiquettes"
msgid "Searches"
msgstr "Recherches"
@@ -939,8 +925,8 @@ msgstr "Paramètre inconnu : %s"
msgid "File system"
msgstr "Système de fichier"
msgid "Nextcloud (Beta)"
msgstr "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr "Nextcloud"
msgid "OneDrive"
msgstr "OneDrive"
@@ -948,8 +934,8 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dév (Pour tester uniquement)"
msgid "WebDAV (Beta)"
msgstr "WebDAV (Bêta)"
msgid "WebDAV"
msgstr "WebDAV"
#, javascript-format
msgid "Unknown log level: %s"
@@ -1024,6 +1010,16 @@ msgstr "Annulation..."
msgid "Completed: %s"
msgstr "Terminé : %s"
#, javascript-format
msgid "Last error: %s"
msgstr "Dernière erreur : %s"
msgid "Idle"
msgstr "Arrêté"
msgid "In progress"
msgstr "En cours"
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "La synchronisation est déjà en cours. État : %s"
@@ -1087,8 +1083,14 @@ msgstr "Clair"
msgid "Dark"
msgstr "Sombre"
msgid "Show uncompleted todos on top of the lists"
msgstr "Tâches non-terminées en haut des listes"
msgid "Uncompleted to-dos on top"
msgstr "Tâches non-terminées en haut"
msgid "Sort notes by"
msgstr "Trier les notes par"
msgid "Reverse sort order"
msgstr "Inverser l'ordre"
msgid "Save geo-location with notes"
msgstr "Enregistrer l'emplacement avec les notes"
@@ -1108,9 +1110,19 @@ msgstr "Lors de la création d'une note :"
msgid "Show tray icon"
msgstr "Afficher icône dans la zone de notifications"
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr "Niveau de zoom"
msgid "Editor font family"
msgstr "Police de l'éditeur"
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
"Le nom de la police ne sera pas vérifié. Si incorrect ou vide une police "
"monospace sera utilisée par défaut."
msgid "Automatically update the application"
msgstr "Mettre à jour le logiciel automatiquement"
@@ -1153,13 +1165,13 @@ msgstr ""
"Le chemin du répertoire avec lequel synchroniser lorsque la synchronisation "
"par système de fichier est activée. Voir `sync.target`."
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr "Nextcloud : URL WebDAV"
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr "Nextcloud : Nom utilisateur"
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr "Nextcloud : Mot de passe"
msgid "WebDAV URL"
@@ -1175,6 +1187,43 @@ msgstr "WebDAV : Mot de passe"
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Option invalide: \"%s\". Les valeurs possibles sont : %s."
msgid "Joplin Export File"
msgstr "Fichier d'export Joplin"
msgid "Markdown"
msgstr "Markdown"
msgid "Joplin Export Directory"
msgstr "Dossier d'export Joplin"
msgid "Evernote Export File"
msgstr "Fichiers d'export Evernote"
msgid "Directory"
msgstr "Dossier"
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "Impossible de charger module \"%s\" pour le format \"%s\""
#, javascript-format
msgid "Please specify import format for %s"
msgstr "Veuillez spécifier le format d'import pour %s"
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
"Cet objet est crypté : %s \"%s\". Veuillez attendre que tout soit décrypté "
"et réessayez."
msgid "There is no data to export."
msgstr "Il n'y a pas de données à exporter."
msgid "Please specify the notebook where the notes should be imported to."
msgstr "Veuillez sélectionner le carnet où les notes doivent être importées."
msgid "Items that cannot be synchronised"
msgstr "Objets qui ne peuvent pas être synchronisés"
@@ -1263,6 +1312,9 @@ msgstr "Confirmer"
msgid "Cancel synchronisation"
msgstr "Annuler synchronisation"
msgid "Joplin website"
msgstr "Site web de Joplin"
#, javascript-format
msgid "Master Key %s"
msgstr "Clef maître %s"
@@ -1348,6 +1400,99 @@ msgstr ""
msgid "Welcome"
msgstr "Bienvenue"
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr ""
#~ "Notes de version :\n"
#~ "\n"
#~ "%s"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importer un carnet Evernote (fichier .enex)."
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "Le fichier \"%s\" va être importé dans le carnet existant \"%s\". "
#~ "Continuer ?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "Un nouveau carnet \"%s\" va être créé et le fichier \"%s\" va être "
#~ "importé dedans. Continuer ?"
#~ msgid "Import Evernote notes"
#~ msgstr "Importer notes d'Evernote"
#~ msgid "Give focus to next pane"
#~ msgstr "Activer le volet suivant"
#~ msgid "Give focus to previous pane"
#~ msgstr "Activer le volet précédent"
#~ msgid "Enter command line mode"
#~ msgstr "Démarrer le mode de ligne de commande"
#~ msgid "Exit command line mode"
#~ msgstr "Sortir du mode de ligne de commande"
#~ msgid "Edit the selected note"
#~ msgstr "Éditer la note sélectionnée"
#~ msgid "Cancel the current command."
#~ msgstr "Annuler la commande en cours."
#~ msgid "Exit the application."
#~ msgstr "Quitter le logiciel."
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "Supprimer la note ou carnet sélectionné."
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "Marquer une tâches comme complétée / non-complétée"
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr "Maximiser, minimiser, cacher ou rendre visible la console."
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "Afficher/Cacher les métadonnées des notes."
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "Créer une nouvelle note"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "Créer une nouvelle tâche"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "Créer un nouveau carnet"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "Copier la note dans un autre carnet."
#~ msgid "Move the note to a notebook."
#~ msgstr "Déplacer la note vers un carnet."
#~ msgid "Error"
#~ msgstr "Erreur"
#~ msgid "WebDAV (Beta)"
#~ msgstr "WebDAV (Bêta)"
#~ msgid "Could not download the update: %s"
#~ msgstr "Impossible de télécharger la mise à jour : %s"
#~ msgid "New version downloaded - application will quit now and update..."
#~ msgstr ""
#~ "La nouvelle version a été téléchargée - le programme va se fermer et se "
#~ "mettre à jour..."
#~ msgid "Could not install the update: %s"
#~ msgstr "Impossible d'installer la mise à jour : %s"
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -17,69 +17,12 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
msgid "Give focus to next pane"
msgstr "Fokusiraj sljedeće okno"
msgid "Give focus to previous pane"
msgstr "Fokusiraj prethodno okno"
msgid "Enter command line mode"
msgstr "Otvori naredbeni redak"
msgid "Exit command line mode"
msgstr "Napusti naredbeni redak"
msgid "Edit the selected note"
msgstr "Uredi odabranu bilješku"
msgid "Cancel the current command."
msgstr "Prekini trenutnu naredbu."
msgid "Exit the application."
msgstr "Izađi iz aplikacije."
msgid "Delete the currently selected note or notebook."
msgstr "Obriši odabranu bilješku ili bilježnicu."
msgid "To delete a tag, untag the associated notes."
msgstr "Da bi mogao obrisati oznaku, skini oznaku s povezanih bilješki."
msgid "Please select the note or notebook to be deleted first."
msgstr "Odaberi bilješku ili bilježnicu za brisanje."
msgid "Set a to-do as completed / not completed"
msgstr "Postavi zadatak kao završen/nezavršen"
#, fuzzy
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgid "Search"
msgstr "Traži"
#, fuzzy
msgid "[t]oggle note [m]etadata."
msgstr "[t]oggle note [m]etadata."
#, fuzzy
msgid "[M]ake a new [n]ote"
msgstr "[M]ake a new [n]ote"
#, fuzzy
msgid "[M]ake a new [t]odo"
msgstr "[M]ake a new [t]odo"
#, fuzzy
msgid "[M]ake a new note[b]ook"
msgstr "[M]ake a new note[b]ook"
#, fuzzy
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "Copy ([Y]ank) the [n]ote to a notebook."
msgid "Move the note to a notebook."
msgstr "Premjesti bilješku u bilježnicu."
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Pritisni Ctrl+D ili napiši \"exit\" za izlazak iz aplikacije"
@@ -245,13 +188,18 @@ msgstr "Bilješka je spremljena."
msgid "Exits the application."
msgstr "Izlaz iz aplikacije."
#, fuzzy
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Izvozi podatke u dati direktorij. Po defaultu izvozi sve podatke uključujući "
"bilježnice, bilješke, zadatke i resurse."
#, fuzzy, javascript-format
msgid "Destination format: %s"
msgstr "Format datuma"
msgid "Exports only the given note."
msgstr "Izvozi samo datu bilješku."
@@ -264,6 +212,10 @@ msgstr "Prikazuje geolokacijski URL bilješke."
msgid "Displays usage information."
msgstr "Prikazuje informacije o korištenju."
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
msgid "Shortcuts are not available in CLI mode."
msgstr "Prečaci nisu podržani u naredbenom retku."
@@ -307,29 +259,21 @@ msgstr "Za ulaz u naredbeni redak, pritisni \":\""
msgid "To exit command line mode, press ESCAPE"
msgstr "Za izlaz iz naredbenog retka, pritisni Esc"
#, fuzzy
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr "Za potpunu listu mogućih prečaca, upiši `help shortcuts`"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Uvozi Evernote bilježnicu (.enex datoteku)."
msgid "Imports data into Joplin."
msgstr ""
#, fuzzy, javascript-format
msgid "Source format: %s"
msgstr "Ne postoji naredba: %s"
msgid "Do not ask for confirmation."
msgstr "Ne pitaj za potvrdu."
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
"Datoteka \"%s\" će biti uvezena u postojeću bilježnicu \"%s\". Nastavi?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"Nova bilježnica \"%s\" će biti stvorena i datoteka \"%s\" će biti uvezena u "
"nju. Nastavi?"
#, javascript-format
msgid "Found: %d."
msgstr "Nađeno: %d."
@@ -566,6 +510,10 @@ msgstr "Default: %s"
msgid "Possible keys/values:"
msgstr "Mogući ključevi/vrijednosti:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Prikazuje informacije o korištenju."
msgid "Fatal error:"
msgstr "Fatalna greška:"
@@ -614,6 +562,18 @@ msgid ""
"background and will be available soon."
msgstr ""
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "Datoteka"
msgid "File"
msgstr "Datoteka"
@@ -626,11 +586,19 @@ msgstr "Novi zadatak"
msgid "New notebook"
msgstr "Nova bilježnica"
msgid "Import Evernote notes"
msgstr "Uvezi Evernote bilješke"
msgid "Import"
msgstr "Uvoz"
msgid "Evernote Export Files"
msgstr "Evernote izvozne datoteke"
#, fuzzy
msgid "Export"
msgstr "Uvoz"
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr ""
msgid "Quit"
msgstr "Izađi"
@@ -650,6 +618,12 @@ msgstr "Zalijepi"
msgid "Search in all the notes"
msgstr "Pretraži u svim bilješkama"
msgid "View"
msgstr ""
msgid "Toggle editor layout"
msgstr ""
msgid "Tools"
msgstr "Alati"
@@ -669,6 +643,10 @@ msgstr "Pomoć"
msgid "Website and documentation"
msgstr "Website i dokumentacija"
#, fuzzy
msgid "Make a donation"
msgstr "Website i dokumentacija"
msgid "Check for updates..."
msgstr ""
@@ -692,10 +670,10 @@ msgstr "U redu"
msgid "Cancel"
msgstr "Odustani"
msgid "Error"
msgid "Current version is up-to-date."
msgstr ""
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
msgid "Yes"
@@ -705,19 +683,9 @@ msgstr ""
msgid "No"
msgstr "N"
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "Prekini sinkronizaciju"
#, javascript-format
msgid "Notes and settings are stored in: %s"
@@ -821,6 +789,9 @@ msgstr "Preimenuj bilježnicu:"
msgid "Set alarm:"
msgstr "Postavi upozorenje:"
msgid "Search"
msgstr "Traži"
msgid "Layout"
msgstr "Izgled"
@@ -856,6 +827,13 @@ msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr "Ovdje nema bilježnica. Stvori novu pritiskom na \"Nova bilježnica\"."
msgid "Open..."
msgstr ""
#, fuzzy
msgid "Save as..."
msgstr "Spremi promjene"
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Nepodržana poveznica ili poruka: %s"
@@ -863,9 +841,30 @@ msgstr "Nepodržana poveznica ili poruka: %s"
msgid "Attach file"
msgstr "Priloži datoteku"
msgid "Tags"
msgstr "Oznake"
msgid "Set alarm"
msgstr "Postavi upozorenje"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
#, fuzzy
msgid "to-do"
msgstr "Novi zadatak"
#, fuzzy
msgid "note"
msgstr "Nova bilješka"
#, fuzzy, javascript-format
msgid "Creating new %s..."
msgstr "Uvozim bilješke..."
msgid "Refresh"
msgstr "Osvježi"
@@ -875,9 +874,6 @@ msgstr "Očisti"
msgid "OneDrive Login"
msgstr "OneDrive Login"
msgid "Import"
msgstr "Uvoz"
msgid "Options"
msgstr "Opcije"
@@ -902,9 +898,6 @@ msgstr "Sinkroniziraj"
msgid "Notebooks"
msgstr "Bilježnice"
msgid "Tags"
msgstr "Oznake"
msgid "Searches"
msgstr "Pretraživanja"
@@ -922,7 +915,7 @@ msgstr "Nepoznata zastavica: %s"
msgid "File system"
msgstr "Datotečni sustav"
msgid "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr ""
msgid "OneDrive"
@@ -931,7 +924,7 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (Samo za testiranje)"
msgid "WebDAV (Beta)"
msgid "WebDAV"
msgstr ""
#, javascript-format
@@ -1005,6 +998,16 @@ msgstr "Prekidam..."
msgid "Completed: %s"
msgstr "Dovršeno: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Fatalna greška:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "Sinkronizacija je već u toku. Stanje: %s"
@@ -1069,9 +1072,17 @@ msgstr "Svijetla"
msgid "Dark"
msgstr "Tamna"
msgid "Show uncompleted todos on top of the lists"
#, fuzzy
msgid "Uncompleted to-dos on top"
msgstr "Prikaži nezavršene zadatke na vrhu liste"
msgid "Sort notes by"
msgstr ""
#, fuzzy
msgid "Reverse sort order"
msgstr "Mijenja redoslijed."
msgid "Save geo-location with notes"
msgstr "Spremi geolokacijske podatke sa bilješkama"
@@ -1093,7 +1104,15 @@ msgstr "Stvara novu bilješku."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr ""
msgid "Editor font family"
msgstr ""
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
msgid "Automatically update the application"
@@ -1135,13 +1154,13 @@ msgstr ""
"Putanja do direktorija za sinkronizaciju u slučaju kad je sinkronizacija sa "
"datotečnim sustavom omogućena. Vidi `sync.target`."
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr ""
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr ""
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr ""
msgid "WebDAV URL"
@@ -1157,6 +1176,44 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Nevažeća vrijednost: \"%s\". Moguće vrijednosti su: %s."
#, fuzzy
msgid "Joplin Export File"
msgstr "Evernote izvozne datoteke"
msgid "Markdown"
msgstr ""
msgid "Joplin Export Directory"
msgstr ""
#, fuzzy
msgid "Evernote Export File"
msgstr "Evernote izvozne datoteke"
msgid "Directory"
msgstr ""
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr ""
#, javascript-format
msgid "Please specify import format for %s"
msgstr ""
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
msgid "There is no data to export."
msgstr ""
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to."
msgstr "Odaberi lokaciju za izvoz statusa sinkronizacije"
msgid "Items that cannot be synchronised"
msgstr "Stavke koje se ne mogu sinkronizirati"
@@ -1240,6 +1297,9 @@ msgstr "Potvrdi"
msgid "Cancel synchronisation"
msgstr "Prekini sinkronizaciju"
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr ""
@@ -1324,6 +1384,85 @@ msgstr "Trenutno nemaš nijednu bilježnicu. Stvori novu klikom na (+) gumb."
msgid "Welcome"
msgstr "Dobro došli"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Obriši bilješke?"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Uvozi Evernote bilježnicu (.enex datoteku)."
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "Datoteka \"%s\" će biti uvezena u postojeću bilježnicu \"%s\". Nastavi?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "Nova bilježnica \"%s\" će biti stvorena i datoteka \"%s\" će biti uvezena "
#~ "u nju. Nastavi?"
#~ msgid "Import Evernote notes"
#~ msgstr "Uvezi Evernote bilješke"
#~ msgid "Give focus to next pane"
#~ msgstr "Fokusiraj sljedeće okno"
#~ msgid "Give focus to previous pane"
#~ msgstr "Fokusiraj prethodno okno"
#~ msgid "Enter command line mode"
#~ msgstr "Otvori naredbeni redak"
#~ msgid "Exit command line mode"
#~ msgstr "Napusti naredbeni redak"
#~ msgid "Edit the selected note"
#~ msgstr "Uredi odabranu bilješku"
#~ msgid "Cancel the current command."
#~ msgstr "Prekini trenutnu naredbu."
#~ msgid "Exit the application."
#~ msgstr "Izađi iz aplikacije."
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "Obriši odabranu bilješku ili bilježnicu."
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "Postavi zadatak kao završen/nezavršen"
#, fuzzy
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#, fuzzy
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "[t]oggle note [m]etadata."
#, fuzzy
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "[M]ake a new [n]ote"
#, fuzzy
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "[M]ake a new [t]odo"
#, fuzzy
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "[M]ake a new note[b]ook"
#, fuzzy
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgid "Move the note to a notebook."
#~ msgstr "Premjesti bilješku u bilježnicu."
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -16,65 +16,12 @@ msgstr ""
"X-Generator: Poedit 2.0.3\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Give focus to next pane"
msgstr "Pannello successivo"
msgid "Give focus to previous pane"
msgstr "Pannello precedente"
msgid "Enter command line mode"
msgstr "Accedi alla modalità linea di comando"
msgid "Exit command line mode"
msgstr "Esci dalla modalità linea di comando"
msgid "Edit the selected note"
msgstr "Modifica la nota selezionata"
msgid "Cancel the current command."
msgstr "Cancella il comando corrente."
msgid "Exit the application."
msgstr "Esci dall'applicazione."
msgid "Delete the currently selected note or notebook."
msgstr "Elimina la nota o il blocco note selezionato."
msgid "To delete a tag, untag the associated notes."
msgstr "Elimina un'etichetta, togli l'etichetta associata alle note."
msgid "Please select the note or notebook to be deleted first."
msgstr "Per favore seleziona la nota o il blocco note da eliminare."
msgid "Set a to-do as completed / not completed"
msgstr "Imposta un'attività come completata / non completata"
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr ""
"Scegli lo s[t]ato della [c]onsole: massimizzato/minimizzato/nascosto/"
"visibile."
msgid "Search"
msgstr "Cerca"
msgid "[t]oggle note [m]etadata."
msgstr "mos[t]ra/nascondi i [m]etadata nelle note."
msgid "[M]ake a new [n]ote"
msgstr "Crea ([M]ake) una nuova [n]ota"
msgid "[M]ake a new [t]odo"
msgstr "Crea ([M]ake) una nuova at[t]ività"
msgid "[M]ake a new note[b]ook"
msgstr "Crea ([M]ake) un nuovo [b]locco note"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "Copia ([Y]) la [n]ota in un blocco note."
msgid "Move the note to a notebook."
msgstr "Sposta la nota in un blocco note."
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Premi Ctrl+D o digita \"exit\" per uscire dall'applicazione"
@@ -236,14 +183,19 @@ msgstr "La nota è stata salvata."
msgid "Exits the application."
msgstr "Esci dall'applicazione."
#, fuzzy
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Esporta i dati da Joplin nella directory selezionata. Come impostazione "
"predefinita verrà esportato il database completo, inclusi blocchi note, "
"note, etichette e risorse."
#, fuzzy, javascript-format
msgid "Destination format: %s"
msgstr "Formato della data"
msgid "Exports only the given note."
msgstr "Esporta solo la seguente nota."
@@ -256,6 +208,10 @@ msgstr "Mostra l'URL di geolocalizzazione per la nota."
msgid "Displays usage information."
msgstr "Mostra le informazioni di utilizzo."
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
msgid "Shortcuts are not available in CLI mode."
msgstr "Le scorciatoie non sono disponibili nella modalità CLI."
@@ -298,30 +254,22 @@ msgstr "Per entrare nella modalità command line, premi \":\""
msgid "To exit command line mode, press ESCAPE"
msgstr "Per uscire dalla modalità command line, premi ESC"
#, fuzzy
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
"Per la lista completa delle scorciatoie disponibili, digita `help shortcuts`"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Importa un file blocco note di Evernote (.enex file)."
msgid "Imports data into Joplin."
msgstr ""
#, fuzzy, javascript-format
msgid "Source format: %s"
msgstr "Nessun comando: %s"
msgid "Do not ask for confirmation."
msgstr "Non chiedere conferma."
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
"Il file \"%s\" sarà importato nel blocco note esistente \"%s\". Continuare?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"Un nuovo blocco note \"%s\" sarà creato e al suo interno verrà importato il "
"file \"%s\" . Continuare?"
#, javascript-format
msgid "Found: %d."
msgstr "Trovato: %d."
@@ -548,6 +496,10 @@ msgstr "Predefinito: %s"
msgid "Possible keys/values:"
msgstr "Chiave/valore possibili:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Mostra le informazioni di utilizzo."
msgid "Fatal error:"
msgstr "Errore fatale:"
@@ -592,6 +544,18 @@ msgid ""
"background and will be available soon."
msgstr ""
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "File"
msgid "File"
msgstr "File"
@@ -604,11 +568,19 @@ msgstr "Nuova attività"
msgid "New notebook"
msgstr "Nuovo blocco note"
msgid "Import Evernote notes"
msgstr "Importa le note da Evernote"
msgid "Import"
msgstr "Importa"
msgid "Evernote Export Files"
msgstr "Esposta i files di Evernote"
#, fuzzy
msgid "Export"
msgstr "Importa"
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr ""
msgid "Quit"
msgstr "Esci"
@@ -628,6 +600,12 @@ msgstr "Incolla"
msgid "Search in all the notes"
msgstr "Cerca in tutte le note"
msgid "View"
msgstr ""
msgid "Toggle editor layout"
msgstr ""
msgid "Tools"
msgstr "Strumenti"
@@ -647,6 +625,10 @@ msgstr "Aiuto"
msgid "Website and documentation"
msgstr "Sito web e documentazione"
#, fuzzy
msgid "Make a donation"
msgstr "Sito web e documentazione"
msgid "Check for updates..."
msgstr ""
@@ -670,10 +652,10 @@ msgstr "OK"
msgid "Cancel"
msgstr "Cancella"
msgid "Error"
msgid "Current version is up-to-date."
msgstr ""
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
msgid "Yes"
@@ -683,19 +665,9 @@ msgstr ""
msgid "No"
msgstr "N"
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "Cancella la sincronizzazione"
#, javascript-format
msgid "Notes and settings are stored in: %s"
@@ -799,6 +771,9 @@ msgstr "Rinomina il blocco note:"
msgid "Set alarm:"
msgstr "Imposta allarme:"
msgid "Search"
msgstr "Cerca"
msgid "Layout"
msgstr "Disposizione"
@@ -835,6 +810,13 @@ msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr "Al momento non ci sono note. Creane una cliccando sul bottone (+)."
msgid "Open..."
msgstr ""
#, fuzzy
msgid "Save as..."
msgstr "Salva i cambiamenti"
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Collegamento o messaggio non supportato: %s"
@@ -842,9 +824,30 @@ msgstr "Collegamento o messaggio non supportato: %s"
msgid "Attach file"
msgstr "Allega file"
msgid "Tags"
msgstr "Etichette"
msgid "Set alarm"
msgstr "Imposta allarme"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
#, fuzzy
msgid "to-do"
msgstr "Nuova attività"
#, fuzzy
msgid "note"
msgstr "Nuova nota"
#, fuzzy, javascript-format
msgid "Creating new %s..."
msgstr "Importazione delle note..."
msgid "Refresh"
msgstr "Aggiorna"
@@ -854,9 +857,6 @@ msgstr "Pulisci"
msgid "OneDrive Login"
msgstr "Login OneDrive"
msgid "Import"
msgstr "Importa"
msgid "Options"
msgstr "Opzioni"
@@ -881,9 +881,6 @@ msgstr "Sincronizza"
msgid "Notebooks"
msgstr "Blocchi note"
msgid "Tags"
msgstr "Etichette"
msgid "Searches"
msgstr "Ricerche"
@@ -902,7 +899,7 @@ msgstr "Etichetta sconosciuta: %s"
msgid "File system"
msgstr "File system"
msgid "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr ""
msgid "OneDrive"
@@ -911,7 +908,7 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (solo per test)"
msgid "WebDAV (Beta)"
msgid "WebDAV"
msgstr ""
#, javascript-format
@@ -987,6 +984,16 @@ msgstr "Cancellazione..."
msgid "Completed: %s"
msgstr "Completata: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Errore fatale:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "La sincronizzazione è già in corso. Stato: %s"
@@ -1051,9 +1058,17 @@ msgstr "Chiaro"
msgid "Dark"
msgstr "Scuro"
msgid "Show uncompleted todos on top of the lists"
#, fuzzy
msgid "Uncompleted to-dos on top"
msgstr "Mostra todo inclompleti in cima alla lista"
msgid "Sort notes by"
msgstr ""
#, fuzzy
msgid "Reverse sort order"
msgstr "Inverti l'ordine."
msgid "Save geo-location with notes"
msgstr "Salva geo-localizzazione con le note"
@@ -1075,7 +1090,15 @@ msgstr "Crea una nuova nota."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr ""
msgid "Editor font family"
msgstr ""
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
msgid "Automatically update the application"
@@ -1117,13 +1140,13 @@ msgstr ""
"Il percorso di sincronizzazione quando la sincronizzazione è abilitata. Vedi "
"`sync.target`."
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr ""
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr ""
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr ""
msgid "WebDAV URL"
@@ -1139,6 +1162,44 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Oprione non valida: \"%s\". I valori possibili sono: %s."
#, fuzzy
msgid "Joplin Export File"
msgstr "Esposta i files di Evernote"
msgid "Markdown"
msgstr ""
msgid "Joplin Export Directory"
msgstr ""
#, fuzzy
msgid "Evernote Export File"
msgstr "Esposta i files di Evernote"
msgid "Directory"
msgstr ""
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr ""
#, javascript-format
msgid "Please specify import format for %s"
msgstr ""
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
msgid "There is no data to export."
msgstr ""
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to."
msgstr "Per favore seleziona la nota o il blocco note da eliminare."
msgid "Items that cannot be synchronised"
msgstr "Elementi che non possono essere sincronizzati"
@@ -1222,6 +1283,9 @@ msgstr "Conferma"
msgid "Cancel synchronisation"
msgstr "Cancella la sincronizzazione"
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr ""
@@ -1308,6 +1372,82 @@ msgstr ""
msgid "Welcome"
msgstr "Benvenuto"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Eliminare le note?"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importa un file blocco note di Evernote (.enex file)."
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "Il file \"%s\" sarà importato nel blocco note esistente \"%s\". "
#~ "Continuare?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "Un nuovo blocco note \"%s\" sarà creato e al suo interno verrà importato "
#~ "il file \"%s\" . Continuare?"
#~ msgid "Import Evernote notes"
#~ msgstr "Importa le note da Evernote"
#~ msgid "Give focus to next pane"
#~ msgstr "Pannello successivo"
#~ msgid "Give focus to previous pane"
#~ msgstr "Pannello precedente"
#~ msgid "Enter command line mode"
#~ msgstr "Accedi alla modalità linea di comando"
#~ msgid "Exit command line mode"
#~ msgstr "Esci dalla modalità linea di comando"
#~ msgid "Edit the selected note"
#~ msgstr "Modifica la nota selezionata"
#~ msgid "Cancel the current command."
#~ msgstr "Cancella il comando corrente."
#~ msgid "Exit the application."
#~ msgstr "Esci dall'applicazione."
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "Elimina la nota o il blocco note selezionato."
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "Imposta un'attività come completata / non completata"
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr ""
#~ "Scegli lo s[t]ato della [c]onsole: massimizzato/minimizzato/nascosto/"
#~ "visibile."
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "mos[t]ra/nascondi i [m]etadata nelle note."
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "Crea ([M]ake) una nuova [n]ota"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "Crea ([M]ake) una nuova at[t]ività"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "Crea ([M]ake) un nuovo [b]locco note"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "Copia ([Y]) la [n]ota in un blocco note."
#~ msgid "Move the note to a notebook."
#~ msgstr "Sposta la nota in un blocco note."
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -16,63 +16,12 @@ msgstr ""
"X-Generator: Poedit 2.0.5\n"
"Plural-Forms: nplurals=1; plural=0;\n"
msgid "Give focus to next pane"
msgstr "次のペインへ"
msgid "Give focus to previous pane"
msgstr "前のペインへ"
msgid "Enter command line mode"
msgstr "コマンドラインモードに入る"
msgid "Exit command line mode"
msgstr "コマンドラインモードの終了"
msgid "Edit the selected note"
msgstr "選択したノートを編集"
msgid "Cancel the current command."
msgstr "現在のコマンドをキャンセル"
msgid "Exit the application."
msgstr "アプリケーションを終了する"
msgid "Delete the currently selected note or notebook."
msgstr "選択中のノートまたはノートブックを削除"
msgid "To delete a tag, untag the associated notes."
msgstr "タグを削除するには、関連するノートからタグを外してください。"
msgid "Please select the note or notebook to be deleted first."
msgstr "ます削除するノートかノートブックを選択してください。"
msgid "Set a to-do as completed / not completed"
msgstr "ToDoを完了/未完に設定"
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr "コンソールを最大表示/最小表示/非表示/可視で切り替える([t][c])"
msgid "Search"
msgstr "検索"
msgid "[t]oggle note [m]etadata."
msgstr "ノートのメタ情報を切り替える [tm]"
msgid "[M]ake a new [n]ote"
msgstr "新しいノートの作成 [mn]"
msgid "[M]ake a new [t]odo"
msgstr "新しいToDoの作成 [mt]"
msgid "[M]ake a new note[b]ook"
msgstr "新しいノートブックの作成 [mb]"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "ノートをノートブックにコピー [yn]"
msgid "Move the note to a notebook."
msgstr "ノートをノートブックに移動"
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "アプリケーションを終了するには、Ctrl+Dまたは\"exit\"と入力してください"
@@ -233,13 +182,18 @@ msgstr "ノートは保存されました。"
msgid "Exits the application."
msgstr "アプリケーションの終了。"
#, fuzzy
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Joplinのデータを選択されたディレクトリに出力する。標準では、ノートブック・"
"ノート・タグ・添付データを含むすべてのデータベースを出力します。"
#, fuzzy, javascript-format
msgid "Destination format: %s"
msgstr "日付の形式"
msgid "Exports only the given note."
msgstr "選択されたノートのみを出力する。"
@@ -252,6 +206,10 @@ msgstr "ノートの位置情報URLを表示する。"
msgid "Displays usage information."
msgstr "使い方を表示する。"
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
msgid "Shortcuts are not available in CLI mode."
msgstr "CLIモードではショートカットは使用できません。"
@@ -291,31 +249,23 @@ msgstr "コマンドラインモードに入るには、\":\"を入力してく
msgid "To exit command line mode, press ESCAPE"
msgstr "コマンドラインモードを終了するには、ESCキーを押してください。"
#, fuzzy
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
"有効なすべてのキーボードショートカットを表示するには、`help shortcuts`と入力"
"してください。"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Evernoteノートブックファイル(.enex)のインポート"
msgid "Imports data into Joplin."
msgstr ""
#, fuzzy, javascript-format
msgid "Source format: %s"
msgstr "コマンドが違います:%s"
msgid "Do not ask for confirmation."
msgstr "確認を行わない。"
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
"ファイル \"%s\" はノートブック \"%s\"に取り込まれます。よろしいですか?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"新しいノートブック\"%s\"が作成され、ファイル\"%s\"が取り込まれます。よろしい"
"ですか?"
#, javascript-format
msgid "Found: %d."
msgstr "見つかりました:%d"
@@ -542,6 +492,10 @@ msgstr "規定値: %s"
msgid "Possible keys/values:"
msgstr "取り得るキーバリュー: "
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "使い方を表示する。"
msgid "Fatal error:"
msgstr "致命的なエラー: "
@@ -591,6 +545,18 @@ msgid ""
"background and will be available soon."
msgstr ""
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "ファイル"
msgid "File"
msgstr "ファイル"
@@ -603,11 +569,19 @@ msgstr "新しいToDo"
msgid "New notebook"
msgstr "新しいノートブック"
msgid "Import Evernote notes"
msgstr "Evernoteのインポート"
msgid "Import"
msgstr "インポート"
msgid "Evernote Export Files"
msgstr "Evernote Exportファイル"
#, fuzzy
msgid "Export"
msgstr "インポート"
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr ""
msgid "Quit"
msgstr "終了"
@@ -627,6 +601,12 @@ msgstr "貼り付け"
msgid "Search in all the notes"
msgstr "すべてのノートを検索"
msgid "View"
msgstr ""
msgid "Toggle editor layout"
msgstr ""
msgid "Tools"
msgstr "ツール"
@@ -646,6 +626,10 @@ msgstr "ヘルプ"
msgid "Website and documentation"
msgstr "Webサイトとドキュメント"
#, fuzzy
msgid "Make a donation"
msgstr "Webサイトとドキュメント"
msgid "Check for updates..."
msgstr ""
@@ -669,10 +653,10 @@ msgstr ""
msgid "Cancel"
msgstr "キャンセル"
msgid "Error"
msgid "Current version is up-to-date."
msgstr ""
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
msgid "Yes"
@@ -681,19 +665,9 @@ msgstr ""
msgid "No"
msgstr ""
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "同期の中止"
#, javascript-format
msgid "Notes and settings are stored in: %s"
@@ -801,6 +775,9 @@ msgstr "ノートブックの名前を変更:"
msgid "Set alarm:"
msgstr "アラームをセット:"
msgid "Search"
msgstr "検索"
msgid "Layout"
msgstr "レイアウト"
@@ -836,6 +813,13 @@ msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr "ノートブックがありません。新しいノートブックを作成してください。"
msgid "Open..."
msgstr ""
#, fuzzy
msgid "Save as..."
msgstr "変更を保存"
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr ""
@@ -843,9 +827,30 @@ msgstr ""
msgid "Attach file"
msgstr "ファイルを添付"
msgid "Tags"
msgstr "タグ"
msgid "Set alarm"
msgstr "アラームをセット"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
#, fuzzy
msgid "to-do"
msgstr "新しいToDo"
#, fuzzy
msgid "note"
msgstr "新しいノート"
#, fuzzy, javascript-format
msgid "Creating new %s..."
msgstr "ノートのインポート…"
msgid "Refresh"
msgstr "更新"
@@ -855,9 +860,6 @@ msgstr "クリア"
msgid "OneDrive Login"
msgstr "OneDriveログイン"
msgid "Import"
msgstr "インポート"
msgid "Options"
msgstr "オプション"
@@ -882,9 +884,6 @@ msgstr "同期"
msgid "Notebooks"
msgstr "ノートブック"
msgid "Tags"
msgstr "タグ"
msgid "Searches"
msgstr "検索"
@@ -902,7 +901,7 @@ msgstr "不明なフラグ: %s"
msgid "File system"
msgstr "ファイルシステム"
msgid "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr ""
msgid "OneDrive"
@@ -911,7 +910,7 @@ msgstr ""
msgid "OneDrive Dev (For testing only)"
msgstr ""
msgid "WebDAV (Beta)"
msgid "WebDAV"
msgstr ""
#, javascript-format
@@ -987,6 +986,16 @@ msgstr "中止中..."
msgid "Completed: %s"
msgstr "完了: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "致命的なエラー: "
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "同期作業はすでに実行中です。状態: %s"
@@ -1053,9 +1062,17 @@ msgstr "明るい"
msgid "Dark"
msgstr "暗い"
msgid "Show uncompleted todos on top of the lists"
#, fuzzy
msgid "Uncompleted to-dos on top"
msgstr "未完のToDoをリストの上部に表示"
msgid "Sort notes by"
msgstr ""
#, fuzzy
msgid "Reverse sort order"
msgstr "逆順に並び替える。"
msgid "Save geo-location with notes"
msgstr "ノートに位置情報を保存"
@@ -1077,7 +1094,15 @@ msgstr "あたらしいノートを作成します。"
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr ""
msgid "Editor font family"
msgstr ""
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
msgid "Automatically update the application"
@@ -1119,13 +1144,13 @@ msgstr ""
"ファイルシステム同期の有効時に同期を行うパスです。`sync.target`も参考にしてく"
"ださい。"
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr ""
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr ""
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr ""
msgid "WebDAV URL"
@@ -1141,6 +1166,44 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "無効な設定値: \"%s\"。有効な値は: %sです。"
#, fuzzy
msgid "Joplin Export File"
msgstr "Evernote Exportファイル"
msgid "Markdown"
msgstr ""
msgid "Joplin Export Directory"
msgstr ""
#, fuzzy
msgid "Evernote Export File"
msgstr "Evernote Exportファイル"
msgid "Directory"
msgstr ""
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr ""
#, javascript-format
msgid "Please specify import format for %s"
msgstr ""
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
msgid "There is no data to export."
msgstr ""
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to."
msgstr "同期状況の出力先を選択してください"
msgid "Items that cannot be synchronised"
msgstr "同期が出来なかったアイテム"
@@ -1224,6 +1287,9 @@ msgstr "確認"
msgid "Cancel synchronisation"
msgstr "同期の中止"
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr ""
@@ -1310,6 +1376,79 @@ msgstr ""
msgid "Welcome"
msgstr "ようこそ"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "ノートを削除しますか?"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Evernoteノートブックファイル(.enex)のインポート"
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "ファイル \"%s\" はノートブック \"%s\"に取り込まれます。よろしいですか?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "新しいノートブック\"%s\"が作成され、ファイル\"%s\"が取り込まれます。よろし"
#~ "いですか?"
#~ msgid "Import Evernote notes"
#~ msgstr "Evernoteのインポート"
#~ msgid "Give focus to next pane"
#~ msgstr "次のペインへ"
#~ msgid "Give focus to previous pane"
#~ msgstr "前のペインへ"
#~ msgid "Enter command line mode"
#~ msgstr "コマンドラインモードに入る"
#~ msgid "Exit command line mode"
#~ msgstr "コマンドラインモードの終了"
#~ msgid "Edit the selected note"
#~ msgstr "選択したノートを編集"
#~ msgid "Cancel the current command."
#~ msgstr "現在のコマンドをキャンセル"
#~ msgid "Exit the application."
#~ msgstr "アプリケーションを終了する"
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "選択中のノートまたはノートブックを削除"
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "ToDoを完了/未完に設定"
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr "コンソールを最大表示/最小表示/非表示/可視で切り替える([t][c])"
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "ノートのメタ情報を切り替える [tm]"
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "新しいノートの作成 [mn]"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "新しいToDoの作成 [mt]"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "新しいノートブックの作成 [mb]"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "ノートをノートブックにコピー [yn]"
#~ msgid "Move the note to a notebook."
#~ msgstr "ノートをノートブックに移動"
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -15,63 +15,12 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Give focus to next pane"
msgstr ""
msgid "Give focus to previous pane"
msgstr ""
msgid "Enter command line mode"
msgstr ""
msgid "Exit command line mode"
msgstr ""
msgid "Edit the selected note"
msgstr ""
msgid "Cancel the current command."
msgstr ""
msgid "Exit the application."
msgstr ""
msgid "Delete the currently selected note or notebook."
msgstr ""
msgid "To delete a tag, untag the associated notes."
msgstr ""
msgid "Please select the note or notebook to be deleted first."
msgstr ""
msgid "Set a to-do as completed / not completed"
msgstr ""
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr ""
msgid "Search"
msgstr ""
msgid "[t]oggle note [m]etadata."
msgstr ""
msgid "[M]ake a new [n]ote"
msgstr ""
msgid "[M]ake a new [t]odo"
msgstr ""
msgid "[M]ake a new note[b]ook"
msgstr ""
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr ""
msgid "Move the note to a notebook."
msgstr ""
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr ""
@@ -225,10 +174,14 @@ msgid "Exits the application."
msgstr ""
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
#, javascript-format
msgid "Destination format: %s"
msgstr ""
msgid "Exports only the given note."
msgstr ""
@@ -241,6 +194,10 @@ msgstr ""
msgid "Displays usage information."
msgstr ""
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
msgid "Shortcuts are not available in CLI mode."
msgstr ""
@@ -276,25 +233,19 @@ msgid "To exit command line mode, press ESCAPE"
msgstr ""
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
msgid "Imports an Evernote notebook file (.enex file)."
msgid "Imports data into Joplin."
msgstr ""
#, javascript-format
msgid "Source format: %s"
msgstr ""
msgid "Do not ask for confirmation."
msgstr ""
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
#, javascript-format
msgid "Found: %d."
msgstr ""
@@ -498,6 +449,9 @@ msgstr ""
msgid "Possible keys/values:"
msgstr ""
msgid "Type `joplin help` for usage information."
msgstr ""
msgid "Fatal error:"
msgstr ""
@@ -535,6 +489,17 @@ msgid ""
"background and will be available soon."
msgstr ""
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr ""
msgid "PDF File"
msgstr ""
msgid "File"
msgstr ""
@@ -547,10 +512,17 @@ msgstr ""
msgid "New notebook"
msgstr ""
msgid "Import Evernote notes"
msgid "Import"
msgstr ""
msgid "Evernote Export Files"
msgid "Export"
msgstr ""
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr ""
msgid "Quit"
@@ -571,6 +543,12 @@ msgstr ""
msgid "Search in all the notes"
msgstr ""
msgid "View"
msgstr ""
msgid "Toggle editor layout"
msgstr ""
msgid "Tools"
msgstr ""
@@ -589,6 +567,9 @@ msgstr ""
msgid "Website and documentation"
msgstr ""
msgid "Make a donation"
msgstr ""
msgid "Check for updates..."
msgstr ""
@@ -612,10 +593,10 @@ msgstr ""
msgid "Cancel"
msgstr ""
msgid "Error"
msgid "Current version is up-to-date."
msgstr ""
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
msgid "Yes"
@@ -624,18 +605,7 @@ msgstr ""
msgid "No"
msgstr ""
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgid "Check synchronisation configuration"
msgstr ""
#, javascript-format
@@ -738,6 +708,9 @@ msgstr ""
msgid "Set alarm:"
msgstr ""
msgid "Search"
msgstr ""
msgid "Layout"
msgstr ""
@@ -772,6 +745,12 @@ msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr ""
msgid "Open..."
msgstr ""
msgid "Save as..."
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr ""
@@ -779,9 +758,28 @@ msgstr ""
msgid "Attach file"
msgstr ""
msgid "Tags"
msgstr ""
msgid "Set alarm"
msgstr ""
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
msgid "to-do"
msgstr ""
msgid "note"
msgstr ""
#, javascript-format
msgid "Creating new %s..."
msgstr ""
msgid "Refresh"
msgstr ""
@@ -791,9 +789,6 @@ msgstr ""
msgid "OneDrive Login"
msgstr ""
msgid "Import"
msgstr ""
msgid "Options"
msgstr ""
@@ -818,9 +813,6 @@ msgstr ""
msgid "Notebooks"
msgstr ""
msgid "Tags"
msgstr ""
msgid "Searches"
msgstr ""
@@ -838,7 +830,7 @@ msgstr ""
msgid "File system"
msgstr ""
msgid "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr ""
msgid "OneDrive"
@@ -847,7 +839,7 @@ msgstr ""
msgid "OneDrive Dev (For testing only)"
msgstr ""
msgid "WebDAV (Beta)"
msgid "WebDAV"
msgstr ""
#, javascript-format
@@ -915,6 +907,16 @@ msgstr ""
msgid "Completed: %s"
msgstr ""
#, javascript-format
msgid "Last error: %s"
msgstr ""
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr ""
@@ -976,7 +978,13 @@ msgstr ""
msgid "Dark"
msgstr ""
msgid "Show uncompleted todos on top of the lists"
msgid "Uncompleted to-dos on top"
msgstr ""
msgid "Sort notes by"
msgstr ""
msgid "Reverse sort order"
msgstr ""
msgid "Save geo-location with notes"
@@ -997,7 +1005,15 @@ msgstr ""
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr ""
msgid "Editor font family"
msgstr ""
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
msgid "Automatically update the application"
@@ -1037,13 +1053,13 @@ msgid ""
"See `sync.target`."
msgstr ""
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr ""
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr ""
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr ""
msgid "WebDAV URL"
@@ -1059,6 +1075,41 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""
msgid "Joplin Export File"
msgstr ""
msgid "Markdown"
msgstr ""
msgid "Joplin Export Directory"
msgstr ""
msgid "Evernote Export File"
msgstr ""
msgid "Directory"
msgstr ""
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr ""
#, javascript-format
msgid "Please specify import format for %s"
msgstr ""
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
msgid "There is no data to export."
msgstr ""
msgid "Please specify the notebook where the notes should be imported to."
msgstr ""
msgid "Items that cannot be synchronised"
msgstr ""
@@ -1142,6 +1193,9 @@ msgstr ""
msgid "Cancel synchronisation"
msgstr ""
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr ""

View File

@@ -16,64 +16,12 @@ msgstr ""
"X-Generator: Poedit 2.0.5\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Give focus to next pane"
msgstr "Focus op het volgende paneel"
msgid "Give focus to previous pane"
msgstr "Focus op het vorige paneel"
msgid "Enter command line mode"
msgstr "Ga naar command line modus"
msgid "Exit command line mode"
msgstr "Ga uit command line modus"
msgid "Edit the selected note"
msgstr "Pas de geselecteerde notitie aan"
msgid "Cancel the current command."
msgstr "Annuleer het huidige commando."
msgid "Exit the application."
msgstr "Sluit de applicatie."
msgid "Delete the currently selected note or notebook."
msgstr "Verwijder de geselecteerde notitie of het geselecteerde notitieboek."
msgid "To delete a tag, untag the associated notes."
msgstr "Untag de geassocieerde notities om een tag te verwijderen."
msgid "Please select the note or notebook to be deleted first."
msgstr "Selecteer eerst het notitieboek of de notitie om te verwijderen."
msgid "Set a to-do as completed / not completed"
msgstr "Zet een to-do als voltooid / niet voltooid"
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr ""
"Wissel de console tussen gemaximaliseerd/geminimaliseerd/verborgen/zichtbaar."
msgid "Search"
msgstr "Zoeken"
msgid "[t]oggle note [m]etadata."
msgstr "Ac[t]iveer notitie [m]etadata."
msgid "[M]ake a new [n]ote"
msgstr "[M]aak een nieuwe [n]otitie"
msgid "[M]ake a new [t]odo"
msgstr "[M]aak een nieuwe [t]o-do"
msgid "[M]ake a new note[b]ook"
msgstr "[M]aak een nieuw notitie[b]oek"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "Kopieer [Y] de [n]otirie in een notitieboek."
msgid "Move the note to a notebook."
msgstr "Verplaats de notitie naar een notitieboek."
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Typ Ctrl+D of \"exit\" om de applicatie te sluiten"
@@ -239,14 +187,19 @@ msgstr "Notitie is opgeslaan."
msgid "Exits the application."
msgstr "Sluit de applicatie."
#, fuzzy
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Exporteert Joplin gegevens naar de opgegeven folder. Standaard zal het de "
"volledige database exporteren, zoals notitieboeken, notities, tags en "
"middelen."
#, fuzzy, javascript-format
msgid "Destination format: %s"
msgstr "Datumnotatie"
msgid "Exports only the given note."
msgstr "Exporteert alleen de opgegeven notitie."
@@ -259,6 +212,10 @@ msgstr "Toont een geolocatie link voor de notitie."
msgid "Displays usage information."
msgstr "Toont gebruiksinformatie."
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
msgid "Shortcuts are not available in CLI mode."
msgstr "Shortcuts zijn niet beschikbaar in command line modus."
@@ -301,31 +258,22 @@ msgstr "Om command line modus te gebruiken, duw \":\""
msgid "To exit command line mode, press ESCAPE"
msgstr "Om command line modus te verlaten, duw ESCAPE"
#, fuzzy
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
"Voor de volledige lijst van beschikbare shortcuts, typ `help shortcuts`"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Importeer een Evernote notitieboek (.enex bestand)."
msgid "Imports data into Joplin."
msgstr ""
#, fuzzy, javascript-format
msgid "Source format: %s"
msgstr "Geen commando gevonden: \"%s\""
msgid "Do not ask for confirmation."
msgstr "Vraag niet om bevestiging. "
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
"Bestand \"%s\" zal toegevoegd worden aan bestaand notitieboek \"%s\". "
"Doorgaan?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"Nieuw notitieboek \"%s\" zal aangemaakt worden en bestand \"%s\" zal eraan "
"toegevoegd worden. Doorgaan?"
#, javascript-format
msgid "Found: %d."
msgstr "Gevonden: %d."
@@ -557,6 +505,10 @@ msgstr "Standaard: %s"
msgid "Possible keys/values:"
msgstr "Mogelijke sleutels/waarden:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "Toont gebruiksinformatie."
msgid "Fatal error:"
msgstr "Fatale fout:"
@@ -610,6 +562,18 @@ msgstr ""
"hoofdsleutel al ingegeven hebt, worden de versleutelde items ontsleuteld in "
"de achtergrond. Ze zijn binnenkort beschikbaar."
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "Bestand"
msgid "File"
msgstr "Bestand"
@@ -622,11 +586,19 @@ msgstr "Nieuwe to-do"
msgid "New notebook"
msgstr "Nieuw notitieboek"
msgid "Import Evernote notes"
msgstr "Importeer Evernote notities"
msgid "Import"
msgstr "Importeer"
msgid "Evernote Export Files"
msgstr "Exporteer Evernote bestanden"
#, fuzzy
msgid "Export"
msgstr "Importeer"
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr ""
msgid "Quit"
msgstr "Stop"
@@ -646,6 +618,12 @@ msgstr "Plak"
msgid "Search in all the notes"
msgstr "Zoek in alle notities"
msgid "View"
msgstr ""
msgid "Toggle editor layout"
msgstr ""
msgid "Tools"
msgstr "Tools"
@@ -664,6 +642,10 @@ msgstr "Help"
msgid "Website and documentation"
msgstr "Website en documentatie"
#, fuzzy
msgid "Make a donation"
msgstr "Website en documentatie"
msgid "Check for updates..."
msgstr ""
@@ -687,10 +669,10 @@ msgstr "OK"
msgid "Cancel"
msgstr "Annuleer"
msgid "Error"
msgid "Current version is up-to-date."
msgstr ""
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
msgid "Yes"
@@ -700,19 +682,9 @@ msgstr ""
msgid "No"
msgstr "N"
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "Annuleer synchronisatie"
#, javascript-format
msgid "Notes and settings are stored in: %s"
@@ -828,6 +800,9 @@ msgstr "Hernoem notitieboek:"
msgid "Set alarm:"
msgstr "Stel melding in:"
msgid "Search"
msgstr "Zoeken"
msgid "Layout"
msgstr "Layout"
@@ -864,6 +839,13 @@ msgstr ""
"U heeft momenteel geen notitieboek. Maak een notitieboek door op \"Nieuw "
"notitieboek\" te klikken."
msgid "Open..."
msgstr ""
#, fuzzy
msgid "Save as..."
msgstr "Sla wijzigingen op"
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Link of bericht \"%s\" wordt niet ondersteund"
@@ -871,9 +853,30 @@ msgstr "Link of bericht \"%s\" wordt niet ondersteund"
msgid "Attach file"
msgstr "Voeg bestand toe"
msgid "Tags"
msgstr "Tags"
msgid "Set alarm"
msgstr "Zet melding"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
#, fuzzy
msgid "to-do"
msgstr "Nieuwe to-do"
#, fuzzy
msgid "note"
msgstr "Nieuwe notitie"
#, fuzzy, javascript-format
msgid "Creating new %s..."
msgstr "Notities importeren..."
msgid "Refresh"
msgstr "Vernieuwen"
@@ -883,9 +886,6 @@ msgstr "Vrijmaken"
msgid "OneDrive Login"
msgstr "OneDrive Login"
msgid "Import"
msgstr "Importeer"
msgid "Options"
msgstr "Opties"
@@ -910,9 +910,6 @@ msgstr "Synchroniseer"
msgid "Notebooks"
msgstr "Notitieboeken"
msgid "Tags"
msgstr "Tags"
msgid "Searches"
msgstr "Zoekopdrachten"
@@ -930,8 +927,9 @@ msgstr "Onbekende optie: %s"
msgid "File system"
msgstr "Bestandssysteem"
msgid "Nextcloud (Beta)"
msgstr ""
#, fuzzy
msgid "Nextcloud"
msgstr "Stel wachtwoord in"
msgid "OneDrive"
msgstr "OneDrive"
@@ -939,7 +937,7 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (Alleen voor testen)"
msgid "WebDAV (Beta)"
msgid "WebDAV"
msgstr ""
#, javascript-format
@@ -1015,6 +1013,16 @@ msgstr "Annuleren..."
msgid "Completed: %s"
msgstr "Voltooid: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Fatale fout:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "Synchronisatie is reeds bezig. Status: %s"
@@ -1081,9 +1089,17 @@ msgstr "Licht"
msgid "Dark"
msgstr "Donker"
msgid "Show uncompleted todos on top of the lists"
#, fuzzy
msgid "Uncompleted to-dos on top"
msgstr "Toon onvoltooide to-do's aan de top van de lijsten"
msgid "Sort notes by"
msgstr ""
#, fuzzy
msgid "Reverse sort order"
msgstr "Draait de sorteervolgorde om."
msgid "Save geo-location with notes"
msgstr "Sla geo-locatie op bij notities"
@@ -1104,7 +1120,15 @@ msgstr "Maakt een nieuwe notitie aan."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr ""
msgid "Editor font family"
msgstr ""
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
msgid "Automatically update the application"
@@ -1146,14 +1170,14 @@ msgstr ""
"Het pad om mee te synchroniseren als bestandssysteem synchronisatie is "
"ingeschakeld. Zie `sync.target`."
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr ""
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr ""
#, fuzzy
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr "Stel wachtwoord in"
msgid "WebDAV URL"
@@ -1170,6 +1194,44 @@ msgstr "Stel wachtwoord in"
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Ongeldige optie: \"%s\". Geldige waarden zijn: %s."
#, fuzzy
msgid "Joplin Export File"
msgstr "Exporteer Evernote bestanden"
msgid "Markdown"
msgstr ""
msgid "Joplin Export Directory"
msgstr ""
#, fuzzy
msgid "Evernote Export File"
msgstr "Exporteer Evernote bestanden"
msgid "Directory"
msgstr ""
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr ""
#, javascript-format
msgid "Please specify import format for %s"
msgstr ""
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
msgid "There is no data to export."
msgstr ""
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to."
msgstr "Selecteer waar de synchronisatie status naar geëxporteerd moet worden"
msgid "Items that cannot be synchronised"
msgstr "Items die niet gesynchroniseerd kunnen worden"
@@ -1257,6 +1319,9 @@ msgstr "Bevestig"
msgid "Cancel synchronisation"
msgstr "Annuleer synchronisatie"
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr "Hoofdsleutel: %s"
@@ -1342,6 +1407,83 @@ msgstr ""
msgid "Welcome"
msgstr "Welkom"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Notities verwijderen?"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importeer een Evernote notitieboek (.enex bestand)."
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "Bestand \"%s\" zal toegevoegd worden aan bestaand notitieboek \"%s\". "
#~ "Doorgaan?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "Nieuw notitieboek \"%s\" zal aangemaakt worden en bestand \"%s\" zal "
#~ "eraan toegevoegd worden. Doorgaan?"
#~ msgid "Import Evernote notes"
#~ msgstr "Importeer Evernote notities"
#~ msgid "Give focus to next pane"
#~ msgstr "Focus op het volgende paneel"
#~ msgid "Give focus to previous pane"
#~ msgstr "Focus op het vorige paneel"
#~ msgid "Enter command line mode"
#~ msgstr "Ga naar command line modus"
#~ msgid "Exit command line mode"
#~ msgstr "Ga uit command line modus"
#~ msgid "Edit the selected note"
#~ msgstr "Pas de geselecteerde notitie aan"
#~ msgid "Cancel the current command."
#~ msgstr "Annuleer het huidige commando."
#~ msgid "Exit the application."
#~ msgstr "Sluit de applicatie."
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr ""
#~ "Verwijder de geselecteerde notitie of het geselecteerde notitieboek."
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "Zet een to-do als voltooid / niet voltooid"
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr ""
#~ "Wissel de console tussen gemaximaliseerd/geminimaliseerd/verborgen/"
#~ "zichtbaar."
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "Ac[t]iveer notitie [m]etadata."
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "[M]aak een nieuwe [n]otitie"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "[M]aak een nieuwe [t]o-do"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "[M]aak een nieuw notitie[b]oek"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "Kopieer [Y] de [n]otirie in een notitieboek."
#~ msgid "Move the note to a notebook."
#~ msgstr "Verplaats de notitie naar een notitieboek."
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -7,72 +7,21 @@ msgid ""
msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Last-Translator: Renato Nunes Bastos <rnbastos@gmail.com>\n"
"Language-Team: \n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.5\n"
"X-Generator: Poedit 2.0.6\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "Give focus to next pane"
msgstr "Dar o foco para o próximo painel"
msgid "Give focus to previous pane"
msgstr "Dar o foco para o painel anterior"
msgid "Enter command line mode"
msgstr "Entrar no modo de linha de comando"
msgid "Exit command line mode"
msgstr "Sair do modo de linha de comando"
msgid "Edit the selected note"
msgstr "Editar nota selecionada"
msgid "Cancel the current command."
msgstr "Cancelar comando atual."
msgid "Exit the application."
msgstr "Sair da aplicação."
msgid "Delete the currently selected note or notebook."
msgstr "Excluir nota selecionada ou notebook."
msgid "To delete a tag, untag the associated notes."
msgstr "Para eliminar uma tag, remova a tag das notas associadas a ela."
msgid "Please select the note or notebook to be deleted first."
msgstr "Por favor, primeiro, selecione a nota ou caderno a excluir."
msgid "Set a to-do as completed / not completed"
msgstr "Marcar uma tarefa como completada / não completada"
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr "al[t]ernar [c]onsole entre maximizado / minimizado / oculto / visível."
msgid "Search"
msgstr "Procurar"
msgid "[t]oggle note [m]etadata."
msgstr "al[t]ernar [m]etadados de notas."
msgid "[M]ake a new [n]ote"
msgstr "Criar ([M]ake) uma nova [n]ota"
msgid "[M]ake a new [t]odo"
msgstr "Criar ([M]ake) nova [t]arefa"
msgid "[M]ake a new note[b]ook"
msgstr "Criar ([M]ake) novo caderno (note[b]ook)"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "Copiar ([Y]ank) a [n]ota a um caderno."
msgid "Move the note to a notebook."
msgstr "Mover nota para um caderno."
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Digite Ctrl+D ou \"exit\" para sair da aplicação"
@@ -101,16 +50,16 @@ msgstr "s"
msgid "Cancelling background synchronisation... Please wait."
msgstr "Cancelando sincronização em segundo plano... Por favor, aguarde."
#, fuzzy, javascript-format
#, javascript-format
msgid "No such command: %s"
msgstr "Comando inválido: \"%s\""
msgstr "Não existe o comando: \"%s\""
#, javascript-format
msgid "The command \"%s\" is only available in GUI mode"
msgstr "O comando \"%s\" está disponível somente em modo gráfico"
msgid "Cannot change encrypted item"
msgstr ""
msgstr "Não pode mudar um item encriptado"
#, javascript-format
msgid "Missing required argument: %s"
@@ -178,22 +127,25 @@ msgid ""
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, "
"`status` and `target-status`."
msgstr ""
"Gerencia a configuração E2EE. Os comandos são `enable`, `disable`, "
"`decrypt`, `status` e `target-status`."
msgid "Enter master password:"
msgstr ""
msgstr "Entre a senha master:"
msgid "Operation cancelled"
msgstr ""
msgstr "Operação cancelada"
msgid ""
"Starting decryption... Please wait as it may take several minutes depending "
"on how much there is to decrypt."
msgstr ""
"Iniciando decriptação... Por favor aguarde, pois isso pode demorar vários "
"minutos, dependendo de quanto há para decriptar."
msgid "Completed decryption."
msgstr ""
msgstr "Decriptação completada."
#, fuzzy
msgid "Enabled"
msgstr "Desabilitado"
@@ -202,7 +154,7 @@ msgstr "Desabilitado"
#, javascript-format
msgid "Encryption is: %s"
msgstr ""
msgstr "Encriptação está: %s"
msgid "Edit note."
msgstr "Editar nota."
@@ -225,7 +177,7 @@ msgstr "Começando a editar a nota. Feche o editor para voltar ao prompt."
#, javascript-format
msgid "Error opening note in editor: %s"
msgstr ""
msgstr "Erro ao abir a nota no editor: %s"
msgid "Note has been saved."
msgstr "Nota gravada."
@@ -234,13 +186,17 @@ msgid "Exits the application."
msgstr "Sai da aplicação."
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Exporta os dados do Joplin para o diretório informado. Por padrão, ele "
"exportará o banco de dados completo, incluindo cadernos, notas, tags e "
"recursos."
#, javascript-format
msgid "Destination format: %s"
msgstr "Formato do destino: %s"
msgid "Exports only the given note."
msgstr "Exporta apenas a nota fornecida."
@@ -253,14 +209,19 @@ msgstr "Exibe uma URL de geolocalização para a nota."
msgid "Displays usage information."
msgstr "Exibe informações de uso."
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr "Para informações sobre como customizar os atalhos, por favor visite %s"
msgid "Shortcuts are not available in CLI mode."
msgstr "Os atalhos não estão disponíveis no modo CLI."
#, fuzzy
msgid ""
"Type `help [command]` for more information about a command; or type `help "
"all` for the complete usage information."
msgstr "Digite `help [comando]` para obter mais informações sobre um comando."
msgstr ""
"Digite `help [comando]` para obter mais informações sobre um comando; ou "
"digite `help all` para informações completas de uso."
msgid "The possible commands are:"
msgstr "Os comandos possíveis são:"
@@ -295,30 +256,20 @@ msgid "To exit command line mode, press ESCAPE"
msgstr "Para sair do modo de linha de comando, pressione o ESC"
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
"Para a lista completa de atalhos de teclado disponíveis, digite `help "
"shortcuts`"
"Para a lista completa de atalhos de teclado disponíveis, digite `help keymap`"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Importa um arquivo de caderno do Evernote (arquivo .enex)."
msgid "Imports data into Joplin."
msgstr "Importa dados para o Joplin."
#, javascript-format
msgid "Source format: %s"
msgstr "Formato da origem: \"%s\""
msgid "Do not ask for confirmation."
msgstr "Não pedir confirmação."
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr ""
"O arquivo \"%s\" será importado para o caderno existente \"%s\". Continuar?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"O novo caderno \"%s\" será criado e o arquivo \"%s\" será importado para "
"ele. Continuar?"
#, javascript-format
msgid "Found: %d."
msgstr "Encontrado: %d."
@@ -413,6 +364,8 @@ msgstr "Exclui o caderno sem pedir confirmação."
msgid "Delete notebook? All notes within this notebook will also be deleted."
msgstr ""
"Excluir o caderno? Todas as notas deste caderno notebook também serão "
"excluídas."
msgid "Deletes the notes matching <note-pattern>."
msgstr "Exclui as notas correspondentes ao padrão <note-pattern>."
@@ -430,13 +383,17 @@ msgstr "Apagar nota?"
msgid "Searches for the given <pattern> in all the notes."
msgstr "Procura o padrão <pattern>em todas as notas."
#, fuzzy, javascript-format
#, javascript-format
msgid ""
"Sets the property <name> of the given <note> to the given [value]. Possible "
"properties are:\n"
"\n"
"%s"
msgstr "Define a propriedade <name> da <note> para o dado [valor]."
msgstr ""
"Define a propriedade <name> da <note> para o valor [value]. As propriedades "
"possíveis são:\n"
"\n"
"%s"
msgid "Displays summary about the notes and notebooks."
msgstr "Exibe sumário sobre as notas e cadernos."
@@ -457,6 +414,8 @@ msgstr ""
#, javascript-format
msgid "Not authentified with %s. Please provide any missing credentials."
msgstr ""
"Não autenticado com %s. Por favor, complete as credenciais que estiverem "
"faltando."
msgid "Synchronisation is already in progress."
msgstr "A sincronização já está em andamento."
@@ -543,6 +502,9 @@ msgstr "Padrão: %s"
msgid "Possible keys/values:"
msgstr "Possíveis chaves/valores:"
msgid "Type `joplin help` for usage information."
msgstr "Digite 'joplin help' para informações de uso."
msgid "Fatal error:"
msgstr "Erro fatal:"
@@ -578,6 +540,13 @@ msgid ""
"\n"
"For example, to create a notebook press `mb`; to create a note press `mn`."
msgstr ""
"Bem-vindo ao Joplin!\n"
"\n"
"Digite `:help shortcuts` para obter a lista de atalhos de teclado, ou apenas "
"`:help` para informações de utilização.\n"
"\n"
"Por exemplo, para criar um caderno digite `mb`; para criar uma nota, digite "
"`mn`."
msgid ""
"One or more items are currently encrypted and you may need to supply a "
@@ -585,6 +554,22 @@ msgid ""
"supplied the password, the encrypted items are being decrypted in the "
"background and will be available soon."
msgstr ""
"Um ou mais itens estão criptografados, e você pode precisar de informar uma "
"senha master. Para fazer isso, por favor digite `e2ee decrypt`. Se você já "
"forneceu a senha, os itens criptografados estão sendo decriptados em "
"background e logo estarão disponíveis."
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "Exportando para \"%s\" com o formato \"%s\". Por favor, aguarde..."
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "Importando de \"%s\" com o formato \"%s\". Por favor, aguarde..."
#, fuzzy
msgid "PDF File"
msgstr "Arquivo"
msgid "File"
msgstr "Arquivo"
@@ -598,11 +583,18 @@ msgstr "Nova tarefa"
msgid "New notebook"
msgstr "Novo caderno"
msgid "Import Evernote notes"
msgstr "Importar notas do Evernote"
msgid "Import"
msgstr "Importar"
msgid "Evernote Export Files"
msgstr "Arquivos de Exportação do Evernote"
msgid "Export"
msgstr "Exportar"
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr "Ocultar %s"
msgid "Quit"
msgstr "Sair"
@@ -622,19 +614,23 @@ msgstr "Colar"
msgid "Search in all the notes"
msgstr "Pesquisar em todas as notas"
msgid "View"
msgstr "Visualizar"
msgid "Toggle editor layout"
msgstr "Alternar layout do editor"
msgid "Tools"
msgstr "Ferramentas"
#, fuzzy
msgid "Synchronisation status"
msgstr "Alvo de sincronização"
msgstr "Status de sincronização"
msgid "Encryption options"
msgstr ""
msgstr "Opções de Encriptação"
#, fuzzy
msgid "General Options"
msgstr "Opções"
msgstr "Opções Gerais"
msgid "Help"
msgstr "Ajuda"
@@ -642,8 +638,11 @@ msgstr "Ajuda"
msgid "Website and documentation"
msgstr "Website e documentação"
msgid "Make a donation"
msgstr "Fazer uma doação"
msgid "Check for updates..."
msgstr ""
msgstr "Verificar atualizações..."
msgid "About Joplin"
msgstr "Sobre o Joplin"
@@ -652,12 +651,12 @@ msgstr "Sobre o Joplin"
msgid "%s %s (%s, %s)"
msgstr "%s %s (%s, %s)"
#, fuzzy, javascript-format
#, javascript-format
msgid "Open %s"
msgstr "Em %s: %s"
msgstr "Abrir %s"
msgid "Exit"
msgstr ""
msgstr "Sair"
msgid "OK"
msgstr "OK"
@@ -665,45 +664,35 @@ msgstr "OK"
msgid "Cancel"
msgstr "Cancelar"
msgid "Error"
msgstr ""
msgid "Current version is up-to-date."
msgstr "A versão atual está atualizada."
msgid "An update is available, do you want to update now?"
msgstr ""
msgid "An update is available, do you want to download it now?"
msgstr "Uma atualização está disponível, você quer baixar agora?"
msgid "Yes"
msgstr ""
msgstr "Sim"
#, fuzzy
msgid "No"
msgstr "N"
msgstr "Não"
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgstr ""
msgid "Check synchronisation configuration"
msgstr "Verificar a configuração da sincronização"
#, javascript-format
msgid "Notes and settings are stored in: %s"
msgstr ""
msgstr "Notas e configurações estão armazenadas em: %s"
msgid "Save"
msgstr ""
msgstr "Salvar"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
"continue?"
msgstr ""
"Desabilitar a encriptação significa que *todas* as suas notas e anexos serão "
"re-sincronizados e enviados sem encriptação. Você quer continuar?"
msgid ""
"Enabling encryption means *all* your notes and attachments are going to be "
@@ -711,59 +700,68 @@ msgid ""
"password as, for security purposes, this will be the *only* way to decrypt "
"the data! To enable encryption, please enter your password below."
msgstr ""
"Habilitar a encriptação significa que *todas* as suas notas e anexos serão "
"re-sincronizados e re-enviados com encriptação. Não perca sua senha, pois, "
"por medidas de segurança, esse será o *único* modo de decriptar seus dados! "
"Para habilitar a encriptação, por favor entre sua senha abaixo."
msgid "Disable encryption"
msgstr ""
msgstr "Desabilitar encriptação"
msgid "Enable encryption"
msgstr ""
msgstr "Habilitar encriptação"
msgid "Master Keys"
msgstr ""
msgstr "Chaves mestras"
msgid "Active"
msgstr ""
msgstr "Ativar"
msgid "ID"
msgstr ""
msgstr "ID"
msgid "Source"
msgstr ""
msgstr "Origem"
#, fuzzy
msgid "Created"
msgstr "Criado: %d."
msgstr "Criado"
#, fuzzy
msgid "Updated"
msgstr "Atualizado: %d."
msgstr "Atualizado"
msgid "Password"
msgstr ""
msgstr "Senha"
msgid "Password OK"
msgstr ""
msgstr "Senha OK"
msgid ""
"Note: Only one master key is going to be used for encryption (the one marked "
"as \"active\"). Any of the keys might be used for decryption, depending on "
"how the notes or notebooks were originally encrypted."
msgstr ""
"Noto: Apenas uma chave máster será usada para encriptação (a que estiver "
"marcada como \"ativa\"). Qualquer uma das chaves pode ser usada para "
"decriptação, dependendo de como as notas ou os cadernos foram encriptados "
"originalmente."
msgid "Missing Master Keys"
msgstr ""
msgstr "Chaves Master faltando"
msgid ""
"The master keys with these IDs are used to encrypt some of your items, "
"however the application does not currently have access to them. It is likely "
"they will eventually be downloaded via synchronisation."
msgstr ""
"As chaves master com essas IDs são usadas para encriptar alguns de seus "
"itens, contudo a aplicação atualmente não tem acesso a elas. Provavelmente, "
"elas serão baixadas via sincronização."
msgid "Status"
msgstr "Status"
msgid "Encryption is:"
msgstr ""
msgstr "Encriptação está:"
msgid "Back"
msgstr "Voltar"
@@ -795,22 +793,23 @@ msgstr "Renomear caderno:"
msgid "Set alarm:"
msgstr "Definir alarme:"
msgid "Search"
msgstr "Procurar"
msgid "Layout"
msgstr "Layout"
#, fuzzy
msgid "Some items cannot be synchronised."
msgstr "Não é possível inicializar o sincronizador."
msgstr "Alguns itens não podem ser sincronizados."
msgid "View them now"
msgstr ""
msgstr "Visualizar agora"
#, fuzzy
msgid "Some items cannot be decrypted."
msgstr "Não é possível inicializar o sincronizador."
msgstr "Alguns itens não podem ser decriptados."
msgid "Set the password"
msgstr ""
msgstr "Configurar a senha"
msgid "Add or remove tags"
msgstr "Adicionar ou remover tags"
@@ -827,10 +826,15 @@ msgstr "Excluir notas?"
msgid "No notes in here. Create one by clicking on \"New note\"."
msgstr "Não há notas aqui. Crie uma, clicando em \"Nova nota\"."
#, fuzzy
msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr "Atualmente, não há notas. Crie uma, clicando no botão (+)."
msgstr "Atualmente, não há cadernos. Crie um, clicando em \"Novo caderno\"."
msgid "Open..."
msgstr "Abrir..."
msgid "Save as..."
msgstr "Salvar como..."
#, javascript-format
msgid "Unsupported link or message: %s"
@@ -839,9 +843,30 @@ msgstr "Link ou mensagem não suportada: %s"
msgid "Attach file"
msgstr "Anexar arquivo"
msgid "Tags"
msgstr "Tags"
msgid "Set alarm"
msgstr "Definir alarme"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
"Esta nota não possui conteúdo. Clique em \"%s\" para alternar para o editor, "
"e edite a nota."
msgid "to-do"
msgstr "tarefa"
msgid "note"
msgstr "nota"
#, javascript-format
msgid "Creating new %s..."
msgstr "Criando novo %s..."
msgid "Refresh"
msgstr "Atualizar"
@@ -851,18 +876,14 @@ msgstr "Limpar (clear)"
msgid "OneDrive Login"
msgstr "Login no OneDrive"
msgid "Import"
msgstr "Importar"
msgid "Options"
msgstr "Opções"
#, fuzzy
msgid "Synchronisation Status"
msgstr "Alvo de sincronização"
msgstr "Status de sincronização"
msgid "Encryption Options"
msgstr ""
msgstr "Opções de Encriptação"
msgid "Remove this tag from all the notes?"
msgstr "Remover esta tag de todas as notas?"
@@ -879,15 +900,13 @@ msgstr "Sincronizar"
msgid "Notebooks"
msgstr "Cadernos"
msgid "Tags"
msgstr "Tags"
msgid "Searches"
msgstr "Pesquisas"
#, fuzzy
msgid "Please select where the sync status should be exported to"
msgstr "Por favor, primeiro, selecione a nota ou caderno a excluir."
msgstr ""
"Favor selecionar o local para onde o status de sincronia deveria ser "
"exportado"
#, javascript-format
msgid "Usage: %s"
@@ -900,8 +919,8 @@ msgstr "Flag desconhecido: %s"
msgid "File system"
msgstr "Sistema de arquivos"
msgid "Nextcloud (Beta)"
msgstr ""
msgid "Nextcloud"
msgstr "Nextcloud"
msgid "OneDrive"
msgstr "OneDrive"
@@ -909,8 +928,8 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (apenas para testes)"
msgid "WebDAV (Beta)"
msgstr ""
msgid "WebDAV"
msgstr "WebDAV"
#, javascript-format
msgid "Unknown log level: %s"
@@ -970,9 +989,9 @@ msgstr "Itens locais excluídos: %d."
msgid "Deleted remote items: %d."
msgstr "Itens remotos excluídos: %d."
#, fuzzy, javascript-format
#, javascript-format
msgid "Fetched items: %d/%d."
msgstr "Itens locais criados: %d."
msgstr "Itens pesquisados: %d/%d."
#, javascript-format
msgid "State: \"%s\"."
@@ -985,16 +1004,25 @@ msgstr "Cancelando..."
msgid "Completed: %s"
msgstr "Completado: %s"
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "Erro fatal:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "Sincronização já em andamento. Estado: %s"
msgid "Encrypted"
msgstr ""
msgstr "Encriptado"
#, fuzzy
msgid "Encrypted items cannot be modified"
msgstr "Não é possível inicializar o sincronizador."
msgstr "Itens encriptados não podem ser modificados"
msgid "Conflicts"
msgstr "Conflitos"
@@ -1016,7 +1044,7 @@ msgstr "Esta nota não possui informações de geolocalização."
#, javascript-format
msgid "Cannot copy note to \"%s\" notebook"
msgstr "Não é possível copiar a nota para o caderno \"%s\" "
msgstr "Não é possível copiar a nota para o caderno \"%s\""
#, javascript-format
msgid "Cannot move note to \"%s\" notebook"
@@ -1050,32 +1078,45 @@ msgstr "Light"
msgid "Dark"
msgstr "Dark"
msgid "Show uncompleted todos on top of the lists"
msgstr "Mostrar tarefas incompletas no topo das listas"
msgid "Uncompleted to-dos on top"
msgstr "Mostrar tarefas incompletas no topo"
msgid "Sort notes by"
msgstr "Ordenar notas por"
msgid "Reverse sort order"
msgstr "Inverter ordem de classificação."
msgid "Save geo-location with notes"
msgstr "Salvar geolocalização com notas"
#, fuzzy
msgid "When creating a new to-do:"
msgstr "Cria uma nova tarefa."
msgstr "Quando criar uma nova tarefa:"
#, fuzzy
msgid "Focus title"
msgstr "Título da nota:"
msgstr "Foco no título"
msgid "Focus body"
msgstr ""
msgstr "Focar no corpo"
#, fuzzy
msgid "When creating a new note:"
msgstr "Cria uma nova nota."
msgstr "Quando criar uma nota nova:"
msgid "Show tray icon"
msgstr ""
msgstr "Exibir tray icon"
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr "Porcentagem global do zoom"
msgid "Editor font family"
msgstr "Família de fontes do editor"
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
"O nomes da fonte não será verificado. Se estiver incorreto ou vazio, será "
"usado por default uma fonte genérica monospace."
msgid "Automatically update the application"
msgstr "Atualizar automaticamente o aplicativo"
@@ -1105,9 +1146,11 @@ msgid ""
"The target to synchonise to. Each sync target may have additional parameters "
"which are named as `sync.NUM.NAME` (all documented below)."
msgstr ""
"O alvo para onde sincronizar. Cada alvo pode ter parâmetros adicionais que "
"são nomeados como `sync.NUM.NAME` (todos documentados abaixo)."
msgid "Directory to synchronise with (absolute path)"
msgstr ""
msgstr "Diretório para sincronizar (caminho absoluto)"
msgid ""
"The path to synchronise with when file system synchronisation is enabled. "
@@ -1116,40 +1159,81 @@ msgstr ""
"O caminho para sincronizar, quando a sincronização do sistema de arquivos "
"está habilitada. Veja `sync.target`."
msgid "Nexcloud WebDAV URL"
msgstr ""
msgid "Nextcloud WebDAV URL"
msgstr "Nextcloud WebDAV URL"
msgid "Nexcloud username"
msgstr ""
msgid "Nextcloud username"
msgstr "Usuário da Nextcloud"
msgid "Nexcloud password"
msgstr ""
msgid "Nextcloud password"
msgstr "Senha da Nextcloud"
msgid "WebDAV URL"
msgstr ""
msgstr "WebDAV URL"
msgid "WebDAV username"
msgstr ""
msgstr "Usuário do WebDAV"
msgid "WebDAV password"
msgstr ""
msgstr "Senha do WebDAV"
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Valor da opção inválida: \"%s\". Os valores possíveis são: %s."
msgid "Items that cannot be synchronised"
msgstr ""
msgid "Joplin Export File"
msgstr "Arquivo de Exportação do Joplin"
#, fuzzy, javascript-format
msgid "Markdown"
msgstr "Markdown"
msgid "Joplin Export Directory"
msgstr "Diretório de Exportação do Joplin"
msgid "Evernote Export File"
msgstr "Arquivo de Exportação do Evernote"
msgid "Directory"
msgstr "DIretório"
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "Não é possível carregar o módulo \"%s\" para o formato \"%s\""
#, javascript-format
msgid "Please specify import format for %s"
msgstr "Favor especificar o formato de importação para %s"
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
"Este item atualmente está encriptado: %s \"%s\". Favor aguardar que todos os "
"itens sejam decriptados e tente novamente."
msgid "There is no data to export."
msgstr "Não há dados a exportar."
msgid "Please specify the notebook where the notes should be imported to."
msgstr ""
"Por favor, especifique o caderno para onde as notas deveriam ser importadas."
msgid "Items that cannot be synchronised"
msgstr "Os itens não podem ser sincronizados"
#, javascript-format
msgid "%s (%s): %s"
msgstr "%s %s (%s)"
msgstr "%s (%s): %s"
msgid ""
"These items will remain on the device but will not be uploaded to the sync "
"target. In order to find these items, either search for the title or the ID "
"(which is displayed in brackets above)."
msgstr ""
"Estes itens continuarão no dispositivo mas não serão enviados ao alvo de "
"sincronização. Para encontrar esses itens, ou pesquise pelo título ou pelo "
"ID (que é exibido nos colchetes acima)"
msgid "Sync status (synced items / total items)"
msgstr "Status de sincronização (sincronizados / totais)"
@@ -1197,7 +1281,7 @@ msgid "Export Debug Report"
msgstr "Exportar Relatório de Debug"
msgid "Encryption Config"
msgstr ""
msgstr "Configuração de Encriptação"
msgid "Configuration"
msgstr "Configuração"
@@ -1210,7 +1294,7 @@ msgid "Move %d notes to notebook \"%s\"?"
msgstr "Mover %d notas para o caderno \"%s\"?"
msgid "Press to set the decryption password."
msgstr ""
msgstr "Pressione para configurar a senha de decriptação."
msgid "Select date"
msgstr "Selecionar data"
@@ -1221,23 +1305,25 @@ msgstr "Confirmar"
msgid "Cancel synchronisation"
msgstr "Cancelar sincronização"
msgid "Joplin website"
msgstr "Site do Joplin"
#, javascript-format
msgid "Master Key %s"
msgstr ""
msgstr "Chave Master %s"
#, fuzzy, javascript-format
#, javascript-format
msgid "Created: %s"
msgstr "Criado: %d."
msgstr "Criado: %s"
msgid "Password:"
msgstr ""
msgstr "Senha:"
msgid "Password cannot be empty"
msgstr ""
msgstr "Senha não pode ser vazia"
#, fuzzy
msgid "Enable"
msgstr "Desabilitado"
msgstr "Habilitar"
#, javascript-format
msgid "The notebook could not be saved: %s"
@@ -1247,10 +1333,10 @@ msgid "Edit notebook"
msgstr "Editar caderno"
msgid "Show all"
msgstr ""
msgstr "Exibir tudo"
msgid "Errors only"
msgstr ""
msgstr "Somente erros"
msgid "This note has been modified:"
msgstr "Esta nota foi modificada:"
@@ -1305,6 +1391,83 @@ msgstr "Você não possui cadernos. Crie um clicando no botão (+)."
msgid "Welcome"
msgstr "Bem-vindo"
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr ""
#~ "Notas da versão:\n"
#~ "\n"
#~ "%s"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Importa um arquivo de caderno do Evernote (arquivo .enex)."
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "O arquivo \"%s\" será importado para o caderno existente \"%s\". "
#~ "Continuar?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "O novo caderno \"%s\" será criado e o arquivo \"%s\" será importado para "
#~ "ele. Continuar?"
#~ msgid "Import Evernote notes"
#~ msgstr "Importar notas do Evernote"
#~ msgid "Give focus to next pane"
#~ msgstr "Dar o foco para o próximo painel"
#~ msgid "Give focus to previous pane"
#~ msgstr "Dar o foco para o painel anterior"
#~ msgid "Enter command line mode"
#~ msgstr "Entrar no modo de linha de comando"
#~ msgid "Exit command line mode"
#~ msgstr "Sair do modo de linha de comando"
#~ msgid "Edit the selected note"
#~ msgstr "Editar nota selecionada"
#~ msgid "Cancel the current command."
#~ msgstr "Cancelar comando atual."
#~ msgid "Exit the application."
#~ msgstr "Sair da aplicação."
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "Excluir nota selecionada ou notebook."
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "Marcar uma tarefa como completada / não completada"
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr ""
#~ "al[t]ernar [c]onsole entre maximizado / minimizado / oculto / visível."
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "al[t]ernar [m]etadados de notas."
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "Criar ([M]ake) uma nova [n]ota"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "Criar ([M]ake) nova [t]arefa"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "Criar ([M]ake) novo caderno (note[b]ook)"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "Copiar ([Y]ank) a [n]ota a um caderno."
#~ msgid "Move the note to a notebook."
#~ msgstr "Mover nota para um caderno."
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -17,63 +17,12 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
msgid "Give focus to next pane"
msgstr "Переключиться на следующую панель"
msgid "Give focus to previous pane"
msgstr "Переключиться на предыдущую панель"
msgid "Enter command line mode"
msgstr "Войти в режим командной строки"
msgid "Exit command line mode"
msgstr "Выйти из режима командной строки"
msgid "Edit the selected note"
msgstr "Редактировать выбранную заметку"
msgid "Cancel the current command."
msgstr "Отменить текущую команду."
msgid "Exit the application."
msgstr "Выйти из приложения."
msgid "Delete the currently selected note or notebook."
msgstr "Удалить текущую выбранную заметку или блокнот."
msgid "To delete a tag, untag the associated notes."
msgstr "Чтобы удалить тег, уберите его с ассоциированных с ним заметок."
msgid "Please select the note or notebook to be deleted first."
msgstr "Сначала выберите заметку или блокнот, которые должны быть удалены."
msgid "Set a to-do as completed / not completed"
msgstr "Отметить задачу как завершённую/незавершённую"
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr "[tc] переключить консоль между развёрнутой/свёрнутой/скрытой/видимой."
msgid "Search"
msgstr "Поиск"
msgid "[t]oggle note [m]etadata."
msgstr "[tm] переключить отображение метаданных заметки."
msgid "[M]ake a new [n]ote"
msgstr "[mn] создать новую заметку"
msgid "[M]ake a new [t]odo"
msgstr "[mt] создать новую задачу"
msgid "[M]ake a new note[b]ook"
msgstr "[mb] создать новый блокнот"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "[yn] копировать заметку в блокнот."
msgid "Move the note to a notebook."
msgstr "Переместить заметку в блокнот."
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "Для выхода из приложения нажмите Ctrl+D или введите «exit»"
@@ -241,12 +190,16 @@ msgid "Exits the application."
msgstr "Выход из приложения."
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"Экспортирует данные Joplin в заданный каталог. По умолчанию экспортируется "
"Экспортирует данные Joplin по заданному пути. По умолчанию экспортируется "
"полная база данных, включая блокноты, заметки, теги и ресурсы."
#, javascript-format
msgid "Destination format: %s"
msgstr "Целевой формат: %s"
msgid "Exports only the given note."
msgstr "Экспортирует только заданную заметку."
@@ -259,6 +212,10 @@ msgstr "Выводит URL геолокации для заметки."
msgid "Displays usage information."
msgstr "Выводит информацию об использовании."
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr "Информацию по настройке сочетаний можно получить, посетив %s"
msgid "Shortcuts are not available in CLI mode."
msgstr "Ярлыки недоступны в режиме командной строки."
@@ -302,29 +259,21 @@ msgid "To exit command line mode, press ESCAPE"
msgstr "Чтобы выйти из режима командной строки, нажмите ESCAPE"
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr ""
"Для просмотра списка доступных клавиатурных сочетаний введите `help "
"shortcuts`"
"Для просмотра списка клавиатурных сочетаний и настроек конфигурации введите "
"`help keymap`"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "Импортирует файл блокнотов Evernote (.enex-файл)."
msgid "Imports data into Joplin."
msgstr "Импортирует данные в Joplin."
#, javascript-format
msgid "Source format: %s"
msgstr "Исходный формат: %s"
msgid "Do not ask for confirmation."
msgstr "Не запрашивать подтверждение."
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr "Файл «%s» будет импортирован в существующий блокнот «%s». Продолжить?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr ""
"Будет создан новый блокнот «%s» и в него будет импортирован файл «%s». "
"Продолжить?"
#, javascript-format
msgid "Found: %d."
msgstr "Найдено: %d."
@@ -556,6 +505,9 @@ msgstr "По умолчанию: %s"
msgid "Possible keys/values:"
msgstr "Возможные ключи/значения:"
msgid "Type `joplin help` for usage information."
msgstr "Введите `joplin help` для получения информации об использовании."
msgid "Fatal error:"
msgstr "Фатальная ошибка:"
@@ -609,6 +561,17 @@ msgstr ""
"decrypt». Если пароль уже был вами предоставлен, зашифрованные элементы "
"расшифруются в фоновом режиме и вскоре станут доступны."
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr "Экспорт в «%s» в формате «%s». Пожалуйста, ожидайте..."
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr "Импорт из «%s» в формате «%s». Пожалуйста, ожидайте..."
msgid "PDF File"
msgstr "PDF-файл"
msgid "File"
msgstr "Файл"
@@ -621,11 +584,18 @@ msgstr "Новая задача"
msgid "New notebook"
msgstr "Новый блокнот"
msgid "Import Evernote notes"
msgstr "Импортировать заметки из Evernote"
msgid "Import"
msgstr "Импорт"
msgid "Evernote Export Files"
msgstr "Файлы экспорта Evernote"
msgid "Export"
msgstr "Экспорт"
msgid "Print"
msgstr "Печать"
#, javascript-format
msgid "Hide %s"
msgstr "Скрыть %s"
msgid "Quit"
msgstr "Выход"
@@ -645,6 +615,12 @@ msgstr "Вставить"
msgid "Search in all the notes"
msgstr "Поиск во всех заметках"
msgid "View"
msgstr "Вид"
msgid "Toggle editor layout"
msgstr "Переключить вид редактора"
msgid "Tools"
msgstr "Инструменты"
@@ -663,6 +639,9 @@ msgstr "Помощь"
msgid "Website and documentation"
msgstr "Сайт и документация"
msgid "Make a donation"
msgstr "Сделать пожертвование"
msgid "Check for updates..."
msgstr "Проверить обновления..."
@@ -673,12 +652,12 @@ msgstr "О Joplin"
msgid "%s %s (%s, %s)"
msgstr "%s %s (%s, %s)"
#, fuzzy, javascript-format
#, javascript-format
msgid "Open %s"
msgstr "В %s: %s"
msgstr "Открыть %s"
msgid "Exit"
msgstr ""
msgstr "Выход"
msgid "OK"
msgstr "OK"
@@ -686,33 +665,20 @@ msgstr "OK"
msgid "Cancel"
msgstr "Отмена"
msgid "Error"
msgstr "Ошибка"
msgid "An update is available, do you want to update now?"
msgstr "Доступно обновление. Обновить сейчас?"
msgid "Yes"
msgstr ""
#, fuzzy
msgid "No"
msgstr "N"
#, javascript-format
msgid "Could not download the update: %s"
msgstr "Не удалось загрузить обновление: %s"
msgid "Current version is up-to-date."
msgstr "Вы используете самую свежую версию."
msgid "New version downloaded - application will quit now and update..."
msgstr ""
"Новая версия загружена — приложение сейчас будет закрыто и обновлено..."
msgid "An update is available, do you want to download it now?"
msgstr "Доступно обновление. Желаете скачать его сейчас?"
#, javascript-format
msgid "Could not install the update: %s"
msgstr "Не удалось установить обновление: %s"
msgid "Yes"
msgstr "Да"
msgid "No"
msgstr "Нет"
msgid "Check synchronisation configuration"
msgstr "Проверить настройки синхронизации"
#, javascript-format
msgid "Notes and settings are stored in: %s"
@@ -782,15 +748,17 @@ msgstr ""
"ключей, в зависимости от того, как изначально были зашифрованы заметки или "
"блокноты."
#, fuzzy
msgid "Missing Master Keys"
msgstr "Мастер-ключи"
msgstr "Недостающие мастер-ключи"
msgid ""
"The master keys with these IDs are used to encrypt some of your items, "
"however the application does not currently have access to them. It is likely "
"they will eventually be downloaded via synchronisation."
msgstr ""
"Мастер-ключи с такими ID используются для шифрования некоторых из ваших "
"элементов, однако у приложения сейчас нет к ним доступа. Скорее всего, они "
"загрузятся при синхронизации."
msgid "Status"
msgstr "Статус"
@@ -827,6 +795,9 @@ msgstr "Переименовать блокнот:"
msgid "Set alarm:"
msgstr "Установить напоминание:"
msgid "Search"
msgstr "Поиск"
msgid "Layout"
msgstr "Вид"
@@ -861,6 +832,12 @@ msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr "Сейчас здесь нет блокнотов. Создайте новый нажав «Новый блокнот»."
msgid "Open..."
msgstr "Открыть..."
msgid "Save as..."
msgstr "Сохранить как..."
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Неподдерживаемая ссыка или сообщение: %s"
@@ -868,9 +845,30 @@ msgstr "Неподдерживаемая ссыка или сообщение: %
msgid "Attach file"
msgstr "Прикрепить файл"
msgid "Tags"
msgstr "Теги"
msgid "Set alarm"
msgstr "Установить напоминание"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
"Заметка пуста. Нажмите на «%s», чтобы переключиться в редактор и "
"отредактировать её."
msgid "to-do"
msgstr "задача"
msgid "note"
msgstr "заметка"
#, javascript-format
msgid "Creating new %s..."
msgstr "Создание новой %s..."
msgid "Refresh"
msgstr "Обновить"
@@ -880,9 +878,6 @@ msgstr "Очистить"
msgid "OneDrive Login"
msgstr "Вход в OneDrive"
msgid "Import"
msgstr "Импорт"
msgid "Options"
msgstr "Настройки"
@@ -907,9 +902,6 @@ msgstr "Синхронизировать"
msgid "Notebooks"
msgstr "Блокноты"
msgid "Tags"
msgstr "Теги"
msgid "Searches"
msgstr "Запросы"
@@ -927,8 +919,8 @@ msgstr "Неизвестный флаг: %s"
msgid "File system"
msgstr "Файловая система"
msgid "Nextcloud (Beta)"
msgstr "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr "Nextcloud"
msgid "OneDrive"
msgstr "OneDrive"
@@ -936,8 +928,8 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive Dev (только для тестирования)"
msgid "WebDAV (Beta)"
msgstr ""
msgid "WebDAV"
msgstr "WebDAV"
#, javascript-format
msgid "Unknown log level: %s"
@@ -1012,6 +1004,16 @@ msgstr "Отмена..."
msgid "Completed: %s"
msgstr "Завершено: %s"
#, javascript-format
msgid "Last error: %s"
msgstr "Последняя ошибка: %s"
msgid "Idle"
msgstr "Простой"
msgid "In progress"
msgstr "Выполнение"
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "Синхронизация уже выполняется. Статус: %s"
@@ -1075,8 +1077,14 @@ msgstr "Светлая"
msgid "Dark"
msgstr "Тёмная"
msgid "Show uncompleted todos on top of the lists"
msgstr "Показывать незавершённые задачи вверху списков"
msgid "Uncompleted to-dos on top"
msgstr "Незавершённые задачи сверху"
msgid "Sort notes by"
msgstr "Сортировать заметки по"
msgid "Reverse sort order"
msgstr "Обратный порядок сортировки"
msgid "Save geo-location with notes"
msgstr "Сохранять информацию о геолокации в заметках"
@@ -1094,10 +1102,20 @@ msgid "When creating a new note:"
msgstr "При создании новой заметки:"
msgid "Show tray icon"
msgstr ""
msgstr "Показывать иконку в панели задач"
msgid "Set application zoom percentage"
msgstr "Масштаб приложения в процентах"
msgid "Global zoom percentage"
msgstr "Глобальный масштаб в процентах"
msgid "Editor font family"
msgstr "Семейство шрифтов редактора"
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
"Название шрифта не проверяется. Если оно указано некорректно или не задано, "
"будет использоваться стандартный моноширинный шрифт."
msgid "Automatically update the application"
msgstr "Автоматически обновлять приложение"
@@ -1140,31 +1158,66 @@ msgstr ""
"Путь для синхронизации при включённой синхронизации с файловой системой. См. "
"`sync.target`."
msgid "Nexcloud WebDAV URL"
msgstr "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr "Nextcloud WebDAV URL"
msgid "Nexcloud username"
msgstr "Имя пользователя Nexcloud"
msgid "Nextcloud username"
msgstr "Имя пользователя Nextcloud"
msgid "Nexcloud password"
msgstr "Пароль Nexcloud"
msgid "Nextcloud password"
msgstr "Пароль Nextcloud"
#, fuzzy
msgid "WebDAV URL"
msgstr "Nexcloud WebDAV URL"
msgstr "URL WebDAV"
#, fuzzy
msgid "WebDAV username"
msgstr "Имя пользователя Nexcloud"
msgstr "Имя пользователя WebDAV"
#, fuzzy
msgid "WebDAV password"
msgstr "Установить пароль"
msgstr "Пароль WebDAV"
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Неверное значение параметра: «%s». Доступные значения: %s."
msgid "Joplin Export File"
msgstr "Файл экспорта Joplin"
msgid "Markdown"
msgstr "Markdown"
msgid "Joplin Export Directory"
msgstr "Папка экспорта Joplin"
msgid "Evernote Export File"
msgstr "Файл экспорта Evernote"
msgid "Directory"
msgstr "Директория"
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr "Не удалось загрузить модуль «%s» для формата «%s»"
#, javascript-format
msgid "Please specify import format for %s"
msgstr "Пожалуйста, укажите формат импорта для %s"
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
"Этот элемент сейчас зашифрован: %s «%s». Пожалуйста, дождитесь расшифровки "
"всех элементов и попробуйте снова."
msgid "There is no data to export."
msgstr "Нет данных для экспорта."
msgid "Please specify the notebook where the notes should be imported to."
msgstr ""
"Пожалуйста, укажите блокнот, в который должны быть импортированы заметки."
msgid "Items that cannot be synchronised"
msgstr "Элементы, которые не могут быть синхронизированы"
@@ -1251,6 +1304,9 @@ msgstr "Подтвердить"
msgid "Cancel synchronisation"
msgstr "Отменить синхронизацию"
msgid "Joplin website"
msgstr "Сайт Joplin"
#, javascript-format
msgid "Master Key %s"
msgstr "Мастер-ключ %s"
@@ -1276,11 +1332,10 @@ msgid "Edit notebook"
msgstr "Редактировать блокнот"
msgid "Show all"
msgstr ""
msgstr "Показать всё"
#, fuzzy
msgid "Errors only"
msgstr "Ошибка"
msgstr "Только ошибки"
msgid "This note has been modified:"
msgstr "Эта заметка была изменена:"
@@ -1335,6 +1390,93 @@ msgstr "У вас сейчас нет блокнота. Создайте его
msgid "Welcome"
msgstr "Добро пожаловать"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "Удалить заметки?"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "Импортирует файл блокнотов Evernote (.enex-файл)."
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr ""
#~ "Файл «%s» будет импортирован в существующий блокнот «%s». Продолжить?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr ""
#~ "Будет создан новый блокнот «%s» и в него будет импортирован файл «%s». "
#~ "Продолжить?"
#~ msgid "Import Evernote notes"
#~ msgstr "Импортировать заметки из Evernote"
#~ msgid "Give focus to next pane"
#~ msgstr "Переключиться на следующую панель"
#~ msgid "Give focus to previous pane"
#~ msgstr "Переключиться на предыдущую панель"
#~ msgid "Enter command line mode"
#~ msgstr "Войти в режим командной строки"
#~ msgid "Exit command line mode"
#~ msgstr "Выйти из режима командной строки"
#~ msgid "Edit the selected note"
#~ msgstr "Редактировать выбранную заметку"
#~ msgid "Cancel the current command."
#~ msgstr "Отменить текущую команду."
#~ msgid "Exit the application."
#~ msgstr "Выйти из приложения."
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "Удалить текущую выбранную заметку или блокнот."
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "Отметить задачу как завершённую/незавершённую"
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr ""
#~ "[tc] переключить консоль между развёрнутой/свёрнутой/скрытой/видимой."
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "[tm] переключить отображение метаданных заметки."
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "[mn] создать новую заметку"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "[mt] создать новую задачу"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "[mb] создать новый блокнот"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "[yn] копировать заметку в блокнот."
#~ msgid "Move the note to a notebook."
#~ msgstr "Переместить заметку в блокнот."
#~ msgid "Error"
#~ msgstr "Ошибка"
#~ msgid "Could not download the update: %s"
#~ msgstr "Не удалось загрузить обновление: %s"
#~ msgid "New version downloaded - application will quit now and update..."
#~ msgstr ""
#~ "Новая версия загружена — приложение сейчас будет закрыто и обновлено..."
#~ msgid "Could not install the update: %s"
#~ msgstr "Не удалось установить обновление: %s"
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -15,63 +15,12 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Give focus to next pane"
msgstr "聚焦于下个面板"
msgid "Give focus to previous pane"
msgstr "聚焦于上个面板"
msgid "Enter command line mode"
msgstr "进入命令行模式"
msgid "Exit command line mode"
msgstr "退出命令行模式"
msgid "Edit the selected note"
msgstr "编辑所选笔记"
msgid "Cancel the current command."
msgstr "取消当前命令。"
msgid "Exit the application."
msgstr "退出程序。"
msgid "Delete the currently selected note or notebook."
msgstr "删除当前所选笔记或笔记本。"
msgid "To delete a tag, untag the associated notes."
msgstr "移除相关笔记的标签后才可删除此标签。"
msgid "Please select the note or notebook to be deleted first."
msgstr "请选择最先删除的笔记或笔记本。"
msgid "Set a to-do as completed / not completed"
msgstr "设置待办事项为已完成或未完成"
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
msgstr "在最大化/最小化/隐藏/显示间切换[t]控制台[c]。"
msgid "Search"
msgstr "搜索"
msgid "[t]oggle note [m]etadata."
msgstr "切换[t]笔记元数据[m]。"
msgid "[M]ake a new [n]ote"
msgstr "创建[M]新笔记[n]"
msgid "[M]ake a new [t]odo"
msgstr "创建[M]新待办事项[t]"
msgid "[M]ake a new note[b]ook"
msgstr "创建[M]新笔记本[b]"
msgid "Copy ([Y]ank) the [n]ote to a notebook."
msgstr "复制[Y]笔记[n]至笔记本。"
msgid "Move the note to a notebook."
msgstr "移动笔记至笔记本。"
msgid "Press Ctrl+D or type \"exit\" to exit the application"
msgstr "按Ctrl+D或输入\"exit\"退出程序"
@@ -228,13 +177,18 @@ msgstr "笔记已被保存。"
msgid "Exits the application."
msgstr "退出程序。"
#, fuzzy
msgid ""
"Exports Joplin data to the given directory. By default, it will export the "
"Exports Joplin data to the given path. By default, it will export the "
"complete database including notebooks, notes, tags and resources."
msgstr ""
"导出Joplin数据至给定文件目录。默认为导出所有的数据库,包含笔记本、笔记、标签"
"及资源。"
#, fuzzy, javascript-format
msgid "Destination format: %s"
msgstr "日期格式"
msgid "Exports only the given note."
msgstr "仅导出给定笔记。"
@@ -247,6 +201,10 @@ msgstr "显示此笔记的地理定位URL地址。"
msgid "Displays usage information."
msgstr "显示使用信息。"
#, javascript-format
msgid "For information on how to customise the shortcuts please visit %s"
msgstr ""
msgid "Shortcuts are not available in CLI mode."
msgstr "快捷键在CLI模式下不可用。"
@@ -284,26 +242,21 @@ msgstr "按\":\"键进入命令行模式"
msgid "To exit command line mode, press ESCAPE"
msgstr "按ESC键退出命令行模式"
#, fuzzy
msgid ""
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
"For the list of keyboard shortcuts and config options, type `help keymap`"
msgstr "输入`help shortcuts`显示全部可用的快捷键列表。"
msgid "Imports an Evernote notebook file (.enex file)."
msgstr "导入Evernote笔记本文件(.enex文件)。"
msgid "Imports data into Joplin."
msgstr ""
#, fuzzy, javascript-format
msgid "Source format: %s"
msgstr "无以下命令:%s"
msgid "Do not ask for confirmation."
msgstr "不再要求确认。"
#, javascript-format
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
msgstr "文件\"%s\"将会被导入至现有笔记本\"%s\"。是否继续?"
#, javascript-format
msgid ""
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
"it. Continue?"
msgstr "将创建新笔记本\"%s\"并将文件\"%s\"导入至其中。是否继续?"
#, javascript-format
msgid "Found: %d."
msgstr "已找到:%d条。"
@@ -518,6 +471,10 @@ msgstr "默认值: %s"
msgid "Possible keys/values:"
msgstr "可用键/值:"
#, fuzzy
msgid "Type `joplin help` for usage information."
msgstr "显示使用信息。"
msgid "Fatal error:"
msgstr "严重错误:"
@@ -558,6 +515,18 @@ msgid ""
"background and will be available soon."
msgstr ""
#, javascript-format
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, javascript-format
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
msgstr ""
#, fuzzy
msgid "PDF File"
msgstr "文件"
msgid "File"
msgstr "文件"
@@ -570,11 +539,19 @@ msgstr "新待办事项"
msgid "New notebook"
msgstr "新笔记本"
msgid "Import Evernote notes"
msgstr "导入Evernote笔记"
msgid "Import"
msgstr "导入"
msgid "Evernote Export Files"
msgstr "Evernote导出文件"
#, fuzzy
msgid "Export"
msgstr "导入"
msgid "Print"
msgstr ""
#, javascript-format
msgid "Hide %s"
msgstr ""
msgid "Quit"
msgstr "退出"
@@ -594,6 +571,12 @@ msgstr "粘贴"
msgid "Search in all the notes"
msgstr "在所有笔记内搜索"
msgid "View"
msgstr ""
msgid "Toggle editor layout"
msgstr ""
msgid "Tools"
msgstr "工具"
@@ -613,6 +596,10 @@ msgstr "帮助"
msgid "Website and documentation"
msgstr "网站与文档"
#, fuzzy
msgid "Make a donation"
msgstr "网站与文档"
msgid "Check for updates..."
msgstr ""
@@ -636,10 +623,10 @@ msgstr "确认"
msgid "Cancel"
msgstr "取消"
msgid "Error"
msgid "Current version is up-to-date."
msgstr ""
msgid "An update is available, do you want to update now?"
msgid "An update is available, do you want to download it now?"
msgstr ""
msgid "Yes"
@@ -649,19 +636,9 @@ msgstr ""
msgid "No"
msgstr "否"
#, javascript-format
msgid "Could not download the update: %s"
msgstr ""
msgid "Current version is up-to-date."
msgstr ""
msgid "New version downloaded - application will quit now and update..."
msgstr ""
#, javascript-format
msgid "Could not install the update: %s"
msgstr ""
#, fuzzy
msgid "Check synchronisation configuration"
msgstr "取消同步"
#, javascript-format
msgid "Notes and settings are stored in: %s"
@@ -765,6 +742,9 @@ msgstr "重命名笔记本:"
msgid "Set alarm:"
msgstr "设置提醒:"
msgid "Search"
msgstr "搜索"
msgid "Layout"
msgstr "布局"
@@ -801,6 +781,13 @@ msgid ""
"There is currently no notebook. Create one by clicking on \"New notebook\"."
msgstr "当前无笔记。点击(+)创建新笔记。"
msgid "Open..."
msgstr ""
#, fuzzy
msgid "Save as..."
msgstr "保存更改"
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "不支持的链接或信息:%s"
@@ -808,9 +795,30 @@ msgstr "不支持的链接或信息:%s"
msgid "Attach file"
msgstr "附加文件"
msgid "Tags"
msgstr "标签"
msgid "Set alarm"
msgstr "设置提醒"
#, javascript-format
msgid ""
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
"note."
msgstr ""
#, fuzzy
msgid "to-do"
msgstr "新待办事项"
#, fuzzy
msgid "note"
msgstr "新笔记"
#, fuzzy, javascript-format
msgid "Creating new %s..."
msgstr "正在导入笔记..."
msgid "Refresh"
msgstr "刷新"
@@ -820,9 +828,6 @@ msgstr "清除"
msgid "OneDrive Login"
msgstr "登陆OneDrive"
msgid "Import"
msgstr "导入"
msgid "Options"
msgstr "选项"
@@ -847,9 +852,6 @@ msgstr "同步"
msgid "Notebooks"
msgstr "笔记本"
msgid "Tags"
msgstr "标签"
msgid "Searches"
msgstr "搜索历史"
@@ -868,7 +870,7 @@ msgstr "未知标记:%s"
msgid "File system"
msgstr "文件系统"
msgid "Nextcloud (Beta)"
msgid "Nextcloud"
msgstr ""
msgid "OneDrive"
@@ -877,7 +879,7 @@ msgstr "OneDrive"
msgid "OneDrive Dev (For testing only)"
msgstr "OneDrive开发员(仅测试用)"
msgid "WebDAV (Beta)"
msgid "WebDAV"
msgstr ""
#, javascript-format
@@ -950,6 +952,16 @@ msgstr "正在取消..."
msgid "Completed: %s"
msgstr "已完成:\"%s\""
#, fuzzy, javascript-format
msgid "Last error: %s"
msgstr "严重错误:"
msgid "Idle"
msgstr ""
msgid "In progress"
msgstr ""
#, javascript-format
msgid "Synchronisation is already in progress. State: %s"
msgstr "同步正在进行中。状态:%s"
@@ -1012,9 +1024,17 @@ msgstr "浅色"
msgid "Dark"
msgstr "深色"
msgid "Show uncompleted todos on top of the lists"
#, fuzzy
msgid "Uncompleted to-dos on top"
msgstr "在列表上方显示未完成的待办事项"
msgid "Sort notes by"
msgstr ""
#, fuzzy
msgid "Reverse sort order"
msgstr "反转排序顺序。"
msgid "Save geo-location with notes"
msgstr "保存笔记时同时保存地理定位信息"
@@ -1036,7 +1056,15 @@ msgstr "创建新笔记。"
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr ""
msgid "Editor font family"
msgstr ""
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
msgid "Automatically update the application"
@@ -1076,13 +1104,13 @@ msgid ""
"See `sync.target`."
msgstr "当文件系统同步开启时的同步路径。参考`sync.target`。"
msgid "Nexcloud WebDAV URL"
msgid "Nextcloud WebDAV URL"
msgstr ""
msgid "Nexcloud username"
msgid "Nextcloud username"
msgstr ""
msgid "Nexcloud password"
msgid "Nextcloud password"
msgstr ""
msgid "WebDAV URL"
@@ -1098,6 +1126,44 @@ msgstr ""
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "无效的选项值:\"%s\"。可用值为:%s。"
#, fuzzy
msgid "Joplin Export File"
msgstr "Evernote导出文件"
msgid "Markdown"
msgstr ""
msgid "Joplin Export Directory"
msgstr ""
#, fuzzy
msgid "Evernote Export File"
msgstr "Evernote导出文件"
msgid "Directory"
msgstr ""
#, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\""
msgstr ""
#, javascript-format
msgid "Please specify import format for %s"
msgstr ""
#, javascript-format
msgid ""
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
"decrypted and try again."
msgstr ""
msgid "There is no data to export."
msgstr ""
#, fuzzy
msgid "Please specify the notebook where the notes should be imported to."
msgstr "请选择最先删除的笔记或笔记本。"
msgid "Items that cannot be synchronised"
msgstr "项目无法被同步。"
@@ -1181,6 +1247,9 @@ msgstr "确认"
msgid "Cancel synchronisation"
msgstr "取消同步"
msgid "Joplin website"
msgstr ""
#, javascript-format
msgid "Master Key %s"
msgstr ""
@@ -1263,6 +1332,76 @@ msgstr "您当前没有任何笔记本。点击(+)按钮创建新笔记本。"
msgid "Welcome"
msgstr "欢迎"
#, fuzzy
#~ msgid ""
#~ "Release notes:\n"
#~ "\n"
#~ "%s"
#~ msgstr "是否删除笔记?"
#~ msgid "Imports an Evernote notebook file (.enex file)."
#~ msgstr "导入Evernote笔记本文件(.enex文件)。"
#~ msgid ""
#~ "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
#~ msgstr "文件\"%s\"将会被导入至现有笔记本\"%s\"。是否继续?"
#~ msgid ""
#~ "New notebook \"%s\" will be created and file \"%s\" will be imported into "
#~ "it. Continue?"
#~ msgstr "将创建新笔记本\"%s\"并将文件\"%s\"导入至其中。是否继续?"
#~ msgid "Import Evernote notes"
#~ msgstr "导入Evernote笔记"
#~ msgid "Give focus to next pane"
#~ msgstr "聚焦于下个面板"
#~ msgid "Give focus to previous pane"
#~ msgstr "聚焦于上个面板"
#~ msgid "Enter command line mode"
#~ msgstr "进入命令行模式"
#~ msgid "Exit command line mode"
#~ msgstr "退出命令行模式"
#~ msgid "Edit the selected note"
#~ msgstr "编辑所选笔记"
#~ msgid "Cancel the current command."
#~ msgstr "取消当前命令。"
#~ msgid "Exit the application."
#~ msgstr "退出程序。"
#~ msgid "Delete the currently selected note or notebook."
#~ msgstr "删除当前所选笔记或笔记本。"
#~ msgid "Set a to-do as completed / not completed"
#~ msgstr "设置待办事项为已完成或未完成"
#~ msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
#~ msgstr "在最大化/最小化/隐藏/显示间切换[t]控制台[c]。"
#~ msgid "[t]oggle note [m]etadata."
#~ msgstr "切换[t]笔记元数据[m]。"
#~ msgid "[M]ake a new [n]ote"
#~ msgstr "创建[M]新笔记[n]"
#~ msgid "[M]ake a new [t]odo"
#~ msgstr "创建[M]新待办事项[t]"
#~ msgid "[M]ake a new note[b]ook"
#~ msgstr "创建[M]新笔记本[b]"
#~ msgid "Copy ([Y]ank) the [n]ote to a notebook."
#~ msgstr "复制[Y]笔记[n]至笔记本。"
#~ msgid "Move the note to a notebook."
#~ msgstr "移动笔记至笔记本。"
#~ msgid ""
#~ "The target to synchonise to. If synchronising with the file system, set "
#~ "`sync.2.path` to specify the target directory."

View File

@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "0.10.92",
"version": "1.0.100",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -64,6 +64,11 @@
}
}
},
"async-mutex": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.1.3.tgz",
"integrity": "sha1-Cq0hEjaXlas/F+M3RFVtLs9UdWY="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -430,6 +435,14 @@
"universalify": "0.1.1"
}
},
"fs-minipass": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"requires": {
"minipass": "2.2.1"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -853,9 +866,9 @@
}
},
"minizlib": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.4.tgz",
"integrity": "sha512-sN4U9tIJtBRwKbwgFh9qJfrPIQ/GGTRr1MGqkgOeMTLy8/lM0FcWU//FqlnZ3Vb7gJ+Mxh3FOg1EklibdajbaQ==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz",
"integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
"requires": {
"minipass": "2.2.1"
}
@@ -1156,6 +1169,20 @@
"semver": "5.4.1",
"simple-get": "2.7.0",
"tar": "3.2.1"
},
"dependencies": {
"tar": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz",
"integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==",
"requires": {
"chownr": "1.0.1",
"minipass": "2.2.1",
"minizlib": "1.1.0",
"mkdirp": "0.5.1",
"yallist": "3.0.2"
}
}
}
},
"simple-concat": {
@@ -2000,13 +2027,14 @@
"integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0="
},
"tar": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz",
"integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==",
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.0.tgz",
"integrity": "sha512-gJlTiiErwo96K904FnoYWl+5+FBgS+FimU6GMh66XLdLa55al8+d4jeDfPoGwSNHdtWI5FJP6xurmVqhBuGJpQ==",
"requires": {
"chownr": "1.0.1",
"fs-minipass": "1.2.5",
"minipass": "2.2.1",
"minizlib": "1.0.4",
"minizlib": "1.1.0",
"mkdirp": "0.5.1",
"yallist": "3.0.2"
}
@@ -2052,9 +2080,9 @@
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"tkwidgets": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/tkwidgets/-/tkwidgets-0.5.21.tgz",
"integrity": "sha512-gJfpYq3UM6AZ23ZM+D9BZ1PhsJLLHgjCOf487/lS9pO0uDdnkMcVXkkKEfRl00EyjPnGc88QZhEkVOvrtKsuPA==",
"version": "0.5.25",
"resolved": "https://registry.npmjs.org/tkwidgets/-/tkwidgets-0.5.25.tgz",
"integrity": "sha512-f+12QbxNCLg9Jou5JoPJxATGLmzpDAQeM7QRTXvuqdEB/QvPD9+UlPUL7eYJP1QJv2zzT6EIWWbdpDkXPEtzCQ==",
"requires": {
"chalk": "2.3.0",
"emphasize": "1.5.0",

View File

@@ -19,7 +19,7 @@
],
"owner": "Laurent Cozic"
},
"version": "0.10.92",
"version": "1.0.100",
"bin": {
"joplin": "./main.js"
},
@@ -28,6 +28,7 @@
},
"dependencies": {
"app-module-path": "^2.2.0",
"async-mutex": "^0.1.3",
"base-64": "^0.1.0",
"compare-version": "^0.1.2",
"follow-redirects": "^1.2.4",
@@ -43,7 +44,6 @@
"node-emoji": "^1.8.1",
"node-fetch": "^1.7.1",
"node-persist": "^2.1.0",
"os-tmpdir": "^1.0.2",
"promise": "^7.1.1",
"proper-lockfile": "^2.0.1",
"query-string": "4.3.4",
@@ -56,8 +56,9 @@
"string-padding": "^1.0.2",
"string-to-stream": "^1.1.0",
"strip-ansi": "^4.0.0",
"tar": "^4.4.0",
"tcp-port-used": "^0.1.2",
"tkwidgets": "^0.5.21",
"tkwidgets": "^0.5.25",
"url-parse": "^1.2.0",
"uuid": "^3.0.1",
"word-wrap": "^1.2.3",

View File

@@ -9,9 +9,7 @@ rsync -a "$ROOT_DIR/build/locales/" "$BUILD_DIR/locales/"
mkdir -p "$BUILD_DIR/data"
if [[ $TEST_FILE == "" ]]; then
(cd "$ROOT_DIR" && npm test tests-build/synchronizer.js)
(cd "$ROOT_DIR" && npm test tests-build/encryption.js)
(cd "$ROOT_DIR" && npm test tests-build/ArrayUtils.js)
(cd "$ROOT_DIR" && npm test tests-build/synchronizer.js tests-build/encryption.js tests-build/ArrayUtils.js tests-build/models_Setting.js tests-build/services_InteropService.js)
else
(cd "$ROOT_DIR" && npm test tests-build/$TEST_FILE.js)
fi

View File

@@ -0,0 +1,32 @@
require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const Setting = require('lib/models/Setting.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('models_Setting', function() {
beforeEach(async (done) => {
done();
});
it('should return only sub-values', asyncTest(async () => {
const settings = {
'sync.5.path': 'http://example.com',
'sync.5.username': 'testing',
}
let output = Setting.subValues('sync.5', settings);
expect(output['path']).toBe('http://example.com');
expect(output['username']).toBe('testing');
output = Setting.subValues('sync.4', settings);
expect('path' in output).toBe(false);
expect('username' in output).toBe(false);
}));
});

View File

@@ -0,0 +1,252 @@
require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const InteropService = require('lib/services/InteropService.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const NoteTag = require('lib/models/NoteTag.js');
const Resource = require('lib/models/Resource.js');
const fs = require('fs-extra');
const ArrayUtils = require('lib/ArrayUtils');
const ObjectUtils = require('lib/ObjectUtils');
const { shim } = require('lib/shim.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
function exportDir() {
return __dirname + '/export';
}
function fieldsEqual(model1, model2, fieldNames) {
for (let i = 0; i < fieldNames.length; i++) {
const f = fieldNames[i];
expect(model1[f]).toBe(model2[f], 'For key ' + f);
}
}
describe('services_InteropService', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
const dir = exportDir();
await fs.remove(dir);
await fs.mkdirp(dir);
done();
});
it('should export and import folders', asyncTest(async () => {
const service = new InteropService();
let folder1 = await Folder.save({ title: "folder1" });
folder1 = await Folder.load(folder1.id);
const filePath = exportDir() + '/test.jex';
await service.export({ path: filePath });
await Folder.delete(folder1.id);
await service.import({ path: filePath });
// Check that a new folder, with a new ID, has been created
expect(await Folder.count()).toBe(1);
let folder2 = (await Folder.all())[0];
expect(folder2.id).not.toBe(folder1.id);
expect(folder2.title).toBe(folder1.title);
await service.import({ path: filePath });
// As there was already a folder with the same title, check that the new one has been renamed
await Folder.delete(folder2.id);
let folder3 = (await Folder.all())[0];
expect(await Folder.count()).toBe(1);
expect(folder3.title).not.toBe(folder2.title);
let fieldNames = Folder.fieldNames();
fieldNames = ArrayUtils.removeElement(fieldNames, 'id');
fieldNames = ArrayUtils.removeElement(fieldNames, 'title');
fieldsEqual(folder3, folder1, fieldNames);
}));
it('should export and import folders and notes', asyncTest(async () => {
const service = new InteropService();
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await Note.load(note1.id);
const filePath = exportDir() + '/test.jex';
await service.export({ path: filePath });
await Folder.delete(folder1.id);
await Note.delete(note1.id);
await service.import({ path: filePath });
expect(await Note.count()).toBe(1);
let note2 = (await Note.all())[0];
let folder2 = (await Folder.all())[0];
expect(note1.parent_id).not.toBe(note2.parent_id);
expect(note1.id).not.toBe(note2.id);
expect(note2.parent_id).toBe(folder2.id);
let fieldNames = Note.fieldNames();
fieldNames = ArrayUtils.removeElement(fieldNames, 'id');
fieldNames = ArrayUtils.removeElement(fieldNames, 'parent_id');
fieldsEqual(note1, note2, fieldNames);
await service.import({ path: filePath });
note2 = (await Note.all())[0];
let note3 = (await Note.all())[1];
expect(note2.id).not.toBe(note3.id);
expect(note2.parent_id).not.toBe(note3.parent_id);
fieldsEqual(note2, note3, fieldNames);
}));
it('should export and import notes to specific folder', asyncTest(async () => {
const service = new InteropService();
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await Note.load(note1.id);
const filePath = exportDir() + '/test.jex';
await service.export({ path: filePath });
await Note.delete(note1.id);
await service.import({ path: filePath, destinationFolderId: folder1.id });
expect(await Note.count()).toBe(1);
expect(await Folder.count()).toBe(1);
expect(await checkThrowAsync(async () => await service.import({ path: filePath, destinationFolderId: 'oops' }))).toBe(true);
}));
it('should export and import tags', asyncTest(async () => {
const service = new InteropService();
const filePath = exportDir() + '/test.jex';
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
let tag1 = await Tag.save({ title: 'mon tag' });
tag1 = await Tag.load(tag1.id);
await Tag.addNote(tag1.id, note1.id);
await service.export({ path: filePath });
await Folder.delete(folder1.id);
await Note.delete(note1.id);
await Tag.delete(tag1.id);
await service.import({ path: filePath });
expect(await Tag.count()).toBe(1);
let tag2 = (await Tag.all())[0];
let note2 = (await Note.all())[0];
expect(tag1.id).not.toBe(tag2.id);
let fieldNames = Note.fieldNames();
fieldNames = ArrayUtils.removeElement(fieldNames, 'id');
fieldsEqual(tag1, tag2, fieldNames);
let noteIds = await Tag.noteIds(tag2.id);
expect(noteIds.length).toBe(1);
expect(noteIds[0]).toBe(note2.id);
await service.import({ path: filePath });
// If importing again, no new tag should be created as one with
// the same name already existed. The newly imported note should
// however go under that already existing tag.
expect(await Tag.count()).toBe(1);
noteIds = await Tag.noteIds(tag2.id);
expect(noteIds.length).toBe(2);
}));
it('should export and import resources', asyncTest(async () => {
const service = new InteropService();
const filePath = exportDir() + '/test.jex';
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');
note1 = await Note.load(note1.id);
let resourceIds = Note.linkedResourceIds(note1.body);
let resource1 = await Resource.load(resourceIds[0]);
await service.export({ path: filePath });
await Note.delete(note1.id);
await service.import({ path: filePath });
expect(await Resource.count()).toBe(2);
let note2 = (await Note.all())[0];
expect(note2.body).not.toBe(note1.body);
resourceIds = Note.linkedResourceIds(note2.body);
expect(resourceIds.length).toBe(1);
let resource2 = await Resource.load(resourceIds[0]);
expect(resource2.id).not.toBe(resource1.id);
let fieldNames = Note.fieldNames();
fieldNames = ArrayUtils.removeElement(fieldNames, 'id');
fieldsEqual(resource1, resource2, fieldNames);
const resourcePath1 = Resource.fullPath(resource1);
const resourcePath2 = Resource.fullPath(resource2);
expect(resourcePath1).not.toBe(resourcePath2);
expect(fileContentEqual(resourcePath1, resourcePath2)).toBe(true);
}));
it('should export and import single notes', asyncTest(async () => {
const service = new InteropService();
const filePath = exportDir() + '/test.jex';
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await service.export({ path: filePath, sourceNoteIds: [note1.id] });
await Note.delete(note1.id);
await Folder.delete(folder1.id);
await service.import({ path: filePath });
expect(await Note.count()).toBe(1);
expect(await Folder.count()).toBe(1);
let folder2 = (await Folder.all())[0];
expect(folder2.title).toBe('test');
}));
it('should export and import single folders', asyncTest(async () => {
const service = new InteropService();
const filePath = exportDir() + '/test.jex';
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await service.export({ path: filePath, sourceFolderIds: [folder1.id] });
await Note.delete(note1.id);
await Folder.delete(folder1.id);
await service.import({ path: filePath });
expect(await Note.count()).toBe(1);
expect(await Folder.count()).toBe(1);
let folder2 = (await Folder.all())[0];
expect(folder2.title).toBe('folder1');
}));
});

View File

@@ -19,7 +19,7 @@ process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
jasmine.DEFAULT_TIMEOUT_INTERVAL = 35000; // The first test is slow because the database needs to be built
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000 + 30000; // The first test is slow because the database needs to be built
async function allItems() {
let folders = await Folder.all();
@@ -60,6 +60,7 @@ async function allSyncTargetItemsEncrypted() {
}
async function localItemsSameAsRemote(locals, expect) {
let error = null;
try {
let files = await fileApi().list();
files = files.items;
@@ -81,12 +82,15 @@ async function localItemsSameAsRemote(locals, expect) {
// }
let remoteContent = await fileApi().get(path);
remoteContent = dbItem.type_ == BaseModel.TYPE_NOTE ? await Note.unserialize(remoteContent) : await Folder.unserialize(remoteContent);
expect(remoteContent.title).toBe(dbItem.title);
}
} catch (error) {
console.error(error);
} catch (e) {
error = e;
}
expect(error).toBe(null);
}
let insideBeforeEach = false;
@@ -115,7 +119,7 @@ describe('Synchronizer', function() {
await localItemsSameAsRemote(all, expect);
}));
it('should update remote item', asyncTest(async () => {
it('should update remote items', asyncTest(async () => {
let folder = await Folder.save({ title: "folder1" });
let note = await Note.save({ title: "un", parent_id: folder.id });
await synchronizer().start();
@@ -287,23 +291,30 @@ describe('Synchronizer', function() {
}));
it('should delete local notes', asyncTest(async () => {
// For these tests we pass the context around for each user. This is to make sure that the "deletedItemsProcessed"
// property of the basicDelta() function is cleared properly at the end of a sync operation. If it is not cleared
// it means items will no longer be deleted locally via sync.
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: "un", parent_id: folder1.id });
await synchronizer().start();
let note2 = await Note.save({ title: "deux", parent_id: folder1.id });
let context1 = await synchronizer().start();
await switchClient(2);
await synchronizer().start();
let context2 = await synchronizer().start();
await Note.delete(note1.id);
await synchronizer().start();
context2 = await synchronizer().start({ context: context2 });
await switchClient(1);
await synchronizer().start();
context1 = await synchronizer().start({ context: context1 });
let items = await allItems();
expect(items.length).toBe(1);
expect(items.length).toBe(2);
let deletedItems = await BaseItem.deletedItems(syncTargetId());
expect(deletedItems.length).toBe(0);
await Note.delete(note2.id);
context1 = await synchronizer().start({ context: context1 });
}));
it('should delete remote folder', asyncTest(async () => {
@@ -985,4 +996,39 @@ describe('Synchronizer', function() {
expect(resource1.encryption_blob_encrypted).toBe(0);
}));
});
it('should create remote items with UTF-8 content', asyncTest(async () => {
let folder = await Folder.save({ title: "Fahrräder" });
await Note.save({ title: "Fahrräder", body: "Fahrräder", parent_id: folder.id });
let all = await allItems();
await synchronizer().start();
await localItemsSameAsRemote(all, expect);
}));
it("should update remote items but not pull remote changes", asyncTest(async () => {
let folder = await Folder.save({ title: "folder1" });
let note = await Note.save({ title: "un", parent_id: folder.id });
await synchronizer().start();
await switchClient(2);
await synchronizer().start();
await Note.save({ title: "deux", parent_id: folder.id });
await synchronizer().start();
await switchClient(1);
await Note.save({ title: "un UPDATE", id: note.id });
await synchronizer().start({ syncSteps: ["update_remote"] });
let all = await allItems();
expect(all.length).toBe(2);
await switchClient(2);
await synchronizer().start();
let note2 = await Note.load(note.id);
expect(note2.title).toBe("un UPDATE");
}));
});

View File

@@ -51,12 +51,12 @@ SyncTargetRegistry.addClass(SyncTargetFilesystem);
SyncTargetRegistry.addClass(SyncTargetOneDrive);
SyncTargetRegistry.addClass(SyncTargetNextcloud);
const syncTargetId_ = SyncTargetRegistry.nameToId('nextcloud');
//const syncTargetId_ = SyncTargetRegistry.nameToId('memory');
// const syncTargetId_ = SyncTargetRegistry.nameToId("nextcloud");
const syncTargetId_ = SyncTargetRegistry.nameToId("memory");
//const syncTargetId_ = SyncTargetRegistry.nameToId('filesystem');
const syncDir = __dirname + '/../tests/sync';
const sleepTime = syncTargetId_ == SyncTargetRegistry.nameToId('filesystem') ? 1001 : 10;//400;
const sleepTime = syncTargetId_ == SyncTargetRegistry.nameToId('filesystem') ? 1001 : 100;//400;
console.info('Testing with sync target: ' + SyncTargetRegistry.idToName(syncTargetId_));
@@ -75,6 +75,8 @@ BaseItem.loadClass('MasterKey', MasterKey);
Setting.setConstant('appId', 'net.cozic.joplin-cli');
Setting.setConstant('appType', 'cli');
Setting.autoSaveEnabled = false;
function syncTargetId() {
return syncTargetId_;
}
@@ -262,6 +264,7 @@ function fileApi() {
fileApi_.setLogger(logger);
fileApi_.setSyncTargetId(syncTargetId_);
fileApi_.requestRepeatCount_ = 0;
return fileApi_;
}

View File

@@ -4,6 +4,8 @@ const { shim } = require('lib/shim');
const url = require('url')
const path = require('path')
const urlUtils = require('lib/urlUtils.js');
const { dirname, basename } = require('lib/path-utils');
const fs = require('fs-extra');
class ElectronAppWrapper {
@@ -13,6 +15,7 @@ class ElectronAppWrapper {
this.win_ = null;
this.willQuitApp_ = false;
this.tray_ = null;
this.buildDir_ = null;
}
electronApp() {
@@ -38,13 +41,14 @@ class ElectronAppWrapper {
const windowState = windowStateKeeper({
defaultWidth: 800,
defaultHeight: 600,
file: 'window-state-' + this.env_ + '.json',
});
const windowOptions = {
'x': windowState.x,
'y': windowState.y,
'width': windowState.width,
'height': windowState.height,
x: windowState.x,
y: windowState.y,
width: windowState.width,
height: windowState.height,
};
// Linux icon workaround for bug https://github.com/electron-userland/electron-builder/issues/2098
@@ -60,7 +64,7 @@ class ElectronAppWrapper {
}))
// Uncomment this to view errors if the application does not start
if (this.env_ === 'dev') this.win_.webContents.openDevTools();
// if (this.env_ === 'dev') this.win_.webContents.openDevTools();
this.win_.on('close', (event) => {
// If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
@@ -75,7 +79,7 @@ class ElectronAppWrapper {
this.win_ = null;
} else {
event.preventDefault();
this.win_.hide();
this.hide();
}
} else {
if (this.trayShown() && !this.willQuitApp_) {
@@ -106,23 +110,63 @@ class ElectronAppWrapper {
});
}
async exit() {
async quit() {
this.electronApp_.quit();
}
exit(errorCode = 0) {
this.electronApp_.exit(errorCode);
}
trayShown() {
return !!this.tray_;
}
// This method is used in macOS only to hide the whole app (and not just the main window)
// including the menu bar. This follows the macOS way of hidding an app.
hide() {
this.electronApp_.hide();
}
buildDir() {
if (this.buildDir_) return this.buildDir_;
let dir = __dirname + '/build';
if (!fs.pathExistsSync(dir)) {
dir = dirname(__dirname) + '/build';
if (!fs.pathExistsSync(dir)) throw new Error('Cannot find build dir');
}
this.buildDir_ = dir;
return dir;
}
trayIconFilename_() {
let output = '';
if (process.platform === 'darwin') {
output = 'macos-16x16Template.png'; // Electron Template Image format
} else {
output = '16x16.png';
}
if (this.env_ === 'dev') output = '16x16-dev.png'
return output;
}
// Note: this must be called only after the "ready" event of the app has been dispatched
createTray(contextMenu) {
this.tray_ = new Tray(__dirname + '/build/icons/16x16.png')
this.tray_.setToolTip(this.electronApp_.getName())
this.tray_.setContextMenu(contextMenu)
try {
this.tray_ = new Tray(this.buildDir() + '/icons/' + this.trayIconFilename_())
this.tray_.setToolTip(this.electronApp_.getName())
this.tray_.setContextMenu(contextMenu)
this.tray_.on('click', () => {
this.window().show();
});
this.tray_.on('click', () => {
this.window().show();
});
} catch (error) {
console.error("Cannot create tray", error);
}
}
destroyTray() {
@@ -131,11 +175,32 @@ class ElectronAppWrapper {
this.tray_ = null;
}
ensureSingleInstance() {
if (this.env_ === 'dev') return false;
return new Promise((resolve, reject) => {
const alreadyRunning = this.electronApp_.makeSingleInstance((commandLine, workingDirectory) => {
const win = this.window();
if (!win) return;
if (win.isMinimized()) win.restore();
win.show();
win.focus();
});
if (alreadyRunning) this.electronApp_.quit();
resolve(alreadyRunning);
});
}
async start() {
// Since we are doing other async things before creating the window, we might miss
// the "ready" event. So we use the function below to make sure that the app is ready.
await this.waitForElectronAppReady();
const alreadyRunning = await this.ensureSingleInstance();
if (alreadyRunning) return;
this.createWindow();
this.electronApp_.on('before-quit', () => {

View File

@@ -0,0 +1,51 @@
const { _ } = require('lib/locale');
const { bridge } = require('electron').remote.require('./bridge');
const InteropService = require('lib/services/InteropService');
class InteropServiceHelper {
static async export(dispatch, module, options = null) {
if (!options) options = {};
let path = null;
if (module.target === 'file') {
path = bridge().showSaveDialog({
filters: [{ name: module.description, extensions: [module.fileExtension]}]
});
} else {
path = bridge().showOpenDialog({
properties: ['openDirectory', 'createDirectory'],
});
}
if (!path || (Array.isArray(path) && !path.length)) return;
if (Array.isArray(path)) path = path[0];
dispatch({
type: 'WINDOW_COMMAND',
name: 'showModalMessage',
message: _('Exporting to "%s" as "%s" format. Please wait...', path, module.format),
});
const exportOptions = {};
exportOptions.path = path;
exportOptions.format = module.format;
if (options.sourceFolderIds) exportOptions.sourceFolderIds = options.sourceFolderIds;
if (options.sourceNoteIds) exportOptions.sourceNoteIds = options.sourceNoteIds;
const service = new InteropService();
const result = await service.export(exportOptions);
console.info('Export result: ', result);
dispatch({
type: 'WINDOW_COMMAND',
name: 'hideModalMessage',
});
}
}
module.exports = InteropServiceHelper;

View File

@@ -20,6 +20,8 @@ const packageInfo = require('./packageInfo.js');
const AlarmService = require('lib/services/AlarmService.js');
const AlarmServiceDriverNode = require('lib/services/AlarmServiceDriverNode');
const DecryptionWorker = require('lib/services/DecryptionWorker');
const InteropService = require('lib/services/InteropService');
const InteropServiceHelper = require('./InteropServiceHelper.js');
const { bridge } = require('electron').remote.require('./bridge');
const Menu = bridge().Menu;
@@ -144,8 +146,12 @@ class Application extends BaseApplication {
this.updateTray();
}
if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) {
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync();
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'style.editor.fontFamily' || action.type == 'SETTING_UPDATE_ALL') {
this.updateEditorFont();
}
if (["NOTE_UPDATE_ONE", "NOTE_DELETE", "FOLDER_UPDATE_ONE", "FOLDER_DELETE"].indexOf(action.type) >= 0) {
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(5, { syncSteps: ["update_remote", "delete_remote"] });
}
if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) {
@@ -175,6 +181,101 @@ class Application extends BaseApplication {
updateMenu(screen) {
if (this.lastMenuScreen_ === screen) return;
const sortNoteItems = [];
const sortNoteOptions = Setting.enumOptions('notes.sortOrder.field');
for (let field in sortNoteOptions) {
if (!sortNoteOptions.hasOwnProperty(field)) continue;
sortNoteItems.push({
label: sortNoteOptions[field],
screens: ['Main'],
type: 'checkbox',
checked: Setting.value('notes.sortOrder.field') === field,
click: () => {
Setting.setValue('notes.sortOrder.field', field);
this.refreshMenu();
}
});
}
const importItems = [];
const exportItems = [];
const ioService = new InteropService();
const ioModules = ioService.modules();
for (let i = 0; i < ioModules.length; i++) {
const module = ioModules[i];
if (module.type === 'exporter') {
exportItems.push({
label: module.fullLabel(),
screens: ['Main'],
click: async () => {
await InteropServiceHelper.export(this.dispatch.bind(this), module);
}
});
} else {
for (let j = 0; j < module.sources.length; j++) {
const moduleSource = module.sources[j];
importItems.push({
label: module.fullLabel(moduleSource),
screens: ['Main'],
click: async () => {
let path = null;
const selectedFolderId = this.store().getState().selectedFolderId;
if (moduleSource === 'file') {
path = bridge().showOpenDialog({
filters: [{ name: module.description, extensions: [module.fileExtension]}]
});
} else {
path = bridge().showOpenDialog({
properties: ['openDirectory', 'createDirectory'],
});
}
if (!path || (Array.isArray(path) && !path.length)) return;
if (Array.isArray(path)) path = path[0];
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'showModalMessage',
message: _('Importing from "%s" as "%s" format. Please wait...', path, module.format),
});
const importOptions = {};
importOptions.path = path;
importOptions.format = module.format;
importOptions.destinationFolderId = !module.isNoteArchive && moduleSource === 'file' ? selectedFolderId : null;
const service = new InteropService();
try {
const result = await service.import(importOptions);
console.info('Import result: ', result);
} catch (error) {
bridge().showErrorMessageBox(error.message);
}
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'hideModalMessage',
});
}
});
}
}
}
exportItems.push({
label: 'PDF - ' + _('PDF File'),
screens: ['Main'],
click: async () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'exportPdf',
});
}
});
const template = [
{
label: _('File'),
@@ -200,6 +301,7 @@ class Application extends BaseApplication {
}
}, {
label: _('New notebook'),
accelerator: 'CommandOrControl+B',
screens: ['Main'],
click: () => {
this.dispatch({
@@ -210,46 +312,53 @@ class Application extends BaseApplication {
}, {
type: 'separator',
}, {
label: _('Import Evernote notes'),
label: _('Import'),
submenu: importItems,
}, {
label: _('Export'),
submenu: exportItems,
}, {
type: 'separator',
}, {
label: _('Print'),
accelerator: 'CommandOrControl+P',
screens: ['Main'],
click: () => {
const filePaths = bridge().showOpenDialog({
properties: ['openFile', 'createDirectory'],
filters: [
{ name: _('Evernote Export Files'), extensions: ['enex'] },
]
});
if (!filePaths || !filePaths.length) return;
this.dispatch({
type: 'NAV_GO',
routeName: 'Import',
props: {
filePath: filePaths[0],
},
type: 'WINDOW_COMMAND',
name: 'print',
});
}
}, {
type: 'separator',
platforms: ['darwin'],
}, {
label: _('Hide %s', 'Joplin'),
platforms: ['darwin'],
accelerator: 'CommandOrControl+H',
click: () => { bridge().electronApp().hide() }
}, {
type: 'separator',
}, {
label: _('Quit'),
accelerator: 'CommandOrControl+Q',
click: () => { bridge().electronApp().exit() }
click: () => { bridge().electronApp().quit() }
}]
}, {
label: _('Edit'),
submenu: [{
label: _('Copy'),
screens: ['Main', 'OneDriveLogin'],
screens: ['Main', 'OneDriveLogin', 'Config', 'EncryptionConfig'],
role: 'copy',
accelerator: 'CommandOrControl+C',
}, {
label: _('Cut'),
screens: ['Main', 'OneDriveLogin'],
screens: ['Main', 'OneDriveLogin', 'Config', 'EncryptionConfig'],
role: 'cut',
accelerator: 'CommandOrControl+X',
}, {
label: _('Paste'),
screens: ['Main', 'OneDriveLogin'],
screens: ['Main', 'OneDriveLogin', 'Config', 'EncryptionConfig'],
role: 'paste',
accelerator: 'CommandOrControl+V',
}, {
@@ -266,6 +375,42 @@ class Application extends BaseApplication {
});
},
}],
}, {
label: _('View'),
submenu: [{
label: _('Toggle editor layout'),
screens: ['Main'],
accelerator: 'CommandOrControl+L',
click: () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'toggleVisiblePanes',
});
}
}, {
type: 'separator',
screens: ['Main'],
}, {
label: Setting.settingMetadata('notes.sortOrder.field').label(),
screens: ['Main'],
submenu: sortNoteItems,
}, {
label: Setting.settingMetadata('notes.sortOrder.reverse').label(),
type: 'checkbox',
checked: Setting.value('notes.sortOrder.reverse'),
screens: ['Main'],
click: () => {
Setting.setValue('notes.sortOrder.reverse', !Setting.value('notes.sortOrder.reverse'));
},
}, {
label: Setting.settingMetadata('uncompletedTodosOnTop').label(),
type: 'checkbox',
checked: Setting.value('uncompletedTodosOnTop'),
screens: ['Main'],
click: () => {
Setting.setValue('uncompletedTodosOnTop', !Setting.value('uncompletedTodosOnTop'));
},
}],
}, {
label: _('Tools'),
submenu: [{
@@ -289,6 +434,7 @@ class Application extends BaseApplication {
}
},{
label: _('General Options'),
accelerator: 'CommandOrControl+,',
click: () => {
this.dispatch({
type: 'NAV_GO',
@@ -302,11 +448,17 @@ class Application extends BaseApplication {
label: _('Website and documentation'),
accelerator: 'F1',
click () { bridge().openExternal('http://joplin.cozic.net') }
}, {
label: _('Make a donation'),
click () { bridge().openExternal('http://joplin.cozic.net/donate') }
}, {
label: _('Check for updates...'),
click: () => {
bridge().checkForUpdates(false, this.checkForUpdateLoggerPath());
bridge().checkForUpdates(false, bridge().window(), this.checkForUpdateLoggerPath());
}
}, {
type: 'separator',
screens: ['Main'],
}, {
label: _('About Joplin'),
click: () => {
@@ -317,8 +469,8 @@ class Application extends BaseApplication {
'Copyright © 2016-2018 Laurent Cozic',
_('%s %s (%s, %s)', p.name, p.version, Setting.value('env'), process.platform),
];
bridge().showMessageBox({
message: message.join('\n'),
bridge().showInfoMessageBox(message.join('\n'), {
icon: bridge().electronApp().buildDir() + '/icons/32x32.png',
});
}
}]
@@ -334,10 +486,13 @@ class Application extends BaseApplication {
}
function removeUnwantedItems(template, screen) {
const platform = shim.platformName();
let output = [];
for (let i = 0; i < template.length; i++) {
const t = Object.assign({}, template[i]);
if (t.screens && t.screens.indexOf(screen) < 0) continue;
if (t.platforms && t.platforms.indexOf(platform) < 0) continue;
if (t.submenu) t.submenu = removeUnwantedItems(t.submenu, screen);
if (('submenu' in t) && isEmptyMenu(t.submenu)) continue;
output.push(t);
@@ -369,12 +524,27 @@ class Application extends BaseApplication {
const contextMenu = Menu.buildFromTemplate([
{ label: _('Open %s', app.electronApp().getName()), click: () => { app.window().show(); } },
{ type: 'separator' },
{ label: _('Exit'), click: () => { app.exit() } },
{ label: _('Exit'), click: () => { app.quit() } },
])
app.createTray(contextMenu);
}
}
updateEditorFont() {
const fontFamilies = [];
if (Setting.value('style.editor.fontFamily')) fontFamilies.push('"' + Setting.value('style.editor.fontFamily') + '"');
fontFamilies.push('monospace');
// The '*' and '!important' parts are necessary to make sure Russian text is displayed properly
// https://github.com/laurent22/joplin/issues/155
const css = '.ace_editor * { font-family: ' + fontFamilies.join(', ') + ' !important; }';
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
styleTag.appendChild(document.createTextNode(css));
document.head.appendChild(styleTag);
}
async start(argv) {
argv = await super.start(argv);
@@ -422,13 +592,14 @@ class Application extends BaseApplication {
if (shim.isWindows() || shim.isMac()) {
const runAutoUpdateCheck = () => {
if (Setting.value('autoUpdateEnabled')) {
bridge().checkForUpdates(true, this.checkForUpdateLoggerPath());
bridge().checkForUpdates(true, bridge().window(), this.checkForUpdateLoggerPath());
}
}
// Initial check on startup
setTimeout(() => { runAutoUpdateCheck() }, 5000);
// For those who leave the app always open
setInterval(() => { runAutoUpdateCheck() }, 2 * 60 * 60 * 1000);
// Then every x hours
setInterval(() => { runAutoUpdateCheck() }, 12 * 60 * 60 * 1000);
}
this.updateTray();

View File

@@ -43,7 +43,7 @@ class Bridge {
const {dialog} = require('electron');
if (!options) options = {};
if (!('defaultPath' in options) && this.lastSelectedPath_) options.defaultPath = this.lastSelectedPath_;
const filePath = dialog.showSaveDialog(options);
const filePath = dialog.showSaveDialog(this.window(), options);
if (filePath) {
this.lastSelectedPath_ = filePath;
}
@@ -55,27 +55,30 @@ class Bridge {
if (!options) options = {};
if (!('defaultPath' in options) && this.lastSelectedPath_) options.defaultPath = this.lastSelectedPath_;
if (!('createDirectory' in options)) options.createDirectory = true;
const filePaths = dialog.showOpenDialog(options);
const filePaths = dialog.showOpenDialog(this.window(), options);
if (filePaths && filePaths.length) {
this.lastSelectedPath_ = dirname(filePaths[0]);
}
return filePaths;
}
showMessageBox(options) {
// Don't use this directly - call one of the showXxxxxxxMessageBox() instead
showMessageBox_(window, options) {
const {dialog} = require('electron');
return dialog.showMessageBox(options);
const nativeImage = require('electron').nativeImage
if (!window) window = this.window();
return dialog.showMessageBox(window, options);
}
showErrorMessageBox(message) {
return this.showMessageBox({
return this.showMessageBox_(this.window(), {
type: 'error',
message: message,
});
}
showConfirmMessageBox(message) {
const result = this.showMessageBox({
const result = this.showMessageBox_(this.window(), {
type: 'question',
message: message,
buttons: [_('OK'), _('Cancel')],
@@ -83,12 +86,12 @@ class Bridge {
return result === 0;
}
showInfoMessageBox(message) {
const result = this.showMessageBox({
showInfoMessageBox(message, options = {}) {
const result = this.showMessageBox_(this.window(), Object.assign({}, {
type: 'info',
message: message,
buttons: [_('OK')],
});
}, options));
return result === 0;
}
@@ -108,26 +111,9 @@ class Bridge {
return require('electron').shell.openItem(fullPath)
}
async checkForUpdatesAndNotify(logFilePath) {
if (!this.autoUpdater_) {
this.autoUpdateLogger_ = new Logger();
this.autoUpdateLogger_.addTarget('file', { path: logFilePath });
this.autoUpdateLogger_.setLevel(Logger.LEVEL_DEBUG);
this.autoUpdateLogger_.info('checkForUpdatesAndNotify: Initializing...');
this.autoUpdater_ = require("electron-updater").autoUpdater;
this.autoUpdater_.logger = this.autoUpdateLogger_;
}
try {
await this.autoUpdater_.checkForUpdatesAndNotify();
} catch (error) {
this.autoUpdateLogger_.error(error);
}
}
checkForUpdates(inBackground, logFilePath) {
checkForUpdates(inBackground, window, logFilePath) {
const { checkForUpdates } = require('./checkForUpdates.js');
checkForUpdates(inBackground, logFilePath);
checkForUpdates(inBackground, window, logFilePath);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

View File

@@ -1,72 +1,110 @@
const { dialog } = require('electron')
const { autoUpdater } = require('electron-updater')
const { Logger } = require('lib/logger.js');
const { _ } = require('lib/locale.js');
const fetch = require('node-fetch');
const packageInfo = require('./packageInfo.js');
const compareVersions = require('compare-versions');
let autoUpdateLogger_ = new Logger();
let checkInBackground_ = false;
let isCheckingForUpdate_ = false;
let parentWindow_ = null;
autoUpdater.autoDownload = false;
function showErrorMessageBox(message) {
return dialog.showMessageBox(parentWindow_, {
type: 'error',
message: message,
});
}
autoUpdater.on('error', (error) => {
autoUpdateLogger_.error(error);
if (checkInBackground_) return;
dialog.showErrorBox(_('Error'), error == null ? "unknown" : (error.stack || error).toString())
})
function onCheckStarted() {
autoUpdateLogger_.info('checkForUpdates: Starting...');
isCheckingForUpdate_ = true;
}
autoUpdater.on('update-available', () => {
dialog.showMessageBox({
type: 'info',
message: _('An update is available, do you want to update now?'),
buttons: [_('Yes'), _('No')]
}, (buttonIndex) => {
if (buttonIndex === 0) {
try {
autoUpdater.downloadUpdate()
} catch (error) {
autoUpdateLogger_.error(error);
dialog.showErrorBox(_('Error'), _('Could not download the update: %s', error.message));
}
function onCheckEnded() {
autoUpdateLogger_.info('checkForUpdates: Done.');
isCheckingForUpdate_ = false;
}
async function fetchLatestRelease() {
const response = await fetch('https://api.github.com/repos/laurent22/joplin/releases/latest');
if (!response.ok) {
const responseText = await response.text();
throw new Error('Cannot get latest release info: ' + responseText.substr(0,500));
}
const json = await response.json();
const version = json.tag_name.substr(1);
let downloadUrl = null;
const platform = process.platform;
for (let i = 0; i < json.assets.length; i++) {
const asset = json.assets[i];
let found = false;
if (platform === 'win32' && asset.name.indexOf('.exe') >= 0) {
found = true;
} else if (platform === 'darwin' && asset.name.indexOf('.dmg') >= 0) {
found = true;
} else if (platform === 'linux' && asset.name.indexOf('.AppImage') >= 0) {
found = true;
}
})
})
autoUpdater.on('update-not-available', () => {
if (checkInBackground_) return;
if (found) {
downloadUrl = asset.browser_download_url;
break;
}
}
dialog.showMessageBox({ message: _('Current version is up-to-date.') })
})
if (!downloadUrl) throw new Error('Cannot find download Url: ' + JSON.stringify(json).substr(0,500));
autoUpdater.on('update-downloaded', () => {
dialog.showMessageBox({ message: _('New version downloaded - application will quit now and update...') }, () => {
setTimeout(() => {
try {
autoUpdater.quitAndInstall();
} catch (error) {
autoUpdateLogger_.error(error);
dialog.showErrorBox(_('Error'), _('Could not install the update: %s', error.message));
}
}, 100);
})
})
return {
version: version,
downloadUrl: downloadUrl,
notes: json.body,
};
}
function checkForUpdates(inBackground, window, logFilePath) {
if (isCheckingForUpdate_) {
autoUpdateLogger_.info('checkForUpdates: Skipping check because it is already running');
return;
}
parentWindow_ = window;
onCheckStarted();
function checkForUpdates(inBackground, logFilePath) {
if (logFilePath && !autoUpdateLogger_.targets().length) {
autoUpdateLogger_ = new Logger();
autoUpdateLogger_.addTarget('file', { path: logFilePath });
autoUpdateLogger_.setLevel(Logger.LEVEL_DEBUG);
autoUpdateLogger_.info('checkForUpdates: Initializing...');
autoUpdater.logger = autoUpdateLogger_;
}
checkInBackground_ = inBackground;
try {
autoUpdater.checkForUpdates()
} catch (error) {
fetchLatestRelease().then(release => {
if (compareVersions(release.version, packageInfo.version) <= 0) {
if (!checkInBackground_) dialog.showMessageBox({ message: _('Current version is up-to-date.') })
} else {
const releaseNotes = release.notes.trim() ? "\n\n" + release.notes.trim() : '';
const buttonIndex = dialog.showMessageBox(parentWindow_, {
type: 'info',
message: _('An update is available, do you want to download it now?' + releaseNotes),
buttons: [_('Yes'), _('No')]
});
if (buttonIndex === 0) require('electron').shell.openExternal(release.downloadUrl);
}
}).catch(error => {
autoUpdateLogger_.error(error);
if (!checkInBackground_) dialog.showErrorBox(_('Error'), error.message);
}
if (!checkInBackground_) showErrorMessageBox(error.message);
}).then(() => {
onCheckEnded();
});
}
module.exports.checkForUpdates = checkForUpdates

View File

@@ -7,14 +7,22 @@ const { Header } = require('./Header.min.js');
const { themeStyle } = require('../theme.js');
const pathUtils = require('lib/path-utils.js');
const { _ } = require('lib/locale.js');
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
const shared = require('lib/components/shared/config-shared.js');
class ConfigScreenComponent extends React.Component {
constructor() {
super();
this.state = {
settings: {},
shared.init(this);
this.checkSyncConfig_ = async () => {
await shared.checkSyncConfig(this, this.state.settings);
}
this.rowStyle_ = {
marginBottom: 10,
};
}
@@ -32,10 +40,6 @@ class ConfigScreenComponent extends React.Component {
});
}
output.sort((a, b) => {
return a.label.toLowerCase() < b.label.toLowerCase() ? -1 : +1;
});
return output;
}
@@ -44,9 +48,7 @@ class ConfigScreenComponent extends React.Component {
let output = null;
const rowStyle = {
marginBottom: 10,
};
const rowStyle = this.rowStyle_;
const labelStyle = Object.assign({}, theme.textStyle, {
display: 'inline-block',
@@ -57,16 +59,27 @@ class ConfigScreenComponent extends React.Component {
display: 'inline-block',
};
const descriptionStyle = Object.assign({}, theme.textStyle, {
color: theme.colorFaded,
marginTop: 5,
fontStyle: 'italic',
});
const updateSettingValue = (key, value) => {
const settings = Object.assign({}, this.state.settings);
settings[key] = value;
this.setState({ settings: settings });
return shared.updateSettingValue(this, key, value);
}
// Component key needs to be key+value otherwise it doesn't update when the settings change.
const md = Setting.settingMetadata(key);
const descriptionText = Setting.keyDescription(key, 'desktop');
const descriptionComp = descriptionText ? (
<div style={descriptionStyle}>
{descriptionText}
</div>
) : null;
if (md.isEnum) {
let items = [];
const settingOptions = md.options();
@@ -82,6 +95,7 @@ class ConfigScreenComponent extends React.Component {
<select value={value} style={controlStyle} onChange={(event) => { updateSettingValue(key, event.target.value) }}>
{items}
</select>
{ descriptionComp }
</div>
);
} else if (md.type === Setting.TYPE_BOOL) {
@@ -96,6 +110,7 @@ class ConfigScreenComponent extends React.Component {
<div key={key+value.toString()} style={rowStyle}>
<div style={controlStyle}>
<input id={'setting_checkbox_' + key} type="checkbox" checked={!!value} onChange={(event) => { onCheckboxClick(event) }}/><label onClick={(event) => { onCheckboxClick(event) }} style={labelStyle} htmlFor={'setting_checkbox_' + key}>{md.label()}</label>
{ descriptionComp }
</div>
</div>
);
@@ -104,12 +119,14 @@ class ConfigScreenComponent extends React.Component {
updateSettingValue(key, event.target.value);
}
const inputStyle = Object.assign({}, controlStyle, { width: '50%', minWidth: '20em' });
const inputType = md.secure === true ? 'password' : 'text';
return (
<div key={key} style={rowStyle}>
<div style={labelStyle}><label>{md.label()}</label></div>
<input type={inputType} style={controlStyle} value={this.state.settings[key]} onChange={(event) => {onTextChange(event)}} />
<input type={inputType} style={inputStyle} value={this.state.settings[key]} onChange={(event) => {onTextChange(event)}} />
{ descriptionComp }
</div>
);
} else if (md.type === Setting.TYPE_INT) {
@@ -121,6 +138,7 @@ class ConfigScreenComponent extends React.Component {
<div key={key} style={rowStyle}>
<div style={labelStyle}><label>{md.label()}</label></div>
<input type="number" style={controlStyle} value={this.state.settings[key]} onChange={(event) => {onNumChange(event)}} min={md.minimum} max={md.maximum} step={md.step}/>
{ descriptionComp }
</div>
);
} else {
@@ -131,10 +149,7 @@ class ConfigScreenComponent extends React.Component {
}
onSaveClick() {
for (let n in this.state.settings) {
if (!this.state.settings.hasOwnProperty(n)) continue;
Setting.setValue(n, this.state.settings[n]);
}
shared.saveSettings(this);
this.props.dispatch({ type: 'NAV_BACK' });
}
@@ -144,7 +159,7 @@ class ConfigScreenComponent extends React.Component {
render() {
const theme = themeStyle(this.props.theme);
const style = this.props.style;
const style = Object.assign({}, this.props.style, { overflow: 'auto' });
const settings = this.state.settings;
const headerStyle = {
@@ -156,23 +171,28 @@ class ConfigScreenComponent extends React.Component {
};
const buttonStyle = {
display: this.state.settings === this.props.settings ? 'none' : 'inline-block',
display: this.state.changedSettingKeys.length ? 'inline-block' : 'none',
marginRight: 10,
}
let settingComps = [];
let keys = Setting.keys(true, 'desktop');
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (!(key in settings)) {
console.warn('Missing setting: ' + key);
continue;
}
const md = Setting.settingMetadata(key);
if (md.show && !md.show(settings)) continue;
const comp = this.settingToComponent(key, settings[key]);
if (!comp) continue;
settingComps.push(comp);
const settingComps = shared.settingsToComponents(this, 'desktop', settings);
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
if (syncTargetMd.supportsConfigCheck) {
const messages = shared.checkSyncConfigMessages(this);
const statusStyle = Object.assign({}, theme.textStyle, { marginTop: 10 });
const statusComp = !messages.length ? null : (
<div style={statusStyle}>
{messages[0]}
{messages.length >= 1 ? (<p>{messages[1]}</p>) : null}
</div>);
settingComps.push(
<div key="check_sync_config_button" style={this.rowStyle_}>
<button disabled={this.state.checkSyncConfigResult === 'checking'} onClick={this.checkSyncConfig_}>{_('Check synchronisation configuration')}</button>
{ statusComp }
</div>);
}
return (

View File

@@ -182,7 +182,7 @@ class EncryptionConfigScreenComponent extends React.Component {
<div>
<Header style={headerStyle} />
<div style={containerStyle}>
<div style={{backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
{/*<div style={{backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
<p style={theme.textStyle}>
Important: This is a <b>beta</b> feature. It has been extensively tested and is already in use by some users, but it is possible that some bugs remain.
</p>
@@ -192,7 +192,7 @@ class EncryptionConfigScreenComponent extends React.Component {
<p style={theme.textStyle}>
For more information about End-To-End Encryption (E2EE) and how it is going to work, please check the documentation: <a onClick={() => {bridge().openExternal('http://joplin.cozic.net/help/e2ee.html')}} href="#">http://joplin.cozic.net/help/e2ee.html</a>
</p>
</div>
</div>*/}
<h1 style={theme.h1Style}>{_('Status')}</h1>
<p style={theme.textStyle}>{_('Encryption is:')} <strong>{this.props.encryptionEnabled ? _('Enabled') : _('Disabled')}</strong></p>
{decryptedItemsInfo}

View File

@@ -25,9 +25,7 @@ class ImportScreenComponent extends React.Component {
doImport: true,
filePath: newProps.filePath,
messages: [],
});
this.doImport();
}, () => { this.doImport() });
}
}

View File

@@ -22,6 +22,10 @@ class MainScreenComponent extends React.Component {
componentWillMount() {
this.setState({
promptOptions: null,
modalLayer: {
visible: false,
message: '',
},
});
}
@@ -163,6 +167,12 @@ class MainScreenComponent extends React.Component {
}
},
});
} else if (command.name === 'toggleVisiblePanes') {
this.toggleVisiblePanes();
} else if (command.name === 'showModalMessage') {
this.setState({ modalLayer: { visible: true, message: command.message } });
} else if (command.name === 'hideModalMessage') {
this.setState({ modalLayer: { visible: false, message: '' } });
} else if (command.name === 'editAlarm') {
const note = await Note.load(command.noteId);
@@ -263,6 +273,17 @@ class MainScreenComponent extends React.Component {
height: height,
};
this.styles_.modalLayer = Object.assign({}, theme.textStyle, {
zIndex: 10000,
position: 'absolute',
top: 0,
left: 0,
backgroundColor: theme.backgroundColorTransparent,
width: width - 20,
height: height - 20,
padding: 10,
});
return this.styles_;
}
@@ -309,9 +330,7 @@ class MainScreenComponent extends React.Component {
title: _('Layout'),
iconName: 'fa-columns',
enabled: !!notes.length,
onClick: () => {
this.toggleVisiblePanes();
},
onClick: () => { this.doCommand({ name: 'toggleVisiblePanes' }) },
});
if (!this.promptOnClose_) {
@@ -353,8 +372,12 @@ class MainScreenComponent extends React.Component {
);
}
const modalLayerStyle = Object.assign({}, styles.modalLayer, { display: this.state.modalLayer.visible ? 'block' : 'none' });
return (
<div style={style}>
<div style={modalLayerStyle}>{this.state.modalLayer.message}</div>
<PromptDialog
autocomplete={promptOptions && ('autocomplete' in promptOptions) ? promptOptions.autocomplete : null}
defaultValue={promptOptions && promptOptions.value ? promptOptions.value : ''}

View File

@@ -9,6 +9,8 @@ const { bridge } = require('electron').remote.require('./bridge');
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const eventManager = require('../eventManager');
const InteropService = require('lib/services/InteropService');
const InteropServiceHelper = require('../InteropServiceHelper.js');
class NoteListComponent extends React.Component {
@@ -91,6 +93,23 @@ class NoteListComponent extends React.Component {
eventManager.emit('noteTypeToggle', { noteId: note.id });
}
}}));
const exportMenu = new Menu();
const ioService = new InteropService();
const ioModules = ioService.modules();
for (let i = 0; i < ioModules.length; i++) {
const module = ioModules[i];
if (module.type !== 'exporter') continue;
exportMenu.append(new MenuItem({ label: module.fullLabel() , click: async () => {
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceNoteIds: noteIds });
}}));
}
const exportMenuItem = new MenuItem({label: _('Export'), submenu: exportMenu});
menu.append(exportMenuItem);
}
menu.append(new MenuItem({label: _('Delete'), click: async () => {

View File

@@ -16,6 +16,7 @@ const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const { shim } = require('lib/shim.js');
const eventManager = require('../eventManager');
const fs = require('fs-extra');
require('brace/mode/markdown');
// https://ace.c9.io/build/kitchen-sink.html
@@ -115,10 +116,14 @@ class NoteTextComponent extends React.Component {
eventManager.removeListener('todoToggle', this.onTodoToggle_);
}
async saveIfNeeded() {
async saveIfNeeded(saveIfNewNote = false) {
const forceSave = saveIfNewNote && (this.state.note && !this.state.note.id);
if (this.scheduleSaveTimeout_) clearTimeout(this.scheduleSaveTimeout_);
this.scheduleSaveTimeout_ = null;
if (!shared.isModified(this)) return;
if (!forceSave) {
if (!shared.isModified(this)) return;
}
await shared.saveNoteButton_press(this);
}
@@ -235,6 +240,10 @@ class NoteTextComponent extends React.Component {
if ('syncStarted' in nextProps && !nextProps.syncStarted && !this.isModified()) {
await this.reloadNote(nextProps, { noReloadIfLocalChanges: true });
}
if (nextProps.windowCommand) {
this.doCommand(nextProps.windowCommand);
}
}
isModified() {
@@ -260,7 +269,7 @@ class NoteTextComponent extends React.Component {
shared.showMetadata_onPress(this);
}
webview_ipcMessage(event) {
async webview_ipcMessage(event) {
const msg = event.channel ? event.channel : '';
const args = event.args;
const arg0 = args && args.length >= 1 ? args[0] : null;
@@ -282,6 +291,32 @@ class NoteTextComponent extends React.Component {
} else if (msg === 'percentScroll') {
this.ignoreNextEditorScroll_ = true;
this.setEditorPercentScroll(arg0);
} else if (msg === 'contextMenu') {
const itemType = arg0 && arg0.type;
const menu = new Menu()
if (itemType === "image" || itemType === "link") {
const resource = await Resource.load(arg0.resourceId);
const resourcePath = Resource.fullPath(resource);
menu.append(new MenuItem({label: _('Open...'), click: async () => {
bridge().openExternal(resourcePath);
}}));
menu.append(new MenuItem({label: _('Save as...'), click: async () => {
const filePath = bridge().showSaveDialog({
defaultPath: resource.filename ? resource.filename : resource.title,
});
if (!filePath) return;
await fs.copy(resourcePath, filePath);
}}));
} else {
reg.logger().error('Unhandled item type: ' + itemType);
return;
}
menu.popup(bridge().window());
} else if (msg.indexOf('joplin://') === 0) {
const resourceId = msg.substr('joplin://'.length);
Resource.load(resourceId).then((resource) => {
@@ -289,10 +324,7 @@ class NoteTextComponent extends React.Component {
bridge().openItem(filePath);
});
} else {
bridge().showMessageBox({
type: 'error',
message: _('Unsupported link or message: %s', msg),
});
bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
}
}
@@ -398,18 +430,47 @@ class NoteTextComponent extends React.Component {
this.scheduleSave();
}
async doCommand(command) {
if (!command) return;
let commandProcessed = true;
if (command.name === 'exportPdf' && this.webview_) {
const path = bridge().showSaveDialog({
filters: [{ name: _('PDF File'), extensions: ['pdf']}]
});
if (path) {
this.webview_.printToPDF({}, (error, data) => {
if (error) {
bridge().showErrorMessageBox(error.message);
} else {
shim.fsDriver().writeFile(path, data, 'buffer');
}
});
}
} else if (command.name === 'print' && this.webview_) {
this.webview_.print();
} else {
commandProcessed = false;
}
if (commandProcessed) {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: null,
});
}
}
async commandAttachFile() {
const noteId = this.props.noteId;
if (!noteId) return;
const filePaths = bridge().showOpenDialog({
properties: ['openFile', 'createDirectory', 'multiSelections'],
});
if (!filePaths || !filePaths.length) return;
await this.saveIfNeeded();
let note = await Note.load(noteId);
await this.saveIfNeeded(true);
let note = await Note.load(this.state.note.id);
for (let i = 0; i < filePaths.length; i++) {
const filePath = filePaths[i];
@@ -427,20 +488,29 @@ class NoteTextComponent extends React.Component {
}
}
commandSetAlarm() {
const noteId = this.props.noteId;
if (!noteId) return;
async commandSetAlarm() {
await this.saveIfNeeded(true);
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: 'editAlarm',
noteId: noteId,
noteId: this.state.note.id,
});
}
async commandSetTags() {
await this.saveIfNeeded(true);
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: 'setTags',
noteId: this.state.note.id,
});
}
itemContextMenu(event) {
const noteId = this.props.noteId;
if (!noteId) return;
const note = this.state.note;
if (!note) return;
const menu = new Menu()
@@ -448,10 +518,16 @@ class NoteTextComponent extends React.Component {
return this.commandAttachFile();
}}));
menu.append(new MenuItem({label: _('Set alarm'), click: async () => {
return this.commandSetAlarm();
menu.append(new MenuItem({label: _('Tags'), click: async () => {
return this.commandSetTags();
}}));
if (!!note.is_todo) {
menu.append(new MenuItem({label: _('Set alarm'), click: async () => {
return this.commandSetAlarm();
}}));
}
menu.popup(bridge().window());
}
@@ -461,6 +537,7 @@ class NoteTextComponent extends React.Component {
const body = note && note.body ? note.body : '';
const theme = themeStyle(this.props.theme);
const visiblePanes = this.props.visiblePanes || ['editor', 'viewer'];
const isTodo = note && !!note.is_todo;
const borderWidth = 1;
@@ -556,7 +633,14 @@ class NoteTextComponent extends React.Component {
},
postMessageSyntax: 'ipcRenderer.sendToHost',
};
const html = this.mdToHtml().render(body, theme, mdOptions);
let bodyToRender = body;
if (!bodyToRender.trim() && visiblePanes.indexOf('viewer') >= 0 && visiblePanes.indexOf('editor') < 0) {
// Fixes https://github.com/laurent22/joplin/issues/217
bodyToRender = '*' + _('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout')) + '*';
}
const html = this.mdToHtml().render(bodyToRender, theme, mdOptions);
this.webview_.send('setHtml', html);
}
@@ -568,6 +652,12 @@ class NoteTextComponent extends React.Component {
onClick: () => { return this.commandAttachFile(); },
});
toolbarItems.push({
title: _('Tags'),
iconName: 'fa-tags',
onClick: () => { return this.commandSetTags(); },
});
if (note.is_todo) {
toolbarItems.push({
title: Note.needAlarm(note) ? time.formatMsToLocal(note.todo_due) : _('Set alarm'),
@@ -588,6 +678,7 @@ class NoteTextComponent extends React.Component {
style={titleEditorStyle}
value={note && note.title ? note.title : ''}
onChange={(event) => { this.title_changeText(event); }}
placeholder={ this.props.newNote ? _('Creating new %s...', isTodo ? _('to-do') : _('note')) : '' }
/>
const titleBarMenuButton = <IconButton style={{
@@ -655,6 +746,7 @@ const mapStateToProps = (state) => {
showAdvancedOptions: state.settings.showAdvancedOptions,
syncStarted: state.syncStarted,
newNote: state.newNote,
windowCommand: state.windowCommand,
};
};

View File

@@ -54,10 +54,7 @@ class OneDriveLoginScreenComponent extends React.Component {
this.props.dispatch({ type: 'NAV_BACK' });
reg.scheduleSync(0);
} catch (error) {
bridge().showMessageBox({
type: 'error',
message: 'Could not login to OneDrive. Please try again.\n\n' + error.message + "\n\n" + url.match(/.{1,64}/g).join('\n'),
});
bridge().showErrorMessageBox('Could not login to OneDrive. Please try again.\n\n' + error.message + "\n\n" + url.match(/.{1,64}/g).join('\n'));
}
this.authCode_ = null;

View File

@@ -1,19 +1,19 @@
const React = require('react');
const { connect } = require('react-redux');
const shared = require('lib/components/shared/side-menu-shared.js');
const { Synchronizer } = require('lib/synchronizer.js');
const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const { _ } = require('lib/locale.js');
const { themeStyle } = require('../theme.js');
const { bridge } = require('electron').remote.require('./bridge');
const React = require("react");
const { connect } = require("react-redux");
const shared = require("lib/components/shared/side-menu-shared.js");
const { Synchronizer } = require("lib/synchronizer.js");
const BaseModel = require("lib/BaseModel.js");
const Folder = require("lib/models/Folder.js");
const Note = require("lib/models/Note.js");
const Tag = require("lib/models/Tag.js");
const { _ } = require("lib/locale.js");
const { themeStyle } = require("../theme.js");
const { bridge } = require("electron").remote.require("./bridge");
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const InteropServiceHelper = require("../InteropServiceHelper.js");
class SideBarComponent extends React.Component {
style() {
const theme = themeStyle(this.props.theme);
@@ -27,63 +27,65 @@ class SideBarComponent extends React.Component {
height: itemHeight,
fontFamily: theme.fontFamily,
fontSize: theme.fontSize,
textDecoration: 'none',
boxSizing: 'border-box',
textDecoration: "none",
boxSizing: "border-box",
color: theme.color2,
paddingLeft: 14,
display: 'flex',
alignItems: 'center',
cursor: 'default',
display: "flex",
alignItems: "center",
cursor: "default",
opacity: 0.8,
whiteSpace: 'nowrap',
whiteSpace: "nowrap",
},
listItemSelected: {
backgroundColor: theme.selectedColor2,
},
conflictFolder: {
color: theme.colorError2,
fontWeight: 'bold',
fontWeight: "bold",
},
header: {
height: itemHeight * 1.8,
fontFamily: theme.fontFamily,
fontSize: theme.fontSize * 1.3,
textDecoration: 'none',
boxSizing: 'border-box',
textDecoration: "none",
boxSizing: "border-box",
color: theme.color2,
paddingLeft: 8,
display: 'flex',
alignItems: 'center',
display: "flex",
alignItems: "center",
},
button: {
padding: 6,
fontFamily: theme.fontFamily,
fontSize: theme.fontSize,
textDecoration: 'none',
boxSizing: 'border-box',
textDecoration: "none",
boxSizing: "border-box",
color: theme.color2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
display: "flex",
alignItems: "center",
justifyContent: "center",
border: "1px solid rgba(255,255,255,0.2)",
marginTop: 10,
marginLeft: 5,
marginRight: 5,
cursor: 'default',
cursor: "default",
},
syncReport: {
fontFamily: theme.fontFamily,
fontSize: Math.round(theme.fontSize * .9),
fontSize: Math.round(theme.fontSize * 0.9),
color: theme.color2,
opacity: .5,
display: 'flex',
alignItems: 'left',
justifyContent: 'top',
flexDirection: 'column',
opacity: 0.5,
display: "flex",
alignItems: "left",
justifyContent: "top",
flexDirection: "column",
marginTop: 10,
marginLeft: 5,
marginRight: 5,
minHeight: 70,
wordWrap: "break-word",
width: "100%",
},
};
@@ -91,19 +93,19 @@ class SideBarComponent extends React.Component {
}
itemContextMenu(event) {
const itemId = event.target.getAttribute('data-id');
const itemId = event.target.getAttribute("data-id");
if (itemId === Folder.conflictFolderId()) return;
const itemType = Number(event.target.getAttribute('data-type'));
if (!itemId || !itemType) throw new Error('No data on element');
let deleteMessage = '';
const itemType = Number(event.target.getAttribute("data-type"));
if (!itemId || !itemType) throw new Error("No data on element");
let deleteMessage = "";
if (itemType === BaseModel.TYPE_FOLDER) {
deleteMessage = _('Delete notebook? All notes within this notebook will also be deleted.');
deleteMessage = _("Delete notebook? All notes within this notebook will also be deleted.");
} else if (itemType === BaseModel.TYPE_TAG) {
deleteMessage = _('Remove this tag from all the notes?');
deleteMessage = _("Remove this tag from all the notes?");
} else if (itemType === BaseModel.TYPE_SEARCH) {
deleteMessage = _('Remove this search from the sidebar?');
deleteMessage = _("Remove this search from the sidebar?");
}
const menu = new Menu();
@@ -113,30 +115,55 @@ class SideBarComponent extends React.Component {
item = BaseModel.byId(this.props.folders, itemId);
}
menu.append(new MenuItem({label: _('Delete'), click: async () => {
const ok = bridge().showConfirmMessageBox(deleteMessage);
if (!ok) return;
menu.append(
new MenuItem({
label: _("Delete"),
click: async () => {
const ok = bridge().showConfirmMessageBox(deleteMessage);
if (!ok) return;
if (itemType === BaseModel.TYPE_FOLDER) {
await Folder.delete(itemId);
} else if (itemType === BaseModel.TYPE_TAG) {
await Tag.untagAll(itemId);
} else if (itemType === BaseModel.TYPE_SEARCH) {
this.props.dispatch({
type: 'SEARCH_DELETE',
id: itemId,
});
}
}}))
if (itemType === BaseModel.TYPE_FOLDER) {
await Folder.delete(itemId);
} else if (itemType === BaseModel.TYPE_TAG) {
await Tag.untagAll(itemId);
} else if (itemType === BaseModel.TYPE_SEARCH) {
this.props.dispatch({
type: "SEARCH_DELETE",
id: itemId,
});
}
},
})
);
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
menu.append(new MenuItem({label: _('Rename'), click: async () => {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: 'renameFolder',
id: itemId,
});
}}))
menu.append(
new MenuItem({
label: _("Rename"),
click: async () => {
this.props.dispatch({
type: "WINDOW_COMMAND",
name: "renameFolder",
id: itemId,
});
},
})
);
menu.append(new MenuItem({ type: "separator" }));
const InteropService = require("lib/services/InteropService.js");
menu.append(
new MenuItem({
label: _("Export"),
click: async () => {
const ioService = new InteropService();
const module = ioService.moduleByFormat_("exporter", "jex");
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
},
})
);
}
menu.popup(bridge().window());
@@ -144,21 +171,21 @@ class SideBarComponent extends React.Component {
folderItem_click(folder) {
this.props.dispatch({
type: 'FOLDER_SELECT',
type: "FOLDER_SELECT",
id: folder ? folder.id : null,
});
}
tagItem_click(tag) {
this.props.dispatch({
type: 'TAG_SELECT',
type: "TAG_SELECT",
id: tag ? tag.id : null,
});
}
searchItem_click(search) {
this.props.dispatch({
type: 'SEARCH_SELECT',
type: "SEARCH_SELECT",
id: search ? search.id : null,
});
}
@@ -173,105 +200,180 @@ class SideBarComponent extends React.Component {
if (folder.id === Folder.conflictFolderId()) style = Object.assign(style, this.style().conflictFolder);
const onDragOver = (event, folder) => {
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault();
}
if (event.dataTransfer.types.indexOf("text/x-jop-note-ids") >= 0) event.preventDefault();
};
const onDrop = async (event, folder) => {
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') < 0) return;
if (event.dataTransfer.types.indexOf("text/x-jop-note-ids") < 0) return;
event.preventDefault();
const noteIds = JSON.parse(event.dataTransfer.getData('text/x-jop-note-ids'));
const noteIds = JSON.parse(event.dataTransfer.getData("text/x-jop-note-ids"));
for (let i = 0; i < noteIds.length; i++) {
await Note.moveToFolder(noteIds[i], folder.id);
}
}
};
const itemTitle = Folder.displayTitle(folder);
return <a
className="list-item"
onDragOver={(event) => { onDragOver(event, folder) } }
onDrop={(event) => { onDrop(event, folder) } }
href="#"
data-id={folder.id}
data-type={BaseModel.TYPE_FOLDER}
onContextMenu={(event) => this.itemContextMenu(event)}
key={folder.id}
style={style} onClick={() => {this.folderItem_click(folder)}}>{itemTitle}
</a>
return (
<a
className="list-item"
onDragOver={event => {
onDragOver(event, folder);
}}
onDrop={event => {
onDrop(event, folder);
}}
href="#"
data-id={folder.id}
data-type={BaseModel.TYPE_FOLDER}
onContextMenu={event => this.itemContextMenu(event)}
key={folder.id}
style={style}
onClick={() => {
this.folderItem_click(folder);
}}
>
{itemTitle}
</a>
);
}
tagItem(tag, selected) {
let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected);
return <a className="list-item" href="#" data-id={tag.id} data-type={BaseModel.TYPE_TAG} onContextMenu={(event) => this.itemContextMenu(event)} key={tag.id} style={style} onClick={() => {this.tagItem_click(tag)}}>{Tag.displayTitle(tag)}</a>
return (
<a
className="list-item"
href="#"
data-id={tag.id}
data-type={BaseModel.TYPE_TAG}
onContextMenu={event => this.itemContextMenu(event)}
key={tag.id}
style={style}
onClick={() => {
this.tagItem_click(tag);
}}
>
{Tag.displayTitle(tag)}
</a>
);
}
searchItem(search, selected) {
let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected);
return <a className="list-item" href="#" data-id={search.id} data-type={BaseModel.TYPE_SEARCH} onContextMenu={(event) => this.itemContextMenu(event)} key={search.id} style={style} onClick={() => {this.searchItem_click(search)}}>{search.title}</a>
return (
<a
className="list-item"
href="#"
data-id={search.id}
data-type={BaseModel.TYPE_SEARCH}
onContextMenu={event => this.itemContextMenu(event)}
key={search.id}
style={style}
onClick={() => {
this.searchItem_click(search);
}}
>
{search.title}
</a>
);
}
makeDivider(key) {
return <div style={{height:2, backgroundColor:'blue' }} key={key}></div>
return <div style={{ height: 2, backgroundColor: "blue" }} key={key} />;
}
makeHeader(key, label, iconName) {
const style = this.style().header;
const icon = <i style={{fontSize: style.fontSize * 1.2, marginRight: 5}} className={"fa " + iconName}></i>
return <div style={style} key={key}>{icon}{label}</div>
const icon = <i style={{ fontSize: style.fontSize * 1.2, marginRight: 5 }} className={"fa " + iconName} />;
return (
<div style={style} key={key}>
{icon}
{label}
</div>
);
}
synchronizeButton(type) {
const style = this.style().button;
const iconName = type === 'sync' ? 'fa-refresh' : 'fa-times';
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
const icon = <i style={{fontSize: style.fontSize, marginRight: 5}} className={"fa " + iconName}></i>
return <a className="synchronize-button" style={style} href="#" key="sync_button" onClick={() => {this.sync_click()}}>{icon}{label}</a>
const iconName = type === "sync" ? "fa-refresh" : "fa-times";
const label = type === "sync" ? _("Synchronise") : _("Cancel");
const icon = <i style={{ fontSize: style.fontSize, marginRight: 5 }} className={"fa " + iconName} />;
return (
<a
className="synchronize-button"
style={style}
href="#"
key="sync_button"
onClick={() => {
this.sync_click();
}}
>
{icon}
{label}
</a>
);
}
render() {
const theme = themeStyle(this.props.theme);
const style = Object.assign({}, this.style().root, this.props.style, {
overflowX: 'hidden',
overflowY: 'auto',
overflowX: "hidden",
overflowY: "auto",
});
let items = [];
items.push(this.makeHeader('folderHeader', _('Notebooks'), 'fa-folder-o'));
items.push(this.makeHeader("folderHeader", _("Notebooks"), "fa-folder-o"));
if (this.props.folders.length) {
const folderItems = shared.renderFolders(this.props, this.folderItem.bind(this));
items = items.concat(folderItems);
}
items.push(this.makeHeader('tagHeader', _('Tags'), 'fa-tags'));
items.push(this.makeHeader("tagHeader", _("Tags"), "fa-tags"));
if (this.props.tags.length) {
const tagItems = shared.renderTags(this.props, this.tagItem.bind(this));
items.push(<div className="tags" key="tag_items">{tagItems}</div>);
items.push(
<div className="tags" key="tag_items">
{tagItems}
</div>
);
}
if (this.props.searches.length) {
items.push(this.makeHeader('searchHeader', _('Searches'), 'fa-search'));
items.push(this.makeHeader("searchHeader", _("Searches"), "fa-search"));
const searchItems = shared.renderSearches(this.props, this.searchItem.bind(this));
items.push(<div className="searches" key="search_items">{searchItems}</div>);
items.push(
<div className="searches" key="search_items">
{searchItems}
</div>
);
}
let lines = Synchronizer.reportToLines(this.props.syncReport);
const syncReportText = [];
for (let i = 0; i < lines.length; i++) {
syncReportText.push(<div key={i}>{lines[i]}</div>);
syncReportText.push(
<div key={i} style={{ wordWrap: "break-word", width: "100%" }}>
{lines[i]}
</div>
);
}
items.push(this.synchronizeButton(this.props.syncStarted ? 'cancel' : 'sync'));
items.push(this.synchronizeButton(this.props.syncStarted ? "cancel" : "sync"));
items.push(<div style={this.style().syncReport} key='sync_report'>{syncReportText}</div>);
items.push(
<div style={this.style().syncReport} key="sync_report">
{syncReportText}
</div>
);
return (
<div className="side-bar" style={style}>
@@ -279,10 +381,9 @@ class SideBarComponent extends React.Component {
</div>
);
}
}
const mapStateToProps = (state) => {
const mapStateToProps = state => {
return {
folders: state.folders,
tags: state.tags,
@@ -300,4 +401,4 @@ const mapStateToProps = (state) => {
const SideBar = connect(mapStateToProps)(SideBarComponent);
module.exports = { SideBar };
module.exports = { SideBar };

View File

@@ -180,6 +180,20 @@
ipcRenderer.sendToHost('percentScroll', percent);
});
document.addEventListener('contextmenu', function(event) {
let element = event.target;
// To handle right clicks on resource icons
if (element && !element.getAttribute('data-resource-id')) element = element.parentElement;
if (element && element.getAttribute('data-resource-id')) {
ipcRenderer.sendToHost('contextMenu', {
type: element.getAttribute('src') ? 'image' : 'link',
resourceId: element.getAttribute('data-resource-id'),
});
}
});
// Disable drag and drop otherwise it's possible to drop a URL
// on it and it will open in the view as a website.
document.addEventListener('drop', function(e) {

View File

@@ -17,11 +17,6 @@
.smalltalk .page {
max-width: 30em;
}
.ace_editor * {
/* Necessary to make sure Russian text is displayed properly */
/* https://github.com/laurent22/joplin/issues/155 */
font-family: monospace !important;
}
</style>
</head>
<body>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
var locales = {};
locales['en_GB'] = require('./en_GB.json');
locales['de_DE'] = require('./de_DE.json');
locales['es_CR'] = require('./es_CR.json');
locales['es_ES'] = require('./es_ES.json');
locales['eu'] = require('./eu.json');
locales['fr_FR'] = require('./fr_FR.json');
locales['hr_HR'] = require('./hr_HR.json');
locales['it_IT'] = require('./it_IT.json');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -64,11 +64,17 @@ document.addEventListener('click', (event) => event.preventDefault());
app().start(bridge().processArgv()).then(() => {
require('./gui/Root.min.js');
}).catch((error) => {
// If something goes wrong at this stage we don't have a console or a log file
// so display the error in a message box.
let msg = ['Fatal error:', error.message];
if (error.fileName) msg.push(error.fileName);
if (error.lineNumber) msg.push(error.lineNumber);
if (error.stack) msg.push(error.stack);
bridge().showErrorMessageBox(msg.join('\n'));
if (error.code == 'flagError') {
bridge().showErrorMessageBox(error.message);
} else {
// If something goes wrong at this stage we don't have a console or a log file
// so display the error in a message box.
let msg = ['Fatal error:', error.message];
if (error.fileName) msg.push(error.fileName);
if (error.lineNumber) msg.push(error.lineNumber);
if (error.stack) msg.push(error.stack);
bridge().showErrorMessageBox(msg.join('\n\n'));
}
bridge().electronApp().exit(1);
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "0.10.55",
"version": "1.0.73",
"description": "Joplin for Desktop",
"main": "main.js",
"scripts": {
@@ -22,6 +22,9 @@
},
"build": {
"appId": "net.cozic.joplin-desktop",
"extraResources": [
"build/icons/*"
],
"win": {
"icon": "../../Assets/Joplin.ico"
},
@@ -40,10 +43,12 @@
},
"homepage": "https://github.com/laurent22/joplin#readme",
"devDependencies": {
"ajv": "^6.2.0",
"app-builder-bin": "^1.5.0",
"babel-cli": "^6.26.0",
"babel-preset-react": "^6.24.1",
"electron": "^1.7.11",
"electron-builder": "^19.45.4"
"electron-builder": "^20.2.0"
},
"optionalDependencies": {
"7zip-bin-mac": "^1.0.1",
@@ -52,10 +57,10 @@
},
"dependencies": {
"app-module-path": "^2.2.0",
"async-mutex": "^0.1.3",
"base-64": "^0.1.0",
"compare-versions": "^3.1.0",
"electron-context-menu": "^0.9.1",
"electron-log": "^2.2.11",
"electron-updater": "^2.16.1",
"electron-window-state": "^4.1.1",
"follow-redirects": "^1.2.5",
"form-data": "^2.3.1",
@@ -67,6 +72,7 @@
"levenshtein": "^1.0.5",
"lodash": "^4.17.4",
"markdown-it": "^8.4.0",
"markdown-it-katex": "^2.0.3",
"md5": "^2.2.1",
"mime": "^2.0.3",
"moment": "^2.19.1",
@@ -86,6 +92,7 @@
"sqlite3": "^3.1.13",
"string-padding": "^1.0.2",
"string-to-stream": "^1.1.0",
"tar": "^4.4.0",
"tcp-port-used": "^0.1.2",
"url-parse": "^1.2.0",
"uuid": "^3.1.0",

View File

@@ -7,6 +7,7 @@ const globalStyle = {
itemMarginTop: 10,
itemMarginBottom: 10,
backgroundColor: "#ffffff",
backgroundColorTransparent: 'rgba(255,255,255,0.9)',
oddBackgroundColor: "#dddddd",
color: "#222222", // For regular text
colorError: "red",

View File

@@ -79,6 +79,10 @@ async function main(argv) {
const macOsUrl = downloadUrl(release, 'macos');
const linuxUrl = downloadUrl(release, 'linux');
console.info('Windows: ', winUrl);
console.info('macOS: ', macOsUrl);
console.info('Linux: ', linuxUrl);
let content = readmeContent();
if (winUrl) content = content.replace(/(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.exe)/, winUrl);
@@ -86,6 +90,8 @@ async function main(argv) {
if (linuxUrl) content = content.replace(/(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.AppImage)/, linuxUrl);
setReadmeContent(content);
console.info("git pull && git add -A && git commit -m 'Update readme downloads' && git push")
}
main(process.argv).catch((error) => {

View File

@@ -1,9 +0,0 @@
License MIT
Copyright (c) 2016-2018 Laurent Cozic
L'autorisation est accordée, gracieusement, à toute personne acquérant une copie de ce logiciel et des fichiers de documentation associés (le "Logiciel"), de commercialiser le Logiciel sans restriction, notamment les droits d'utiliser, de copier, de modifier, de fusionner, de publier, de distribuer, de sous-licencier et/ou de vendre des copies du Logiciel, ainsi que d'autoriser les personnes auxquelles le Logiciel est fourni à le faire, sous réserve des conditions suivantes :
La déclaration de copyright ci-dessus et la présente autorisation doivent être incluses dans toutes copies ou parties substantielles du Logiciel.
LE LOGICIEL EST FOURNI "TEL QUEL", SANS GARANTIE D'AUCUNE SORTE, EXPLICITE OU IMPLICITE, NOTAMMENT SANS GARANTIE DE QUALITÉ MARCHANDE, D’ADÉQUATION À UN USAGE PARTICULIER ET D'ABSENCE DE CONTREFAÇON. EN AUCUN CAS, LES AUTEURS OU TITULAIRES DU DROIT D'AUTEUR NE SERONT RESPONSABLES DE TOUT DOMMAGE, RÉCLAMATION OU AUTRE RESPONSABILITÉ, QUE CE SOIT DANS LE CADRE D'UN CONTRAT, D'UN DÉLIT OU AUTRE, EN PROVENANCE DE, CONSÉCUTIF À OU EN RELATION AVEC LE LOGICIEL OU SON UTILISATION, OU AVEC D'AUTRES ÉLÉMENTS DU LOGICIEL.

151
README.md
View File

@@ -1,12 +1,12 @@
# Joplin
Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in [Markdown format](https://daringfireball.net/projects/markdown/basics).
Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in [Markdown format](#markdown).
Notes exported from Evernote via .enex files [can be imported](#importing-notes-from-evernote) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.).
Notes exported from Evernote via .enex files [can be imported](#importing) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported.
The notes can be [synchronised](#synchronisation) with various targets including [Nextcloud](https://nextcloud.com/), the file system (for example with a network directory) or with Microsoft OneDrive. When synchronising the notes, notebooks, tags and other metadata are saved to plain text files which can be easily inspected, backed up and moved around.
Joplin is still under development but is out of Beta and should be suitable for every day use. The UI of the terminal client is built on top of the great [terminal-kit](https://github.com/cronvel/terminal-kit) library, the desktop client using [Electron](https://electronjs.org/), and the Android client front end is done using [React Native](https://facebook.github.io/react-native/).
The UI of the terminal client is built on top of the great [terminal-kit](https://github.com/cronvel/terminal-kit) library, the desktop client using [Electron](https://electronjs.org/), and the Android client front end is done using [React Native](https://facebook.github.io/react-native/).
<div class="top-screenshot"><img src="https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/AllClients.jpg" style="max-width: 100%; max-height: 35em;"></div>
@@ -18,15 +18,15 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
Operating System | Download
-----------------|--------
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.54/Joplin-Setup-0.10.54.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.54/Joplin-0.10.54.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.54/Joplin-0.10.54-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.70/Joplin-Setup-1.0.70.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.70/Joplin-1.0.70.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.70/Joplin-1.0.70-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
## Mobile applications
Operating System | Download | Alt. Download
-----------------|----------|----------------
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin/releases/download/android-v0.10.81/joplin-v0.10.81.apk)
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.107/joplin-v1.0.107.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeIOS.png'/></a> | -
## Terminal application
@@ -51,18 +51,23 @@ For usage information, please refer to the full [Joplin Terminal Application Doc
# Features
- Desktop, mobile and terminal applications.
- End To End Encryption (E2EE)
- Synchronisation with various services, including NextCloud, WebDAV and OneDrive. Dropbox is planned.
- Import Enex files (Evernote export format) and Markdown files.
- Export JEX files (Joplin Export format) and raw files.
- Support notes, to-dos, tags and notebooks.
- Sort notes by multiple criteria - title, updated time, etc.
- Support for alarms (notifications) in mobile and desktop applications.
- Offline first, so the entire data is always available on the device even without an internet connection.
- Ability to synchronise with multiple targets, including NextCloud, the file system and OneDrive (Dropbox is planned).
- Synchronises to a plain text format, which can be easily manipulated, backed up, or exported to a different format.
- Markdown notes, which are rendered with images and formatting in the desktop and mobile applications.
- Tag support
- File attachment support (images are displayed, and other files are linked and can be opened in the relevant application).
- Markdown notes, which are rendered with images and formatting in the desktop and mobile applications. Support for extra features such as math notation and checkboxes.
- File attachment support - images are displayed, and other files are linked and can be opened in the relevant application.
- Search functionality.
- Geo-location support.
- Supports multiple languages
# Importing notes from Evernote
# Importing
## Importing from Evernote
Joplin was designed as a replacement for Evernote and so can import complete Evernote notebooks, as well as notes, tags, resources (attached files) and note metadata (such as author, geo-location, etc.) via ENEX files. In terms of data, the only two things that might slightly differ are:
@@ -72,17 +77,29 @@ Joplin was designed as a replacement for Evernote and so can import complete Eve
To import Evernote data, first export your Evernote notebooks to ENEX files as described [here](https://help.evernote.com/hc/en-us/articles/209005557-How-to-back-up-export-and-restore-import-notes-and-notebooks). Then follow these steps:
On the **desktop application**, open the "File" menu, click "Import Evernote notes" and select your ENEX file. This will open a new screen which will display the import progress. The notes will be imported into a new separate notebook (so that, in case of a mistake, the notes are not mixed up with any existing notes). If needed then can then be moved to a different notebook, or the notebook can be renamed, etc.
On the **desktop application**, open File > Import > ENEX and select your file. The notes will be imported into a new separate notebook. If needed they can then be moved to a different notebook, or the notebook can be renamed, etc.
On the **terminal application**, in [command-line mode](/terminal#command-line-mode), type `import-enex /path/to/file.enex`. This will import the notes into a new notebook named after the filename.
On the **terminal application**, in [command-line mode](/terminal#command-line-mode), type `import /path/to/file.enex`. This will import the notes into a new notebook named after the filename.
# Importing notes from other applications
## Importing from Markdown files
Joplin can import notes from plain Markdown file. You can either import a complete directory of Markdown files or individual files.
On the **desktop application**, open File > Import > MD and select your Markdown file or directory.
On the **terminal application**, in [command-line mode](/terminal#command-line-mode), type `import --format md /path/to/file.md` or `import --format md /path/to/directory/`.
## Importing from other applications
In general the way to import notes from any application into Joplin is to convert the notes to ENEX files (Evernote format) and to import these ENEX files into Joplin using the method above. Most note-taking applications support ENEX files so it should be relatively straightforward. For help about specific applications, see below:
* Standard Notes: Please see [this tutorial](https://programadorwebvalencia.com/migrate-notes-from-standard-notes-to-joplin/)
* Tomboy Notes: Export the notes to ENEX files [as described here](https://askubuntu.com/questions/243691/how-can-i-export-my-tomboy-notes-into-evernote/608551) for example, and import these ENEX files into Joplin.
# Exporting
Joplin can export to the JEX format (Joplin Export file), which is a tar file that can contain multiple notes, notebooks, etc. This is a lossless format in that all the notes, but also metadata such as geo-location, updated time, tags, etc. are preserved. This format is convenient for backup purposes and can be re-imported into Joplin. A "raw" format is also available. This is the same as the JEX format except that the data is saved to a directory and each item represented by a single file.
# Synchronisation
One of the goals of Joplin was to avoid being tied to any particular company or service, whether it is Evernote, Google or Microsoft. As such the synchronisation is designed without any hard dependency to any particular service. Most of the synchronisation process is done at an abstract level and access to external services, such as Nextcloud or OneDrive, is done via lightweight drivers. It is easy to support new services by creating simple drivers that provide a filesystem-like interface, i.e. the ability to read, write, delete and list items. It is also simple to switch from one service to another or to even sync to multiple services at once. Each note, notebook, tags, as well as the relation between items is transmitted as plain text files during synchronisation, which means the data can also be moved to a different application, can be easily backed up, inspected, etc.
@@ -91,8 +108,6 @@ Currently, synchronisation is possible with Nextcloud and OneDrive (by default)
## Nextcloud synchronisation
**Important: This is a beta feature. It has been extensively tested and is already in use by some users, but it is possible that some bugs remain. If you wish to you use it, it is recommended that you keep a backup of your data. The simplest way is to regularly backup the profile directory of the desktop or terminal application.**
On the **desktop application** or **mobile application**, go to the config screen and select Nextcloud as the synchronisation target. Then input [the WebDAV URL](https://docs.nextcloud.com/server/9/user_manual/files/access_webdav.html), this is normally `https://example.com/nextcloud/remote.php/dav/files/USERNAME/Joplin` (make sure to create the "Joplin" directory in Nextcloud and to replace USERNAME by your Nextcloud username), and set the username and password.
On the **terminal application**, you will need to set the `sync.target` config variable and all the `sync.5.path`, `sync.5.username` and `sync.5.password` config variables to, respectively the Nextcloud WebDAV URL, your username and your password. This can be done from the command line mode using:
@@ -106,10 +121,17 @@ If synchronisation does not work, please consult the logs in the app profile dir
## WebDAV synchronisation
**Important: This is a beta feature. It has been extensively tested and is already in use by some users, but it is possible that some bugs remain. If you wish to you use it, it is recommended that you keep a backup of your data. The simplest way is to regularly backup the profile directory of the desktop or terminal application.**
Select the "WebDAV" synchronisation target and follow the same instructions as for Nextcloud above.
WebDAV-compatible services that are known to work with Joplin:
- [Box.com](https://www.box.com/)
- [DriveHQ](https://www.drivehq.com)
- [OwnCloud](https://owncloud.org/)
- [Seafile](https://www.seafile.com/)
- [Stack](https://www.transip.nl/stack/)
- [Zimbra](https://www.zimbra.com/)
## OneDrive synchronisation
When syncing with OneDrive, Joplin creates a sub-directory in OneDrive, in /Apps/Joplin and read/write the notes and notebooks from it. The application does not have access to anything outside this directory.
@@ -144,6 +166,50 @@ On mobile, the alarms will be displayed using the built-in notification system.
If for any reason the notifications do not work, please [open an issue](https://github.com/laurent22/joplin/issues).
# Markdown
Joplin uses and renders [Github-flavoured Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) with a few variations and additions. In particular:
## Math notation
Math expressions can be added using the [Katex notation](https://khan.github.io/KaTeX/). To add an inline equation, wrap the expression in `$EXPRESSION$`, eg. `$\sqrt{3x-1}+(1+x)^2$`. To create an expression block, wrap it as follow:
$$
EXPRESSION
$$
For example:
$$
f(x) = \int_{-\infty}^\infty
\hat f(\xi)\,e^{2 \pi i \xi x}
\,d\xi
$$
Here is an example with the Markdown and rendered result side by side:
<img src="https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/Katex.png" style="max-width: 100%; max-height: 35em;">
## Checkboxes
Checkboxes can be added like so:
- [ ] Milk
- [ ] Rice
- [ ] Eggs
The checkboxes can then be ticked in the mobile and desktop applications.
# Donations
Donations to Joplin support the development of the project. Developing quality applications mostly takes time, but there are also some expenses, such as digital certificates to sign the applications, app store fees, hosting, etc. Most of all, your donation will make it possible to keep up the current development standard.
Please see the [donation page](http://joplin.cozic.net/donate/) for information on how to support the development of Joplin.
# Contributing
Please see the guide for information on how to contribute to the development of Joplin: https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md
# Localisation
Joplin is currently available in the languages below. If you would like to contribute a **new translation**, it is quite straightforward, please follow these steps:
@@ -153,49 +219,38 @@ Joplin is currently available in the languages below. If you would like to contr
- In Poedit, open this .pot file, go into the Catalog menu and click Configuration. Change "Country" and "Language" to your own country and language.
- From then you can translate the file. Once it is done, please either [open a pull request](https://github.com/laurent22/joplin/pulls) or send the file to [this address](https://raw.githubusercontent.com/laurent22/joplin/master/Assets/Adresse.png).
To **update a translation**, follow the same steps as above but instead of getting the .pot file, get the .po file for your language from there: https://github.com/laurent22/joplin/tree/master/CliClient/locales
This translation will apply to the three applications - desktop, mobile and terminal.
To **update a translation**, follow the same steps as above but instead of getting the .pot file, get the .po file for your language from the table below.
Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Code | Last translator | Percent done
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png) | Croatian | hr_HR | Hrvoje Mandić <trbuhom@net.hr> | 72%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png) | Deutsch | de_DE | Tobias Strobel <git@strobeltobias.de> | 92%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/gb.png) | English | en_GB | | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/es.png) | Español | es_ES | Lucas Vieites | 80%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cr.png) | Español (Costa Rica) | es_CR | | 68%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/fr.png) | Français | fr_FR | Laurent Cozic | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/it.png) | Italiano | it_IT | | 76%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/be.png) | Nederlands | nl_BE | | 90%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/br.png) | Português (Brasil) | pt_BR | | 74%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png) | Русский | ru_RU | Artyom Karlov <artyom.karlov@gmail.com> | 96%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png) | 中文 (简体) | zh_CN | RCJacH <RCJacH@outlook.com> | 76%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/jp.png) | 日本語 | ja_JP | | 74%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 80%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić <trbuhom@net.hr> | 65%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Tobias Strobel <git@strobeltobias.de> | 97%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/gb.png) | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín <f@mrtn.es> | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 66%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 80%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos <rnbastos@gmail.com> | 98%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov <artyom.karlov@gmail.com> | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | RCJacH <RCJacH@outlook.com> | 66%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 65%
<!-- LOCALE-TABLE-AUTO-GENERATED -->
# Contributing
Please see the guide for information on how to contribute to the development of Joplin: https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md
# Coming features
- Mobile: manage tags
- Windows: Tray icon
- Desktop apps: Tag auto-complete
- Desktop apps: Dark theme
- Linux: Enable auto-update for desktop app
# Known bugs
- Non-alphabetical characters such as Chinese or Arabic might create glitches in the terminal on Windows. This is a limitation of the current Windows console.
- Auto-update is not working in the Linux desktop application.
- While the mobile can sync and load tags, it is not currently possible to create new ones. The desktop and terminal apps can create, delete and edit tags.
# License
MIT License
Copyright (c) 2016-2018 Laurent Cozic
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,4 +0,0 @@
# When I open a note in vim, the cursor is not visible
It seems to be due to the setting `set term=ansi` in .vimrc. Removing it should fix the issue. See https://github.com/laurent22/joplin/issues/147 for more information.

View File

@@ -90,8 +90,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion 16
targetSdkVersion 22
versionCode 2097259
versionName "0.10.81"
versionCode 2097285
versionName "1.0.107"
ndk {
abiFilters "armeabi-v7a", "x86"
}

View File

@@ -1,51 +1,3 @@
const { main } = require('./main.js');
main();
// const React = require('react'); const Component = React.Component;
// import {
// AppRegistry,
// StyleSheet,
// Text,
// View
// } from 'react-native';
// module.exports = default class Joplin extends Component {;
// render() {
// return (
// <View style={styles.container}>
// <Text style={styles.welcome}>
// Welcome to React Native!
// </Text>
// <Text style={styles.instructions}>
// To get started, edit index.ios.js
// </Text>
// <Text style={styles.instructions}>
// Press Cmd+R to reload,{'\n'}
// Cmd+D or shake for dev menu
// </Text>
// </View>
// );
// }
// }
// const styles = StyleSheet.create({
// container: {
// flex: 1,
// justifyContent: 'center',
// alignItems: 'center',
// backgroundColor: '#F5FCFF',
// },
// welcome: {
// fontSize: 20,
// textAlign: 'center',
// margin: 10,
// },
// instructions: {
// textAlign: 'center',
// color: '#333333',
// marginBottom: 5,
// },
// });
// AppRegistry.registerComponent('Joplin', () => Joplin);
main();

View File

@@ -1302,7 +1302,7 @@
PRODUCT_NAME = Joplin;
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
TARGETED_DEVICE_FAMILY = 1;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
@@ -1342,7 +1342,7 @@
PRODUCT_NAME = Joplin;
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
TARGETED_DEVICE_FAMILY = 1;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;

View File

@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.10.9</string>
<string>1.0.13</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>9</string>
<string>13</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

View File

@@ -15,7 +15,6 @@ ArrayUtils.removeElement = function(array, element) {
// https://stackoverflow.com/a/10264318/561309
ArrayUtils.binarySearch = function(items, value) {
var startIndex = 0,
stopIndex = items.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
@@ -37,4 +36,13 @@ ArrayUtils.binarySearch = function(items, value) {
return (items[middle] != value) ? -1 : middle;
}
ArrayUtils.findByKey = function(array, key, value) {
for (let i = 0; i < array.length; i++) {
const o = array[i];
if (typeof o !== 'object') continue;
if (o[key] === value) return o;
}
return null;
}
module.exports = ArrayUtils;

View File

@@ -1,5 +1,5 @@
const { createStore, applyMiddleware } = require('redux');
const { reducer, defaultState } = require('lib/reducer.js');
const { reducer, defaultState, stateUtils } = require('lib/reducer.js');
const { JoplinDatabase } = require('lib/joplin-database.js');
const { Database } = require('lib/database.js');
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
@@ -21,6 +21,7 @@ const { shim } = require('lib/shim.js');
const { _, setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js');
const os = require('os');
const fs = require('fs-extra');
const JoplinError = require('lib/JoplinError');
const EventEmitter = require('events');
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
@@ -95,14 +96,14 @@ class BaseApplication {
let nextArg = argv.length >= 2 ? argv[1] : null;
if (arg == '--profile') {
if (!nextArg) throw new Error(_('Usage: %s', '--profile <dir-path>'));
if (!nextArg) throw new JoplinError(_('Usage: %s', '--profile <dir-path>'), 'flagError');
matched.profileDir = nextArg;
argv.splice(0, 2);
continue;
}
if (arg == '--env') {
if (!nextArg) throw new Error(_('Usage: %s', '--env <dev|prod>'));
if (!nextArg) throw new JoplinError(_('Usage: %s', '--env <dev|prod>'), 'flagError');
matched.env = nextArg;
argv.splice(0, 2);
continue;
@@ -133,14 +134,14 @@ class BaseApplication {
}
if (arg == '--log-level') {
if (!nextArg) throw new Error(_('Usage: %s', '--log-level <none|error|warn|info|debug>'));
if (!nextArg) throw new JoplinError(_('Usage: %s', '--log-level <none|error|warn|info|debug>'), 'flagError');
matched.logLevel = Logger.levelStringToId(nextArg);
argv.splice(0, 2);
continue;
}
if (arg.length && arg[0] == '-') {
throw new Error(_('Unknown flag: %s', arg));
throw new JoplinError(_('Unknown flag: %s', arg), 'flagError');
} else {
break;
}
@@ -184,8 +185,9 @@ class BaseApplication {
this.logger().debug('Refreshing notes:', parentType, parentId);
let options = {
order: state.notesOrder,
order: stateUtils.notesOrder(state.settings),
uncompletedTodosOnTop: Setting.value('uncompletedTodosOnTop'),
caseInsensitive: true,
};
const source = JSON.stringify({
@@ -255,14 +257,31 @@ class BaseApplication {
const result = next(action);
const newState = store.getState();
let refreshNotes = false;
if (action.type == 'FOLDER_SELECT' || action.type === 'FOLDER_DELETE') {
Setting.setValue('activeFolderId', newState.selectedFolderId);
this.currentFolder_ = newState.selectedFolderId ? await Folder.load(newState.selectedFolderId) : null;
await this.refreshNotes(newState);
refreshNotes = true;
}
if (this.hasGui() && action.type == 'SETTING_UPDATE_ONE' && action.key == 'uncompletedTodosOnTop' || action.type == 'SETTING_UPDATE_ALL') {
if (this.hasGui() && ((action.type == 'SETTING_UPDATE_ONE' && action.key == 'uncompletedTodosOnTop') || action.type == 'SETTING_UPDATE_ALL')) {
refreshNotes = true;
}
if (this.hasGui() && ((action.type == 'SETTING_UPDATE_ONE' && action.key.indexOf('notes.sortOrder') === 0) || action.type == 'SETTING_UPDATE_ALL')) {
refreshNotes = true;
}
if (action.type == 'TAG_SELECT' || action.type === 'TAG_DELETE') {
refreshNotes = true;
}
if (action.type == 'SEARCH_SELECT' || action.type === 'SEARCH_DELETE') {
refreshNotes = true;
}
if (refreshNotes) {
await this.refreshNotes(newState);
}
@@ -288,14 +307,6 @@ class BaseApplication {
}
}
if (action.type == 'TAG_SELECT' || action.type === 'TAG_DELETE') {
await this.refreshNotes(newState);
}
if (action.type == 'SEARCH_SELECT' || action.type === 'SEARCH_DELETE') {
await this.refreshNotes(newState);
}
if (action.type === 'NOTE_UPDATE_ONE') {
// If there is a conflict, we refresh the folders so as to display "Conflicts" folder
if (action.note && action.note.is_conflict) {
@@ -303,11 +314,6 @@ class BaseApplication {
}
}
// if (action.type === 'NOTE_DELETE') {
// // Update folders if a note is deleted in case the deleted note was a conflict
// await FoldersScreenUtils.refreshFolders();
// }
if (this.hasGui() && action.type == 'SETTING_UPDATE_ONE' && action.key == 'sync.interval' || action.type == 'SETTING_UPDATE_ALL') {
reg.setupRecurrentSync();
}

View File

@@ -1,7 +1,7 @@
const { Log } = require('lib/log.js');
const { Database } = require('lib/database.js');
const { uuid } = require('lib/uuid.js');
const { time } = require('lib/time-utils.js');
const Mutex = require('async-mutex').Mutex;
class BaseModel {
@@ -44,6 +44,14 @@ class BaseModel {
return null;
}
static modelTypeToName(type) {
for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
const e = BaseModel.typeEnum_[i];
if (e[1] === type) return e[0].substr(5).toLowerCase();
}
throw new Error('Unknown model type: ' + type);
}
static hasField(name) {
let fields = this.fieldNames();
return fields.indexOf(name) >= 0;
@@ -247,6 +255,40 @@ class BaseModel {
return !Object.getOwnPropertyNames(diff).length;
}
static saveMutex(modelOrId) {
const noLockMutex = {
acquire: function() { return null; }
};
if (!modelOrId) return noLockMutex;
let modelId = typeof modelOrId === 'string' ? modelOrId : modelOrId.id;
if (!modelId) return noLockMutex;
let mutex = BaseModel.saveMutexes_[modelId];
if (mutex) return mutex;
mutex = new Mutex();
BaseModel.saveMutexes_[modelId] = mutex;
return mutex;
}
static releaseSaveMutex(modelOrId, release) {
if (!release) return;
if (!modelOrId) return release();
let modelId = typeof modelOrId === 'string' ? modelOrId : modelOrId.id;
if (!modelId) return release();
let mutex = BaseModel.saveMutexes_[modelId];
if (!mutex) return release();
delete BaseModel.saveMutexes_[modelId];
release();
}
static saveQuery(o, options) {
let temp = {}
let fieldNames = this.fieldNames();
@@ -320,7 +362,16 @@ class BaseModel {
return query;
}
static save(o, options = null) {
static async save(o, options = null) {
// When saving, there's a mutex per model ID. This is because the model returned from this function
// is basically its input `o` (instead of being read from the database, for performance reasons).
// This works well in general except if that model is saved simultaneously in two places. In that
// case, the output won't be up-to-date and would cause for example display issues with out-dated
// notes being displayed. This was an issue when notes were being synchronised while being decrypted
// at the same time.
const mutexRelease = await this.saveMutex(o).acquire();
options = this.modOptions(options);
options.isNew = this.isNew(o, options);
@@ -348,7 +399,11 @@ class BaseModel {
queries = queries.concat(options.nextQueries);
}
return this.db().transactionExecBatch(queries).then(() => {
let output = null;
try {
await this.db().transactionExecBatch(queries);
o = Object.assign({}, o);
if (modelId) o.id = modelId;
if ('updated_time' in saveQuery.modObject) o.updated_time = saveQuery.modObject.updated_time;
@@ -365,10 +420,14 @@ class BaseModel {
}
}
return this.filter(o);
}).catch((error) => {
Log.error('Cannot save model', error);
});
output = this.filter(o);
} catch (error) {
this.logger().error('Cannot save model', error);
}
this.releaseSaveMutex(o, mutexRelease);
return output;
}
static isNew(object, options) {
@@ -435,17 +494,35 @@ class BaseModel {
}
BaseModel.TYPE_NOTE = 1;
BaseModel.TYPE_FOLDER = 2;
BaseModel.TYPE_SETTING = 3;
BaseModel.TYPE_RESOURCE = 4;
BaseModel.TYPE_TAG = 5;
BaseModel.TYPE_NOTE_TAG = 6;
BaseModel.TYPE_SEARCH = 7;
BaseModel.TYPE_ALARM = 8;
BaseModel.TYPE_MASTER_KEY = 9;
BaseModel.typeEnum_ = [
['TYPE_NOTE', 1],
['TYPE_FOLDER', 2],
['TYPE_SETTING', 3],
['TYPE_RESOURCE', 4],
['TYPE_TAG', 5],
['TYPE_NOTE_TAG', 6],
['TYPE_SEARCH', 7],
['TYPE_ALARM', 8],
['TYPE_MASTER_KEY', 9],
];
for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
const e = BaseModel.typeEnum_[i];
BaseModel[e[0]] = e[1];
}
// BaseModel.TYPE_NOTE = 1;
// BaseModel.TYPE_FOLDER = 2;
// BaseModel.TYPE_SETTING = 3;
// BaseModel.TYPE_RESOURCE = 4;
// BaseModel.TYPE_TAG = 5;
// BaseModel.TYPE_NOTE_TAG = 6;
// BaseModel.TYPE_SEARCH = 7;
// BaseModel.TYPE_ALARM = 8;
// BaseModel.TYPE_MASTER_KEY = 9;
BaseModel.db_ = null;
BaseModel.dispatch = function(o) {};
BaseModel.saveMutexes_ = {};
module.exports = BaseModel;

View File

@@ -10,6 +10,10 @@ class BaseSyncTarget {
this.options_ = options;
}
static supportsConfigCheck() {
return false;
}
option(name, defaultValue = null) {
return this.options_ && (name in this.options_) ? this.options_[name] : defaultValue;
}

View File

@@ -28,8 +28,7 @@ class Cache {
Cache.storage = async function() {
if (Cache.storage_) return Cache.storage_;
Cache.storage_ = require('node-persist');
const osTmpdir = require('os-tmpdir');
await Cache.storage_.init({ dir: osTmpdir() + '/joplin-cache', ttl: 1000 * 60 });
await Cache.storage_.init({ dir: require('os').tmpdir() + '/joplin-cache', ttl: 1000 * 60 });
return Cache.storage_;
}

View File

@@ -117,7 +117,7 @@ class MdToHtml {
if (mime == 'image/png' || mime == 'image/jpg' || mime == 'image/jpeg' || mime == 'image/gif') {
let src = './' + Resource.filename(resource);
if (this.resourceBaseUrl_ !== null) src = this.resourceBaseUrl_ + src;
let output = '<img title="' + htmlentities(title) + '" src="' + src + '"/>';
let output = '<img data-resource-id="' + resource.id + '" title="' + htmlentities(title) + '" src="' + src + '"/>';
return output;
}
@@ -135,13 +135,17 @@ class MdToHtml {
// Ideally they should be opened in the user's browser.
return '<span style="opacity: 0.5">(Resource not yet supported: '; //+ htmlentities(text) + ']';
} else {
let resourceIdAttr = "";
let icon = "";
if (isResourceUrl) {
const resourceId = Resource.pathToId(href);
href = 'joplin://' + resourceId;
href = "joplin://" + resourceId;
resourceIdAttr = "data-resource-id='" + resourceId + "'";
icon = '<span class="resource-icon"></span>';
}
const js = options.postMessageSyntax + "(" + JSON.stringify(href) + "); return false;";
let output = "<a title='" + htmlentities(title) + "' href='#' onclick='" + js + "'>";
let output = "<a " + resourceIdAttr + " title='" + htmlentities(title) + "' href='#' onclick='" + js + "'>" + icon;
return output;
}
}
@@ -157,7 +161,7 @@ class MdToHtml {
}
}
customCodeHandler_(language) {
rendererPlugin_(language) {
if (!language) return null;
const handlers = {};
@@ -192,13 +196,14 @@ class MdToHtml {
let openTag = null;
let closeTag = null;
let attrs = t.attrs ? t.attrs : [];
let tokenContent = t.content ? t.content : null;
let tokenContent = t.content ? t.content : '';
const isCodeBlock = tag === 'code' && t.block;
const isInlineCode = t.type === 'code_inline';
const codeBlockLanguage = t && t.info ? t.info : null;
let codeBlockHandler = null;
let rendererPlugin = null;
let rendererPluginOptions = { tagType: 'inline' };
if (isCodeBlock) codeBlockHandler = this.customCodeHandler_(codeBlockLanguage);
if (isCodeBlock) rendererPlugin = this.rendererPlugin_(codeBlockLanguage);
if (previousToken && previousToken.tag === 'li' && tag === 'p') {
// Markdown-it render list items as <li><p>Text<p></li> which makes it
@@ -207,6 +212,8 @@ class MdToHtml {
openTag = null;
} else if (isInlineCode) {
openTag = null;
} else if (tag && t.type.indexOf('html_inline') >= 0) {
openTag = null;
} else if (tag && t.type.indexOf('_open') >= 0) {
openTag = tag;
} else if (tag && t.type.indexOf('_close') >= 0) {
@@ -216,7 +223,7 @@ class MdToHtml {
} else if (t.type === 'link_open') {
openTag = 'a';
} else if (isCodeBlock) {
if (codeBlockHandler) {
if (rendererPlugin) {
openTag = null;
} else {
openTag = 'pre';
@@ -235,27 +242,38 @@ class MdToHtml {
if (isCodeBlock) {
const codeAttrs = ['code'];
if (!codeBlockHandler) {
if (!rendererPlugin) {
if (codeBlockLanguage) codeAttrs.push(t.info); // t.info contains the language when the token is a codeblock
output.push('<code class="' + codeAttrs.join(' ') + '">');
}
} else if (isInlineCode) {
const result = this.parseInlineCodeLanguage_(tokenContent);
if (result) {
codeBlockHandler = this.customCodeHandler_(result.language);
rendererPlugin = this.rendererPlugin_(result.language);
tokenContent = result.newContent;
}
if (!codeBlockHandler) {
if (!rendererPlugin) {
output.push('<code>');
}
}
if (codeBlockHandler) codeBlockHandler.loadAssets();
if (t.type === 'math_inline' || t.type === 'math_block') {
rendererPlugin = this.rendererPlugin_('katex');
rendererPluginOptions = { tagType: t.type === 'math_block' ? 'block' : 'inline' };
}
if (rendererPlugin) {
rendererPlugin.loadAssets().catch((error) => {
console.warn('MdToHtml: Error loading assets for ' + rendererPlugin.name() + ': ', error.message);
});
}
if (t.type === 'image') {
if (tokenContent) attrs.push(['title', tokenContent]);
output.push(this.renderImage_(attrs, options));
} else if (t.type === 'html_inline') {
output.push(t.content);
} else if (t.type === 'softbreak') {
output.push('<br/>');
} else if (t.type === 'hr') {
@@ -266,8 +284,10 @@ class MdToHtml {
output = output.concat(parsedChildren);
} else {
if (tokenContent) {
if ((isCodeBlock || isInlineCode) && codeBlockHandler) {
output = codeBlockHandler.processContent(output, tokenContent);
if ((isCodeBlock || isInlineCode) && rendererPlugin) {
output = rendererPlugin.processContent(output, tokenContent, isCodeBlock ? 'block' : 'inline');
} else if (rendererPlugin) {
output = rendererPlugin.processContent(output, tokenContent, rendererPluginOptions.tagType);
} else {
output.push(htmlentities(tokenContent));
}
@@ -282,15 +302,15 @@ class MdToHtml {
} else if (tag && t.type.indexOf('inline') >= 0) {
closeTag = openTag;
} else if (isCodeBlock) {
if (!codeBlockHandler) closeTag = openTag;
if (!rendererPlugin) closeTag = openTag;
}
if (isCodeBlock) {
if (!codeBlockHandler) {
if (!rendererPlugin) {
output.push('</code>');
}
} else if (isInlineCode) {
if (!codeBlockHandler) {
if (!rendererPlugin) {
output.push('</code>');
}
}
@@ -303,9 +323,9 @@ class MdToHtml {
}
}
if (codeBlockHandler) {
const extraCss = codeBlockHandler.extraCss();
const name = codeBlockHandler.name();
if (rendererPlugin) {
const extraCss = rendererPlugin.extraCss();
const name = rendererPlugin.name();
if (extraCss && !(name in extraCssBlocks)) {
extraCssBlocks[name] = extraCss;
}
@@ -339,8 +359,16 @@ class MdToHtml {
const md = new MarkdownIt({
breaks: true,
linkify: true,
html: true,
});
// This is currently used only so that the $expression$ and $$\nexpression\n$$ blocks are translated
// to math_inline and math_block blocks. These blocks are then processed directly with the Katex
// library. It is better this way as then it is possible to conditionally load the CSS required by
// Katex and use an up-to-date version of Katex (as of 2018, the plugin is still using 0.6, which is
// buggy instead of 0.9).
md.use(require('markdown-it-katex'));
// Hack to make checkboxes clickable. Ideally, checkboxes should be parsed properly in
// renderTokens_(), but for now this hack works. Marking it with HORRIBLE_HACK so
// that it can be removed and replaced later on.
@@ -370,7 +398,6 @@ class MdToHtml {
let loopCount = 0;
while (renderedBody.indexOf('mJOPm') >= 0) {
renderedBody = renderedBody.replace(/mJOPmCHECKBOXm([A-Z]+)m(\d+)m/, function(v, type, index) {
const js = options.postMessageSyntax + "('checkboxclick:" + type + ':' + index + "'); this.classList.contains('tick') ? this.classList.remove('tick') : this.classList.add('tick'); return false;";
return '<a href="#" onclick="' + js + '" class="checkbox ' + (type == 'NOTICK' ? '' : 'tick') + '"><span>' + '' + '</span></a>';
});
@@ -419,6 +446,18 @@ class MdToHtml {
ul {
padding-left: 1.3em;
}
.resource-icon {
display: inline-block;
position: relative;
top: .5em;
text-decoration: none;
width: 1.15em;
height: 1.5em;
margin-right: 0.4em;
background-color: ` + style.htmlColor + `;
/* Awesome Font file */
-webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'><path d='M369.9 97.9L286 14C277 5 264.8-.1 252.1-.1H48C21.5 0 0 21.5 0 48v416c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V131.9c0-12.7-5.1-25-14.1-34zM332.1 128H256V51.9l76.1 76.1zM48 464V48h160v104c0 13.3 10.7 24 24 24h104v288H48z'/></svg>");
}
a.checkbox {
display: inline-block;
position: relative;
@@ -456,13 +495,34 @@ class MdToHtml {
max-width: 100%;
}
.katex .mfrac .frac-line:before {
/* top: 50%; */
/* padding-bottom: .7em; */
@media print {
body {
height: auto !important;
}
a.checkbox {
border: 1pt solid ` + style.htmlColor + `;
border-radius: 2pt;
width: 1em;
height: 1em;
line-height: 1em;
text-align: center;
top: .4em;
}
a.checkbox.tick:after {
content: "X";
}
a.checkbox.tick {
top: 0;
left: -0.02em;
color: ` + style.htmlColor + `;
}
}
`;
const styleHtml = '<style>' + normalizeCss + "\n" + css + '</style>'; //+ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css">';
const styleHtml = '<style>' + normalizeCss + "\n" + css + '</style>';
const output = styleHtml + renderedBody;

View File

@@ -9,9 +9,12 @@ class MdToHtml_Katex {
return 'katex';
}
processContent(renderedTokens, content) {
processContent(renderedTokens, content, tagType) {
try {
const renderered = katex.renderToString(content);
let renderered = katex.renderToString(content);
if (tagType === 'block') renderered = '<p>' + renderered + '</p>';
renderedTokens.push(renderered);
} catch (error) {
renderedTokens.push('Cannot render Katex content: ' + error.message);
@@ -33,6 +36,7 @@ class MdToHtml_Katex {
// Fonts must go under the resourceDir directory because this is the baseUrl of NoteBodyViewer
const baseDir = Setting.value('resourceDir');
await shim.fsDriver().mkdir(baseDir + '/fonts');
await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Main-Regular.woff2', { overwrite: false, path: baseDir + '/fonts/KaTeX_Main-Regular.woff2' });
await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Math-Italic.woff2', { overwrite: false, path: baseDir + '/fonts/KaTeX_Math-Italic.woff2' });
await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Size1-Regular.woff2', { overwrite: false, path: baseDir + '/fonts/KaTeX_Size1-Regular.woff2' });

View File

@@ -0,0 +1,56 @@
const ObjectUtils = {};
ObjectUtils.sortByValue = function(object) {
const temp = [];
for (let k in object) {
if (!object.hasOwnProperty(k)) continue;
temp.push({
key: k,
value: object[k],
});
}
temp.sort(function(a, b) {
let v1 = a.value;
let v2 = b.value;
if (typeof v1 === 'string') v1 = v1.toLowerCase();
if (typeof v2 === 'string') v2 = v2.toLowerCase();
if (v1 === v2) return 0;
return v1 < v2 ? -1 : +1;
});
const output = {};
for (let i = 0; i < temp.length; i++) {
const item = temp[i];
output[item.key] = item.value;
}
return output;
}
ObjectUtils.fieldsEqual = function(o1, o2) {
if ((!o1 || !o2) && (o1 !== o2)) return false;
for (let k in o1) {
if (!o1.hasOwnProperty(k)) continue;
if (o1[k] !== o2[k]) return false;
}
const c1 = Object.getOwnPropertyNames(o1);
const c2 = Object.getOwnPropertyNames(o2);
if (c1.length !== c2.length) return false;
return true;
}
ObjectUtils.convertValuesToFunctions = function(o) {
const output = {};
for (let n in o) {
if (!o.hasOwnProperty(n)) continue;
output[n] = () => { return typeof o[n] === 'function' ? o[n]() : o[n]; }
}
return output;
}
module.exports = ObjectUtils;

View File

@@ -1,9 +1,13 @@
// The Nextcloud sync target is essentially a wrapper over the WebDAV sync target,
// thus all the calls to SyncTargetWebDAV to avoid duplicate code.
const BaseSyncTarget = require('lib/BaseSyncTarget.js');
const { _ } = require('lib/locale.js');
const Setting = require('lib/models/Setting.js');
const { FileApi } = require('lib/file-api.js');
const { Synchronizer } = require('lib/synchronizer.js');
const WebDavApi = require('lib/WebDavApi');
const SyncTargetWebDAV = require('lib/SyncTargetWebDAV');
const { FileApiDriverWebDav } = require('lib/file-api-driver-webdav');
class SyncTargetNextcloud extends BaseSyncTarget {
@@ -12,9 +16,8 @@ class SyncTargetNextcloud extends BaseSyncTarget {
return 5;
}
constructor(db, options = null) {
super(db, options);
// this.authenticated_ = false;
static supportsConfigCheck() {
return true;
}
static targetName() {
@@ -22,26 +25,26 @@ class SyncTargetNextcloud extends BaseSyncTarget {
}
static label() {
return _('Nextcloud (Beta)');
return _('Nextcloud');
}
isAuthenticated() {
return true;
//return this.authenticated_;
}
static async checkConfig(options) {
return SyncTargetWebDAV.checkConfig(options);
}
async initFileApi() {
const options = {
baseUrl: () => Setting.value('sync.5.path'),
const fileApi = await SyncTargetWebDAV.newFileApi_(SyncTargetNextcloud.id(), {
path: () => Setting.value('sync.5.path'),
username: () => Setting.value('sync.5.username'),
password: () => Setting.value('sync.5.password'),
};
});
const api = new WebDavApi(options);
const driver = new FileApiDriverWebDav(api);
const fileApi = new FileApi('', driver);
fileApi.setSyncTargetId(SyncTargetNextcloud.id());
fileApi.setLogger(this.logger());
return fileApi;
}

View File

@@ -12,6 +12,7 @@ class SyncTargetRegistry {
name: SyncTargetClass.targetName(),
label: SyncTargetClass.label(),
classRef: SyncTargetClass,
supportsConfigCheck: SyncTargetClass.supportsConfigCheck(),
};
}

View File

@@ -12,8 +12,8 @@ class SyncTargetWebDAV extends BaseSyncTarget {
return 6;
}
constructor(db, options = null) {
super(db, options);
static supportsConfigCheck() {
return true;
}
static targetName() {
@@ -21,25 +21,57 @@ class SyncTargetWebDAV extends BaseSyncTarget {
}
static label() {
return _('WebDAV (Beta)');
return _('WebDAV');
}
isAuthenticated() {
return true;
}
async initFileApi() {
const options = {
baseUrl: () => Setting.value('sync.6.path'),
username: () => Setting.value('sync.6.username'),
password: () => Setting.value('sync.6.password'),
static async newFileApi_(syncTargetId, options) {
const apiOptions = {
baseUrl: () => options.path(),
username: () => options.username(),
password: () => options.password(),
};
const api = new WebDavApi(options);
const api = new WebDavApi(apiOptions);
const driver = new FileApiDriverWebDav(api);
const fileApi = new FileApi('', driver);
fileApi.setSyncTargetId(SyncTargetWebDAV.id());
fileApi.setSyncTargetId(syncTargetId);
return fileApi;
}
static async checkConfig(options) {
const fileApi = await SyncTargetWebDAV.newFileApi_(SyncTargetWebDAV.id(), options);
fileApi.requestRepeatCount_ = 0;
const output = {
ok: false,
errorMessage: '',
};
try {
const result = await fileApi.stat('');
if (!result) throw new Error('WebDAV directory not found: ' + options.path);
output.ok = true;
} catch (error) {
output.errorMessage = error.message;
if (error.code) output.errorMessage += ' (Code ' + error.code + ')';
}
return output;
}
async initFileApi() {
const fileApi = await SyncTargetWebDAV.newFileApi_(SyncTargetWebDAV.id(), {
path: () => Setting.value('sync.6.path'),
username: () => Setting.value('sync.6.username'),
password: () => Setting.value('sync.6.password'),
});
fileApi.setLogger(this.logger());
return fileApi;
}

View File

@@ -29,7 +29,15 @@ class WebDavApi {
authToken() {
if (!this.options_.username() || !this.options_.password()) return null;
return base64.encode(this.options_.username() + ':' + this.options_.password());
try {
// Note: Non-ASCII passwords will throw an error about Latin1 characters - https://github.com/laurent22/joplin/issues/246
// Tried various things like the below, but it didn't work on React Native:
//return base64.encode(utf8.encode(this.options_.username() + ':' + this.options_.password()));
return base64.encode(this.options_.username() + ':' + this.options_.password());
} catch (error) {
error.message = 'Cannot encode username/password: ' + error.message;
throw error;
}
}
baseUrl() {
@@ -133,6 +141,42 @@ class WebDavApi {
return this.valueFromJson(json, keys, 'array');
}
resourcePropByName(resource, outputType, propName) {
const propStats = resource['d:propstat'];
let output = null;
if (!Array.isArray(propStats)) throw new Error('Missing d:propstat property');
for (let i = 0; i < propStats.length; i++) {
const props = propStats[i]['d:prop'];
if (!Array.isArray(props) || !props.length) continue;
const prop = props[0];
if (Array.isArray(prop[propName])) {
output = prop[propName];
break;
}
}
if (outputType === 'string') {
// If the XML has not attribute the value is directly a string
// If the XML node has attributes, the value is under "_".
// Eg for this XML, the string will be under {"_":"Thu, 01 Feb 2018 17:24:05 GMT"}:
// <a:getlastmodified b:dt="dateTime.rfc1123">Thu, 01 Feb 2018 17:24:05 GMT</a:getlastmodified>
// For this XML, the value will be "Thu, 01 Feb 2018 17:24:05 GMT"
// <a:getlastmodified>Thu, 01 Feb 2018 17:24:05 GMT</a:getlastmodified>
output = output[0];
if (typeof output === 'object' && '_' in output) output = output['_'];
if (typeof output !== 'string') return null;
return output;
}
if (outputType === 'array') {
return output;
}
throw new Error('Invalid output type: ' + outputType);
}
async execPropFind(path, depth, fields = null, options = null) {
if (fields === null) fields = ['d:getlastmodified'];
@@ -158,6 +202,22 @@ class WebDavApi {
return this.exec('PROPFIND', path, body, { 'Depth': depth }, options);
}
requestToCurl_(url, options) {
let output = [];
output.push('curl');
if (options.method) output.push('-X ' + options.method);
if (options.headers) {
for (let n in options.headers) {
if (!options.headers.hasOwnProperty(n)) continue;
output.push('-H ' + '"' + n + ': ' + options.headers[n] + '"');
}
}
if (options.body) output.push('--data ' + "'" + options.body + "'");
output.push(url);
return output.join(' ');
}
// curl -u admin:123456 'http://nextcloud.local/remote.php/dav/files/admin/' -X PROPFIND --data '<?xml version="1.0" encoding="UTF-8"?>
// <d:propfind xmlns:d="DAV:">
// <d:prop xmlns:oc="http://owncloud.org/ns">
@@ -175,7 +235,16 @@ class WebDavApi {
if (authToken) headers['Authorization'] = 'Basic ' + authToken;
if (typeof body === 'string') headers['Content-Length'] = body.length;
// On iOS, the network lib appends a If-None-Match header to PROPFIND calls, which is kind of correct because
// the call is idempotent and thus could be cached. According to RFC-7232 though only GET and HEAD should have
// this header for caching purposes. It makes no mention of PROPFIND.
// So possibly because of this, Seafile (and maybe other WebDAV implementations) responds with a "412 Precondition Failed"
// error when this header is present for PROPFIND call on existing resources. This is also kind of correct because there is a resource
// with this eTag and since this is neither a GET nor HEAD call, it is supposed to respond with 412 if the resource is present.
// The "solution", an ugly one, is to send a purposely invalid string as eTag, which will bypass the If-None-Match check - Seafile
// finds out that no resource has this ID and simply sends the requested data.
// Also add a random value to make sure the eTag is unique for each call.
if (['GET', 'HEAD'].indexOf(method) < 0) headers['If-None-Match'] = 'JoplinIgnore-' + Math.floor(Math.random() * 100000);
const fetchOptions = {};
fetchOptions.headers = headers;
@@ -187,11 +256,17 @@ class WebDavApi {
let response = null;
// console.info('WebDAV', method + ' ' + url, headers, options);
// console.info('WebDAV Call', method + ' ' + url, headers, options);
// console.info(this.requestToCurl_(url, fetchOptions));
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
if (fetchOptions.path) {
const fileStat = await shim.fsDriver().stat(fetchOptions.path);
if (fileStat) fetchOptions.headers['Content-Length'] = fileStat.size + '';
}
response = await shim.uploadBlob(url, fetchOptions);
} else if (options.target == 'string') {
if (typeof body === 'string') fetchOptions.headers['Content-Length'] = shim.stringByteLength(body) + '';
response = await shim.fetch(url, fetchOptions);
} else { // file
response = await shim.fetchBlob(url, fetchOptions);
@@ -199,10 +274,14 @@ class WebDavApi {
const responseText = await response.text();
// Gives a shorter response for error messages. Useful for cases where a full HTML page is accidentally loaded instead of
// JSON. That way the error message will still show there's a problem but without filling up the log or screen.
const shortResponseText = () => {
return (responseText + '').substr(0, 1024);
// console.info('WebDAV Response', responseText);
// Creates an error object with as much data as possible as it will appear in the log, which will make debugging easier
const newError = (message, code = 0) => {
// Gives a shorter response for error messages. Useful for cases where a full HTML page is accidentally loaded instead of
// JSON. That way the error message will still show there's a problem but without filling up the log or screen.
const shortResponseText = (responseText + '').substr(0, 1024);
return new JoplinError(method + ' ' + path + ': ' + message + ' (' + code + '): ' + shortResponseText, code);
}
let responseJson_ = null;
@@ -210,32 +289,42 @@ class WebDavApi {
if (!responseText) return null;
if (responseJson_) return responseJson_;
responseJson_ = await this.xmlToJson(responseText);
if (!responseJson_) throw new JoplinError('Cannot parse JSON response: ' + shortResponseText(), response.status);
if (!responseJson_) throw newError('Cannot parse XML response', response.status);
return responseJson_;
}
if (!response.ok) {
// When using fetchBlob we only get a string (not xml or json) back
if (options.target === 'file') throw new JoplinError(shortResponseText(), response.status);
if (options.target === 'file') throw newError('fetchBlob error', response.status);
const json = await loadResponseJson();
let json = null;
try {
json = await loadResponseJson();
} catch (error) {
// Just send back the plain text in newErro()
}
if (json && json['d:error']) {
const code = json['d:error']['s:exception'] ? json['d:error']['s:exception'].join(' ') : response.status;
const message = json['d:error']['s:message'] ? json['d:error']['s:message'].join("\n") : shortResponseText();
throw new JoplinError(method + ' ' + path + ': ' + message + ' (' + code + ')', response.status);
const message = json['d:error']['s:message'] ? json['d:error']['s:message'].join("\n") : 'Unknown error 1';
throw newError(message + ' (Exception ' + code + ')', response.status);
}
throw new JoplinError(method + ' ' + path + ': ' + shortResponseText(), response.status);
throw newError('Unknown error 2', response.status);
}
if (options.responseFormat === 'text') return responseText;
// The following methods may have a response depending on the server but it's not
// standard (some return a plain string, other XML, etc.) and we don't check the
// response anyway since we rely on the HTTP status code so return null.
if (['MKCOL', 'DELETE', 'PUT', 'MOVE'].indexOf(method) >= 0) return null;
const output = await loadResponseJson();
// Check that we didn't get for example an HTML page (as an error) instead of the JSON response
// null responses are possible, for example for DELETE calls
if (output !== null && typeof output === 'object' && !('d:multistatus' in output)) throw new Error('Not a valid JSON response: ' + shortResponseText());
if (output !== null && typeof output === 'object' && !('d:multistatus' in output)) throw newError('Not a valid WebDAV response');
return output;
}

View File

@@ -0,0 +1,81 @@
const React = require('react');
const { Text, Modal, View, StyleSheet, Button } = require('react-native');
const { themeStyle } = require('lib/components/global-style.js');
const { _ } = require('lib/locale');
class ModalDialog extends React.Component {
constructor() {
super();
this.styles_ = {};
}
styles() {
const themeId = this.props.theme;
const theme = themeStyle(themeId);
if (this.styles_[themeId]) return this.styles_[themeId];
this.styles_ = {};
let styles = {
modalWrapper: {
flex: 1,
justifyContent: 'center',
},
modalContentWrapper: {
flex:1,
flexDirection: 'column',
backgroundColor: theme.backgroundColor,
borderWidth: 1,
borderColor:theme.dividerColor,
margin: 20,
padding: 10,
},
modalContentWrapper2: {
paddingTop: 10,
flex:1,
},
title: {
borderBottomWidth: 1,
borderBottomColor: theme.dividerColor,
paddingBottom: 10,
},
buttonRow: {
flexDirection: 'row',
borderTopWidth: 1,
borderTopColor: theme.dividerColor,
paddingTop: 10,
},
};
this.styles_[themeId] = StyleSheet.create(styles);
return this.styles_[themeId];
}
render() {
const ContentComponent = this.props.ContentComponent;
return (
<View style={this.styles().modalWrapper}>
<Modal transparent={true} visible={true} onRequestClose={() => { }} >
<View style={this.styles().modalContentWrapper}>
<Text style={this.styles().title}>Title</Text>
<View style={this.styles().modalContentWrapper2}>
{ContentComponent}
</View>
<View style={this.styles().buttonRow}>
<View style={{flex:1}}>
<Button title={_('OK')} onPress={() => {}}></Button>
</View>
<View style={{flex:1, marginLeft: 5}}>
<Button title={_('Cancel')} onPress={() => {}}></Button>
</View>
</View>
</View>
</Modal>
</View>
);
}
}
module.exports = ModalDialog;

View File

@@ -4,7 +4,6 @@ const Icon = require('react-native-vector-icons/Ionicons').default;
const ReactNativeActionButton = require('react-native-action-button').default;
const { connect } = require('react-redux');
const { globalStyle } = require('lib/components/global-style.js');
const { Log } = require('lib/log.js');
const { _ } = require('lib/locale.js');
const styles = StyleSheet.create({

View File

@@ -2,7 +2,7 @@ const React = require('react'); const Component = React.Component;
const { connect } = require('react-redux');
const { NotesScreen } = require('lib/components/screens/notes.js');
const { SearchScreen } = require('lib/components/screens/search.js');
const { View } = require('react-native');
const { KeyboardAvoidingView, Keyboard, Platform, View } = require('react-native');
const { _ } = require('lib/locale.js');
const { themeStyle } = require('lib/components/global-style.js');
@@ -11,6 +11,31 @@ class AppNavComponent extends Component {
constructor() {
super();
this.previousRouteName_ = null;
this.state = {
autoCompletionBarExtraHeight: 0, // Extra padding for the auto completion bar at the top of the keyboard
}
}
componentWillMount() {
if (Platform.OS === 'ios') {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this));
}
}
componentWillUnmount() {
if (this.keyboardDidShowListener) this.keyboardDidShowListener.remove();
if (this.keyboardDidHideListener) this.keyboardDidHideListener.remove();
this.keyboardDidShowListener = null;
this.keyboardDidHideListener = null;
}
keyboardDidShow () {
this.setState({ autoCompletionBarExtraHeight: 30 })
}
keyboardDidHide () {
this.setState({ autoCompletionBarExtraHeight:0 })
}
render() {
@@ -44,11 +69,12 @@ class AppNavComponent extends Component {
const style = { flex: 1, backgroundColor: theme.backgroundColor }
return (
<View style={style}>
<KeyboardAvoidingView behavior={ Platform.OS === 'ios' ? "padding" : null } style={style}>
<NotesScreen visible={notesScreenVisible} navigation={{ state: route }} />
{ searchScreenLoaded && <SearchScreen visible={searchScreenVisible} navigation={{ state: route }} /> }
{ (!notesScreenVisible && !searchScreenVisible) && <Screen navigation={{ state: route }} /> }
</View>
<View style={{ height: this.state.autoCompletionBarExtraHeight }} />
</KeyboardAvoidingView>
);
}

View File

@@ -104,7 +104,7 @@ class NoteBodyViewer extends Component {
style={webViewStyle}
source={source}
onLoadEnd={() => this.onLoadEnd()}
onError={(e) => reg.logger().error('WebView error', e) }
onError={() => reg.logger().error('WebView error') }
onMessage={(event) => {
let msg = event.nativeEvent.data;

View File

@@ -1,7 +1,6 @@
const React = require('react'); const Component = React.Component;
const { connect } = require('react-redux');
const { ListView, Text, TouchableOpacity , View, StyleSheet } = require('react-native');
const { Log } = require('lib/log.js');
const { _ } = require('lib/locale.js');
const { Checkbox } = require('lib/components/checkbox.js');
const { reg } = require('lib/registry.js');

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