From ce6f1f723a9ad9d4b9b8b3f20bc9f6db5b9a20db Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 25 Jul 2025 17:47:36 +0300 Subject: [PATCH 1/6] feat: init paraglide --- .vscode/extensions.json | 5 ++++ bun.lock | 51 +++++++++++++++++++++++++++++++++++- messages/en.json | 8 ++++++ package.json | 3 ++- project.inlang/.gitignore | 1 + project.inlang/project_id | 1 + project.inlang/settings.json | 15 +++++++++++ src/app.html | 2 +- src/hooks.server.ts | 15 +++++++++++ src/hooks.ts | 6 +++++ src/lib/workers/pandoc.ts | 2 -- src/routes/+layout.svelte | 6 +++++ vite.config.ts | 2 ++ 13 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 messages/en.json create mode 100644 project.inlang/.gitignore create mode 100644 project.inlang/project_id create mode 100644 project.inlang/settings.json create mode 100644 src/hooks.server.ts create mode 100644 src/hooks.ts diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..116d685 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "inlang.vs-code-extension" + ] +} \ No newline at end of file diff --git a/bun.lock b/bun.lock index cede655..33ce87a 100644 --- a/bun.lock +++ b/bun.lock @@ -24,6 +24,7 @@ "vite-plugin-wasm": "^3.4.1", }, "devDependencies": { + "@inlang/paraglide-js": "2.2.0", "@poppanator/sveltekit-svg": "^5.0.0", "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/kit": "^2.16.0", @@ -140,6 +141,12 @@ "@imagemagick/magick-wasm": ["@imagemagick/magick-wasm@0.0.34", "", {}, "sha512-vSTe4cfR8U8r2Ityo6s3iRFVK903W4fhur3t/ixc6vLSAJXmqXC9cKXz2NkkDBUVU7ssJTYMiTKAb1S9RY+ZdQ=="], + "@inlang/paraglide-js": ["@inlang/paraglide-js@2.2.0", "", { "dependencies": { "@inlang/recommend-sherlock": "0.2.1", "@inlang/sdk": "2.4.9", "commander": "11.1.0", "consola": "3.4.0", "json5": "2.2.3", "unplugin": "^2.1.2", "urlpattern-polyfill": "^10.0.0" }, "bin": { "paraglide-js": "bin/run.js" } }, "sha512-pkpXu1LanvpcAbvpVPf7PgF11Uq7DliSEBngrcUN36l4ZOOpzn3QBTvVr/tJxvks0O67WseQgiMHet8KH7Oz5A=="], + + "@inlang/recommend-sherlock": ["@inlang/recommend-sherlock@0.2.1", "", { "dependencies": { "comment-json": "^4.2.3" } }, "sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg=="], + + "@inlang/sdk": ["@inlang/sdk@2.4.9", "", { "dependencies": { "@lix-js/sdk": "0.4.7", "@sinclair/typebox": "^0.31.17", "kysely": "^0.27.4", "sqlite-wasm-kysely": "0.3.0", "uuid": "^10.0.0" } }, "sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ=="], + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], @@ -152,6 +159,10 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + "@lix-js/sdk": ["@lix-js/sdk@0.4.7", "", { "dependencies": { "@lix-js/server-protocol-schema": "0.1.1", "dedent": "1.5.1", "human-id": "^4.1.1", "js-sha256": "^0.11.0", "kysely": "^0.27.4", "sqlite-wasm-kysely": "0.3.0", "uuid": "^10.0.0" } }, "sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ=="], + + "@lix-js/server-protocol-schema": ["@lix-js/server-protocol-schema@0.1.1", "", {}, "sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -236,6 +247,10 @@ "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + "@sinclair/typebox": ["@sinclair/typebox@0.31.28", "", {}, "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ=="], + + "@sqlite.org/sqlite-wasm": ["@sqlite.org/sqlite-wasm@3.48.0-build4", "", { "bin": { "sqlite-wasm": "bin/index.js" } }, "sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ=="], + "@stripe/stripe-js": ["@stripe/stripe-js@7.4.0", "", {}, "sha512-lQHQPfXPTBeh0XFjq6PqSBAyR7umwcJbvJhXV77uGCUDD6ymXJU/f2164ydLMLCCceNuPlbV9b+1smx98efwWQ=="], "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], @@ -322,6 +337,8 @@ "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + "array-timsort": ["array-timsort@1.0.3", "", {}, "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="], + "autoprefixer": ["autoprefixer@10.4.20", "", { "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g=="], "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], @@ -358,14 +375,20 @@ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], + + "comment-json": ["comment-json@4.2.5", "", { "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", "esprima": "^4.0.1", "has-own-prop": "^2.0.0", "repeat-string": "^1.6.1" } }, "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="], + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="], @@ -380,6 +403,8 @@ "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "dedent": ["dedent@1.5.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], @@ -432,6 +457,8 @@ "espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], "esrap": ["esrap@1.4.5", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g=="], @@ -490,8 +517,12 @@ "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + "has-own-prop": ["has-own-prop@2.0.0", "", {}, "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ=="], + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ieee754-buffer": ["ieee754-buffer@2.0.0", "", {}, "sha512-AXUAT0nMEi7h1Is8HXGXof3eejl/GabZFKSj8Ym6kVRUSwrAb52EkAXywiCQYSHGQMRn7lvfY7vhPMjVc+Kybg=="], @@ -528,6 +559,8 @@ "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + "js-sha256": ["js-sha256@0.11.1", "", {}, "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg=="], + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], @@ -536,12 +569,16 @@ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], "known-css-properties": ["known-css-properties@0.35.0", "", {}, "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A=="], + "kysely": ["kysely@0.27.6", "", {}, "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ=="], + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], @@ -668,6 +705,8 @@ "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="], + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], @@ -698,6 +737,8 @@ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "sqlite-wasm-kysely": ["sqlite-wasm-kysely@0.3.0", "", { "dependencies": { "@sqlite.org/sqlite-wasm": "^3.48.0-build2" }, "peerDependencies": { "kysely": "*" } }, "sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg=="], + "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -750,10 +791,14 @@ "uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="], + "unplugin": ["unplugin@2.3.5", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw=="], + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "urlpattern-polyfill": ["urlpattern-polyfill@10.1.0", "", {}, "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="], + "utf8-buffer": ["utf8-buffer@1.0.0", "", {}, "sha512-ueuhzvWnp5JU5CiGSY4WdKbiN/PO2AZ/lpeLiz2l38qwdLy/cW40XobgyuIWucNyum0B33bVB0owjFCeGBSLqg=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], @@ -770,6 +815,8 @@ "vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="], + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], @@ -822,6 +869,8 @@ "svelte-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + "svgo/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + "tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "tailwindcss/postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 0000000..ff66278 --- /dev/null +++ b/messages/en.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "upload": {}, + "convert": {}, + "settings": {}, + "about": {}, + "toasts": {} +} diff --git a/package.json b/package.json index de1534d..b3d7216 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "typescript": "^5.7.3", "typescript-eslint": "^8.20.0", "vite": "^5.4.11", - "vite-plugin-top-level-await": "^1.5.0" + "vite-plugin-top-level-await": "^1.5.0", + "@inlang/paraglide-js": "2.2.0" }, "dependencies": { "@bjorn3/browser_wasi_shim": "^0.4.1", diff --git a/project.inlang/.gitignore b/project.inlang/.gitignore new file mode 100644 index 0000000..5e46596 --- /dev/null +++ b/project.inlang/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/project.inlang/project_id b/project.inlang/project_id new file mode 100644 index 0000000..f0a498b --- /dev/null +++ b/project.inlang/project_id @@ -0,0 +1 @@ +ff77Td2rnvEqQyzBYT \ No newline at end of file diff --git a/project.inlang/settings.json b/project.inlang/settings.json new file mode 100644 index 0000000..62d77ad --- /dev/null +++ b/project.inlang/settings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "baseLocale": "en", + "locales": [ + "en", + "de" + ], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": "./messages/{locale}.json" + } +} \ No newline at end of file diff --git a/src/app.html b/src/app.html index a8f439a..7febd2a 100644 --- a/src/app.html +++ b/src/app.html @@ -1,5 +1,5 @@ - + diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..dffeb65 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,15 @@ +import type { Handle } from '@sveltejs/kit'; +import { paraglideMiddleware } from '$lib/paraglide/server'; + +// creating a handle to use the paraglide middleware +const paraglideHandle: Handle = ({ event, resolve }) => + paraglideMiddleware(event.request, ({ request: localizedRequest, locale }) => { + event.request = localizedRequest; + return resolve(event, { + transformPageChunk: ({ html }) => { + return html.replace('%lang%', locale); + } + }); + }); + +export const handle: Handle = paraglideHandle; \ No newline at end of file diff --git a/src/hooks.ts b/src/hooks.ts new file mode 100644 index 0000000..3711dd0 --- /dev/null +++ b/src/hooks.ts @@ -0,0 +1,6 @@ +import type { Reroute } from '@sveltejs/kit'; +import { deLocalizeUrl } from '$lib/paraglide/runtime'; + +export const reroute: Reroute = (request) => { + return deLocalizeUrl(request.url).pathname; +}; \ No newline at end of file diff --git a/src/lib/workers/pandoc.ts b/src/lib/workers/pandoc.ts index 825095f..0377252 100644 --- a/src/lib/workers/pandoc.ts +++ b/src/lib/workers/pandoc.ts @@ -113,8 +113,6 @@ const formatToReader = (format: Format): string => { return "rtf"; case ".rst": return "rst"; - case ".xml": - return "xml"; } throw new Error(`Unsupported format: ${format}`); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 8f754d4..729f786 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -24,6 +24,7 @@ import { browser } from "$app/environment"; import { page } from "$app/state"; import { initStores as initAnimStores } from "$lib/animation/index.js"; + import { locales, localizeHref } from "$lib/paraglide/runtime"; let { children, data } = $props(); let enablePlausible = $state(false); @@ -162,6 +163,11 @@ `` or `{@render ...}` tag missing — inner content will not be rendered --> +
+ {#each locales as locale} + {locale} + {/each} +
diff --git a/vite.config.ts b/vite.config.ts index 2f53f4d..838a94b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,3 +1,4 @@ +import { paraglideVitePlugin } from '@inlang/paraglide-js' import { sveltekit } from "@sveltejs/kit/vite"; import { defineConfig, type PluginOption } from "vite"; import svg from "@poppanator/sveltekit-svg"; @@ -6,6 +7,7 @@ import wasm from "vite-plugin-wasm"; export default defineConfig(({ command }) => { const plugins: PluginOption[] = [ sveltekit(), + paraglideVitePlugin({ project: './project.inlang', outdir: './src/lib/paraglide' }), svg({ includePaths: ["./src/lib/assets"], svgoOptions: { From dff9c94ed5a216f545b7bada77f2eebd3bed4073 Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 25 Jul 2025 20:46:07 +0300 Subject: [PATCH 2/6] feat: translations god this was hell --- messages/en.json | 155 +++++++++++++++++- .../functional/ConversionPanel.svelte | 13 +- src/lib/sections/about/Credits.svelte | 57 +++---- src/lib/sections/about/Donate.svelte | 37 +++-- src/lib/sections/about/Resources.svelte | 9 +- src/lib/sections/about/Sponsors.svelte | 16 +- src/lib/sections/about/Why.svelte | 18 +- src/lib/sections/settings/Appearance.svelte | 20 +-- src/lib/sections/settings/Conversion.svelte | 18 +- src/lib/sections/settings/Privacy.svelte | 33 ++-- src/lib/sections/settings/Vertd.svelte | 71 ++++---- src/routes/+page.svelte | 46 +++--- src/routes/about/+page.svelte | 15 +- src/routes/convert/+page.svelte | 30 ++-- src/routes/settings/+page.svelte | 5 +- 15 files changed, 333 insertions(+), 210 deletions(-) diff --git a/messages/en.json b/messages/en.json index ff66278..c78da32 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1,8 +1,153 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "upload": {}, - "convert": {}, - "settings": {}, - "about": {}, - "toasts": {} + "upload": { + "title": "The file converter you'll love.", + "subtitle": "All image, audio, and document processing is done on your device. Videos are converted on our lightning-fast servers. No file size limit, no ads, and completely open source.", + "supports_title": "VERT supports...", + "images": "Images", + "audio": "Audio", + "documents": "Documents", + "video": "Video", + "video_server_processing": "Video uploads to a server for processing by default, learn how to set it up locally [wiki_link]here[/wiki_link].", + "local_supported": "Local fully supported", + "status": "Status:", + "ready": "ready", + "not_ready": "not ready", + "supported_formats": "Supported formats:", + "tooltip": { + "partial_support": "This format can only be converted as {direction}.", + "direction_input": "input (from)", + "direction_output": "output (to)" + } + }, + "convert": { + "panel": { + "convert_all": "Convert all", + "download_all": "Download all as .zip", + "remove_all": "Remove all files", + "set_all_to": "Set all to", + "na": "N/A" + }, + "tooltips": { + "unknown_file": "Unknown file type", + "audio_file": "Audio file", + "video_file": "Video file", + "document_file": "Document file", + "image_file": "Image file", + "convert_file": "Convert this file", + "download_file": "Download this file" + }, + "errors": { + "cant_convert": "We can't convert this file.", + "vertd_server": "what are you doing..? you're supposed to run the vertd server!", + "unsupported_format": "Only image, video, audio, and document files are supported", + "vertd_not_found": "Could not find the vertd instance to start video conversion. Are you sure the instance URL is set correctly?" + } + }, + "settings": { + "title": "Settings", + "errors": { + "save_failed": "Failed to save settings!" + }, + "appearance": { + "title": "Appearance", + "brightness_theme": "Brightness theme", + "brightness_description": "Want a sunny flash-bang, or a quiet lonely night?", + "light": "Light", + "dark": "Dark", + "effect_settings": "Effect settings", + "effect_description": "Would you like fancy effects, or a more static experience?", + "enable": "Enable", + "disable": "Disable" + }, + "conversion": { + "title": "Conversion", + "filename_format": "File name format", + "filename_description": "This will determine the name of the file on download, not including the file extension. You can put these following templates in the format, which will be replaced with the relevant information: %name% for the original file name, %extension% for the original file extension, and %date% for a date string of when the file was converted.", + "placeholder": "VERT_%name%" + }, + "vertd": { + "title": "Video conversion", + "status": "status:", + "loading": "loading...", + "available": "available, commit id {commitId}", + "unavailable": "unavailable (is the url right?)", + "description": "The vertd project is a server wrapper for FFmpeg. This allows you to convert videos through the convenience of VERT's web interface, while still being able to harness the power of your GPU to do it as quickly as possible.", + "hosting_info": "We host a public instance for your convenience, but it is quite easy to host your own on your PC or server if you know what you are doing. You can download the server binaries [vertd_link]here[/vertd_link] - the process of setting this up will become easier in the future, so stay tuned!", + "instance_url": "Instance URL", + "url_placeholder": "Example: http://localhost:24153", + "conversion_speed": "Conversion speed", + "speed_description": "This describes the tradeoff between speed and quality. Faster speeds will result in lower quality, but will get the job done quicker.", + "speeds": { + "very_slow": "Very Slow", + "slower": "Slower", + "slow": "Slow", + "medium": "Medium", + "fast": "Fast", + "ultra_fast": "Ultra Fast" + } + }, + "privacy": { + "title": "Privacy", + "plausible_title": "Plausible analytics", + "plausible_description": "We use [plausible_link]Plausible[/plausible_link], a privacy-focused analytics tool, to gather completely anonymous statistics. All data is anonymized and aggregated, and no identifiable information is ever sent or stored. You can view the analytics [analytics_link]here[/analytics_link] and choose to opt out below.", + "opt_in": "Opt-in", + "opt_out": "Opt-out" + } + }, + "about": { + "title": "About", + "why": { + "title": "Why VERT?", + "description": "File converters have always disappointed us. They're ugly, riddled with ads, and most importantly; slow. We decided to solve this problem once and for all by making an alternative that solves all those problems, and more.

All non-video files are converted completely on-device; this means that there's no delay between sending and receiving the files from a server, and we never get to snoop on the files you convert. Video files get uploaded to our lightning-fast RTX 4000 Ada server.

Your videos stay on there for an hour if you do not convert them. If you do convert the file, the video will stay on the server for an hour, or until it is downloaded. The file will then be deleted from our server." + }, + "sponsors": { + "title": "Sponsors", + "description": "Want to support us? Contact a developer in the [discord_link]Discord[/discord_link] server, or send an email to", + "email_copied": "Email copied to clipboard!" + }, + "resources": { + "title": "Resources", + "discord": "Discord", + "source": "Source", + "email": "Email" + }, + "donate": { + "title": "Donate to VERT", + "description": "With your support, we can keep maintaining and improving VERT.", + "one_time": "One-time", + "monthly": "Monthly", + "custom": "Custom", + "pay_now": "Pay now", + "donate_amount": "Donate ${amount} USD", + "thank_you": "Thank you for your donation!", + "payment_failed": "Payment failed: {message}{period} You have not been charged.", + "donation_error": "An error occurred while processing your donation. Please try again later.", + "payment_error": "Error fetching payment details. Please try again later." + }, + "credits": { + "title": "Credits", + "contact_team": "If you would like to contact the development team, please use the email found on the \"Resources\" card.", + "notable_contributors": "Notable contributors", + "notable_description": "We'd like to thank these people for their major contributions to VERT.", + "github_contributors": "GitHub contributors", + "github_description": "Big [jpegify_link]thanks[/jpegify_link] to all these people for helping out! [github_link]Want to help too?[/github_link]", + "no_contributors": "Seems like no one has contributed yet... [contribute_link]be the first to contribute![/contribute_link]", + "libraries": "Libraries", + "libraries_description": "A big thanks to FFmpeg (audio, video), ImageMagick (images) and Pandoc (documents) for maintaining such excellent libraries for so many years. VERT relies on them to provide you with your conversions.", + "roles": { + "lead_developer": "Lead developer; conversion backend, UI implementation", + "developer": "Developer; UI implementation", + "designer": "Designer; UX, branding, marketing", + "docker_ci": "Maintaining Docker & CI support", + "former_cofounder": "Former co-founder & designer" + } + }, + "errors": { + "github_contributors": "Error fetching GitHub contributors" + } + }, + "toasts": { + "hello": "world" + } } diff --git a/src/lib/components/functional/ConversionPanel.svelte b/src/lib/components/functional/ConversionPanel.svelte index 572af77..cc8ec8d 100644 --- a/src/lib/components/functional/ConversionPanel.svelte +++ b/src/lib/components/functional/ConversionPanel.svelte @@ -7,6 +7,7 @@ import ProgressBar from "../visual/ProgressBar.svelte"; import FormatDropdown from "./FormatDropdown.svelte"; import { categories } from "$lib/converters"; + import { m } from "$lib/paraglide/messages"; const length = $derived(files.files.length); const progress = $derived(files.files.filter((f) => f.result).length); @@ -27,7 +28,7 @@ disabled={!files.ready} > -

Convert all

+

{m["convert.panel.convert_all"]()}

{#if $isMobile} {:else} - +
@@ -229,7 +238,7 @@
@@ -288,7 +297,7 @@ class="btn w-full h-12 bg-accent-red text-black rounded-full mt-4" onclick={donate} > - Donate ${amount.toFixed(2)} USD + {m["about.donate.donate_amount"]({ amount: amount.toFixed(2) })}
@@ -304,7 +313,7 @@ class="row-start-1 col-start-1 flex justify-center items-center" > - Pay now + {m["about.donate.pay_now"]()} {/if} diff --git a/src/lib/sections/about/Resources.svelte b/src/lib/sections/about/Resources.svelte index e75f3f3..696d2a8 100644 --- a/src/lib/sections/about/Resources.svelte +++ b/src/lib/sections/about/Resources.svelte @@ -8,6 +8,7 @@ MailIcon, MessageCircleMoreIcon, } from "lucide-svelte"; + import { m } from "$lib/paraglide/messages"; @@ -17,7 +18,7 @@ > - Resources + {m["about.resources.title"]()} diff --git a/src/lib/sections/about/Sponsors.svelte b/src/lib/sections/about/Sponsors.svelte index 61b914c..59bc865 100644 --- a/src/lib/sections/about/Sponsors.svelte +++ b/src/lib/sections/about/Sponsors.svelte @@ -5,6 +5,8 @@ import { DISCORD_URL } from "$lib/consts"; import { error } from "$lib/logger"; import { addToast } from "$lib/store/ToastProvider"; + import { m } from "$lib/paraglide/messages"; + import { link } from "$lib/paraglide"; let copied = false; let timeoutId: number | undefined; @@ -13,7 +15,7 @@ try { navigator.clipboard.writeText("hello@vert.sh"); copied = true; - addToast("success", "Email copied to clipboard!"); + addToast("success", m["about.sponsors.email_copied"]()); if (timeoutId) clearTimeout(timeoutId); timeoutId = setTimeout(() => (copied = false), 2000); @@ -30,7 +32,7 @@ > - Sponsors + {m["about.sponsors.title"]()}
@@ -43,11 +45,11 @@

- Want to support us? Contact a developer in the Discord - server, or send an email to + {@html link( + "discord_link", + m["about.sponsors.description"](), + DISCORD_URL, + )}

- Why VERT? + {m["about.why.title"]()}

- File converters have always disappointed us. They're ugly, - riddled with ads, and most importantly; slow. We decided to solve this - problem once and for all by making an alternative that solves all those - problems, and more.
-
- All non-video files are converted completely on-device; this means that there's - no delay between sending and receiving the files from a server, and we never - get to snoop on the files you convert. -
-
- Video files get uploaded to our lightning-fast RTX 4000 Ada server. Your - videos stay on there for an hour if you do not convert them. If you do convert - the file, the video will stay on the server for an hour, or until it is downloaded. - The file will then be deleted from our server. + {@html m["about.why.description"]()}

diff --git a/src/lib/sections/settings/Appearance.svelte b/src/lib/sections/settings/Appearance.svelte index 574f3ce..8f36374 100644 --- a/src/lib/sections/settings/Appearance.svelte +++ b/src/lib/sections/settings/Appearance.svelte @@ -14,6 +14,7 @@ SunIcon, } from "lucide-svelte"; import { onMount, onDestroy } from "svelte"; + import { m } from "$lib/paraglide/messages"; let lightElement: HTMLButtonElement; let darkElement: HTMLButtonElement; @@ -70,14 +71,14 @@ class="inline-block -mt-1 mr-2 bg-accent-purple p-2 rounded-full" color="black" /> - Appearance + {m["settings.appearance.title"]()}
-

Brightness theme

+

{m["settings.appearance.brightness_theme"]()}

- Want a sunny flash-bang, or a quiet lonely night? + {m["settings.appearance.brightness_description"]()}

@@ -88,7 +89,7 @@ class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" > - Light + {m["settings.appearance.light"]()}
-

Effect settings

+

{m["settings.appearance.effect_settings"]()}

- Would you like fancy effects, or a more static - experience? + {m["settings.appearance.effect_description"]()}

@@ -118,7 +118,7 @@ class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" > - Enable + {m["settings.appearance.enable"]()}
diff --git a/src/lib/sections/settings/Conversion.svelte b/src/lib/sections/settings/Conversion.svelte index 21e0b26..8b15fd5 100644 --- a/src/lib/sections/settings/Conversion.svelte +++ b/src/lib/sections/settings/Conversion.svelte @@ -3,6 +3,7 @@ import Panel from "$lib/components/visual/Panel.svelte"; import { RefreshCwIcon } from "lucide-svelte"; import type { ISettings } from "./index.svelte"; + import { m } from "$lib/paraglide/messages"; const { settings }: { settings: ISettings } = $props(); @@ -15,25 +16,14 @@ class="inline-block -mt-1 mr-2 bg-accent p-2 rounded-full" color="black" /> - Conversion + {m["settings.conversion.title"]()}
-

File name format

+

{m["settings.conversion.filename_format"]()}

- This will determine the name of the file on download, not including the file extension. - You can put these following templates in the format, which - will be replaced with the relevant information: - %name% - for the original file name, - %extension% - for the original file extension, and - %date% - for a date string of when the file was converted. + {@html m["settings.conversion.filename_description"]()}

@@ -15,26 +17,23 @@ class="inline-block -mt-1 mr-2 bg-accent-blue p-2 rounded-full" color="black" /> - Privacy + {m["settings.privacy.title"]()}
-

Plausible analytics

+

+ {m["settings.privacy.plausible_title"]()} +

- We use Plausible, a privacy-focused analytics tool, to gather - completely anonymous statistics. All data is anonymized - and aggregated, and no identifiable information is ever - sent or stored. You can view the analytics - here and choose to opt out below. + {@html link( + ["plausible_link", "analytics_link"], + m["settings.privacy.plausible_description"](), + [ + "https://plausible.io/privacy-focused-web-analytics", + "https://ats.vert.sh/vert.sh", + ], + )}

@@ -48,7 +47,7 @@ : ''} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" > - Opt-in + {m["settings.privacy.opt_in"]()}
diff --git a/src/lib/sections/settings/Vertd.svelte b/src/lib/sections/settings/Vertd.svelte index b17ca92..364820a 100644 --- a/src/lib/sections/settings/Vertd.svelte +++ b/src/lib/sections/settings/Vertd.svelte @@ -6,6 +6,8 @@ import clsx from "clsx"; import Dropdown from "$lib/components/functional/Dropdown.svelte"; import { vertdLoaded } from "$lib/store/index.svelte"; + import { m } from "$lib/paraglide/messages"; + import { link } from "$lib/paraglide"; let vertdCommit = $state(null); let abortController: AbortController | null = null; @@ -55,7 +57,7 @@ class="inline-block -mt-1 mr-2 bg-accent-red p-2 rounded-full overflow-visible" color="black" /> - Video conversion + {m["settings.vertd.title"]()}

- status: {vertdCommit + {m["settings.vertd.status"]()} {vertdCommit ? vertdCommit === "loading" - ? "loading..." - : `available, commit id ${vertdCommit}` - : "unavailable (is the url right?)"} + ? m["settings.vertd.loading"]() + : m["settings.vertd.available"]({ commitId: vertdCommit }) + : m["settings.vertd.unavailable"]()}

- The vertd project is a server wrapper for FFmpeg. - This allows you to convert videos through the convenience of - VERT's web interface, while still being able to harness the power - of your GPU to do it as quickly as possible. + {@html m["settings.vertd.description"]()}

- We host a public instance for your convenience, but it is - quite easy to host your own on your PC or server if you know - what you are doing. You can download the server binaries here - the process of setting this up will become easier in the - future, so stay tuned! + {@html link("vertd_link", m["settings.vertd.hosting_info"](), GITHUB_URL_VERTD)}

-

Instance URL

+

{m["settings.vertd.instance_url"]()}

-

Conversion speed

+

{m["settings.vertd.conversion_speed"]()}

- This describes the tradeoff between speed and - quality. Faster speeds will result in lower quality, - but will get the job done quicker. + {m["settings.vertd.speed_description"]()}

{ switch (settings.vertdSpeed) { case "verySlow": - return "Very Slow"; + return m["settings.vertd.speeds.very_slow"](); case "slower": - return "Slower"; + return m["settings.vertd.speeds.slower"](); case "slow": - return "Slow"; + return m["settings.vertd.speeds.slow"](); case "medium": - return "Medium"; + return m["settings.vertd.speeds.medium"](); case "fast": - return "Fast"; + return m["settings.vertd.speeds.fast"](); case "ultraFast": - return "Ultra Fast"; + return m["settings.vertd.speeds.ultra_fast"](); } })()} onselect={(selected) => { switch (selected) { - case "Very Slow": + case m["settings.vertd.speeds.very_slow"](): settings.vertdSpeed = "verySlow"; break; - case "Slower": + case m["settings.vertd.speeds.slower"](): settings.vertdSpeed = "slower"; break; - case "Slow": + case m["settings.vertd.speeds.slow"](): settings.vertdSpeed = "slow"; break; - case "Medium": + case m["settings.vertd.speeds.medium"](): settings.vertdSpeed = "medium"; break; - case "Fast": + case m["settings.vertd.speeds.fast"](): settings.vertdSpeed = "fast"; break; - case "Ultra Fast": + case m["settings.vertd.speeds.ultra_fast"](): settings.vertdSpeed = "ultraFast"; break; } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 4f5b691..f45a8ff 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -5,6 +5,8 @@ import { vertdLoaded } from "$lib/store/index.svelte"; import clsx from "clsx"; import { AudioLines, BookText, Check, Film, Image } from "lucide-svelte"; + import { m } from "$lib/paraglide/messages"; + import { link } from "$lib/paraglide"; const getSupportedFormats = (name: string) => converters @@ -20,6 +22,7 @@ ready: boolean; formats: string; icon: typeof Image; + title: string; }; } = $derived({ Images: { @@ -28,16 +31,19 @@ false, formats: getSupportedFormats("imagemagick"), icon: Image, + title: m["upload.images"](), }, Audio: { ready: converters.find((c) => c.name === "ffmpeg")?.ready || false, formats: getSupportedFormats("ffmpeg"), icon: AudioLines, + title: m["upload.audio"](), }, Documents: { ready: converters.find((c) => c.name === "pandoc")?.ready || false, formats: getSupportedFormats("pandoc"), icon: BookText, + title: m["upload.documents"](), }, Video: { ready: @@ -45,6 +51,7 @@ (false && $vertdLoaded), formats: getSupportedFormats("vertd"), icon: Film, + title: m["upload.video"](), }, }); @@ -58,9 +65,10 @@ ); if (formatInfo) { - return `This format can only be converted as ${ - formatInfo.fromSupported ? "input (from)" : "output (to)" - }.`; + const direction = formatInfo.fromSupported + ? m["upload.tooltip.direction_input"]() + : m["upload.tooltip.direction_output"](); + return m["upload.tooltip.partial_support"]({ direction }); } return ""; }; @@ -75,14 +83,12 @@

- The file converter you'll love. + {m["upload.title"]()}

- All image, audio, and document processing is done on your - device. Videos are converted on our lightning-fast servers. - No file size limit, no ads, and completely open source. + {m["upload.subtitle"]()}

@@ -94,7 +100,7 @@
-

VERT supports...

+

{m["upload.supports_title"]()}

{#each Object.entries(status) as [key, s]} @@ -111,31 +117,33 @@ >
- {key} + {s.title}
{#if key === "Video"}

- Video uploads to a server for processing by - default, learn how to set it up locally here. + {@html link( + "wiki_link", + m["upload.video_server_processing"](), + "https://github.com/VERT-sh/VERT/wiki/How-to-convert-video-with-VERT", + )}

{:else}

- Local fully supported + + {m["upload.local_supported"]()}

{/if}

- Status: - {s.ready ? "ready" : "not ready"} + {m["upload.status"]()} + {s.ready + ? m["upload.ready"]() + : m["upload.not_ready"]()}

- Supported formats:  + {m["upload.supported_formats"]()}  {#each s.formats.split(", ") as format, index} {@const isPartial = format.endsWith("*")} {@const formatName = isPartial diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte index ff2916c..e553c9b 100644 --- a/src/routes/about/+page.svelte +++ b/src/routes/about/+page.svelte @@ -12,6 +12,7 @@ import { addToast } from "$lib/store/ToastProvider"; import { dev } from "$app/environment"; import { page } from "$app/state"; + import { m } from "$lib/paraglide/messages"; // import { dev } from "$app/environment"; // import { page } from "$app/state"; @@ -34,19 +35,19 @@ { name: "nullptr", github: "https://github.com/not-nullptr", - role: "Lead developer; conversion backend, UI implementation", + role: m["about.credits.roles.lead_developer"](), avatar: avatarNullptr, }, { name: "JovannMC", github: "https://github.com/JovannMC", - role: "Developer; UI implementation", + role: m["about.credits.roles.developer"](), avatar: avatarJovannMC, }, { name: "Liam", github: "https://x.com/z2rMC", - role: "Designer; UX, branding, marketing", + role: m["about.credits.roles.designer"](), avatar: avatarLiam, }, ]; @@ -55,13 +56,13 @@ { name: "azurejelly", github: "https://github.com/azurejelly", - role: "Maintaining Docker & CI support", + role: m["about.credits.roles.docker_ci"](), avatar: avatarAzurejelly, }, { name: "Realmy", github: "https://github.com/RealmyTheMan", - role: "Former co-founder & designer", + role: m["about.credits.roles.former_cofounder"](), avatar: avatarRealmy, }, ]; @@ -80,7 +81,7 @@ try { const response = await fetch(`${GITHUB_API_URL}/contributors`); if (!response.ok) { - addToast("error", "Error fetching GitHub contributors"); + addToast("error", m["about.errors.github_contributors"]()); throw new Error(`HTTP error, status: ${response.status}`); } const allContribs = await response.json(); @@ -135,7 +136,7 @@

- About + {m["about.title"]()}

{ // depending on format, select right category and format @@ -120,23 +121,23 @@
{#if !converters.length} - + {:else if isAudio} - + {:else if isVideo} - + {:else if isDocument} - + {:else} - + {/if} @@ -172,11 +173,10 @@ class="h-full flex flex-col text-center justify-center text-failure" >

- We can't convert this file. + {m["convert.errors.cant_convert"]()}

- what are you doing..? you're supposed to run the vertd - server! + {m["convert.errors.vertd_server"]()}

{:else} @@ -184,11 +184,10 @@ class="h-full flex flex-col text-center justify-center text-failure" >

- We can't convert this file. + {m["convert.errors.cant_convert"]()}

- Only image, video, audio, and document files are - supported + {m["convert.errors.unsupported_format"]()}

{/if} @@ -196,10 +195,9 @@
-

We can't convert this file.

+

{m["convert.errors.cant_convert"]()}

- Could not find the vertd instance to start video conversion. - Are you sure the instance URL is set correctly? + {m["convert.errors.vertd_not_found"]()}

{:else} @@ -251,7 +249,7 @@ onselect={(option) => handleSelect(option, file)} />
- +
-

{m["settings.appearance.effect_settings"]()}

+

+ {m["settings.appearance.effect_settings"]()} +

{m["settings.appearance.effect_description"]()}

@@ -115,7 +154,9 @@
+
+
+

+ {m["settings.language.title"]()} +

+

+ {m["settings.language.description"]()} +

+
+
+ +
+
diff --git a/src/lib/store/index.svelte.ts b/src/lib/store/index.svelte.ts index bd2b376..ac19c1a 100644 --- a/src/lib/store/index.svelte.ts +++ b/src/lib/store/index.svelte.ts @@ -6,6 +6,7 @@ import { parseBlob, selectCover } from "music-metadata"; import { writable } from "svelte/store"; import { addDialog } from "./DialogProvider"; import PQueue from "p-queue"; +import { getLocale, setLocale } from "$lib/paraglide/runtime"; class Files { public files = $state([]); @@ -292,3 +293,17 @@ export const vertdLoaded = writable(false); export const isMobile = writable(false); export const effects = writable(true); export const theme = writable<"light" | "dark">("light"); +export const locale = writable(getLocale()); +export const availableLocales = { + "en": "English", + "uwu": "UwU", +} + +export function updateLocale(newLocale: string) { + log(["locale"], `set to ${newLocale}`); + localStorage.setItem("locale", newLocale); + // @ts-expect-error shush + setLocale(newLocale, { reload: false }); + // @ts-expect-error shush + locale.set(newLocale); +} \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 729f786..fc7e58d 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -19,6 +19,8 @@ theme, dropping, vertdLoaded, + locale, + updateLocale, } from "$lib/store/index.svelte"; import "$lib/css/app.scss"; import { browser } from "$app/environment"; @@ -73,6 +75,7 @@ theme.set( (localStorage.getItem("theme") as "light" | "dark") || "light", ); + updateLocale(localStorage.getItem("locale") || "en"); Settings.instance.load(); diff --git a/src/routes/jpegify/+page.svelte b/src/routes/jpegify/+page.svelte index a1d566b..efb9485 100644 --- a/src/routes/jpegify/+page.svelte +++ b/src/routes/jpegify/+page.svelte @@ -5,6 +5,7 @@ import { files } from "$lib/store/index.svelte"; import { quintOut } from "svelte/easing"; import { blur } from "svelte/transition"; + import { m } from "$lib/paraglide/messages"; const images = $derived( files.files.filter((f) => @@ -31,9 +32,9 @@
-

SECRET JPEGIFY!!!

+

{m["jpegify.title"]()}

- (shh... don't tell anyone!) + {m["jpegify.subtitle"]()}

JPEGIFY {compressionInverted}%!!!{m["jpegify.button"]({ compression: compressionInverted })}
{#each images as file, i (file.id)} @@ -89,7 +90,7 @@ disabled={!!!file.result} class="btn bg-accent text-black rounded-2xl text-2xl w-full mx-auto" > - Download + {m["jpegify.download"]()}
diff --git a/vite.config.ts b/vite.config.ts index 838a94b..3e2c9e6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,4 @@ -import { paraglideVitePlugin } from '@inlang/paraglide-js' +import { paraglideVitePlugin } from "@inlang/paraglide-js"; import { sveltekit } from "@sveltejs/kit/vite"; import { defineConfig, type PluginOption } from "vite"; import svg from "@poppanator/sveltekit-svg"; @@ -7,7 +7,11 @@ import wasm from "vite-plugin-wasm"; export default defineConfig(({ command }) => { const plugins: PluginOption[] = [ sveltekit(), - paraglideVitePlugin({ project: './project.inlang', outdir: './src/lib/paraglide' }), + paraglideVitePlugin({ + project: "./project.inlang", + outdir: "./src/lib/paraglide", + strategy: ["globalVariable", "preferredLanguage", "baseLocale"], + }), svg({ includePaths: ["./src/lib/assets"], svgoOptions: { From bb1715682a71fdf5ade4e1a1812e77124790dd15 Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 25 Jul 2025 22:53:45 +0300 Subject: [PATCH 5/6] fix: language reactivity --- src/routes/+layout.svelte | 56 +++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index fc7e58d..ba2f613 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -146,40 +146,44 @@ -
handleDrag(e, true)} - ondragover={(e) => handleDrag(e, true)} - ondragleave={(e) => handleDrag(e, false)} - role="region" -> - +{#key $locale} +
handleDrag(e, true)} + ondragover={(e) => handleDrag(e, true)} + ondragleave={(e) => handleDrag(e, false)} + role="region" + > + -
- - -
+
+ + +
- - -
- {#each locales as locale} - {locale} - {/each} -
+ +
+ {#each locales as locale} + {locale} + {/each} +
- - + + -
- - +
+ + +
-
+{/key} From 0727e32438d227372835176ffe3771fdb1d7e31c Mon Sep 17 00:00:00 2001 From: Maya Date: Sat, 26 Jul 2025 11:02:22 +0300 Subject: [PATCH 6/6] fix: oops --- src/lib/sections/about/Credits.svelte | 2 +- src/lib/sections/about/Sponsors.svelte | 2 +- src/lib/sections/settings/Privacy.svelte | 2 +- src/lib/sections/settings/Vertd.svelte | 2 +- src/lib/store/index.svelte.ts | 30 ++++++++++++++++++++++++ src/routes/+page.svelte | 2 +- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/lib/sections/about/Credits.svelte b/src/lib/sections/about/Credits.svelte index bbf8ab8..e95af6a 100644 --- a/src/lib/sections/about/Credits.svelte +++ b/src/lib/sections/about/Credits.svelte @@ -3,7 +3,7 @@ import { HeartHandshakeIcon } from "lucide-svelte"; import { GITHUB_URL_VERT } from "$lib/consts"; import { m } from "$lib/paraglide/messages"; - import { link } from "$lib/paraglide"; + import { link } from "$lib/store/index.svelte"; let { mainContribs, notableContribs, ghContribs } = $props(); diff --git a/src/lib/sections/about/Sponsors.svelte b/src/lib/sections/about/Sponsors.svelte index 59bc865..86ea317 100644 --- a/src/lib/sections/about/Sponsors.svelte +++ b/src/lib/sections/about/Sponsors.svelte @@ -6,7 +6,7 @@ import { error } from "$lib/logger"; import { addToast } from "$lib/store/ToastProvider"; import { m } from "$lib/paraglide/messages"; - import { link } from "$lib/paraglide"; + import { link } from "$lib/store/index.svelte"; let copied = false; let timeoutId: number | undefined; diff --git a/src/lib/sections/settings/Privacy.svelte b/src/lib/sections/settings/Privacy.svelte index 3da06bb..5748d5b 100644 --- a/src/lib/sections/settings/Privacy.svelte +++ b/src/lib/sections/settings/Privacy.svelte @@ -4,7 +4,7 @@ import type { ISettings } from "./index.svelte"; import { effects } from "$lib/store/index.svelte"; import { m } from "$lib/paraglide/messages"; - import { link } from "$lib/paraglide"; + import { link } from "$lib/store/index.svelte"; const { settings }: { settings: ISettings } = $props(); diff --git a/src/lib/sections/settings/Vertd.svelte b/src/lib/sections/settings/Vertd.svelte index 364820a..d19546b 100644 --- a/src/lib/sections/settings/Vertd.svelte +++ b/src/lib/sections/settings/Vertd.svelte @@ -7,7 +7,7 @@ import Dropdown from "$lib/components/functional/Dropdown.svelte"; import { vertdLoaded } from "$lib/store/index.svelte"; import { m } from "$lib/paraglide/messages"; - import { link } from "$lib/paraglide"; + import { link } from "$lib/store/index.svelte"; let vertdCommit = $state(null); let abortController: AbortController | null = null; diff --git a/src/lib/store/index.svelte.ts b/src/lib/store/index.svelte.ts index ac19c1a..66a0c94 100644 --- a/src/lib/store/index.svelte.ts +++ b/src/lib/store/index.svelte.ts @@ -306,4 +306,34 @@ export function updateLocale(newLocale: string) { setLocale(newLocale, { reload: false }); // @ts-expect-error shush locale.set(newLocale); +} + +export function link( + tag: string | string[], + text: string, + links: string | string[], + newTab?: boolean | boolean[], + className?: string | string[] +) { + if (!text) return ""; + + const tags = Array.isArray(tag) ? tag : [tag]; + const linksArr = Array.isArray(links) ? links : [links]; + const newTabArr = Array.isArray(newTab) ? newTab : [newTab]; + const classArr = Array.isArray(className) ? className : [className]; + + let result = text; + + tags.forEach((t, i) => { + const link = linksArr[i] ?? "#"; + const target = newTabArr[i] ? 'target="_blank" rel="noopener noreferrer"' : ""; + const cls = classArr[i] ? `class="${classArr[i]}"` : ""; + + const regex = new RegExp(`\\[${t}\\](.*?)\\[\\/${t}\\]`, "g"); + result = result.replace(regex, (_, inner) => + `${inner}` + ); + }); + + return result; } \ No newline at end of file diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index d0e5d96..de8b8e2 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -6,7 +6,7 @@ import clsx from "clsx"; import { AudioLines, BookText, Check, Film, Image } from "lucide-svelte"; import { m } from "$lib/paraglide/messages"; - import { link } from "$lib/paraglide"; + import { link } from "$lib/store/index.svelte"; const getSupportedFormats = (name: string) => converters