diff --git a/README.md b/README.md index 22bb14c941..dc0e1f6621 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # TriliumNext Notes +![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/notes) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/notes/total) + [English](./README.md) | [Chinese](./README-ZH_CN.md) | [Russian](./README.ru.md) | [Japanese](./README.ja.md) | [Italian](./README.it.md) | [Spanish](./README.es.md) TriliumNext Notes is an open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases. diff --git a/bin/deb-options.json b/bin/deb-options.json deleted file mode 100644 index 86531cc48e..0000000000 --- a/bin/deb-options.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "src": "dist/trilium-linux-x64", - "dest": "dist/", - "compression": "xz", - "name": "trilium", - "productName": "Trilium Notes", - "genericName": "Note taker", - "description": "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", - "sections": "misc", - "maintainer": "zadam.apps@gmail.com", - "homepage": "https://github.com/zadam/trilium", - "bin": "trilium", - "icon": "dist/trilium-linux-x64/icon.png", - "categories": [ "Office" ] -} diff --git a/bin/electron-forge/desktop.ejs b/bin/electron-forge/desktop.ejs new file mode 100644 index 0000000000..f803f37b21 --- /dev/null +++ b/bin/electron-forge/desktop.ejs @@ -0,0 +1,12 @@ +[Desktop Entry] +<% if (productName) { %>Name=<%= productName %> +<% } %><% if (description) { %>Comment=<%= description %> +<% } %><% if (genericName) { %>GenericName=<%= genericName %> +<% } %><% if (name) { %>Exec=<%= name %> %U +Icon=<%= name %> +<% } %>Type=Application +StartupNotify=true +<% if (productName) { %>StartupWMClass=<%= productName %> +<% } if (categories && categories.length) { %>Categories=<%= categories.join(';') %>; +<% } %><% if (mimeType && mimeType.length) { %>MimeType=<%= mimeType.join(';') %>; +<% } %> \ No newline at end of file diff --git a/forge.config.cjs b/forge.config.cjs index 91730ed432..8d2db5da56 100644 --- a/forge.config.cjs +++ b/forge.config.cjs @@ -20,13 +20,20 @@ module.exports = { afterComplete: [(buildPath, _electronVersion, platform, _arch, callback) => { const extraResources = getExtraResourcesForPlatform(); for (const resource of extraResources) { + const baseName = path.basename(resource); let sourcePath; if (platform === 'darwin') { - sourcePath = path.join(buildPath, `${APP_NAME}.app`, 'Contents', 'Resources', path.basename(resource)); + sourcePath = path.join(buildPath, `${APP_NAME}.app`, 'Contents', 'Resources', baseName); } else { - sourcePath = path.join(buildPath, 'resources', path.basename(resource)); + sourcePath = path.join(buildPath, 'resources', baseName); + } + let destPath; + + if (baseName !== "256x256.png") { + destPath = path.join(buildPath, baseName); + } else { + destPath = path.join(buildPath, "icon.png"); } - const destPath = path.join(buildPath, path.basename(resource)); // Copy files from resources folder to root fs.move(sourcePath, destPath) @@ -44,6 +51,7 @@ module.exports = { config: { options: { icon: "./images/app-icons/png/128x128.png", + desktopTemplate: path.resolve("./bin/electron-forge/desktop.ejs") } } }, @@ -95,6 +103,7 @@ function getExtraResourcesForPlatform() { case 'darwin': break; case 'linux': + resources.push("images/app-icons/png/256x256.png") for (const script of scripts) { resources.push(`./bin/tpl/${script}.sh`) } diff --git a/package-lock.json b/package-lock.json index b8a1e3a6ca..aed9eedac5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "trilium", - "version": "0.90.8", + "version": "0.90.9-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "trilium", - "version": "0.90.8", + "version": "0.90.9-beta", "license": "AGPL-3.0-only", "dependencies": { "@braintree/sanitize-url": "7.1.0", @@ -23,11 +23,11 @@ "cls-hooked": "4.2.2", "codemirror": "5.65.18", "compression": "1.7.4", - "cookie-parser": "1.4.6", + "cookie-parser": "1.4.7", "csurf": "1.11.0", "dayjs": "1.11.13", "dayjs-plugin-utc": "0.1.2", - "debounce": "2.1.1", + "debounce": "2.2.0", "ejs": "3.1.10", "electron-debug": "4.0.1", "electron-dl": "4.0.0", @@ -35,18 +35,18 @@ "electron-window-state": "5.0.3", "escape-html": "1.0.3", "eslint": "9.10.0", - "express": "4.21.0", + "express": "4.21.1", "express-partial-content": "1.0.2", - "express-rate-limit": "7.4.0", - "express-session": "1.18.0", - "force-graph": "1.43.5", + "express-rate-limit": "7.4.1", + "express-session": "1.18.1", + "force-graph": "1.45.0", "fs-extra": "11.2.0", "helmet": "7.1.0", "html": "1.0.0", "html2plaintext": "2.1.4", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.5", - "i18next": "23.16.1", + "i18next": "23.16.2", "i18next-fs-backend": "2.3.2", "i18next-http-backend": "2.6.2", "image-type": "4.1.0", @@ -63,10 +63,10 @@ "katex": "0.16.11", "knockout": "3.5.1", "mark.js": "8.11.1", - "marked": "14.1.2", + "marked": "14.1.3", "mermaid": "11.3.0", "mime-types": "2.1.35", - "mind-elixir": "4.2.2", + "mind-elixir": "4.2.3", "multer": "1.4.5-lts.1", "node-abi": "3.67.0", "normalize-strings": "1.1.1", @@ -79,7 +79,7 @@ "request": "2.88.2", "safe-compare": "1.1.4", "sanitize-filename": "1.6.3", - "sanitize-html": "2.13.0", + "sanitize-html": "2.13.1", "sax": "1.4.1", "semver": "7.6.3", "serve-favicon": "2.5.0", @@ -92,7 +92,7 @@ "tree-kill": "1.2.2", "turndown": "7.2.0", "unescape": "1.0.1", - "vanilla-js-wheel-zoom": "9.0.2", + "vanilla-js-wheel-zoom": "9.0.4", "ws": "8.18.0", "xml2js": "0.6.2", "yauzl": "3.1.3" @@ -126,7 +126,7 @@ "@types/jsdom": "21.1.7", "@types/mime-types": "2.1.4", "@types/multer": "1.4.12", - "@types/node": "22.5.4", + "@types/node": "22.7.8", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.13.0", "@types/sax": "1.2.7", @@ -145,14 +145,14 @@ "electron-rebuild": "3.2.9", "esm": "3.2.25", "iconsur": "1.7.0", - "jasmine": "5.3.1", + "jasmine": "5.4.0", "jsdoc": "4.0.3", "lorem-ipsum": "2.0.8", "nodemon": "3.1.7", "rcedit": "4.0.1", "rimraf": "6.0.1", "ts-node": "10.9.2", - "tslib": "2.7.0", + "tslib": "2.8.0", "tsx": "4.19.1", "typescript": "5.6.3", "webpack": "5.95.0", @@ -3360,9 +3360,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz", + "integrity": "sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==", "dev": true, "dependencies": { "@types/node": "*", @@ -3380,6 +3380,18 @@ "@types/express": "*" } }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -3528,9 +3540,9 @@ } }, "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "version": "22.7.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.8.tgz", + "integrity": "sha512-a922jJy31vqR5sk+kAdIENJjHblqcZ4RmERviFsER4WJcEONqxKcjNOlk0q7OUfrF5sddT+vng070cdfMlrPLg==", "dependencies": { "undici-types": "~6.19.2" } @@ -5319,9 +5331,9 @@ ] }, "node_modules/canvas-color-tracker": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/canvas-color-tracker/-/canvas-color-tracker-1.2.1.tgz", - "integrity": "sha512-i5clg2pEdaWqHuEM/B74NZNLkHh5+OkXbA/T4iaBiaNDagkOCXkLNrhqUfdUugsRwuaNRU20e/OygzxWRor3yg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/canvas-color-tracker/-/canvas-color-tracker-1.3.1.tgz", + "integrity": "sha512-eNycxGS7oQ3IS/9QQY41f/aQjiO9Y/MtedhCgSdsbLSxC9EyUD8L3ehl/Q3Kfmvt8um79S45PBV+5Rxm5ztdSw==", "dependencies": { "tinycolor2": "^1.6.0" }, @@ -5926,11 +5938,11 @@ } }, "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "dependencies": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" }, "engines": { @@ -5938,9 +5950,9 @@ } }, "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } @@ -6753,9 +6765,9 @@ "integrity": "sha512-ExERH5o3oo6jFOdkvMP3gytTCQ9Ksi5PtylclJWghr7k7m3o2U5QrwtdiJkOxLOH4ghr0EKhpqGefzGz1VvVJg==" }, "node_modules/debounce": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.1.tgz", - "integrity": "sha512-+xRWxgel9LgTC4PwKlm7TJUK6B6qsEK77NaiNvXmeQ7Y3e6OVVsBC4a9BSptS/mAYceyAz37Oa8JTTuPRft7uQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", + "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", "engines": { "node": ">=18" }, @@ -8229,16 +8241,16 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -8278,9 +8290,9 @@ } }, "node_modules/express-rate-limit": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.0.tgz", - "integrity": "sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.1.tgz", + "integrity": "sha512-KS3efpnpIDVIXopMc65EMbWbUht7qvTCdtCR2dD/IZmi9MIkopYESwyRqLgv8Pfu589+KqDqOdzJWW7AHoACeg==", "engines": { "node": ">= 16" }, @@ -8292,11 +8304,11 @@ } }, "node_modules/express-session": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", - "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", "dependencies": { - "cookie": "0.6.0", + "cookie": "0.7.2", "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", @@ -8310,9 +8322,9 @@ } }, "node_modules/express-session/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } @@ -8358,9 +8370,9 @@ ] }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -8835,14 +8847,14 @@ } }, "node_modules/force-graph": { - "version": "1.43.5", - "resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.43.5.tgz", - "integrity": "sha512-HveLELh9yhZXO/QOfaFS38vlwJZ/3sKu+jarfXzRmbmihSOH/BbRWnUvmg8wLFiYy6h4HlH4lkRfZRccHYmXgA==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.45.0.tgz", + "integrity": "sha512-QM/J72Vji5D3ug+TDu8wH+qne0zEKE9Cn7m9ocH/1RtaVY0BBqZQ4Mn6MiwNRyxwl28lsUd0F54kDpINnagvOA==", "dependencies": { - "@tweenjs/tween.js": "18 - 23", + "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "bezier-js": "3 - 6", - "canvas-color-tracker": "1", + "canvas-color-tracker": "^1.3", "d3-array": "1 - 3", "d3-drag": "2 - 3", "d3-force-3d": "2 - 3", @@ -9715,9 +9727,9 @@ } }, "node_modules/i18next": { - "version": "23.16.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.1.tgz", - "integrity": "sha512-H73h/H7BN7PI38Sq9XsOXzWFBH6mtyCYFiUMVtd9BxiYNDWPPIzKcBmDrqhjKbw3IXP5j6JoSW4ugJlaZuOvKw==", + "version": "23.16.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.2.tgz", + "integrity": "sha512-dFyxwLXxEQK32f6tITBMaRht25mZPJhQ0WbC0p3bO2mWBal9lABTMqSka5k+GLSRWLzeJBKDpH7BeIA9TZI7Jg==", "funding": [ { "type": "individual", @@ -10828,22 +10840,22 @@ } }, "node_modules/jasmine": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.3.1.tgz", - "integrity": "sha512-3zeUCfr3d1iga3s+NgDpggCP+ex5sdbNgqNn+Tq4yw/QfnwGrWC/ZvXX1IRm5deSIZ1LnvoeGY55F/ztbVOXPQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.4.0.tgz", + "integrity": "sha512-E2u4ylX5tgGYvbynImU6EUBKKrSVB1L72FEPjGh4M55ov1VsxR26RA2JU91L9YSPFgcjo4mCLyKn/QXvEYGBkA==", "dev": true, "dependencies": { "glob": "^10.2.2", - "jasmine-core": "~5.3.0" + "jasmine-core": "~5.4.0" }, "bin": { "jasmine": "bin/jasmine.js" } }, "node_modules/jasmine-core": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.3.0.tgz", - "integrity": "sha512-zsOmeBKESky4toybvWEikRiZ0jHoBEu79wNArLfMdSnlLMZx3Xcp6CSm2sUcYyoJC+Uyj8LBJap/MUbVSfJ27g==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.4.0.tgz", + "integrity": "sha512-T4fio3W++llLd7LGSGsioriDHgWyhoL6YTu4k37uwJLF7DzOzspz7mNxRoM3cQdLWtL/ebazQpIf/yZGJx/gzg==", "dev": true }, "node_modules/jasmine/node_modules/brace-expansion": { @@ -11817,9 +11829,9 @@ } }, "node_modules/marked": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.2.tgz", - "integrity": "sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==", + "version": "14.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.3.tgz", + "integrity": "sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==", "bin": { "marked": "bin/marked.js" }, @@ -12028,9 +12040,9 @@ } }, "node_modules/mind-elixir": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.2.2.tgz", - "integrity": "sha512-Dfb3fb0IAVpAhOh0c9zmHgtLQI++ODbFawNbHk2xkyZ5uLuZPkkN1a5rtrUkqYFxglxflpmsPElTY0zqxxlPrA==" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/mind-elixir/-/mind-elixir-4.2.3.tgz", + "integrity": "sha512-o/t9/mrJkRu0PE5UjXBv8ZZuhwSdm6C1Hw65v/+bIlB2CS2MOGZ/GNPvU3U4kPDu6LnCZ0kw0L7hoVfHhrZLtw==" }, "node_modules/minimalistic-assert": { "version": "1.0.1", @@ -14321,9 +14333,9 @@ } }, "node_modules/sanitize-html": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz", - "integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.1.tgz", + "integrity": "sha512-ZXtKq89oue4RP7abL9wp/9URJcqQNABB5GGJ2acW1sdO8JTVl92f4ygD7Yc9Ze09VAZhnt2zegeU0tbNsdcLYg==", "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", @@ -15647,9 +15659,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" }, "node_modules/tsscmp": { "version": "1.0.6", @@ -16032,10 +16044,9 @@ "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" }, "node_modules/vanilla-js-wheel-zoom": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/vanilla-js-wheel-zoom/-/vanilla-js-wheel-zoom-9.0.2.tgz", - "integrity": "sha512-GleJm/qTDcfQC7gdFH4BT5vCZmnz2LOTgLAQ5CCCwftFC/zrBURRWr5Y7jRPi3W3rRf8bTIAN3hLZYFqvK5jwA==", - "license": "MIT" + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/vanilla-js-wheel-zoom/-/vanilla-js-wheel-zoom-9.0.4.tgz", + "integrity": "sha512-OjmS9ihEKBCRw2OQ7IiIdQGXdC5gTEEmtcAWZcPeNAJaYiS61KCd02Z72YMtIoXLGN5TZP+wliBMylLAsr6wow==" }, "node_modules/vary": { "version": "1.1.2", diff --git a/package.json b/package.json index 4f7de8237e..c0f1e7bdfd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "trilium", "productName": "TriliumNext Notes", "description": "Build your personal knowledge base with TriliumNext Notes", - "version": "0.90.8", + "version": "0.90.9-beta", "license": "AGPL-3.0-only", "main": "./dist/electron-main.js", "author": { @@ -63,11 +63,11 @@ "cls-hooked": "4.2.2", "codemirror": "5.65.18", "compression": "1.7.4", - "cookie-parser": "1.4.6", + "cookie-parser": "1.4.7", "csurf": "1.11.0", "dayjs": "1.11.13", "dayjs-plugin-utc": "0.1.2", - "debounce": "2.1.1", + "debounce": "2.2.0", "ejs": "3.1.10", "electron-debug": "4.0.1", "electron-dl": "4.0.0", @@ -75,18 +75,18 @@ "electron-window-state": "5.0.3", "escape-html": "1.0.3", "eslint": "9.10.0", - "express": "4.21.0", + "express": "4.21.1", "express-partial-content": "1.0.2", - "express-rate-limit": "7.4.0", - "express-session": "1.18.0", - "force-graph": "1.43.5", + "express-rate-limit": "7.4.1", + "express-session": "1.18.1", + "force-graph": "1.45.0", "fs-extra": "11.2.0", "helmet": "7.1.0", "html": "1.0.0", "html2plaintext": "2.1.4", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.5", - "i18next": "23.16.1", + "i18next": "23.16.2", "i18next-fs-backend": "2.3.2", "i18next-http-backend": "2.6.2", "image-type": "4.1.0", @@ -103,10 +103,10 @@ "katex": "0.16.11", "knockout": "3.5.1", "mark.js": "8.11.1", - "marked": "14.1.2", + "marked": "14.1.3", "mermaid": "11.3.0", "mime-types": "2.1.35", - "mind-elixir": "4.2.2", + "mind-elixir": "4.2.3", "multer": "1.4.5-lts.1", "node-abi": "3.67.0", "normalize-strings": "1.1.1", @@ -119,7 +119,7 @@ "request": "2.88.2", "safe-compare": "1.1.4", "sanitize-filename": "1.6.3", - "sanitize-html": "2.13.0", + "sanitize-html": "2.13.1", "sax": "1.4.1", "semver": "7.6.3", "serve-favicon": "2.5.0", @@ -132,7 +132,7 @@ "tree-kill": "1.2.2", "turndown": "7.2.0", "unescape": "1.0.1", - "vanilla-js-wheel-zoom": "9.0.2", + "vanilla-js-wheel-zoom": "9.0.4", "ws": "8.18.0", "xml2js": "0.6.2", "yauzl": "3.1.3" @@ -163,7 +163,7 @@ "@types/jsdom": "21.1.7", "@types/mime-types": "2.1.4", "@types/multer": "1.4.12", - "@types/node": "22.5.4", + "@types/node": "22.7.8", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.13.0", "@types/sax": "1.2.7", @@ -182,14 +182,14 @@ "electron-rebuild": "3.2.9", "esm": "3.2.25", "iconsur": "1.7.0", - "jasmine": "5.3.1", + "jasmine": "5.4.0", "jsdoc": "4.0.3", "lorem-ipsum": "2.0.8", "nodemon": "3.1.7", "rcedit": "4.0.1", "rimraf": "6.0.1", "ts-node": "10.9.2", - "tslib": "2.7.0", + "tslib": "2.8.0", "tsx": "4.19.1", "typescript": "5.6.3", "webpack": "5.95.0", diff --git a/src/public/app/components/entrypoints.js b/src/public/app/components/entrypoints.js index c5fd96e280..6ee06ff654 100644 --- a/src/public/app/components/entrypoints.js +++ b/src/public/app/components/entrypoints.js @@ -9,6 +9,7 @@ import ws from "../services/ws.js"; import bundleService from "../services/bundle.js"; import froca from "../services/froca.js"; import linkService from "../services/link.js"; +import { t } from "../services/i18n.js"; export default class Entrypoints extends Component { constructor() { diff --git a/src/public/app/desktop.js b/src/public/app/desktop.js index febb9a8f93..835624d359 100644 --- a/src/public/app/desktop.js +++ b/src/public/app/desktop.js @@ -9,9 +9,9 @@ import electronContextMenu from "./menus/electron_context_menu.js"; import glob from "./services/glob.js"; import { t } from "./services/i18n.js"; +await appContext.earlyInit(); + bundleService.getWidgetBundlesByParent().then(async widgetBundles => { - await appContext.earlyInit(); - // A dynamic import is required for layouts since they initialize components which require translations. const DesktopLayout = (await import("./layouts/desktop_layout.js")).default; diff --git a/src/public/app/services/bundle.js b/src/public/app/services/bundle.js index 51348111a2..e0a81eee47 100644 --- a/src/public/app/services/bundle.js +++ b/src/public/app/services/bundle.js @@ -3,6 +3,7 @@ import server from "./server.js"; import toastService from "./toast.js"; import froca from "./froca.js"; import utils from "./utils.js"; +import { t } from "./i18n.js"; async function getAndExecuteBundle(noteId, originEntity = null, script = null, params = null) { const bundle = await server.post(`script/bundle/${noteId}`, { @@ -75,9 +76,23 @@ async function getWidgetBundlesByParent() { try { widget = await executeBundle(bundle); - widgetsByParent.add(widget); - } - catch (e) { + if (widget) { + widget._noteId = bundle.noteId; + widgetsByParent.add(widget); + } + } catch (e) { + const noteId = bundle.noteId; + const note = await froca.getNote(noteId); + toastService.showPersistent({ + title: t("toast.bundle-error.title"), + icon: "alert", + message: t("toast.bundle-error.message", { + id: noteId, + title: note.title, + message: e.message + }) + }); + logError("Widget initialization failed: ", e); continue; } diff --git a/src/public/app/services/glob.js b/src/public/app/services/glob.js index 7177a6f958..f523a677e2 100644 --- a/src/public/app/services/glob.js +++ b/src/public/app/services/glob.js @@ -26,21 +26,6 @@ function setupGlobs() { // for CKEditor integration (button on block toolbar) window.glob.importMarkdownInline = async () => appContext.triggerCommand("importMarkdownInline"); - window.glob.SEARCH_HELP_TEXT = ` - Search tips - also see -

-

-

`; - window.onerror = function (msg, url, lineNo, columnNo, error) { const string = msg.toLowerCase(); @@ -64,6 +49,28 @@ function setupGlobs() { return false; }; + window.addEventListener("unhandledrejection", (e) => { + const string = e?.reason?.message?.toLowerCase(); + + let message = "Uncaught error: "; + + if (string?.includes("script error")) { + message += 'No details available'; + } else { + message += [ + `Message: ${e.reason.message}`, + `Line: ${e.reason.lineNumber}`, + `Column: ${e.reason.columnNumber}`, + `Error object: ${JSON.stringify(e.reason)}`, + `Stack: ${e.reason && e.reason.stack}` + ].join(', '); + } + + ws.logError(message); + + return false; + }); + for (const appCssNoteId of glob.appCssNoteIds || []) { libraryLoader.requireCss(`api/notes/download/${appCssNoteId}`, false); } diff --git a/src/public/app/services/note_create.js b/src/public/app/services/note_create.js index 995dec8929..cc3bbb42f6 100644 --- a/src/public/app/services/note_create.js +++ b/src/public/app/services/note_create.js @@ -5,6 +5,7 @@ import ws from "./ws.js"; import froca from "./froca.js"; import treeService from "./tree.js"; import toastService from "./toast.js"; +import { t } from "./i18n.js"; async function createNote(parentNotePath, options = {}) { options = Object.assign({ diff --git a/src/public/app/services/toast.js b/src/public/app/services/toast.js index 369a50e9d9..31991a3c92 100644 --- a/src/public/app/services/toast.js +++ b/src/public/app/services/toast.js @@ -16,7 +16,7 @@ function toast(options) { ); $toast.find('.toast-title').text(options.title); - $toast.find('.toast-body').text(options.message); + $toast.find('.toast-body').html(options.message); if (options.id) { $toast.attr("id", `toast-${options.id}`); diff --git a/src/public/app/widgets/basic_widget.js b/src/public/app/widgets/basic_widget.js index b60a74e8c5..4dd580bd21 100644 --- a/src/public/app/widgets/basic_widget.js +++ b/src/public/app/widgets/basic_widget.js @@ -1,4 +1,5 @@ import Component from "../components/component.js"; +import froca from "../services/froca.js"; import { t } from "../services/i18n.js"; import toastService from "../services/toast.js"; @@ -84,15 +85,8 @@ class BasicWidget extends Component { render() { try { this.doRender(); - } catch (e) { - toastService.showPersistent({ - title: t("toast.widget-error.title"), - icon: "alert", - message: t("toast.widget-error.message", { - title: this.widgetTitle, - message: e.message - }) - }); + } catch (e) { + this.logRenderingError(e); } this.$widget.attr('data-component-id', this.componentId); @@ -131,6 +125,35 @@ class BasicWidget extends Component { return this.$widget; } + logRenderingError(e) { + console.log("Got issue in widget ", this); + console.error(e); + + let noteId = this._noteId; + if (this._noteId) { + froca.getNote(noteId, true).then((note) => { + toastService.showPersistent({ + title: t("toast.widget-error.title"), + icon: "alert", + message: t("toast.widget-error.message-custom", { + id: noteId, + title: note.title, + message: e.message + }) + }); + }); + return; + } + + toastService.showPersistent({ + title: t("toast.widget-error.title"), + icon: "alert", + message: t("toast.widget-error.message-unknown", { + message: e.message + }) + }); + } + /** * Indicates if the widget is enabled. Widgets are enabled by default. Generally setting this to `false` will cause the widget not to be displayed, however it will still be available on the DOM but hidden. * @returns diff --git a/src/public/app/widgets/containers/container.js b/src/public/app/widgets/containers/container.js index ba47a8db0c..07c759bb03 100644 --- a/src/public/app/widgets/containers/container.js +++ b/src/public/app/widgets/containers/container.js @@ -8,7 +8,11 @@ export default class Container extends BasicWidget { renderChildren() { for (const widget of this.children) { - this.$widget.append(widget.render()); + try { + this.$widget.append(widget.render()); + } catch (e) { + widget.logRenderingError(e); + } } } } diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js index c79e79ea81..cefba22a26 100644 --- a/src/public/app/widgets/note_tree.js +++ b/src/public/app/widgets/note_tree.js @@ -666,8 +666,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } const node = this.prepareNode(branch); - - noteList.push(node); + if (node) { + noteList.push(node); + } } return noteList; @@ -709,7 +710,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const note = branch.getNoteFromCache(); if (!note) { - throw new Error(`Branch '${branch.branchId}' has no child note '${branch.noteId}'`); + console.warn(`Branch '${branch.branchId}' has no child note '${branch.noteId}'`); + return null; } const title = `${branch.prefix ? (`${branch.prefix} - `) : ""}${note.title}`; diff --git a/src/public/app/widgets/right_panel_widget.js b/src/public/app/widgets/right_panel_widget.js index a9831fb5aa..019d5f4c24 100644 --- a/src/public/app/widgets/right_panel_widget.js +++ b/src/public/app/widgets/right_panel_widget.js @@ -1,4 +1,6 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js"; +import toastService from "../services/toast.js"; +import { t } from "../services/i18n.js"; const WIDGET_TPL = `
@@ -54,7 +56,9 @@ class RightPanelWidget extends NoteContextAwareWidget { this.$buttons.append(buttonWidget.render()); } - this.initialized = this.doRenderBody(); + this.initialized = this.doRenderBody().catch(e => { + this.logRenderingError(e); + }); } /** diff --git a/src/public/app/widgets/search_options/search_string.js b/src/public/app/widgets/search_options/search_string.js index b430b691ff..f71df76ac8 100644 --- a/src/public/app/widgets/search_options/search_string.js +++ b/src/public/app/widgets/search_options/search_string.js @@ -61,9 +61,9 @@ export default class SearchString extends AbstractSearchOption { await this.setAttribute('label', 'searchString', searchString); - if (this.note.title.startsWith('Search: ')) { + if (this.note.title.startsWith(t("search_string.search_prefix"))) { await server.put(`notes/${this.note.noteId}/title`, { - title: `Search: ${searchString.length < 30 ? searchString : `${searchString.substr(0, 30)}…`}` + title: `${t("search_string.search_prefix")} ${searchString.length < 30 ? searchString : `${searchString.substr(0, 30)}…`}` }); } }, 1000); diff --git a/src/public/app/widgets/type_widgets/editable_code.js b/src/public/app/widgets/type_widgets/editable_code.js index 452fbbf9b9..a62f3fafbb 100644 --- a/src/public/app/widgets/type_widgets/editable_code.js +++ b/src/public/app/widgets/type_widgets/editable_code.js @@ -25,6 +25,7 @@ export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget { doRender() { this.$widget = $(TPL); + this.contentSized(); this.$editor = this.$widget.find('.note-detail-code-editor'); keyboardActionService.setupActionsForElement('code-detail', this.$widget, this); diff --git a/src/public/app/widgets/type_widgets/read_only_code.js b/src/public/app/widgets/type_widgets/read_only_code.js index 4cc2fb5e12..72e2e05e5f 100644 --- a/src/public/app/widgets/type_widgets/read_only_code.js +++ b/src/public/app/widgets/type_widgets/read_only_code.js @@ -21,6 +21,7 @@ export default class ReadOnlyCodeTypeWidget extends AbstractCodeTypeWidget { doRender() { this.$widget = $(TPL); + this.contentSized(); this.$editor = this.$widget.find('.note-detail-readonly-code-content'); super.doRender(); diff --git a/src/public/translations/cn/translation.json b/src/public/translations/cn/translation.json index ecad19c865..054cb16e3f 100644 --- a/src/public/translations/cn/translation.json +++ b/src/public/translations/cn/translation.json @@ -15,8 +15,7 @@ "message": "发生了严重错误,导致客户端应用程序无法启动:\n\n{{message}}\n\n这很可能是由于脚本以意外的方式失败引起的。请尝试以安全模式启动应用程序并解决问题。" }, "widget-error": { - "title": "小部件初始化失败", - "message": "标题为 \"{{title}}\" 的小部件由于以下原因无法初始化:\n\n{{message}}" + "title": "小部件初始化失败" } }, "add_link": { diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 35fb226e59..06604c638c 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -16,7 +16,12 @@ }, "widget-error": { "title": "Failed to initialize a widget", - "message": "Widget with title \"{{title}}\" could not be initialized due to:\n\n{{message}}" + "message-custom": "Custom widget from note with ID \"{{id}}\", titled \"{{title}}\" could not be initialized due to:\n\n{{message}}", + "message-unknown": "Unknown widget could not be initialized due to:\n\n{{message}}" + }, + "bundle-error": { + "title": "Failed to load a custom script", + "message": "Script from note with ID \"{{id}}\", titled \"{{title}}\" could not be executed due to:\n\n{{message}}" } }, "add_link": { @@ -635,7 +640,7 @@ "print_note": "Print note", "save_revision": "Save revision", "convert_into_attachment_failed": "Converting note '{{title}}' failed.", - "convert_into_attachment_successful": "Note '{{title}' has been converted to attachment.", + "convert_into_attachment_successful": "Note '{{title}}' has been converted to attachment.", "convert_into_attachment_prompt": "Are you sure you want to convert note '{{title}}' into an attachment of the parent note?" }, "onclick_button": { @@ -891,7 +896,8 @@ "label_rock_or_pop": "only one of the labels must be present", "label_year_comparison": "numerical comparison (also >, >=, <).", "label_date_created": "notes created in the last month", - "error": "Search error: {{error}}" + "error": "Search error: {{error}}", + "search_prefix": "Search:" }, "attachment_detail": { "open_help_page": "Open help page on attachments", @@ -1213,7 +1219,7 @@ "password": { "heading": "Password", "alert_message": "Please take care to remember your new password. Password is used for logging into the web interface and to encrypt protected notes. If you forget your password, then all your protected notes are forever lost.", - "reset_link": "click here to reset it.", + "reset_link": "Click here to reset it.", "old_password": "Old password", "new_password": "New password", "new_password_confirmation": "New password confirmation", diff --git a/src/public/translations/es/translation.json b/src/public/translations/es/translation.json index e2c438475d..330d84c142 100644 --- a/src/public/translations/es/translation.json +++ b/src/public/translations/es/translation.json @@ -16,7 +16,12 @@ }, "widget-error": { "title": "No se pudo inicializar un widget", - "message": "Widget con título \"{{title}}\" no pudo ser inicializado debido a:\n\n{{message}}" + "message-custom": "El widget personalizado de la nota con ID \"{{id}}\", titulada \"{{title}}\" no pudo ser inicializado debido a:\n\n{{message}}", + "message-unknown": "Un widget no pudo ser inicializado debido a:\n\n{{message}}" + }, + "bundle-error": { + "title": "Hubo un fallo al cargar un script personalizado", + "message": "El script de la nota con ID \"{{id}}\", titulado \"{{title}}\" no pudo ser ejecutado debido a:\n\n{{message}}" } }, "add_link": { @@ -166,7 +171,8 @@ "textImportedAsText": "Importar HTML, Markdown y TXT como notas de texto si no está claro en los metadatos", "codeImportedAsCode": "Importar archivos de código reconocidos (por ejemplo, .json) como notas de código si no están claros en los metadatos", "replaceUnderscoresWithSpaces": "Reemplazar guiones bajos con espacios en nombres de notas importadas", - "import": "Importar" + "import": "Importar", + "failed": "La importación falló: {{message}}." }, "include_note": { "dialog_title": "Incluir nota", @@ -330,9 +336,9 @@ "run_on_instance": "Definir en qué instancia de Trilium se debe ejecutar esto. Predeterminado para todas las instancias.", "run_at_hour": "¿A qué hora debería funcionar? Debe usarse junto con #run=hourly. Se puede definir varias veces para varias ejecuciones durante el día.", "disable_inclusion": "los scripts con esta etiqueta no se incluirán en la ejecución del script principal.", - "sorted": "mantiene las notas hijo ordenadas alfabéticamente por título", + "sorted": "mantiene las subnotas ordenadas alfabéticamente por título", "sort_direction": "ASC (el valor predeterminado) o DESC", - "sort_folders_first": "Las carpetas (notas con hijos) deben ordenarse en la parte superior", + "sort_folders_first": "Las carpetas (notas con subnotas) deben ordenarse en la parte superior", "top": "mantener la nota dada en la parte superior de su padre (se aplica solo en padres ordenados)", "hide_promoted_attributes": "Ocultar atributos promovidos en esta nota", "read_only": "el editor está en modo de sólo lectura. Funciona sólo para notas de texto y código.", @@ -350,9 +356,9 @@ "workspace_tab_background_color": "color CSS utilizado en la pestaña de nota cuando se ancla a esta nota", "workspace_calendar_root": "Define la raíz del calendario por cada espacio de trabajo", "workspace_template": "Esta nota aparecerá en la selección de plantillas disponibles al crear una nueva nota, pero solo cuando se levante a un espacio de trabajo que contenga esta plantilla", - "search_home": "se crearán nuevas notas de búsqueda como hijas de esta nota", - "workspace_search_home": "se crearán nuevas notas de búsqueda como hijo de esta nota cuando se anclan a algún antecesor de esta nota del espacio de trabajo", - "inbox": "ubicación predeterminada de la bandeja de entrada para nuevas notas - cuando crea una nota usando el botón \"nueva nota\" en la barra lateral, las notas serán creadas como notas hijo de la nota marcada con la etiqueta #inbox.", + "search_home": "se crearán nuevas notas de búsqueda como subnotas de esta nota", + "workspace_search_home": "se crearán nuevas notas de búsqueda como subnotas de esta nota cuando se anclan a algún antecesor de esta nota del espacio de trabajo", + "inbox": "ubicación predeterminada de la bandeja de entrada para nuevas notas - cuando crea una nota usando el botón \"nueva nota\" en la barra lateral, las notas serán creadas como subnotas de la nota marcada con la etiqueta #inbox.", "workspace_inbox": "ubicación predeterminada de la bandeja de entrada para nuevas notas cuando se anclan a algún antecesor de esta nota del espacio de trabajo", "sql_console_home": "ubicación predeterminada de las notas de la consola SQL", "bookmark_folder": "la nota con esta etiqueta aparecerá en los marcadores como carpeta (permitiendo el acceso a sus elementos hijos).", @@ -368,7 +374,7 @@ "share_index": "tenga en cuenta que con esto esta etiqueta enumerará todas las raíces de las notas compartidas", "display_relations": "nombres de relaciones delimitados por comas que deben mostrarse. Todos los demás estarán ocultos.", "hide_relations": "nombres de relaciones delimitados por comas que deben ocultarse. Se mostrarán todos los demás.", - "title_template": "título por defecto de notas creadas como hijo de esta nota. El valor es evaluado como una cadena de JavaScript \n y por lo tanto puede ser enriquecida con contenido dinámico vía las variables inyectadas now y parentNote. Ejemplos:\n \n \n \n Consulte la wiki para obtener más detalles, documentación de la API para parentNote y now para más detalles.", + "title_template": "título por defecto de notas creadas como subnota de esta nota. El valor es evaluado como una cadena de JavaScript \n y por lo tanto puede ser enriquecida con contenido dinámico vía las variables inyectadas now y parentNote. Ejemplos:\n \n \n \n Consulte la wiki para obtener más detalles, documentación de la API para parentNote y now para más detalles.", "template": "Esta nota aparecerá en la selección de plantillas disponibles al crear una nueva nota", "toc": "#toc o #toc=show forzará que se muestre la tabla de contenido, #toc=hide forzará a ocultarla. Si la etiqueta no existe, se observa la configuración global", "color": "define el color de la nota en el árbol de notas, enlaces, etc. Utilice cualquier valor de color CSS válido como 'red' o #a13d5f", @@ -632,7 +638,10 @@ "export_note": "Exportar nota", "delete_note": "Eliminar nota", "print_note": "Imprimir nota", - "save_revision": "Guardar revisión" + "save_revision": "Guardar revisión", + "convert_into_attachment_failed": "La conversión de nota '{{title}}' falló.", + "convert_into_attachment_successful": "La nota '{{title}}' ha sido convertida a un archivo adjunto.", + "convert_into_attachment_prompt": "¿Está seguro que desea convertir la nota '{{title}}' en un archivo adjunto de la nota padre?" }, "onclick_button": { "no_click_handler": "El widget de botón '{{componentId}}' no tiene un controlador de clics definido" @@ -670,7 +679,7 @@ "button_title": "Exportar diagrama como SVG" }, "relation_map_buttons": { - "create_child_note_title": "Crear una nueva nota hijo y agregarla a este mapa de relaciones", + "create_child_note_title": "Crear una nueva subnota y agregarla a este mapa de relaciones", "reset_pan_zoom_title": "Restablecer la panorámica y el zoom a las coordenadas y ampliación iniciales", "zoom_in_title": "Acercar", "zoom_out_title": "Alejar" @@ -681,7 +690,7 @@ "relation": "relación" }, "mobile_detail_menu": { - "insert_child_note": "Insertar nota hijo", + "insert_child_note": "Insertar subnota", "delete_this_note": "Eliminar esta nota", "error_cannot_get_branch_id": "No se puede obtener el branchID del notePath '{{notePath}}'", "error_unrecognized_command": "Comando no reconocido {{command}}" @@ -702,7 +711,7 @@ "grid": "Cuadrícula", "list": "Lista", "collapse_all_notes": "Contraer todas las notas", - "expand_all_children": "Ampliar todos los hijos", + "expand_all_children": "Ampliar todas las subnotas", "collapse": "Colapsar", "expand": "Expandir", "book_properties": "Propiedades del libro", @@ -856,7 +865,7 @@ "content_and_attachments_size": "Tenga en cuenta el tamaño del contenido, incluidos los archivos adjuntos", "content_and_attachments_and_revisions_size": "Tenga en cuenta el tamaño del contenido, incluidos los archivos adjuntos y las revisiones", "revision_count": "Número de revisiones", - "children_count": "Notas sobre el número de hijos", + "children_count": "Número de subnotas", "parent_count": "Número de clones", "owned_label_count": "Número de etiquetas", "owned_relation_count": "Número de relaciones", @@ -892,7 +901,7 @@ "attachment_detail": { "open_help_page": "Abrir página de ayuda en archivos adjuntos", "owning_note": "Nota dueña: ", - "you_can_also_open": ", también puedes abri el ", + "you_can_also_open": ", también puede abrir el ", "list_of_all_attachments": "Lista de todos los archivos adjuntos", "attachment_deleted": "Este archivo adjunto ha sido eliminado." }, @@ -903,7 +912,7 @@ "no_attachments": "Esta nota no tiene archivos adjuntos." }, "book": { - "no_children_help": "Esta nota de tipo libro no tieneninguna nota hijo así que no hay nada que mostrar. Véa la wiki para más detalles." + "no_children_help": "Esta nota de tipo libro no tieneninguna subnota así que no hay nada que mostrar. Véa la wiki para más detalles." }, "editable_code": { "placeholder": "Escriba el contenido de su nota de código aquí..." @@ -921,7 +930,15 @@ }, "protected_session": { "enter_password_instruction": "Para mostrar una nota protegida es necesario ingresar su contraseña:", - "start_session_button": "Iniciar sesión protegida" + "start_session_button": "Iniciar sesión protegida", + "started": "La sesión protegida ha iniciado.", + "wrong_password": "Contraseña incorrecta.", + "protecting-finished-successfully": "La protección finalizó exitosamente.", + "unprotecting-finished-successfully": "La desprotección finalizó exitosamente.", + "protecting-in-progress": "Protección en progreso: {{count}}", + "unprotecting-in-progress-count": "Desprotección en progreso: {{count}}", + "protecting-title": "Estado de protección", + "unprotecting-title": "Estado de desprotección" }, "relation_map": { "open_in_new_tab": "Abrir en nueva pestaña", @@ -992,7 +1009,9 @@ "fill_entity_changes_button": "Llenar registros de cambios de entidad", "full_sync_triggered": "Sincronización completa activada", "filling_entity_changes": "Rellenar filas de cambios de entidad...", - "sync_rows_filled_successfully": "Sincronizar filas completadas correctamente" + "sync_rows_filled_successfully": "Sincronizar filas completadas correctamente", + "finished-successfully": "La sincronización finalizó exitosamente.", + "failed": "La sincronización falló: {{message}}" }, "vacuum_database": { "title": "Limpiar base de datos", @@ -1138,7 +1157,7 @@ }, "table_of_contents": { "title": "Tabla de contenido", - "description": "La tabla de contenido aparecerá en las notas de texto cuando la nota tenga más de un número definido de títulos. Puedes personalizar este número:", + "description": "La tabla de contenido aparecerá en las notas de texto cuando la nota tenga más de un número definido de títulos. Puede personalizar este número:", "disable_info": "También puede utilizar esta opción para desactivar la TDC (TOC) de forma efectiva estableciendo un número muy alto.", "shortcut_info": "Puede configurar un atajo de teclado para alternar rápidamente el panel derecho (incluido el TDC) en Opciones -> Atajos (nombre 'toggleRightPane')." }, @@ -1287,7 +1306,7 @@ "open-in-a-new-tab": "Abrir en nueva pestaña", "open-in-a-new-split": "Abrir en nueva división", "insert-note-after": "Insertar nota después de", - "insert-child-note": "Insertar nota hijo", + "insert-child-note": "Insertar subnota", "delete": "Eliminar", "search-in-subtree": "Buscar en subárbol", "hoist-note": "Anclar nota", @@ -1311,7 +1330,9 @@ "duplicate-subtree": "Duplicar subárbol", "export": "Exportar", "import-into-note": "Importar a nota", - "apply-bulk-actions": "Aplicar acciones en lote" + "apply-bulk-actions": "Aplicar acciones en lote", + "converted-to-attachments": "{{count}} notas han sido convertidas en archivos adjuntos.", + "convert-to-attachment-confirm": "¿Está seguro que desea convertir las notas seleccionadas en archivos adjuntos de sus notas padres?" }, "shared_info": { "shared_publicly": "Esta nota está compartida públicamente en", @@ -1334,7 +1355,8 @@ "image": "Imagen", "launcher": "Lanzador", "doc": "Doc", - "widget": "Widget" + "widget": "Widget", + "confirm-change": "No es recomendado cambiar el tipo de nota cuando el contenido de la nota no está vacío. ¿Desea continuar de cualquier manera?" }, "protect_note": { "toggle-on": "Proteger la nota", @@ -1380,7 +1402,9 @@ "hide-archived-notes": "Ocultar notas archivadas", "automatically-collapse-notes": "Colapsar notas automaticamente", "automatically-collapse-notes-title": "Las notas serán colapsadas después de un periodo de inactividad para despejar el árbol.", - "save-changes": "Guardar y aplicar cambios" + "save-changes": "Guardar y aplicar cambios", + "auto-collapsing-notes-after-inactivity": "Colapsando notas automáticamente después de inactividad...", + "saved-search-note-refreshed": "La nota de búsqueda guardada fue recargada." }, "title_bar_buttons": { "window-on-top": "Mantener esta ventana en la parte superior." @@ -1424,5 +1448,53 @@ }, "app_context": { "please_wait_for_save": "Por favor espere algunos segundos a que se termine de guardar, después intente de nuevo." + }, + "note_create": { + "duplicated": "La nota \"{{title}}\" ha sido duplicada." + }, + "image": { + "copied-to-clipboard": "Una referencia a la imagen ha sido copiada al portapapeles. Esta puede ser pegada en cualquier nota de texto.", + "cannot-copy": "No se pudo copiar la referencia de imagen al portapapeles." + }, + "clipboard": { + "cut": "La(s) notas(s) han sido cortadas al portapapeles.", + "copied": "La(s) notas(s) han sido copiadas al portapapeles." + }, + "entrypoints": { + "note-revision-created": "Una revisión de nota ha sido creada.", + "note-executed": "Nota ejecutada.", + "sql-error": "Ocurrió un error al ejecutar la consulta SQL: {{message}}" + }, + "branches": { + "cannot-move-notes-here": "No se pueden mover notas aquí.", + "delete-status": "Estado de eliminación", + "delete-notes-in-progress": "Eliminación de notas en progreso: {{count}}", + "delete-finished-successfully": "La eliminación finalizó exitosamente.", + "undeleting-notes-in-progress": "Recuperación de notas en progreso: {{count}}", + "undeleting-notes-finished-successfully": "La recuperación de notas finalizó exitosamente." + }, + "frontend_script_api": { + "async_warning": "Está pasando una función asíncrona a `api.runOnBackend ()` que probablemente no funcionará como pretendía.", + "sync_warning": "Estás pasando una función sincrónica a `api.runasynconbackendwithmanualTransactionHandling ()`, \\ n while debería usar `api.runonbackend ()` en su lugar." + }, + "ws": { + "sync-check-failed": "¡La comprobación de sincronización falló!", + "consistency-checks-failed": "¡Las comprobaciones de consistencia fallaron! Vea los registros para más detalles.", + "encountered-error": "Error encontrado \"{{message}}\", compruebe la consola." + }, + "hoisted_note": { + "confirm_unhoisting": "La nota requerida '{{requestedNote}}' está fuera del subárbol de la nota anclada '{{hoistedNote}}' y debe desanclarla para acceder a la nota. ¿Desea proceder con el desanclaje?" + }, + "launcher_context_menu": { + "reset_launcher_confirm": "¿Realmente desea restaurar \"{{title}}\"? Todos los datos / ajustes en esta nota (y sus subnotas) se van a perder y el lanzador regresará a su ubicación original.", + "add-note-launcher": "Agregar un lanzador de nota", + "add-script-launcher": "Agregar un lanzador de script", + "add-custom-widget": "Agregar un widget personalizado", + "add-spacer": "Agregar espaciador", + "delete": "Eliminar", + "reset": "Restaurar", + "move-to-visible-launchers": "Mover a lanzadores visibles", + "move-to-available-launchers": "Mover a lanzadores disponibles", + "duplicate-launcher": "Duplicar lanzador" } } diff --git a/src/public/translations/fr/translation.json b/src/public/translations/fr/translation.json index f95d27becd..4ff9b99206 100644 --- a/src/public/translations/fr/translation.json +++ b/src/public/translations/fr/translation.json @@ -15,8 +15,7 @@ "message": "Une erreur critique s'est produite qui empêche l'application client de démarrer :\n\n{{message}}\n\nCeci est probablement dû à un échec inattendu d'un script. Essayez de démarrer l'application en mode sans échec et de résoudre le problème." }, "widget-error": { - "title": "Impossible d'initialiser un widget", - "message": "Le widget portant le titre \"{{title}}\" n'a pas pu être initialisé en raison de :\n\n{{message}}" + "title": "Impossible d'initialiser un widget" } }, "add_link": { diff --git a/src/public/translations/ro/translation.json b/src/public/translations/ro/translation.json index a6bed6d62e..db59fd359c 100644 --- a/src/public/translations/ro/translation.json +++ b/src/public/translations/ro/translation.json @@ -1094,7 +1094,8 @@ "label_year_comparison": "comparații numerice (de asemenea >, >=, <).", "placeholder": "cuvinte cheie pentru căutarea în conținut, #etichetă = valoare...", "search_syntax": "Sintaxa de căutare", - "title_column": "Textul de căutat:" + "title_column": "Textul de căutat:", + "search_prefix": "Căutare:" }, "shortcuts": { "action_name": "Denumirea acțiunii", @@ -1189,8 +1190,13 @@ "title": "Eroare critică" }, "widget-error": { - "message": "Widget-ul intitulat „{{title}}” nu a putut fi inițializat din cauza:\n\n{{message}}", - "title": "Eroare la inițializarea unui widget" + "title": "Eroare la inițializarea unui widget", + "message-custom": "Widget-ul personalizat din notița cu ID-ul „{{id}}”, întitulată ”{{title}}” nu a putut fi inițializată din cauza:\n\n{{message}}", + "message-unknown": "Un widget necunoscut nu a putut fi inițializat din cauza:\n\n{{message}}" + }, + "bundle-error": { + "title": "Eroare la încărcarea unui script personalizat", + "message": "Scriptul din notița cu ID-ul „{{id}}”, întitulată „{{title}}” nu a putut fi executată din cauza:\n\n{{message}}" } }, "tray": { diff --git a/src/routes/api/options.ts b/src/routes/api/options.ts index 8f4f661ffb..1cd2457636 100644 --- a/src/routes/api/options.ts +++ b/src/routes/api/options.ts @@ -5,6 +5,7 @@ import log from "../../services/log.js"; import searchService from "../../services/search/services/search.js"; import ValidationError from "../../errors/validation_error.js"; import { Request } from 'express'; +import { changeLanguage } from "../../services/i18n.js"; // options allowed to be updated directly in the Options dialog const ALLOWED_OPTIONS = new Set([ @@ -108,6 +109,11 @@ function update(name: string, value: string) { optionService.setOption(name, value); + if (name === "locale") { + // This runs asynchronously, so it's not perfect, but it does the trick for now. + changeLanguage(value); + } + return true; } diff --git a/src/services/date_notes.ts b/src/services/date_notes.ts index 465757328a..3f355f4291 100644 --- a/src/services/date_notes.ts +++ b/src/services/date_notes.ts @@ -9,14 +9,20 @@ import searchService from "../services/search/services/search.js"; import SearchContext from "../services/search/search_context.js"; import hoistedNoteService from "./hoisted_note.js"; import BNote from "../becca/entities/bnote.js"; +import { t } from "i18next"; const CALENDAR_ROOT_LABEL = 'calendarRoot'; const YEAR_LABEL = 'yearNote'; const MONTH_LABEL = 'monthNote'; const DATE_LABEL = 'dateNote'; -const DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; -const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December']; +const WEEKDAY_TRANSLATION_IDS = [ + "weekdays.sunday", "weekdays.monday", "weekdays.tuesday", "weekdays.wednesday", "weekdays.thursday", "weekdays.friday", "weekdays.saturday", "weekdays.sunday" +]; + +const MONTH_TRANSLATION_IDS = [ + "months.january", "months.february", "months.march", "months.april", "months.may", "months.june", "months.july", "months.august", "months.september", "months.october", "months.november", "months.december" +]; type StartOfWeek = "monday" | "sunday"; @@ -92,7 +98,7 @@ function getYearNote(dateStr: string, _rootNote: BNote | null = null): BNote { function getMonthNoteTitle(rootNote: BNote, monthNumber: string, dateObj: Date) { const pattern = rootNote.getOwnedLabelValue("monthPattern") || "{monthNumberPadded} - {month}"; - const monthName = MONTHS[dateObj.getMonth()]; + const monthName = t(MONTH_TRANSLATION_IDS[dateObj.getMonth()]); return pattern .replace(/{shortMonth3}/g, monthName.slice(0,3)) @@ -138,7 +144,7 @@ function getMonthNote(dateStr: string, _rootNote: BNote | null = null): BNote { function getDayNoteTitle(rootNote: BNote, dayNumber: string, dateObj: Date) { const pattern = rootNote.getOwnedLabelValue("datePattern") || "{dayInMonthPadded} - {weekDay}"; - const weekDay = DAYS[dateObj.getDay()]; + const weekDay = t(WEEKDAY_TRANSLATION_IDS[dateObj.getDay()]); return pattern .replace(/{ordinal}/g, ordinal(parseInt(dayNumber))) diff --git a/src/services/i18n.ts b/src/services/i18n.ts index 3498045bd2..84a707b89c 100644 --- a/src/services/i18n.ts +++ b/src/services/i18n.ts @@ -41,4 +41,8 @@ function getCurrentLanguage() { } return language; -} \ No newline at end of file +} + +export function changeLanguage(locale: string) { + return i18next.changeLanguage(locale); +} diff --git a/src/services/special_notes.ts b/src/services/special_notes.ts index 50ba2eccf8..991e7f3eae 100644 --- a/src/services/special_notes.ts +++ b/src/services/special_notes.ts @@ -8,6 +8,7 @@ import hoistedNoteService from "./hoisted_note.js"; import searchService from "./search/services/search.js"; import SearchContext from "./search/search_context.js"; import hiddenSubtree from "./hidden_subtree.js"; +import { t } from "i18next"; const { LBTPL_NOTE_LAUNCHER, LBTPL_CUSTOM_WIDGET, LBTPL_SPACER, LBTPL_SCRIPT } = hiddenSubtree; function getInboxNote(date: string) { @@ -75,7 +76,7 @@ function saveSqlConsole(sqlConsoleNoteId: string) { function createSearchNote(searchString: string, ancestorNoteId: string) { const {note} = noteService.createNewNote({ parentNoteId: getMonthlyParentNoteId('_search', 'search'), - title: `Search: ${searchString}`, + title: `${t("special_notes.search_prefix")} ${searchString}`, content: "", type: 'search', mime: 'application/json' diff --git a/translations/en/server.json b/translations/en/server.json index 3d8a46e55c..069094f280 100644 --- a/translations/en/server.json +++ b/translations/en/server.json @@ -157,5 +157,31 @@ "clipped-from": "This note was originally clipped from {{- url}}", "child-notes": "Child notes:", "no-content": "This note has no content." + }, + "weekdays": { + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday", + "sunday": "Sunday" + }, + "months": { + "january": "January", + "february": "February", + "march": "March", + "april": "April", + "may": "May", + "june": "June", + "july": "July", + "august": "August", + "september": "September", + "october": "October", + "november": "November", + "december": "December" + }, + "special_notes": { + "search_prefix": "Search:" } } diff --git a/translations/es/server.json b/translations/es/server.json index 654e5fd6b9..ab4c281477 100644 --- a/translations/es/server.json +++ b/translations/es/server.json @@ -1,161 +1,184 @@ { - "keyboard_actions": { - "open-jump-to-note-dialog": "Abrir cuadro de diálogo \"Saltar a nota\"", - "search-in-subtree": "Buscar notas en el subárbol de la nota activa", - "expand-subtree": "Expandir el subárbol de la nota actual", - "collapse-tree": "Colapsa el árbol de notas completo", - "collapse-subtree": "Colapsa el subárbol de la nota actual", - "sort-child-notes": "Ordenar notas hijas", - "creating-and-moving-notes": "Creando y moviendo notas", - "create-note-into-inbox": "Crear una nota en la bandeja de entrada (si está definida) o nota del día", - "delete-note": "Eliminar nota", - "move-note-up": "Mover nota hacia arriba", - "move-note-down": "Mover nota hacia abajo", - "move-note-up-in-hierarchy": "Mover nota hacia arriba en la jerarquía", - "move-note-down-in-hierarchy": "Mover nota hacia abajo en la jerarquía", - "edit-note-title": "Saltar del árbol al detalle de la nota y editar el título", - "edit-branch-prefix": "Mostrar cuadro de diálogo Editar prefijo de rama", - "note-clipboard": "Portapapeles de notas", - "copy-notes-to-clipboard": "Copiar las notas seleccionadas al portapapeles", - "paste-notes-from-clipboard": "Pegar las notas del portapapeles en una nota activa", - "cut-notes-to-clipboard": "Cortar las notas seleccionadas al portapapeles", - "select-all-notes-in-parent": "Seleccionar todas las notas del nivel de la nota actual", - "add-note-above-to-the-selection": "Agregar nota arriba de la selección", - "add-note-below-to-selection": "Agregar nota arriba de la selección", - "duplicate-subtree": "Duplicar subárbol", - "tabs-and-windows": "Pestañas y ventanas", - "open-new-tab": "Abre una nueva pestaña", - "close-active-tab": "Cierra la pestaña activa", - "reopen-last-tab": "Vuelve a abrir la última pestaña cerrada", - "activate-next-tab": "Activa la pestaña de la derecha", - "activate-previous-tab": "Activa la pestaña de la izquierda", - "open-new-window": "Abrir nueva ventana vacía", - "toggle-tray": "Muestra/Oculta la aplicación en la bandeja del sistema", - "first-tab": "Activa la primera pestaña de la lista", - "second-tab": "Activa la segunda pestaña de la lista", - "third-tab": "Activa la tercera pestaña de la lista", - "fourth-tab": "Activa la cuarta pestaña de la lista", - "fifth-tab": "Activa la quinta pestaña de la lista", - "sixth-tab": "Activa la sexta pestaña de la lista", - "seventh-tab": "Activa la séptima pestaña de la lista", - "eight-tab": "Activa la octava pestaña de la lista", - "ninth-tab": "Activa la novena pestaña de la lista", - "last-tab": "Activa la última pestaña de la lista", - "dialogs": "Diálogos", - "show-note-source": "Muestra el cuadro de diálogo Fuente de nota", - "show-options": "Muestra el cuadro de diálogo Opciones", - "show-revisions": "Muestra el cuadro de diálogo Revisiones de notas", - "show-recent-changes": "Muestra el cuadro de diálogo Cambios recientes", - "show-sql-console": "Muestra el cuadro de diálogo Consola SQL", - "show-backend-log": "Muestra el cuadro de diálogo Registro de backend", - "text-note-operations": "Operaciones de notas de texto", - "add-link-to-text": "Abrir cuadro de diálogo para agregar un enlace al texto", - "follow-link-under-cursor": "Seguir el enlace dentro del cual se coloca el cursor", - "insert-date-and-time-to-text": "Insertar fecha y hora actuales en el texto", - "paste-markdown-into-text": "Pega Markdown del portapapeles en la nota de texto", - "cut-into-note": "Corta la selección de la nota actual y crea una subnota con el texto seleccionado", - "add-include-note-to-text": "Abre el cuadro de diálogo para incluir una nota", - "edit-readonly-note": "Editar una nota de sólo lectura", - "attributes-labels-and-relations": "Atributos (etiquetas y relaciones)", - "add-new-label": "Crear nueva etiqueta", - "create-new-relation": "Crear nueva relación", - "ribbon-tabs": "Pestañas de cinta", - "toggle-basic-properties": "Alternar propiedades básicas", - "toggle-file-properties": "Alternar propiedades de archivo", - "toggle-image-properties": "Alternar propiedades de imagen", - "toggle-owned-attributes": "Alternar atributos de propiedad", - "toggle-inherited-attributes": "Alternar atributos heredados", - "toggle-promoted-attributes": "Alternar atributos promocionados", - "toggle-link-map": "Alternar mapa de enlaces", - "toggle-note-info": "Alternar información de nota", - "toggle-note-paths": "Alternar rutas de notas", - "toggle-similar-notes": "Alternar notas similares", - "other": "Otro", - "toggle-right-pane": "Alternar la visualización del panel derecho, que incluye la tabla de contenidos y aspectos destacados", - "print-active-note": "Imprimir nota activa", - "open-note-externally": "Abrir nota como un archivo con la aplicación predeterminada", - "render-active-note": "Renderizar (volver a renderizar) nota activa", - "run-active-note": "Ejecutar nota de código JavaScript activa (frontend/backend)", - "toggle-note-hoisting": "Alterna la elevación de la nota activa", - "unhoist": "Bajar desde cualquier lugar", - "reload-frontend-app": "Recargar frontend de la aplicación", - "open-dev-tools": "Abrir herramientas de desarrollo", - "toggle-left-note-tree-panel": "Alternar panel izquierdo (árbol de notas)", - "toggle-full-screen": "Alternar pantalla completa", - "zoom-out": "Alejar", - "zoom-in": "Acercar", - "note-navigation": "Navegación de notas", - "reset-zoom-level": "Restablecer nivel de zoom", - "copy-without-formatting": "Copiar el texto seleccionado sin formatear", - "force-save-revision": "Forzar la creación/guardado de una nueva revisión de nota de la nota activa", - "show-help": "Muestra ayuda/hoja de referencia integrada", - "toggle-book-properties": "Alternar propiedades del libro" - }, - "login": { - "title": "Iniciar sesión", - "heading": "Iniciar sesión en Trillium", - "incorrect-password": "La contraseña es incorrecta. Por favor inténtalo de nuevo.", - "password": "Contraseña", - "remember-me": "Recordarme", - "button": "Iniciar sesión" - }, - "set_password": { - "heading": "Establecer contraseña", - "description": "Antes de poder comenzar a usar Trilium desde la web, primero debe establecer una contraseña. Luego utilizará esta contraseña para iniciar sesión.", - "password": "Contraseña", - "password-confirmation": "Confirmación de contraseña", - "button": "Establecer contraseña" - }, - "javascript-required": "Trilium requiere que JavaScript esté habilitado.", - "setup": { - "heading": "Configuración de TrilliumNext Notes", - "new-document": "Soy un usuario nuevo y quiero crear un nuevo documento de Trilium para mis notas", - "sync-from-desktop": "Ya tengo una instancia de escritorio y quiero configurar la sincronización con ella", - "sync-from-server": "Ya tengo una instancia de servidor y quiero configurar la sincronización con ella", - "next": "Siguiente", - "init-in-progress": "Inicialización del documento en curso", - "redirecting": "En breve será redirigido a la aplicación.", - "title": "Configuración" - }, - "setup_sync-from-desktop": { - "heading": "Sincronizar desde el escritorio", - "description": "Esta configuración debe iniciarse desde la instancia de escritorio:", - "step1": "Abra su instancia de escritorio de TriliumNext Notes.", - "step2": "En el menú Trilium, dé clic en Opciones.", - "step3": "Dé clic en la categoría Sincronizar.", - "step4": "Cambie la dirección de la instancia del servidor a: {{- host}} y dé clic en Guardar.", - "step5": "Dé clic en el botón \"Probar sincronización\" para verificar que la conexión fue exitosa.", - "step6": "Una vez que haya completado estos pasos, dé clic en {{- link}}.", - "step6-here": "aquí" - }, - "setup_sync-from-server": { - "heading": "Sincronización desde el servidor", - "instructions": "Por favor, ingrese la dirección y las credenciales del servidor Trilium a continuación. Esto descargará todo el documento de Trilium desde el servidor y configurará la sincronización. Dependiendo del tamaño del documento y de la velocidad de su conexión, esto puede tardar un poco.", - "server-host": "Dirección del servidor Trilium", - "server-host-placeholder": "https://:", - "proxy-server": "Servidor proxy (opcional)", - "proxy-server-placeholder": "https://:", - "note": "Nota:", - "proxy-instruction": "Si deja la configuración de proxy en blanco, se utilizará el proxy del sistema (aplica únicamente a la aplicación de escritorio)", - "password": "Contraseña", - "password-placeholder": "Contraseña", - "back": "Atrás", - "finish-setup": "Finalizar la configuración" - }, - "setup_sync-in-progress": { - "heading": "Sincronización en progreso", - "successful": "La sincronización se ha configurado correctamente. La sincronización inicial tardará algún tiempo en finalizar. Una vez hecho esto, será redirigido a la página de inicio de sesión.", - "outstanding-items": "Elementos de sincronización destacados:", - "outstanding-items-default": "N/A" - }, - "share_404": { - "title": "No encontrado", - "heading": "No encontrado" - }, - "share_page": { - "parent": "padre:", - "clipped-from": "Esta nota fue recortada originalmente de {{- url}}", - "child-notes": "Notas hijo:", - "no-content": "Esta nota no tiene contenido." - } + "keyboard_actions": { + "open-jump-to-note-dialog": "Abrir cuadro de diálogo \"Saltar a nota\"", + "search-in-subtree": "Buscar notas en el subárbol de la nota activa", + "expand-subtree": "Expandir el subárbol de la nota actual", + "collapse-tree": "Colapsa el árbol de notas completo", + "collapse-subtree": "Colapsa el subárbol de la nota actual", + "sort-child-notes": "Ordenar subnotas", + "creating-and-moving-notes": "Creando y moviendo notas", + "create-note-into-inbox": "Crear una nota en la bandeja de entrada (si está definida) o nota del día", + "delete-note": "Eliminar nota", + "move-note-up": "Mover nota hacia arriba", + "move-note-down": "Mover nota hacia abajo", + "move-note-up-in-hierarchy": "Mover nota hacia arriba en la jerarquía", + "move-note-down-in-hierarchy": "Mover nota hacia abajo en la jerarquía", + "edit-note-title": "Saltar del árbol al detalle de la nota y editar el título", + "edit-branch-prefix": "Mostrar cuadro de diálogo Editar prefijo de rama", + "note-clipboard": "Portapapeles de notas", + "copy-notes-to-clipboard": "Copiar las notas seleccionadas al portapapeles", + "paste-notes-from-clipboard": "Pegar las notas del portapapeles en una nota activa", + "cut-notes-to-clipboard": "Cortar las notas seleccionadas al portapapeles", + "select-all-notes-in-parent": "Seleccionar todas las notas del nivel de la nota actual", + "add-note-above-to-the-selection": "Agregar nota arriba de la selección", + "add-note-below-to-selection": "Agregar nota arriba de la selección", + "duplicate-subtree": "Duplicar subárbol", + "tabs-and-windows": "Pestañas y ventanas", + "open-new-tab": "Abre una nueva pestaña", + "close-active-tab": "Cierra la pestaña activa", + "reopen-last-tab": "Vuelve a abrir la última pestaña cerrada", + "activate-next-tab": "Activa la pestaña de la derecha", + "activate-previous-tab": "Activa la pestaña de la izquierda", + "open-new-window": "Abrir nueva ventana vacía", + "toggle-tray": "Muestra/Oculta la aplicación en la bandeja del sistema", + "first-tab": "Activa la primera pestaña de la lista", + "second-tab": "Activa la segunda pestaña de la lista", + "third-tab": "Activa la tercera pestaña de la lista", + "fourth-tab": "Activa la cuarta pestaña de la lista", + "fifth-tab": "Activa la quinta pestaña de la lista", + "sixth-tab": "Activa la sexta pestaña de la lista", + "seventh-tab": "Activa la séptima pestaña de la lista", + "eight-tab": "Activa la octava pestaña de la lista", + "ninth-tab": "Activa la novena pestaña de la lista", + "last-tab": "Activa la última pestaña de la lista", + "dialogs": "Diálogos", + "show-note-source": "Muestra el cuadro de diálogo Fuente de nota", + "show-options": "Muestra el cuadro de diálogo Opciones", + "show-revisions": "Muestra el cuadro de diálogo Revisiones de notas", + "show-recent-changes": "Muestra el cuadro de diálogo Cambios recientes", + "show-sql-console": "Muestra el cuadro de diálogo Consola SQL", + "show-backend-log": "Muestra el cuadro de diálogo Registro de backend", + "text-note-operations": "Operaciones de notas de texto", + "add-link-to-text": "Abrir cuadro de diálogo para agregar un enlace al texto", + "follow-link-under-cursor": "Seguir el enlace dentro del cual se coloca el cursor", + "insert-date-and-time-to-text": "Insertar fecha y hora actuales en el texto", + "paste-markdown-into-text": "Pega Markdown del portapapeles en la nota de texto", + "cut-into-note": "Corta la selección de la nota actual y crea una subnota con el texto seleccionado", + "add-include-note-to-text": "Abre el cuadro de diálogo para incluir una nota", + "edit-readonly-note": "Editar una nota de sólo lectura", + "attributes-labels-and-relations": "Atributos (etiquetas y relaciones)", + "add-new-label": "Crear nueva etiqueta", + "create-new-relation": "Crear nueva relación", + "ribbon-tabs": "Pestañas de cinta", + "toggle-basic-properties": "Alternar propiedades básicas", + "toggle-file-properties": "Alternar propiedades de archivo", + "toggle-image-properties": "Alternar propiedades de imagen", + "toggle-owned-attributes": "Alternar atributos de propiedad", + "toggle-inherited-attributes": "Alternar atributos heredados", + "toggle-promoted-attributes": "Alternar atributos promocionados", + "toggle-link-map": "Alternar mapa de enlaces", + "toggle-note-info": "Alternar información de nota", + "toggle-note-paths": "Alternar rutas de notas", + "toggle-similar-notes": "Alternar notas similares", + "other": "Otro", + "toggle-right-pane": "Alternar la visualización del panel derecho, que incluye la tabla de contenidos y aspectos destacados", + "print-active-note": "Imprimir nota activa", + "open-note-externally": "Abrir nota como un archivo con la aplicación predeterminada", + "render-active-note": "Renderizar (volver a renderizar) nota activa", + "run-active-note": "Ejecutar nota de código JavaScript activa (frontend/backend)", + "toggle-note-hoisting": "Alterna la elevación de la nota activa", + "unhoist": "Bajar desde cualquier lugar", + "reload-frontend-app": "Recargar frontend de la aplicación", + "open-dev-tools": "Abrir herramientas de desarrollo", + "toggle-left-note-tree-panel": "Alternar panel izquierdo (árbol de notas)", + "toggle-full-screen": "Alternar pantalla completa", + "zoom-out": "Alejar", + "zoom-in": "Acercar", + "note-navigation": "Navegación de notas", + "reset-zoom-level": "Restablecer nivel de zoom", + "copy-without-formatting": "Copiar el texto seleccionado sin formatear", + "force-save-revision": "Forzar la creación/guardado de una nueva revisión de nota de la nota activa", + "show-help": "Muestra ayuda/hoja de referencia integrada", + "toggle-book-properties": "Alternar propiedades del libro" + }, + "login": { + "title": "Iniciar sesión", + "heading": "Iniciar sesión en Trillium", + "incorrect-password": "La contraseña es incorrecta. Por favor inténtalo de nuevo.", + "password": "Contraseña", + "remember-me": "Recordarme", + "button": "Iniciar sesión" + }, + "set_password": { + "heading": "Establecer contraseña", + "description": "Antes de poder comenzar a usar Trilium desde la web, primero debe establecer una contraseña. Luego utilizará esta contraseña para iniciar sesión.", + "password": "Contraseña", + "password-confirmation": "Confirmación de contraseña", + "button": "Establecer contraseña" + }, + "javascript-required": "Trilium requiere que JavaScript esté habilitado.", + "setup": { + "heading": "Configuración de TrilliumNext Notes", + "new-document": "Soy un usuario nuevo y quiero crear un nuevo documento de Trilium para mis notas", + "sync-from-desktop": "Ya tengo una instancia de escritorio y quiero configurar la sincronización con ella", + "sync-from-server": "Ya tengo una instancia de servidor y quiero configurar la sincronización con ella", + "next": "Siguiente", + "init-in-progress": "Inicialización del documento en curso", + "redirecting": "En breve será redirigido a la aplicación.", + "title": "Configuración" + }, + "setup_sync-from-desktop": { + "heading": "Sincronizar desde el escritorio", + "description": "Esta configuración debe iniciarse desde la instancia de escritorio:", + "step1": "Abra su instancia de escritorio de TriliumNext Notes.", + "step2": "En el menú Trilium, dé clic en Opciones.", + "step3": "Dé clic en la categoría Sincronizar.", + "step4": "Cambie la dirección de la instancia del servidor a: {{- host}} y dé clic en Guardar.", + "step5": "Dé clic en el botón \"Probar sincronización\" para verificar que la conexión fue exitosa.", + "step6": "Una vez que haya completado estos pasos, dé clic en {{- link}}.", + "step6-here": "aquí" + }, + "setup_sync-from-server": { + "heading": "Sincronización desde el servidor", + "instructions": "Por favor, ingrese la dirección y las credenciales del servidor Trilium a continuación. Esto descargará todo el documento de Trilium desde el servidor y configurará la sincronización. Dependiendo del tamaño del documento y de la velocidad de su conexión, esto puede tardar un poco.", + "server-host": "Dirección del servidor Trilium", + "server-host-placeholder": "https://:", + "proxy-server": "Servidor proxy (opcional)", + "proxy-server-placeholder": "https://:", + "note": "Nota:", + "proxy-instruction": "Si deja la configuración de proxy en blanco, se utilizará el proxy del sistema (aplica únicamente a la aplicación de escritorio)", + "password": "Contraseña", + "password-placeholder": "Contraseña", + "back": "Atrás", + "finish-setup": "Finalizar la configuración" + }, + "setup_sync-in-progress": { + "heading": "Sincronización en progreso", + "successful": "La sincronización se ha configurado correctamente. La sincronización inicial tardará algún tiempo en finalizar. Una vez hecho esto, será redirigido a la página de inicio de sesión.", + "outstanding-items": "Elementos de sincronización destacados:", + "outstanding-items-default": "N/A" + }, + "share_404": { + "title": "No encontrado", + "heading": "No encontrado" + }, + "share_page": { + "parent": "padre:", + "clipped-from": "Esta nota fue recortada originalmente de {{- url}}", + "child-notes": "Subnotas:", + "no-content": "Esta nota no tiene contenido." + }, + "weekdays": { + "monday": "Lunes", + "tuesday": "Martes", + "wednesday": "Miércoles", + "thursday": "Jueves", + "friday": "Viernes", + "saturday": "Sábado", + "sunday": "Domingo" + }, + "months": { + "january": "Enero", + "february": "Febrero", + "march": "Marzo", + "april": "Abril", + "may": "Mayo", + "june": "Junio", + "july": "Julio", + "august": "Agosto", + "september": "Septiembre", + "october": "Octubre", + "november": "Noviembre", + "december": "Diciembre" + } } diff --git a/translations/ro/server.json b/translations/ro/server.json index b71a9b34dc..5fd329a80c 100644 --- a/translations/ro/server.json +++ b/translations/ro/server.json @@ -157,5 +157,31 @@ "clipped-from": "Această notiță a fost decupată inițial de pe {{- url}}", "no-content": "Această notiță nu are conținut.", "parent": "părinte:" + }, + "weekdays": { + "monday": "Luni", + "tuesday": "Marți", + "wednesday": "Miercuri", + "thursday": "Joi", + "friday": "Vineri", + "saturday": "Sâmbătă", + "sunday": "Duminică" + }, + "months": { + "january": "Ianuarie", + "february": "Februarie", + "march": "Martie", + "april": "Aprilie", + "may": "Mai", + "june": "Iunie", + "july": "Iulie", + "august": "August", + "september": "Septembrie", + "october": "Octombrie", + "november": "Noiembrie", + "december": "Decembrie" + }, + "special_notes": { + "search_prefix": "Căutare:" } }