diff --git a/package-lock.json b/package-lock.json index f03f7c7d..53ddfd2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "standard-version": "^9.5.0", "svelte": "^4.2.19", "svelte-dnd-action": "^0.9.50", + "svelte-i18n": "^4.0.1", "svelte-loader": "^3.2.3", "svelte-multiselect": "^10.2.0", "svelte-preprocess": "^6.0.2", @@ -1138,6 +1139,62 @@ "dev": true, "license": "MIT" }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", + "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.2", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", + "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/icu-skeleton-parser": "1.8.16", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", + "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", + "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", @@ -3424,6 +3481,23 @@ "dev": true, "license": "MIT" }, + "node_modules/cli-color": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", + "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.64", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -3982,6 +4056,20 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/dargs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", @@ -4057,6 +4145,13 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -4318,6 +4413,62 @@ "license": "MIT", "peer": true }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "node_modules/esbuild": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", @@ -4897,6 +5048,22 @@ "node": ">=4.0" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -4946,6 +5113,17 @@ "@types/estree": "^1.0.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -5007,6 +5185,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/fast-copy": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", @@ -5437,6 +5625,13 @@ "node": ">=4" } }, + "node_modules/globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true, + "license": "MIT" + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -5458,6 +5653,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5660,6 +5862,19 @@ "dev": true, "license": "MIT" }, + "node_modules/intl-messageformat": { + "version": "10.7.18", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", + "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.4", + "tslib": "^2.8.0" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5769,6 +5984,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-reference": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", @@ -8119,6 +8341,16 @@ "yallist": "^3.0.2" } }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, "node_modules/luxon": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-2.5.2.tgz", @@ -8185,6 +8417,26 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/memoizee": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", + "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -8452,6 +8704,16 @@ "node": "*" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -8485,6 +8747,13 @@ "dev": true, "license": "MIT" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true, + "license": "ISC" + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -9462,6 +9731,19 @@ "tslib": "^2.1.0" } }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -10581,6 +10863,468 @@ "svelte": ">=3.19.0" } }, + "node_modules/svelte-i18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-4.0.1.tgz", + "integrity": "sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-color": "^2.0.3", + "deepmerge": "^4.2.2", + "esbuild": "^0.19.2", + "estree-walker": "^2", + "intl-messageformat": "^10.5.3", + "sade": "^1.8.1", + "tiny-glob": "^0.2.9" + }, + "bin": { + "svelte-i18n": "dist/cli.js" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "svelte": "^3 || ^4 || ^5" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/svelte-i18n/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/svelte-i18n/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/svelte-loader": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/svelte-loader/-/svelte-loader-3.2.3.tgz", @@ -10810,6 +11554,31 @@ "readable-stream": "3" } }, + "node_modules/timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, "node_modules/title-case": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/title-case/-/title-case-4.3.1.tgz", @@ -10875,9 +11644,9 @@ "license": "Apache-2.0" }, "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.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, @@ -11018,6 +11787,13 @@ "webidl-conversions": "^4.0.2" } }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true, + "license": "ISC" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", diff --git a/package.json b/package.json index 946ddcfa..f8b69f1d 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "standard-version": "^9.5.0", "svelte": "^4.2.19", "svelte-dnd-action": "^0.9.50", + "svelte-i18n": "^4.0.1", "svelte-loader": "^3.2.3", "svelte-multiselect": "^10.2.0", "svelte-preprocess": "^6.0.2", diff --git a/src/combatant/index.ts b/src/combatant/index.ts index 3e75c398..edfba8d3 100644 --- a/src/combatant/index.ts +++ b/src/combatant/index.ts @@ -9,6 +9,7 @@ import { import { Bestiary } from "src/bestiary/bestiary"; import type StatBlockPlugin from "src/main"; import { MonsterSuggestionModal } from "src/util/creature"; +import { t } from "src/util/i18n"; export const CREATURE_VIEW = "fantasy-statblocks-creature-pane"; @@ -44,7 +45,7 @@ export class CreatureView extends ItemView { } onload() { const search = new SearchComponent(this.topEl).setPlaceholder( - "Find a creature" + t("Find a creature") ); const suggester = new MonsterSuggestionModal( this.plugin.app, @@ -62,7 +63,7 @@ export class CreatureView extends ItemView { }); new ExtraButtonComponent(this.topEl) .setIcon("cross") - .setTooltip("Close Statblock") + .setTooltip(t("Close Statblock")) .onClick(async () => { await this.render(); search.setValue(""); @@ -72,7 +73,7 @@ export class CreatureView extends ItemView { this.statblockEl.empty(); if (!creature) { this.statblockEl.createEl("em", { - text: "Select a creature to view it here." + text: t("Select a creature to view it here.") }); return; } @@ -81,7 +82,7 @@ export class CreatureView extends ItemView { } getDisplayText(): string { - return "Combatant"; + return t("Combatant"); } getIcon(): string { return "skull"; diff --git a/src/data/translations/pt-br.json b/src/data/translations/pt-br.json new file mode 100644 index 00000000..a1d056b8 --- /dev/null +++ b/src/data/translations/pt-br.json @@ -0,0 +1,314 @@ +{ + " (current monster being rendered) parameters available in the callback.": " (o monstro atual sendo renderizado) disponíveis no retorno de chamada.", + " (the matched text), ": " (o texto correspondente), ", + " (the RegExpMatchArray), and ": " (o RegExpMatchArray) e ", + "A source could not be found for some imported monsters. Do you wish to manually add one?": "Não foi possível encontrar uma fonte para alguns monstros importados. Deseja adicionar uma manualmente?", + "A valid property must be supplied.": "Uma propriedade válida deve ser fornecida.", + "Ability Modifier Calculation": "Cálculo do Modificador de Habilidade", + "Action": "Ação", + "Actions": "Ações", + "Add ": "Adicionar", + "Add an example, for reference only.": "Adicione um exemplo, apenas para referência.", + "Add Condition": "Adicionar Condição", + "Add Creature": "Adicionar Criatura", + "Add Dice Roller dice to statblocks by default. Use ": "Adicione dados do Dice Roller aos blocos de estatísticas por padrão. Use ", + "Add new condition": "Adicionar nova condição", + "Add New Layout": "Adicionar Novo Layout", + "Add Property as CSS Class": "Adicionar Propriedade como Classe CSS","Add Block": "Adicionar Bloco", + "Added": "Adicionado", + "Advanced Settings": "Configurações Avançadas", + "All conditions must return a true/false value. For example:": "Todas as condições devem retornar um valor true/false. Por exemplo:", + "All nested elements inside this group container will receive this CSS class. If blank, no class will be applied.": "Elementos aninhados dentro deste contêiner de grupo receberão esta classe CSS. Se estiver em branco, nenhuma classe será aplicada.", + "Allows a custom modifier for the stat.": "Permite um modificador personalizado para o atributo.", + "always": "sempre", + "Always try to split into this many columns, regardless of height.": "Sempre tente dividir em tantas colunas, independentemente da altura.", + " and ": " e ", + "and %d more": "e %d mais", + " and all objects must have the ": "e todos os objetos devem ter a propriedade", + "are accessible, use these to calculate the modifier.": "estão acessíveis; use-as para calcular o modificador.", + "Are you sure?": "Tem certeza?", + "Are you sure you want to import this layout?": "Tem certeza de que deseja importar este layout?", + "Armor Class": "Classe de Armadura", + "as-is": "como são", + "Automatically Parse Frontmatter for Creatures": "Analisar Automaticamente Frontmatter para Criaturas", + " be ran.": " serão executados.", + "Bestiary": "Bestiário", + "Bestiary Folder": "Pasta do Bestiário", + "Block Has Rule": "Bloco Possui Regra", + "Blocks": "Blocos", + "Bonus Actions": "Ações Bônus", + "Both": "Ambos", + "Buy me a coffee": "Compre-me um café", + "Calculate Modifiers": "Calcular Modificadores", + "Callback": "Função de Retorno", + "Cancel": "Cancelar", + "Cha": "Car", + "Charisma": "Carisma", + "Challenge": "Nível de Desafio", + "Change the default statblock layout used, if not specified.": "Altere o layout padrão usado no bloco de estatística, se não especificado.", + "Choose a Command to run when this action is executed.": "Escolha um Comando para executar quando esta ação for executada.", + "Choose File(s)": "Escolher Arquivo(s)", + "Choose the icon to use for the button.": "Escolha o ícone a ser usado para o botão.", + "Close Statblock": "Fechar Bloco de Estatísticas", + "Collapse": "Recolher", + "Column width": "Largura da coluna", + "Columns": "Colunas", + "Combatant": "Combatente", + "Con": "Con", + "Conditional": "Condicional", + "Conditioned": "Condicionado", + "Conditions are used to determine what block is rendered. Conditions are evaluated top to bottom - the first to evaluate to": "As condições são usadas para determinar qual bloco será renderizado. As condições são avaliadas de cima para baixo - a primeira a ser avaliada como", + "Constitution": "Constituição", + "Condition Immunities": "Imunidade a Condição", + "Copy YAML": "Copiar YAML", + "Create Copy": "Criar Cópia", + "Create Layout": "Criar Layout", + "Creature YAML copied to clipboard": "YAML da criatura copiado para a área de transferência", + "creature%s": "criatura%s", + "Creatures must be given a name.": "As criaturas devem ter um nome.", + "CSS Container Class": "Classe CSS do Contêiner", + "Current:": "Atual:", + "Damage Immunities": "Imunidade a Dano", + "Damage Resistances": "Resistência a Dano", + "Damage Vulnerabilities": "Vulnerabilidade a Dano", + "Dark": "Escuro", + "Debug messages will be displayed by the file parser.": "Mensagens de depuração serão exibidas pelo analisador de arquivos.", + "Default Layout": "Layout Padrão", + "Delete": "Excluir", + "Delete Block": "Excluir Bloco", + "Delete filtered creatures": "Excluir criaturas filtradas", + "Dex": "Des", + "Dexterity": "Destreza", + "Dice Callback": "Retorno de Chamada de Dado", + "Dice Parsing": "Análise de Dados", + "Disable this to prevent adding the property to the containing HTML element as a CSS class. This can be used to avoid collisions with native Obsidian CSS.": "Desabilite isso para impedir a adição da propriedade ao elemento HTML que a contém como uma classe CSS. Isso pode ser usado para evitar conflitos com o CSS nativo do Obsidian.", + "Display Text": "Texto de Exibição", + " display the text entered here.": " exibirá o texto inserido aqui.", + "Edit": "Editar", + "Edit Block": "Editar Bloco", + "Edit Name": "Editar Nome", + "Editing Dice Parser": "Editando o Analisador de Dados", + "Enable Debug Messages": "Habilitar Mensagens de Depuração", + "Enable 5e SRD": "Habilitar SRD 5e", + "Error importing Pathbuilder file": "Erro ao importar o arquivo Pathbuilder", + "Example": "Exemplo", + "Executing the action will run the callback. Any registered commands will ": "Executar a ação executará o retorno de chamada. Quaisquer comandos registrados ", + "Export as JSON": "Exportar como JSON", + "Export as PNG": "Exportar como PNG", + "Fallback": "Fallback", + "Fantasy StatBlocks loaded": "Fantasy StatBlocks carregado", + "Fantasy Statblocks Settings": "Configurações de Fantasy Statblocks", + "Fantasy StatBlocks unloaded": "Fantasy StatBlocks descarregado", + "Fantasy Statblocks:": "Fantasy Statblocks:", + "Fantasy Statblocks: Adding %d paths to queue": "Fantasy Statblocks: Adicionando %d caminhos à fila", + "Fantasy Statblocks: Adding %s1 to bestiary from %s2": "Fantasy Statblocks: Adicionando %s1 ao bestiário a partir de %s2", + "Fantasy Statblocks: found Statblock: %s": "Fantasy Statblocks: Statblock encontrado: %s", + "Fantasy Statblocks: Frontmatter Parsing complete.": "Fantasy Statblocks: Análise do Frontmatter concluída.", + "Fantasy Statblocks: Frontmatter Parsing Complete in %d seconds.": "Fantasy Statblocks: Análise do Frontmatter Concluída em %d segundos.", + "Fantasy Statblocks: Handling rename of %s1 to %s2": "Fantasy Statblocks: Lidando com a renomeação de %s1 para %s2", + "Fantasy Statblocks: Multiple image properties provided, using first.": "Fantasy Statblocks: Várias propriedades de imagem fornecidas, usando a primeira.", + "Fantasy Statblocks: Parsing %s for statblocks (%d to go)": "Fantasy Statblocks: Analisando %s para os statblocks (%d restantes)", + "Fantasy Statblocks: Process Content: %s": "Fantasy Statblocks: Processando Conteúdo: %s", + "Fantasy Statblocks: Received queue message for %d paths": "Fantasy Statblocks: Mensagem de fila recebida para %d caminhos", + "Fantasy Statblocks: Removing %s from bestiary": "Fantasy Statblocks: Removendo %s do bestiário", + "Fantasy Statblocks: Reparsing": "Fantasy Statblocks: Reanalisando", + "Fantasy Statblocks: Starting Frontmatter Parsing.": "Fantasy Statblocks: Iniciando a Análise do Frontmatter.", + "Find a creature": "Encontre uma criatura", + "For example: ": "Por exemplo: ", + "Force columns": "Forçar colunas", + " freckles.": " sardas.", + "General Settings": "Configuração Geral", + "Has Callback": "Possui Retorno de Chamada", + "Has Heading": "Possui Cabeçalho", + "Has Rule": "Tem Regra", + "header": "cabeçalho", + "Header Size": "Tamanho do Cabeçalho", + "Hit Points": "Pontos de Vida", + "Homebrew": "Homebrew", + "Icon": "Ícone", + "If not present, this text will be displayed.": "Se não estiver presente, este texto será exibido.", + "If present, the block will have a horizontal rule placed after it.": "Se presente, o bloco terá uma linha horizontal após ele.", + "Ignore available space when calculating columns.": "Ignorar espaço disponível ao calcular colunas.", + "Import": "Importar", + "Import 5e.tools Data": "Importar Dados do 5e.tools", + "Import a custom layout from a JSON file.": "Importar um layout personalizado de um arquivo JSON.", + "Import a PC or NPC exported from Pathbuilder2e.": "Importar um PC ou NPC exportado do Pathbuilder2e.", + "Import and don't ask again": "Importar e não perguntar novamente", + "Import creatures from creature files. Monsters are stored by name, so only the last creature by that name will be saved. This is destructive - any saved creature will be overwritten.": "Importar criaturas de arquivos de criaturas. Os monstros são armazenados por nome, portanto, apenas a última criatura com esse nome será salva. Isso é destrutivo - qualquer criatura salva será sobrescrita.", + "Import CritterDB Data": "Importar Dados do CritterDB", + "Import DnDAppFile": "Importar DnDAppFile", + "Import DnDAppFile Data": "Importar Dados do DnDAppFile", + "Import From JSON": "Importar de JSON", + "Import Generic Data": "Importar Dados Genéricos", + "Import generic JSON files. JSON objects will be imported ": "Importar arquivos JSON genéricos. Os objetos JSON serão importados", + "Import Homebrew Creatures": "Importar Criaturas Homebrew", + "Import Improved Initiative Data": "Importar Dados do Improved Initiative", + "Import Pathbuilder Data": "Importar Dados do 5e.tools", + "Import PF2eMonsterTools Data": "Importar Dados do PF2eMonsterTools", + "Import TetraCube Data": "Importar Dados do TetraCube", + "Int": "Int", + "Intelligence": "Inteligência", + "Integrate Dice Roller": "Integrar Dice Roller", + "Invalid layout imported": "Layout inválido importado", + "Invalid layout imported: layout does not have a name": "Layout inválido importado: o layout não possui um nome", + "Invalid layout imported: no blocks defined in layout.": "Layout inválido importado: nenhum bloco definido no layout.", + "Invalid monster": "Monstro inválido", + "Invalid monster JSON provided.": "JSON de monstro inválido fornecido.", + "Invalid monster JSON provided. Must be array or object.": "JSON de monstro inválido fornecido. Deve ser uma matriz ou um objeto.", + "Invalid monster.": "Monstro inválido.", + "is the condition that will be used. If the last condition is left blank and no others were true, it will be used.": "é a condição que será usada. Se a última condição for deixada em branco e nenhuma outra for verdadeira, ela será usada.", + "JavaScript": "JavaScript", + "JavaScript blocks can be used to do highly advanced HTML elements. The JavaScript code will be provided the ": "Blocos JavaScript podem ser usados ​​para criar elementos HTML altamente avançados. O código JavaScript receberá os parâmetros ", + "JSON": "JSON", + "knows the following spells:": "conhece as seguintes magias:", + "Languages": "Idiomas", + "Layout": "Layout", + "Layout Editor": "Editor de Layout", + "Layout to Insert": "Layout para Inserir", + "Layouts": "Layouts", + "Legendary Actions": "Ações Lendárias", + "Light": "Claro", + "Link Dice to Property": "Vincular Dados à Propriedade", + "Link Monster Properties": "Vincular Propriedades do Monstro", + "Link Monster Property": "Vincular Propriedade do Monstro", + "Mode: Dark": "Modo: Escuro", + "Mode: Light": "Modo: Claro", + "Name to display for the Spellcasting trait. Defaults to Spellcasting if not provided.": "Nome a ser exibido para a característica de Conjuração. O padrão é Conjuração se não for fornecido.", + "New Dice Parser": "Novo Analisador de Dados", + "New statblock layouts can be created and managed here. A specific layout can be used for a creature using the ": "Novos layouts de blocos de estatísticas podem ser criados e gerenciados aqui. Um layout específico pode ser usado para uma criatura usando o parâmetro", + "No": "Não", + "No condition set": "Nenhuma condição definida", + "No image could be loaded": "Nenhuma imagem pôde ser carregada", + "No layout selected": "Nenhum layout selecionado", + "not": "não", + "Note Parsing": "Análise de Notas", + "Obsidian Statblock Error:": "Erro no bloco de estatísticas do Obsidian:", + "Only import content that you own.": "Importe apenas conteúdo que você possui.", + "Open by Default": "Aberto por Padrão", + "Open Creature pane": "Abrir Painel de Criatura", + "Open new Creature pane": "Abrir Novo Painel de Criatura", + "optional, shown in parenthesis": "opcional, mostrado entre parênteses", + " parameter.": ".", + " parameter. ": " . ", + " parameter. The callback should return a string. For example: ": " parâmetro. A função de retorno deve retornar uma string. Por exemplo: ", + "parameter, which can be used to access properties of the monster being rendered, and the ": ", que pode ser usado para acessar as propriedades do monstro que está sendo renderizado, e o parâmetro", + "parameter, which is a reference to the Fantasy Statblocks plugin and can be used for accessing app and plugin settings.": ", que é uma referência ao plugin Fantasy Statblocks e pode ser usado para acessar as configurações do aplicativo e do plugin.", + "parameters and should return a HTML element, which will be attached to the block's container element.": "e deverá retornar um elemento HTML, que será anexado ao elemento contêiner do bloco.", + "parameters. Dice callbacks should return an array of strings and objects, with the objects defining the dice rolls:": "parâmetros. Os retornos de chamada do dado devem retornar uma matriz de strings e objetos, com os objetos definindo as rolagens do dado:", + " parameters. The callback should return a string. For example: ": " parâmetros. A função de retorno deve retornar uma string. Por exemplo:", + " parameters. The callback should return an object with a single key and value. For example: ": " parâmetros. A função de retorno deve retornar um objeto com uma única chave e valor. Por exemplo: ", + "Parse for Dice": "Análise para Dados", + "per page": "por página", + "Please note: these links will not be added to the graph.": "Observação: esses links não serão adicionados ao gráfico.", + "Please Note: This will not run if a dice callback is provided.": "Observação: Isso não será executado se uma função de retorno de dados for fornecida.", + "Proficiency Bonus": "Bônus de Proficiência", + " property.": ".", + "Reactions": "Reações", + "regular expressions": "expressões regulares", + "Regular Expression": "Expressão Regular", + "Remove default parsers": "Remover analisadores padrão", + "Render Dice Rolls": "Renderizar Rolagens de Dados", + "Render markdown enabled": "Renderizar markdown ativado", + "Reset Dice": "Redefinir Dados", + "Restore Default Layouts": "Restaurar Layouts Padrão", + "Restore default parsers": "Restaurar analisadores padrão", + "Reveal Creature pane": "Revelar Painel de Criatura", + "Roll graphical dice inside statblocks by default. Use ": "Role dados gráficos dentro dos blocos de estatísticas por padrão. Use ", + "Save": "Salvar", + "Save Changes": "Salvar Alterações", + "Save to Bestiary": "Salvar no Bestiário", + "Saves": "Salvaguardas", + "Scale:": "Escala:", + "Scale preview": "Pré-visualização em escala", + "Search Creatures": "Pesquisar Criaturas", + "Section Heading": "Título da Seção", + "Section Subheading Text": "Texto do Subtítulo da Seção", + "Select a creature to preview the layout, or enter your own definition below.": "Selecione uma criatura para visualizar o layout ou insira sua própria definição abaixo.", + "Select a creature to view it here.": "Selecione uma criatura para visualizá-la aqui.", + "Senses": "Sentidos", + "Separator": "Separador", + "Set colors for...": "Definir cores para...", + "Set Sources": "Definir Fontes", + "Set theme mode": "Definir modo de tema", + "Show Advanced Options": "Exibir Opções Avançadas", + "Show advanced options when editing layout blocks.": "Exibir opções avançadas ao editar blocos de layout.", + "Skills": "Perícias", + "Sources": "Fontes", + "Speed": "Deslocamento", + "Spellcasting": "Conjuração", + "Str": "For", + "Strength": "Força", + "string to be parsed into a dice roll": "string a ser analisada em uma rolagem de dado", + "Successfully imported %d Monsters": "%d Monstros importados com sucesso", + "Table Headers": "Cabeçalhos da Tabela", + "Text entered here will appear directly after the section heading, before the actual traits. Use ": "O texto inserido aqui aparecerá diretamente após o título da seção, antes das características propriamente ditas. Use ", + "Text separating properties": "Texto que separa as propriedades", + "Text to Show": "Texto a ser exibido", + "The block will ": "O bloco ", + "The block will attempt to calculate modifiers for table values.": "O bloco tentará calcular os modificadores para os valores da tabela.", + "The block will not be added if the associated properties are not present.": "O bloco não será adicionado se as propriedades associadas não estiverem presentes.", + "The block will run the callback and use the returned string as the property.": "O bloco executará a função de retorno e usará a string retornada como a propriedade.", + "The block will run the callback and use the returned values for the dice strings.": "O bloco executará o retorno de chamada e usará os valores retornados para as strings do dado.", + "The block will run the callback on each save object and use the returned object as the save.": "O bloco executará a função de retorno em cada objeto salvo e usará o objeto retornado como o salvo.", + "The block will run the callback on each trait and use the returned string as the trait description.": "O bloco executará a função de retorno em cada característica e usará a string retornada como a descrição da característica.", + "The block will start open.": "O bloco será iniciado aberto.", + "The callback should return an instance of:": "A função de retorno deve retornar uma instância de:", + "The callback will have the ": "O retorno de chamada terá os parâmetros ", + "The callback will receive the ": "O retorno de chamada receberá o parâmetro ", + "The dice parser callback needs to parse the results of the regular expression and return the correct dice string to display.": "O retorno de chamada do analisador de dados precisa analisar os resultados da expressão regular e retornar a string de dados correta para exibição.", + "The dice roller will parse this property instead of the original.": "O rolador de dados analisará esta propriedade em vez da original.", + "The expression receives the ": "A expressão recebe o parâmetro", + "The heading will use this size.": "O cabeçalho usará este tamanho.", + "The monster guesses you have: ": "O monstro acha que você tem:", + "The \"Parse Frontmatter for Creatures\" command can also be used.": "O comando \"Parse Frontmatter for Creatures\" também pode ser usado.", + "The plugin will attempt to add dice rollers as specified.": "O plugin tentará adicionar roladores de dados conforme especificado.", + "The plugin will attempt to detect wikilinks inside Statblocks.": "O plugin tentará detectar wikilinks dentro dos blocos de estatísticas.", + "The plugin will only parse notes inside these folders and their children.": "O plugin analisará apenas as notas dentro dessas pastas e suas subpastas.", + "The plugin will watch the vault for creatures defined in note frontmatter.": "O plugin monitorará o cofre em busca de criaturas definidas nas notas frontmatter.", + "The Section heading will be set to the value of the specified property.": "O título da seção será definido com o valor da propriedade especificada.", + "The section will use this property for the section heading. If the property does not exist or is blank, the section heading will not appear.": "A seção usará essa propriedade para o título. Se a propriedade não existir ou estiver em branco, o título da seção não aparecerá.", + "These are parsed in order, and the first one to trigger is what will be used.": "Elas são analisadas em ordem, e a primeira a ser acionada será usada.", + "There was an error creating the image:": "Ocorreu um erro ao criar a imagem:", + "There was an error displaying the settings tab for 5e Statblocks.": "Ocorreu um erro ao exibir a aba de configurações para Statblocks 5e.", + "There was an error executing the command for the action block.": "Ocorreu um erro ao executar o comando para o bloco de ação.", + "There was an error executing the provided callback for": "Ocorreu um erro ao executar o retorno de chamada fornecido para", + "There was an error executing the provided callback for the action block.": "Ocorreu um erro ao executar o retorno de chamada fornecido para o bloco de ação.", + "There was an error executing the provided dice callback for": "Ocorreu um erro ao executar o retorno de chamada de dados fornecido para", + "There was an error importing the file.": "Ocorreu um erro ao importar o arquivo.", + "There was an error parsing the at-will spells.": "Ocorreu um erro ao analisar as magias à vontade.", + "There was an error parsing the ritual spells.": "Ocorreu um erro ao analisar as magias rituais.", + "There was an error parsing the spells.": "Ocorreu um erro ao analisar as magias.", + "There was an error parsing the Statblock in %s:": "Ocorreu um erro ao analisar o Statblock em %s:", + "There was an error rendering the statblock:": "Ocorreu um erro ao renderizar o bloco de estatísticas:", + "There was an error saving the creaturen": "Ocorreu um erro ao salvar a criatura.", + "There was an issue copying the yaml:": "Ocorreu um problema ao copiar o yaml:", + "This can cause issues sometimes when using sync services.": "Isso pode causar problemas ao usar serviços de sincronização.", + "This layout does not have any dice parsers defined. Add one to begin parsing for dice.": "Este layout não possui nenhum analisador de dados definido. Adicione um para começar a analisar os dados.", + "This Layout includes JavaScript blocks. JavaScript blocks can execute code in your vault, which could cause loss or corruption of data.": "Este Layout inclui blocos JavaScript. Blocos JavaScript podem executar código em seu cofre, o que pode causar perda ou corrupção de dados.", + "This layout is currently using the default dice parsers. Add a custom dice parser to override this behavior.": "Este layout está usando os analisadores de dados padrão. Adicione um analisador de dados personalizado para substituir esse comportamento.", + "This setting is only usable with the Dice Roller plugin enabled.": "Esta configuração só pode ser usada com o plugin Dice Roller ativado.", + "This setting is currently disabled.": "Essa configuração está desativada no momento.", + "This text will be used for the property name.": "Este texto será usado para o nome da propriedade.", + "This text will be used for the section heading. Can be left blank.": "Este texto será usado para o título da seção. Pode ser deixado em branco.", + "This will cause to plugin to save data to a temporary file before saving the actual data file in an attempt to prevent data loss.": "Isso fará com que o plugin salve os dados em um arquivo temporário antes de salvar o arquivo de dados real, numa tentativa de evitar a perda de dados.", + "This will overwrite an existing monster in settings. Are you sure?": "Isso substituirá um monstro existente nas configurações. Tem certeza?", + " to detect dice rolls inside your layout.": " para detectar rolagens de dados dentro de seu layout.", + " to disable per-statblock.": " para desativar no bloco de estatística.", + " to insert the current monster's name.": " para inserir o nome do monstro atual.", + "Trait Name": "Nome da Característica", + "Try to Render Wikilinks": "Tentar Renderizar Wikilinks.", + "Try to Save Data Atomically": "Tentar Salvar Dados Atomicamente", + "Updated": "Atualizado", + "Unknown": "Desconhecido", + "Unknown source:": "Fonte desconhecida:", + "Use Monster Property for Heading": "Usar Propriedade do Monstro para Título", + "Use the Dungeons & Dragons 5th Edition System Reference Document monsters.": "Use os monstros do Documento de Referência do Sistema do Dungeons & Dragons 5ª Edição.", + "Variables ": "Variáveis ", + "View": "Visualizar", + "Width in pixels. Default: 400px": "Largura em pixels. Padrão: 400px", + "Will Parse for Dice Rolls": "Analisará Rolagens de Dados", + "Wis": "Sab", + "Wisdom": "Sabedoria", + "YAML": "YAML", + "Yes": "Sim" +} diff --git a/src/data/translations/sample.json b/src/data/translations/sample.json new file mode 100644 index 00000000..546f497f --- /dev/null +++ b/src/data/translations/sample.json @@ -0,0 +1,314 @@ +{ + " (current monster being rendered) parameters available in the callback.": "", + " (the matched text), ": "", + " (the RegExpMatchArray), and ": "", + "A source could not be found for some imported monsters. Do you wish to manually add one?": "", + "A valid property must be supplied.": "", + "Ability Modifier Calculation": "", + "Action": "", + "Actions": "", + "Add ": "", + "Add an example, for reference only.": "", + "Add Condition": "", + "Add Creature": "", + "Add Dice Roller dice to statblocks by default. Use ": "", + "Add new condition": "", + "Add New Layout": "", + "Add Property as CSS Class": "Adicionar Propriedade como Classe CSS","Add Block": "", + "Added": "", + "Advanced Settings": "", + "All conditions must return a true/false value. For example:": "", + "All nested elements inside this group container will receive this CSS class. If blank, no class will be applied.": "", + "Allows a custom modifier for the stat.": "", + "always": "", + "Always try to split into this many columns, regardless of height.": "", + " and ": "", + "and %d more": "", + " and all objects must have the ": "", + "are accessible, use these to calculate the modifier.": "", + "Are you sure?": "", + "Are you sure you want to import this layout?": "", + "Armor Class": "", + "as-is": "", + "Automatically Parse Frontmatter for Creatures": "", + " be ran.": "", + "Bestiary": "", + "Bestiary Folder": "", + "Block Has Rule": "", + "Blocks": "", + "Bonus Actions": "", + "Both": "", + "Buy me a coffee": "", + "Calculate Modifiers": "", + "Callback": "", + "Cancel": "", + "Cha": "", + "Charisma": "", + "Challenge": "", + "Change the default statblock layout used, if not specified.": "", + "Choose a Command to run when this action is executed.": "", + "Choose File(s)": "", + "Choose the icon to use for the button.": "", + "Close Statblock": "", + "Collapse": "", + "Column width": "", + "Columns": "", + "Combatant": "", + "Con": "", + "Conditional": "", + "Conditioned": "", + "Conditions are used to determine what block is rendered. Conditions are evaluated top to bottom - the first to evaluate to": "", + "Constitution": "", + "Condition Immunities": "", + "Copy YAML": "", + "Create Copy": "", + "Create Layout": "", + "Creature YAML copied to clipboard": "", + "creature%s": "", + "Creatures must be given a name.": "", + "CSS Container Class": "", + "Current:": "", + "Damage Immunities": "", + "Damage Resistances": "", + "Damage Vulnerabilities": "", + "Dark": "", + "Debug messages will be displayed by the file parser.": "", + "Default Layout": "", + "Delete": "", + "Delete Block": "", + "Delete filtered creatures": "", + "Dex": "", + "Dexterity": "", + "Dice Callback": "", + "Dice Parsing": "", + "Disable this to prevent adding the property to the containing HTML element as a CSS class. This can be used to avoid collisions with native Obsidian CSS.": "", + "Display Text": "", + " display the text entered here.": "", + "Edit": "", + "Edit Block": "", + "Edit Name": "", + "Editing Dice Parser": "", + "Enable Debug Messages": "", + "Enable 5e SRD": "", + "Error importing Pathbuilder file": "", + "Example": "", + "Executing the action will run the callback. Any registered commands will ": "", + "Export as JSON": "", + "Export as PNG": "", + "Fallback": "", + "Fantasy StatBlocks loaded": "", + "Fantasy Statblocks Settings": "", + "Fantasy StatBlocks unloaded": "", + "Fantasy Statblocks:": "", + "Fantasy Statblocks: Adding %d paths to queue": "", + "Fantasy Statblocks: Adding %s1 to bestiary from %s2": "", + "Fantasy Statblocks: found Statblock: %s": "", + "Fantasy Statblocks: Frontmatter Parsing complete.": "", + "Fantasy Statblocks: Frontmatter Parsing Complete in %d seconds.": "", + "Fantasy Statblocks: Handling rename of %s1 to %s2": "", + "Fantasy Statblocks: Multiple image properties provided, using first.": "", + "Fantasy Statblocks: Parsing %s for statblocks (%d to go)": "", + "Fantasy Statblocks: Process Content: %s": "", + "Fantasy Statblocks: Received queue message for %d paths": "", + "Fantasy Statblocks: Removing %s from bestiary": "", + "Fantasy Statblocks: Reparsing": "", + "Fantasy Statblocks: Starting Frontmatter Parsing.": "", + "Find a creature": "", + "For example: ": "", + "Force columns": "", + " freckles.": "", + "General Settings": "", + "Has Callback": "", + "Has Heading": "", + "Has Rule": "", + "header": "", + "Header Size": "", + "Hit Points": "", + "Homebrew": "", + "Icon": "", + "If not present, this text will be displayed.": "", + "If present, the block will have a horizontal rule placed after it.": "", + "Ignore available space when calculating columns.": "", + "Import": "", + "Import 5e.tools Data": "", + "Import a custom layout from a JSON file.": "", + "Import a PC or NPC exported from Pathbuilder2e.": "", + "Import and don't ask again": "", + "Import creatures from creature files. Monsters are stored by name, so only the last creature by that name will be saved. This is destructive - any saved creature will be overwritten.": "", + "Import CritterDB Data": "", + "Import DnDAppFile": "", + "Import DnDAppFile Data": "", + "Import From JSON": "", + "Import Generic Data": "", + "Import generic JSON files. JSON objects will be imported ": "", + "Import Homebrew Creatures": "", + "Import Improved Initiative Data": "", + "Import Pathbuilder Data": "", + "Import PF2eMonsterTools Data": "", + "Import TetraCube Data": "", + "Int": "", + "Intelligence": "", + "Integrate Dice Roller": "", + "Invalid layout imported": "", + "Invalid layout imported: layout does not have a name": "", + "Invalid layout imported: no blocks defined in layout.": "", + "Invalid monster": "", + "Invalid monster JSON provided.": "", + "Invalid monster JSON provided. Must be array or object.": "", + "Invalid monster.": "", + "is the condition that will be used. If the last condition is left blank and no others were true, it will be used.": "", + "JavaScript": "", + "JavaScript blocks can be used to do highly advanced HTML elements. The JavaScript code will be provided the ": "", + "JSON": "", + "knows the following spells:": "", + "Languages": "", + "Layout": "", + "Layout Editor": "", + "Layout to Insert": "", + "Layouts": "", + "Legendary Actions": "", + "Light": "", + "Link Dice to Property": "", + "Link Monster Properties": "", + "Link Monster Property": "", + "Mode: Dark": "", + "Mode: Light": "", + "Name to display for the Spellcasting trait. Defaults to Spellcasting if not provided.": "", + "New Dice Parser": "", + "New statblock layouts can be created and managed here. A specific layout can be used for a creature using the ": "", + "No": "", + "No condition set": "", + "No image could be loaded": "", + "No layout selected": "", + "not": "", + "Note Parsing": "", + "Obsidian Statblock Error:": "", + "Only import content that you own.": "", + "Open by Default": "", + "Open Creature pane": "", + "Open new Creature pane": "", + "optional, shown in parenthesis": "", + " parameter.": "", + " parameter. ": "", + " parameter. The callback should return a string. For example: ": "", + "parameter, which can be used to access properties of the monster being rendered, and the ": "", + "parameter, which is a reference to the Fantasy Statblocks plugin and can be used for accessing app and plugin settings.": "", + "parameters and should return a HTML element, which will be attached to the block's container element.": "", + "parameters. Dice callbacks should return an array of strings and objects, with the objects defining the dice rolls:": "", + " parameters. The callback should return a string. For example: ": "", + " parameters. The callback should return an object with a single key and value. For example: ": "", + "Parse for Dice": "", + "per page": "", + "Please note: these links will not be added to the graph.": "", + "Please Note: This will not run if a dice callback is provided.": "", + "Proficiency Bonus": "", + " property.": "", + "Reactions": "", + "regular expressions": "", + "Regular Expression": "", + "Remove default parsers": "", + "Render Dice Rolls": "", + "Render markdown enabled": "", + "Reset Dice": "", + "Restore Default Layouts": "", + "Restore default parsers": "", + "Reveal Creature pane": "", + "Roll graphical dice inside statblocks by default. Use ": "", + "Save": "", + "Save Changes": "", + "Save to Bestiary": "", + "Saves": "", + "Scale:": "", + "Scale preview": "", + "Search Creatures": "", + "Section Heading": "", + "Section Subheading Text": "", + "Select a creature to preview the layout, or enter your own definition below.": "", + "Select a creature to view it here.": "", + "Senses": "", + "Separator": "", + "Set colors for...": "", + "Set Sources": "", + "Set theme mode": "", + "Show Advanced Options": "", + "Show advanced options when editing layout blocks.": "", + "Skills": "", + "Sources": "", + "Speed": "", + "Spellcasting": "", + "Str": "", + "Strength": "", + "string to be parsed into a dice roll": "", + "Successfully imported %d Monsters": "", + "Table Headers": "", + "Text entered here will appear directly after the section heading, before the actual traits. Use ": "", + "Text separating properties": "", + "Text to Show": "", + "The block will ": "", + "The block will attempt to calculate modifiers for table values.": "", + "The block will not be added if the associated properties are not present.": "", + "The block will run the callback and use the returned string as the property.": "", + "The block will run the callback and use the returned values for the dice strings.": "", + "The block will run the callback on each save object and use the returned object as the save.": "", + "The block will run the callback on each trait and use the returned string as the trait description.": "", + "The block will start open.": "", + "The callback should return an instance of:": "", + "The callback will have the ": "", + "The callback will receive the ": "", + "The dice parser callback needs to parse the results of the regular expression and return the correct dice string to display.": "", + "The dice roller will parse this property instead of the original.": "", + "The expression receives the ": "", + "The heading will use this size.": "", + "The monster guesses you have: ": "", + "The \"Parse Frontmatter for Creatures\" command can also be used.": "", + "The plugin will attempt to add dice rollers as specified.": "", + "The plugin will attempt to detect wikilinks inside Statblocks.": "", + "The plugin will only parse notes inside these folders and their children.": "", + "The plugin will watch the vault for creatures defined in note frontmatter.": "", + "The Section heading will be set to the value of the specified property.": "", + "The section will use this property for the section heading. If the property does not exist or is blank, the section heading will not appear.": "", + "These are parsed in order, and the first one to trigger is what will be used.": "", + "There was an error creating the image:": "", + "There was an error displaying the settings tab for 5e Statblocks.": "", + "There was an error executing the command for the action block.": "", + "There was an error executing the provided callback for": "", + "There was an error executing the provided callback for the action block.": "", + "There was an error executing the provided dice callback for": "", + "There was an error importing the file.": "", + "There was an error parsing the at-will spells.": "", + "There was an error parsing the ritual spells.": "", + "There was an error parsing the spells.": "", + "There was an error parsing the Statblock in %s:": "", + "There was an error rendering the statblock:": "", + "There was an error saving the creaturen": "", + "There was an issue copying the yaml:": "", + "This can cause issues sometimes when using sync services.": "", + "This layout does not have any dice parsers defined. Add one to begin parsing for dice.": "", + "This Layout includes JavaScript blocks. JavaScript blocks can execute code in your vault, which could cause loss or corruption of data.": "", + "This layout is currently using the default dice parsers. Add a custom dice parser to override this behavior.": "", + "This setting is only usable with the Dice Roller plugin enabled.": "", + "This setting is currently disabled.": "", + "This text will be used for the property name.": "", + "This text will be used for the section heading. Can be left blank.": "", + "This will cause to plugin to save data to a temporary file before saving the actual data file in an attempt to prevent data loss.": "", + "This will overwrite an existing monster in settings. Are you sure?": "", + " to detect dice rolls inside your layout.": "", + " to disable per-statblock.": "", + " to insert the current monster's name.": "", + "Trait Name": "", + "Try to Render Wikilinks": "", + "Try to Save Data Atomically": "", + "Updated": "", + "Unknown": "", + "Unknown source:": "", + "Use Monster Property for Heading": "", + "Use the Dungeons & Dragons 5th Edition System Reference Document monsters.": "", + "Variables ": "", + "View": "", + "Width in pixels. Default: 400px": "", + "Will Parse for Dice Rolls": "", + "Wis": "", + "Wisdom": "", + "YAML": "", + "Yes": "" +} diff --git a/src/importers/5eToolsImport.ts b/src/importers/5eToolsImport.ts index acd853ef..30e6d719 100644 --- a/src/importers/5eToolsImport.ts +++ b/src/importers/5eToolsImport.ts @@ -1,4 +1,5 @@ import { stringify } from "src/util/util"; +import { t } from "src/util/i18n"; import type { ConditionImmunityArray, Creature5eTools, @@ -62,7 +63,7 @@ export async function build5eMonsterFromFile(file: File): Promise { } else if (typeof json == "object") { monsters = [json]; } else { - reject("Invalid monster JSON provided."); + reject(t("Invalid monster JSON provided.")); } const imported: Monster[] = []; for (const monster of monsters) { @@ -349,7 +350,7 @@ function extractSpellsBlocks(spellBlock: EntrySpellcasting): ExtractedSpells { ret.push({ [name]: sp }); } } catch (e) { - throw new Error("There was an error parsing the spells."); + throw new Error(t("There was an error parsing the spells.")); } } if ("will" in spellBlock) { @@ -360,7 +361,7 @@ function extractSpellsBlocks(spellBlock: EntrySpellcasting): ExtractedSpells { }); } catch (e) { throw new Error( - "There was an error parsing the at-will spells." + t("There was an error parsing the at-will spells.") ); } } @@ -373,7 +374,7 @@ function extractSpellsBlocks(spellBlock: EntrySpellcasting): ExtractedSpells { }); } catch (e) { throw new Error( - "There was an error parsing the ritual spells." + t("There was an error parsing the ritual spells.") ); } } diff --git a/src/importers/PathbuilderImport.ts b/src/importers/PathbuilderImport.ts index 7a08243b..484c143e 100644 --- a/src/importers/PathbuilderImport.ts +++ b/src/importers/PathbuilderImport.ts @@ -1,3 +1,4 @@ +import { t } from "src/util/i18n"; import { Monster, Trait } from "index"; import { ONE_ACTION, getACStats, addSign, getModifierToDiceRoll, toTitleCase } from "./pf2eMonsterToolImport"; import { Abilities, Proficiencies, PathbuilderCharacter, Weapon, Armor, Build, FocusTradition } from "./Pathbuilder.d"; @@ -101,7 +102,7 @@ export async function buildMonsterFromPathbuilderFile(file: File): Promise { - t.setPlaceholder("Unknown").onChange((v) => { + .addText((x) => { + x.setPlaceholder(t("Unknown")).onChange((v) => { this.source = v; }); }); @@ -28,7 +29,7 @@ class SourcePromptModal extends FantasyStatblockModal { b .setCta() .setIcon("checkmark") - .setTooltip("Save") + .setTooltip(t("Save")) .onClick(() => { this.saved = true; this.close(); @@ -37,7 +38,7 @@ class SourcePromptModal extends FantasyStatblockModal { .addExtraButton((b) => b .setIcon("cross") - .setTooltip("Cancel") + .setTooltip(t("Cancel")) .onClick(() => { this.close(); }) @@ -74,7 +75,7 @@ export default class Importer { }; if (monsters) { new Notice( - `Successfully imported ${monsters.length} Monsters` + `${t("Successfully imported %d Monsters").replace("%d", monsters.length)}` ); const sourceless = monsters.filter( (monster) => @@ -96,7 +97,7 @@ export default class Importer { }; //@ts-ignore worker.onerror = (e) => { new Notice( - `There was an error importing the file.\n\n${e.message}` + `${t("There was an error importing the file.")}\n\n${e.message}` ); worker.terminate(); this.workers.delete(id); diff --git a/src/importers/importer.worker.ts b/src/importers/importer.worker.ts index 6a7fe6c9..3c499bee 100644 --- a/src/importers/importer.worker.ts +++ b/src/importers/importer.worker.ts @@ -1,3 +1,4 @@ +import { t } from "src/util/i18n"; import type { Monster } from "index"; import { buildMonsterFromAppFile, @@ -78,7 +79,7 @@ ctx.onmessage = async (event) => { } } else { reject( - "Invalid monster JSON provided. Must be array or object." + t("Invalid monster JSON provided. Must be array or object.") ); } const imported: Monster[] = []; @@ -101,7 +102,7 @@ ctx.onmessage = async (event) => { monsters.push(...(imported ?? [])); } default: { - console.error(`Unknown source: ${source}`); + console.error(`${t("Unknown source:")} ${source}`); } } } diff --git a/src/main.ts b/src/main.ts index 33d2e664..5b14891e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,7 @@ import { import domtoimage from "dom-to-image"; import StatBlockRenderer from "./view/statblock"; +import { initI18n, t } from "./util/i18n"; import { nanoid } from "./util/util"; import type { Monster, StatblockParameters } from "../index"; import StatblockSettingTab from "./settings/settings"; @@ -129,7 +130,9 @@ export default class StatBlockPlugin extends Plugin { } }; async onload() { - console.log("Fantasy StatBlocks loaded"); + initI18n(); + + console.log(t("Fantasy StatBlocks loaded")); this.app.workspace.trigger("fantasy-statblocks:loaded", null); await this.loadSettings(); @@ -150,7 +153,7 @@ export default class StatBlockPlugin extends Plugin { this.addCommand({ id: "open-creature-view", - name: "Open Creature pane", + name: t("Open Creature pane"), checkCallback: (checking) => { const existing = this.app.workspace.getLeavesOfType(CREATURE_VIEW); @@ -165,7 +168,7 @@ export default class StatBlockPlugin extends Plugin { }); this.addCommand({ id: "reveal-creature-view", - name: "Reveal Creature pane", + name: t("Reveal Creature pane"), checkCallback: (checking) => { const existing = this.app.workspace.getLeavesOfType(CREATURE_VIEW); @@ -180,12 +183,12 @@ export default class StatBlockPlugin extends Plugin { }); this.addCommand({ id: "open-new-creature-view", - name: "Open new Creature pane", + name: t("Open new Creature pane"), callback: () => { this.openCreatureView(true); } }); - this.addRibbonIcon("skull", "Open Creature pane", async (evt) => { + this.addRibbonIcon("skull", t("Open Creature pane"), async (evt) => { this.openCreatureView(evt.getModifierState("Meta")); }); @@ -381,7 +384,7 @@ export default class StatBlockPlugin extends Plugin { } onunload() { - console.log("Fantasy StatBlocks unloaded"); + console.log(t("Fantasy StatBlocks unloaded")); this.app.workspace .getLeavesOfType(CREATURE_VIEW) @@ -409,7 +412,7 @@ export default class StatBlockPlugin extends Plugin { }) .catch((e) => { new Notice( - `There was an error creating the image: \n\n${e.message}` + `${t("There was an error creating the image:")} \n\n${e.message}` ); console.error(e); }); @@ -451,10 +454,10 @@ export default class StatBlockPlugin extends Plugin { ctx.addChild(statblock); } catch (e) { - console.error(`Obsidian Statblock Error:\n${e}`); + console.error(`${t("Obsidian Statblock Error:")}\n${e}`); let pre = createEl("pre"); pre.setText(`\`\`\`statblock -There was an error rendering the statblock: +${t("There was an error rendering the statblock:")} ${e.stack .split("\n") .filter((line: string) => !/^at/.test(line?.trim())) diff --git a/src/settings/EditMonster.svelte b/src/settings/EditMonster.svelte index b90ef2f4..a8c39710 100644 --- a/src/settings/EditMonster.svelte +++ b/src/settings/EditMonster.svelte @@ -7,6 +7,7 @@ parseYaml, stringifyYaml } from "obsidian"; + import { t } from "src/util/i18n"; import type { Monster } from "index"; const dispatch = createEventDispatcher(); @@ -16,17 +17,17 @@ let textArea: HTMLTextAreaElement; const json = (node: HTMLElement) => { - new ExtraButtonComponent(node).setIcon("code-glyph").setTooltip("JSON"); + new ExtraButtonComponent(node).setIcon("code-glyph").setTooltip(t("JSON")); }; const yaml = (node: HTMLElement) => { new ExtraButtonComponent(node) .setIcon("lines-of-text") - .setTooltip("YAML"); + .setTooltip(t("YAML")); }; const save = (node: HTMLElement) => { new ButtonComponent(node) .setIcon("checkmark") - .setTooltip("Save Changes") + .setTooltip(t("Save Changes")) .onClick(() => { if (useJson) { try { @@ -38,7 +39,7 @@ } catch (e) { console.error(e); new Notice( - `There was an error saving the creaturen\n\n${e.message}` + `${t("There was an error saving the creaturen")}\n\n${e.message}` ); return; } @@ -49,7 +50,7 @@ const cancel = (node: HTMLElement) => { new ExtraButtonComponent(node) .setIcon("cross") - .setTooltip("Cancel") + .setTooltip(t("Cancel")) .onClick(() => { dispatch("cancel"); }); diff --git a/src/settings/creatures/Creature.svelte b/src/settings/creatures/Creature.svelte index 8537a0c3..cac8346e 100644 --- a/src/settings/creatures/Creature.svelte +++ b/src/settings/creatures/Creature.svelte @@ -7,6 +7,7 @@ import { getContext } from "../layout/context"; import { NameFilter } from "./filters/filters"; import { createEventDispatcher } from "svelte"; + import { t } from "src/util/i18n"; export let item: Monster; @@ -21,7 +22,7 @@ if (Array.isArray(item.source)) { let source = item.source.slice(0, 4); if (item.source.length > 4) { - source.push(`and ${item.source.length - 4} more`); + source.push(`${t("and %d more").replace("%d", item.source.length - 4)}`); needTooltip = true; } desc = stringify(source, 0, ", ", false); @@ -40,7 +41,7 @@ content .addExtraButton((b) => { b.setIcon("pencil") - .setTooltip("Edit") + .setTooltip(t("Edit")) .onClick(() => { const modal = new EditMonsterModal(plugin, item); modal.open(); @@ -48,7 +49,7 @@ }) .addExtraButton((b) => { b.setIcon("trash") - .setTooltip("Delete") + .setTooltip(t("Delete")) .onClick(async () => { await plugin.deleteMonsters(item.name); }); @@ -66,7 +67,7 @@ } content.addExtraButton((b) => { b.setIcon("info") - .setTooltip("View") + .setTooltip(t("View")) .onClick(() => { const modal = new ViewMonsterModal(plugin, item as Monster); modal.open(); diff --git a/src/settings/creatures/Creatures.svelte b/src/settings/creatures/Creatures.svelte index eacb182c..31812e18 100644 --- a/src/settings/creatures/Creatures.svelte +++ b/src/settings/creatures/Creatures.svelte @@ -1,4 +1,5 @@ @@ -54,7 +55,7 @@ class:layout={block.type == "layout"} > {#if block.type == "javascript"} - JavaScript + {t("JavaScript")} {:else if block.type != "ifelse" && block.type != "collapse"} {/if} diff --git a/src/settings/layout/blocks/ui/Creator.svelte b/src/settings/layout/blocks/ui/Creator.svelte index d23f5f27..70140a25 100644 --- a/src/settings/layout/blocks/ui/Creator.svelte +++ b/src/settings/layout/blocks/ui/Creator.svelte @@ -15,6 +15,7 @@ import Rule from "src/view/ui/Rule.svelte"; import { type StatblockItem, TypeNames } from "src/layouts/layout.types"; import { setNodeIcon } from "src/util"; + import { t } from "src/util/i18n"; const dispatch = createEventDispatcher(); @@ -111,7 +112,7 @@ } else { new Menu() .addItem((item) => { - item.setTitle("Add") + item.setTitle(t("Add")) .setIcon("plus-with-circle") .onClick((e: MouseEvent | KeyboardEvent) => { add(block, evt); @@ -119,7 +120,7 @@ }) .addItem((item) => item - .setTitle("Edit") + .setTitle(t("Edit")) .setIcon("pencil") .onClick(() => { editBlock(block); @@ -127,7 +128,7 @@ ) .addItem((item) => item - .setTitle("Delete") + .setTitle(t("Delete")) .setIcon("trash") .onClick(() => trash(block)) ) @@ -138,13 +139,13 @@ const editIcon = (node: HTMLDivElement) => { new ExtraButtonComponent(node) .setIcon("pencil") - .setTooltip("Edit Block"); + .setTooltip(t("Edit Block")); }; const trashIcon = (node: HTMLDivElement) => { new ExtraButtonComponent(node) .setIcon("trash") - .setTooltip("Delete Block"); + .setTooltip(t("Delete Block")); }; @@ -209,7 +210,7 @@ {#if block.heading} {block.heading} {:else} - Collapse + {t("Collapse")} {/if} {#if "hasRule" in block && block.hasRule} -
+
{/if} diff --git a/src/settings/layout/blocks/ui/IfElseConditions.svelte b/src/settings/layout/blocks/ui/IfElseConditions.svelte index 5da44752..28f20a08 100644 --- a/src/settings/layout/blocks/ui/IfElseConditions.svelte +++ b/src/settings/layout/blocks/ui/IfElseConditions.svelte @@ -1,5 +1,6 @@ @@ -59,35 +60,35 @@
{/if} {#if block.conditioned}
{/if} {#if "callback" in block}
{/if} {#if ("dice" in block && block.dice) || ("diceCallback" in block && block.diceCallback?.length)}
{/if} {#if "markdown" in block && block.markdown}
{/if} diff --git a/src/settings/layout/blocks/ui/block.ts b/src/settings/layout/blocks/ui/block.ts index 1467d685..430453c5 100644 --- a/src/settings/layout/blocks/ui/block.ts +++ b/src/settings/layout/blocks/ui/block.ts @@ -31,6 +31,7 @@ import type StatBlockPlugin from "src/main"; import TableHeaders from "./TableHeaders.svelte"; import SubheadingProperty from "./SubheadingProperty.svelte"; import IfElseConditions from "./IfElseConditions.svelte"; +import { t } from "src/util/i18n"; import { editorFromTextArea, nanoid } from "src/util/util"; import { EditorView } from "@codemirror/view"; import type { Monster } from "index"; @@ -134,7 +135,7 @@ abstract class BlockModal< this.containerEl.addClass("statblock-edit-block"); } onOpen() { - this.titleEl.setText("Edit Block"); + this.titleEl.setText(t("Edit Block")); this.display(); } @@ -147,7 +148,7 @@ abstract class BlockModal< b .setCta() .setIcon("checkmark") - .setTooltip("Save") + .setTooltip(t("Save")) .onClick(() => { this.saved = true; this.close(); @@ -156,7 +157,7 @@ abstract class BlockModal< .addExtraButton((b) => b .setIcon("cross") - .setTooltip("Cancel") + .setTooltip(t("Cancel")) .onClick(() => { this.close(); }) @@ -170,9 +171,9 @@ class GroupModal extends BlockModal { async display() { this.contentEl.empty(); new Setting(this.contentEl) - .setName("Section Heading") + .setName(t("Section Heading")) .setDesc( - "This text will be used for the section heading. Can be left blank." + t("This text will be used for the section heading. Can be left blank.") ) .addText((t) => { t.setValue(this.block.heading).onChange( @@ -180,9 +181,9 @@ class GroupModal extends BlockModal { ); }); new Setting(this.contentEl) - .setName("Has Rule") + .setName(t("Has Rule")) .setDesc( - "If present, the block will have a horizontal rule placed after it." + t("If present, the block will have a horizontal rule placed after it.") ) .addToggle((t) => { t.setValue(this.block.hasRule).onChange( @@ -190,9 +191,9 @@ class GroupModal extends BlockModal { ); }); new Setting(this.contentEl) - .setName("CSS Container Class") + .setName(t("CSS Container Class")) .setDesc( - "All nested elements inside this group container will receive this CSS class. If blank, no class will be applied." + t("All nested elements inside this group container will receive this CSS class. If blank, no class will be applied.") ) .addText((t) => { t.setValue(this.block.cls).onChange( @@ -206,9 +207,9 @@ class GroupModal extends BlockModal { el.empty(); const block = this.block; new Setting(el) - .setName("Conditional") + .setName(t("Conditional")) .setDesc( - "The block will not be added if the associated properties are not present." + t("The block will not be added if the associated properties are not present.") ) .addToggle((t) => { t.setValue(block.conditioned).onChange((v) => { @@ -224,9 +225,9 @@ class CollapseModal extends BlockModal { async display() { this.contentEl.empty(); new Setting(this.contentEl) - .setName("Section Heading") + .setName(t("Section Heading")) .setDesc( - "This text will be used for the section heading. Can be left blank." + t("This text will be used for the section heading. Can be left blank.") ) .addText((t) => { t.setValue(this.block.heading).onChange( @@ -234,17 +235,17 @@ class CollapseModal extends BlockModal { ); }); new Setting(this.contentEl) - .setName("Open by Default") - .setDesc("The block will start open.") + .setName(t("Open by Default")) + .setDesc(t("The block will start open.")) .addToggle((t) => { t.setValue(this.block.open).onChange( (v) => (this.block.open = v) ); }); new Setting(this.contentEl) - .setName("Has Rule") + .setName(t("Has Rule")) .setDesc( - "If present, the block will have a horizontal rule placed after it." + t("If present, the block will have a horizontal rule placed after it.") ) .addToggle((t) => { t.setValue(this.block.hasRule).onChange( @@ -260,12 +261,12 @@ class JavaScriptModal extends BlockModal { this.contentEl.empty(); new Setting(this.contentEl) - .setName("JavaScript") + .setName(t("JavaScript")) .setHeading() .setDesc( createFragment((e) => { e.createSpan({ - text: "JavaScript blocks can be used to do highly advanced HTML elements. The JavaScript code will be provided the " + text: t("JavaScript blocks can be used to do highly advanced HTML elements. The JavaScript code will be provided the ") }); e.createEl("code", { text: "monster" @@ -275,7 +276,7 @@ class JavaScriptModal extends BlockModal { text: "property" }); e.createSpan({ - text: "parameters and should return a HTML element, which will be attached to the block's container element." + text: t("parameters and should return a HTML element, which will be attached to the block's container element.") }); }) ); @@ -317,7 +318,7 @@ class LayoutModal extends BlockModal { this.contentEl.empty(); new Setting(this.contentEl) - .setName("Layout to Insert") + .setName(t("Layout to Insert")) .addDropdown((d) => { for (const layout of this.plugin.manager.getAllLayouts()) { if (layout.id == this.layout) continue; @@ -392,7 +393,7 @@ abstract class EditorEnabledModal< this.plugin.saveSettings(); }; const summary = el.createEl("summary"); - new Setting(summary).setHeading().setName("Advanced Settings"); + new Setting(summary).setHeading().setName(t("Advanced Settings")); summary.createDiv("collapser").createDiv("handle"); this.buildAdvanced(el.createDiv()); } @@ -413,8 +414,8 @@ class ActionModal extends EditorEnabledModal { el.empty(); new Setting(el) - .setName("Icon") - .setDesc("Choose the icon to use for the button.") + .setName(t("Icon")) + .setDesc(t("Choose the icon to use for the button.")) .addText((t) => { t.setValue(this.block.icon); const icons = getIconIds().map((v) => @@ -439,8 +440,8 @@ class ActionModal extends EditorEnabledModal { b.setIcon(this.block.icon).setDisabled(true); }); new Setting(el) - .setName("Action") - .setDesc("Choose a Command to run when this action is executed.") + .setName(t("Action")) + .setDesc(t("Choose a Command to run when this action is executed.")) .addText((t) => { t.setValue(this.block.action); const commands = this.app.commands.listCommands(); @@ -465,23 +466,23 @@ class ActionModal extends EditorEnabledModal { new Setting(el) .setHeading() - .setName("Callback") + .setName(t("Callback")) .setDesc( createFragment((e) => { e.createSpan({ - text: "Executing the action will run the callback. Any registered commands will " + text: t("Executing the action will run the callback. Any registered commands will ") }); - e.createEl("strong", { text: "not" }); + e.createEl("strong", { text: t("not") }); e.createSpan({ - text: " be ran." + text: t(" be ran.") }); e.createEl("br"); e.createSpan({ - text: "The callback will receive the " + text: t("The callback will receive the ") }); e.createEl("code", { text: "monster" }); e.createSpan({ - text: " parameter. " + text: t(" parameter. ") }); }) ); @@ -503,9 +504,9 @@ class ActionModal extends EditorEnabledModal { class BasicModal extends EditorEnabledModal { addPropertyAsCssClassToggleSetting(el: HTMLDivElement) { new Setting(el) - .setName("Add Property as CSS Class") + .setName(t("Add Property as CSS Class")) .setDesc( - "Disable this to prevent adding the property to the containing HTML element as a CSS class. This can be used to avoid collisions with native Obsidian CSS." + t("Disable this to prevent adding the property to the containing HTML element as a CSS class. This can be used to avoid collisions with native Obsidian CSS.") ) .addToggle((t) => { t.setValue(!this.block.doNotAddClass).onChange((v) => { @@ -517,7 +518,7 @@ class BasicModal extends EditorEnabledModal { buildProperties(el: HTMLDivElement) { el.empty(); const block = this.block; - new Setting(el).setName("Link Monster Property").addText((t) => + new Setting(el).setName(t("Link Monster Property")).addText((t) => t.setValue(block.properties[0]).onChange((v) => { block.properties[0] = v; }) @@ -528,29 +529,29 @@ class BasicModal extends EditorEnabledModal { if (this.plugin.canUseDiceRoller) { new Setting(el) .setHeading() - .setName("Dice Callback") + .setName(t("Dice Callback")) .setDesc( createFragment((e) => { e.createSpan({ - text: "The block will run the callback and use the returned values for the dice strings." + text: t("The block will run the callback and use the returned values for the dice strings.") }); e.createEl("br"); e.createSpan({ - text: "The callback will receive the " + text: t("The callback will receive the ") }); e.createEl("code", { text: "monster" }); - e.createSpan({ text: " and " }); + e.createSpan({ text: t(" and ") }); e.createEl("code", { text: "property" }); e.createSpan({ - text: "parameters. Dice callbacks should return an array of strings and objects, with the objects defining the dice rolls:" + text: t("parameters. Dice callbacks should return an array of strings and objects, with the objects defining the dice rolls:") }); e.createEl("br"); MarkdownRenderer.render( this.plugin.app, `\`\`\`ts interface DiceCallbackObject { - text: string // string to be parsed into a dice roll - original?: string // optional, shown in parenthesis + text: string // ${t("string to be parsed into a dice roll")} + original?: string // ${t("optional, shown in parenthesis")} } \`\`\``, e.createDiv(), @@ -559,13 +560,13 @@ interface DiceCallbackObject { ); e.createEl("br"); - e.createEl("span", { text: "For example: " }); + e.createEl("span", { text: t("For example: ") }); e.createEl("br"); MarkdownRenderer.render( this.plugin.app, `\`\`\`ts const diceText = monster.stats[5] + "d20 + 2"; -return ["The monster guesses you have: ", { text: diceText }, " freckles."]; +return ["${t("The monster guesses you have: ")}", { text: diceText }, "${t(" freckles.")}"]; \`\`\``, e.createDiv(), "", @@ -598,9 +599,9 @@ return ["The monster guesses you have: ", { text: diceText }, " freckles."]; el.empty(); const block = this.block; new Setting(el) - .setName("Conditional") + .setName(t("Conditional")) .setDesc( - "The block will not be added if the associated properties are not present." + t("The block will not be added if the associated properties are not present.") ) .addToggle((t) => { t.setValue(block.conditioned).onChange((v) => { @@ -611,8 +612,8 @@ return ["The monster guesses you have: ", { text: diceText }, " freckles."]; }); if (!this.block.conditioned) { new Setting(el) - .setName("Fallback") - .setDesc("If not present, this text will be displayed.") + .setName(t("Fallback")) + .setDesc(t("If not present, this text will be displayed.")) .addText((t) => { if (!block.fallback) { block.fallback = "-"; @@ -623,9 +624,9 @@ return ["The monster guesses you have: ", { text: diceText }, " freckles."]; }); } new Setting(el) - .setName("Has Rule") + .setName(t("Has Rule")) .setDesc( - "If present, the block will have a horizontal rule placed after it." + t("If present, the block will have a horizontal rule placed after it.") ) .addToggle((t) => { t.setValue(block.hasRule).onChange((v) => (block.hasRule = v)); @@ -636,9 +637,9 @@ return ["The monster guesses you have: ", { text: diceText }, " freckles."]; const block = this.block; if (this.plugin.canUseDiceRoller) { new Setting(el) - .setName("Parse for Dice") + .setName(t("Parse for Dice")) .setDesc( - "The plugin will attempt to add dice rollers as specified." + t("The plugin will attempt to add dice rollers as specified.") ) .addToggle((t) => @@ -649,9 +650,9 @@ return ["The monster guesses you have: ", { text: diceText }, " freckles."]; ); if (block.dice) { new Setting(el.createDiv()) - .setName("Link Dice to Property") + .setName(t("Link Dice to Property")) .setDesc( - "The dice roller will parse this property instead of the original." + t("The dice roller will parse this property instead of the original.") ) .addText((t) => { t.setValue(`${block.diceProperty}`).onChange((v) => { @@ -687,8 +688,8 @@ class HeadingModal extends BasicModal { buildProperties(el: HTMLDivElement): void { super.buildProperties(el); new Setting(el) - .setName("Header Size") - .setDesc("The heading will use this size.") + .setName(t("Header Size")) + .setDesc(t("The heading will use this size.")) .addDropdown((d) => { if (!this.block.size) { this.block.size == 1; @@ -711,25 +712,25 @@ class PropertyModal extends MarkdownEnabledModal { super.buildAdvanced(el); new Setting(el) .setHeading() - .setName("Callback") + .setName(t("Callback")) .setDesc( createFragment((e) => { e.createSpan({ - text: "The block will run the callback and use the returned string as the property." + text: t("The block will run the callback and use the returned string as the property.") }); e.createEl("br"); e.createSpan({ - text: "The callback will receive the " + text: t("The callback will receive the ") }); e.createEl("code", { text: "monster" }); e.createSpan({ - text: " parameter. The callback should return a string. For example: " + text: t(" parameter. The callback should return a string. For example: ") }); e.createEl("code", { text: "return monster.name" }); e.createEl("br"); e.createEl("strong", { - text: "Please Note: This will not run if a dice callback is provided." + text: t("Please Note: This will not run if a dice callback is provided.") }); }) ); @@ -755,8 +756,8 @@ class PropertyModal extends MarkdownEnabledModal { super.buildProperties(el); super.addPropertyAsCssClassToggleSetting(el); new Setting(el) - .setName("Display Text") - .setDesc("This text will be used for the property name.") + .setName(t("Display Text")) + .setDesc(t("This text will be used for the property name.")) .addText((t) => { t.setValue(this.block.display).onChange( (v) => (this.block.display = v) @@ -769,8 +770,8 @@ class SavesModal extends MarkdownEnabledModal { super.buildProperties(el); super.addPropertyAsCssClassToggleSetting(el); new Setting(el) - .setName("Display Text") - .setDesc("This text will be used for the property name.") + .setName(t("Display Text")) + .setDesc(t("This text will be used for the property name.")) .addText((t) => { t.setValue(this.block.display).onChange( (v) => (this.block.display = v) @@ -781,27 +782,27 @@ class SavesModal extends MarkdownEnabledModal { super.buildAdvanced(el); new Setting(el) .setHeading() - .setName("Callback") + .setName(t("Callback")) .setDesc( createFragment((e) => { e.createSpan({ - text: "The block will run the callback on each save object and use the returned object as the save." + text: t("The block will run the callback on each save object and use the returned object as the save.") }); e.createEl("br"); e.createSpan({ - text: "The callback will receive the " + text: t("The callback will receive the ") }); e.createEl("code", { text: "monster" }); - e.createSpan({ text: " and " }); + e.createSpan({ text: t(" and ") }); e.createEl("code", { text: "property" }); e.createSpan({ - text: " parameters. The callback should return an object with a single key and value. For example: " + text: t(" parameters. The callback should return an object with a single key and value. For example: ") }); e.createEl("code", { text: "return {\"fort\": property.fortitude}" }); e.createEl("br"); e.createEl("strong", { - text: "Please Note: This will not run if a dice callback is provided." + text: t("Please Note: This will not run if a dice callback is provided.") }); }) ); @@ -824,9 +825,9 @@ class SpellsModal extends MarkdownEnabledModal { buildProperties(el: HTMLDivElement): void { super.buildProperties(el); new Setting(el) - .setName("Trait Name") + .setName(t("Trait Name")) .setDesc( - "Name to display for the Spellcasting trait. Defaults to Spellcasting if not provided." + t("Name to display for the Spellcasting trait. Defaults to Spellcasting if not provided.") ) .addText((t) => { t.setValue(this.block.heading).onChange( @@ -843,7 +844,7 @@ class SubheadingModal extends BasicModal { let tempProp = ""; new Setting(container) .setHeading() - .setName("Link Monster Properties") + .setName(t("Link Monster Properties")) .addText((t) => t .setPlaceholder("property") @@ -853,7 +854,7 @@ class SubheadingModal extends BasicModal { .addExtraButton((b) => b.setIcon("plus-with-circle").onClick(() => { if (!tempProp || !tempProp.length) { - new Notice("A valid property must be supplied."); + new Notice(t("A valid property must be supplied.")); return; } block.properties.push(tempProp); @@ -878,8 +879,8 @@ class SubheadingModal extends BasicModal { el.empty(); new Setting(el) - .setName("Separator") - .setDesc("Text separating properties") + .setName(t("Separator")) + .setDesc(t("Text separating properties")) .addText((t) => { t.setValue(this.block.separator).onChange((v) => { //If onchange(v) parameter is empty, get default ", " or v value @@ -898,19 +899,19 @@ class TableModal extends BasicModal { super.buildAdvanced(el); new Setting(el) .setHeading() - .setName("Ability Modifier Calculation") + .setName(t("Ability Modifier Calculation")) .setDesc( createFragment((e) => { e.createSpan({ - text: "Allows a custom modifier for the stat." + text: t("Allows a custom modifier for the stat.") }); e.createEl("br"); - e.createSpan({ text: "Variables " }); + e.createSpan({ text: t("Variables ") }); e.createEl("code", { text: "stat" }); - e.createSpan({ text: " and " }); + e.createSpan({ text: t(" and ") }); e.createEl("code", { text: "monster" }); e.createSpan({ - text: "are accessible, use these to calculate the modifier." + text: t("are accessible, use these to calculate the modifier.") }); }) ); @@ -936,17 +937,17 @@ class TableModal extends BasicModal { let tempProp = ""; new Setting(container) .setHeading() - .setName("Table Headers") - .addText((t) => - t - .setPlaceholder("header") + .setName(t("Table Headers")) + .addText((x) => + x + .setPlaceholder(t("header")) .setValue(tempProp) .onChange((v) => (tempProp = v)) ) .addExtraButton((b) => b.setIcon("plus-with-circle").onClick(() => { if (!tempProp || !tempProp.length) { - new Notice("A valid property must be supplied."); + new Notice(t("A valid property must be supplied.")); return; } this.block.headers.push(tempProp); @@ -963,9 +964,9 @@ class TableModal extends BasicModal { this.block.headers = [...(e.detail?.map((v) => v.name) ?? [])]; }); new Setting(el) - .setName("Calculate Modifiers") + .setName(t("Calculate Modifiers")) .setDesc( - "The block will attempt to calculate modifiers for table values." + t("The block will attempt to calculate modifiers for table values.") ) .addToggle((t) => { t.setValue(this.block.calculate).onChange((v) => { @@ -980,9 +981,9 @@ class TraitsModal extends MarkdownEnabledModal { super.buildProperties(el); super.addPropertyAsCssClassToggleSetting(el); new Setting(el) - .setName("Use Monster Property for Heading") + .setName(t("Use Monster Property for Heading")) .setDesc( - "The Section heading will be set to the value of the specified property." + t("The Section heading will be set to the value of the specified property.") ) .addToggle((t) => { t.setValue(this.block.headingProp).onChange((v) => { @@ -994,8 +995,8 @@ class TraitsModal extends MarkdownEnabledModal { .setName("Section Heading") .setDesc( this.block.headingProp - ? "The section will use this property for the section heading. If the property does not exist or is blank, the section heading will not appear." - : "This text will be used for the section heading. Can be left blank." + ? t("The section will use this property for the section heading. If the property does not exist or is blank, the section heading will not appear.") + : t("This text will be used for the section heading. Can be left blank.") ) .addText((t) => { t.setValue(this.block.heading).onChange( @@ -1003,15 +1004,15 @@ class TraitsModal extends MarkdownEnabledModal { ); }); const subheading = new Setting(el) - .setName("Section Subheading Text") + .setName(t("Section Subheading Text")) .setDesc( createFragment((e) => { e.createSpan({ - text: "Text entered here will appear directly after the section heading, before the actual traits. Use " + text: t("Text entered here will appear directly after the section heading, before the actual traits. Use ") }); e.createEl("code", { text: "{{monster}}" }); e.createSpan({ - text: " to insert the current monster's name." + text: t(" to insert the current monster's name.") }); }) ); @@ -1025,27 +1026,27 @@ class TraitsModal extends MarkdownEnabledModal { super.buildAdvanced(el); new Setting(el) .setHeading() - .setName("Callback") + .setName(t("Callback")) .setDesc( createFragment((e) => { e.createSpan({ - text: "The block will run the callback on each trait and use the returned string as the trait description." + text: t("The block will run the callback on each trait and use the returned string as the trait description.") }); e.createEl("br"); e.createSpan({ - text: "The callback will receive the " + text: t("The callback will receive the ") }); e.createEl("code", { text: "monster" }); e.createSpan({ text: " and " }); e.createEl("code", { text: "property" }); e.createSpan({ - text: " parameters. The callback should return a string. For example: " + text: t(" parameters. The callback should return a string. For example: ") }); e.createEl("code", { text: "return monster.name" }); e.createEl("br"); e.createEl("strong", { - text: "Please Note: This will not run if a dice callback is provided." + text: t("Please Note: This will not run if a dice callback is provided.") }); }) ); @@ -1069,13 +1070,13 @@ class TextModal extends MarkdownEnabledModal { super.buildAdvanced(el); new Setting(el) .setHeading() - .setName("Text to Show") + .setName(t("Text to Show")) .setDesc( createFragment((e) => { - e.createSpan({ text: "The block will " }); - e.createEl("strong", { text: "always" }); + e.createSpan({ text: t("The block will ") }); + e.createEl("strong", { text: t("always") }); e.createSpan({ - text: " display the text entered here." + text: t(" display the text entered here.") }); }) ); @@ -1086,9 +1087,9 @@ class TextModal extends MarkdownEnabledModal { buildProperties(el: HTMLDivElement): void { super.buildProperties(el); new Setting(el) - .setName("Use Monster Property for Heading") + .setName(t("Use Monster Property for Heading")) .setDesc( - "The Section heading will be set to the value of the specified property." + t("The Section heading will be set to the value of the specified property.") ) .addToggle((t) => { t.setValue(this.block.headingProp).onChange((v) => { @@ -1097,11 +1098,11 @@ class TextModal extends MarkdownEnabledModal { }); }); new Setting(el) - .setName("Section Heading") + .setName(t("Section Heading")) .setDesc( this.block.headingProp - ? "The section will use this property for the section heading. If the property does not exist or is blank, the section heading will not appear." - : "This text will be used for the section heading. Can be left blank." + ? t("The section will use this property for the section heading. If the property does not exist or is blank, the section heading will not appear.") + : t("This text will be used for the section heading. Can be left blank.") ) .addText((t) => { t.setValue(this.block.heading).onChange( diff --git a/src/settings/layout/previewer/Previewer.svelte b/src/settings/layout/previewer/Previewer.svelte index 645870dc..e6889abd 100644 --- a/src/settings/layout/previewer/Previewer.svelte +++ b/src/settings/layout/previewer/Previewer.svelte @@ -12,6 +12,7 @@ import { MonsterSuggestionModal } from "src/util/creature"; import type { Monster } from "index"; import { buildTextArea, setNodeIcon } from "src/util"; + import { t } from "src/util/i18n"; import type { EditorView } from "@codemirror/view"; import StatBlockRenderer from "src/view/statblock"; import { derived, writable } from "svelte/store"; @@ -48,7 +49,7 @@ }); const search = (node: HTMLElement) => { const search = new SearchComponent(node).setPlaceholder( - "Find a creature" + t("Find a creature") ); const suggester = new MonsterSuggestionModal( plugin.app, @@ -108,12 +109,12 @@ }); }; const settingsDesc = derived([mode, scale], ([mode, scale]) => { - const desc = [`Scale: ${scale}`]; + const desc = [`${t("Scale:")} ${scale}`]; if (mode === ThemeMode.Light) { - desc.push("Mode: Light"); + desc.push(t("Mode: Light")); } if (mode === ThemeMode.Dark) { - desc.push("Mode: Dark"); + desc.push(t("Mode: Dark")); } return desc.join(", "); }); @@ -126,8 +127,7 @@ style={`--scale: ${$scale}`} >
- Select a creature to preview the layout, or enter your own definition - below. + {t("Select a creature to preview the layout, or enter your own definition below.")}
@@ -138,18 +138,18 @@
-
Set theme mode
+
{t("Set theme mode")}
@@ -157,8 +157,8 @@
-
Scale preview
-
Current: {$scale}
+
{t("Scale preview")}
+
{t("Current:")} {$scale}
{ if (!detail.name) { - new Notice("Creatures must be given a name."); + new Notice(t("Creatures must be given a name.")); return; } await this.plugin.updateMonster(this.monster as Monster, detail); diff --git a/src/settings/settings.ts b/src/settings/settings.ts index bf91b286..78879b7d 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -23,6 +23,7 @@ import { Layout5e } from "src/layouts/basic 5e/basic5e"; import type { DefaultLayout, Layout } from "src/layouts/layout.types"; import { DICE_ROLLER_SOURCE } from "src/main"; import FantasyStatblockModal from "src/modal/modal"; +import { t } from "src/util/i18n"; import { nanoid } from "src/util/util"; import { Watcher } from "src/watcher/watcher"; import Creatures from "./creatures/Creatures.svelte"; @@ -46,7 +47,7 @@ export default class StatblockSettingTab extends PluginSettingTab { containerEl.addClass("statblock-settings"); - containerEl.createEl("h2", { text: "Fantasy Statblocks Settings" }); + containerEl.createEl("h2", { text: t("Fantasy Statblocks Settings") }); this.generateTopSettings(containerEl.createDiv()); this.generateParseSettings(containerEl.createDiv()); @@ -63,30 +64,30 @@ export default class StatblockSettingTab extends PluginSettingTab { href: "https://www.buymeacoffee.com/valentine195" }).createEl("img", { attr: { - src: "https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=☕&slug=valentine195&button_colour=e3e7ef&font_colour=262626&font_family=Inter&outline_colour=262626&coffee_colour=ff0000" + src: "https://img.buymeacoffee.com/button-api/?text=" + t("Buy me a coffee") + "&emoji=☕&slug=valentine195&button_colour=e3e7ef&font_colour=262626&font_family=Inter&outline_colour=262626&coffee_colour=ff0000" } }); } catch (e) { console.error(e); new Notice( - "There was an error displaying the settings tab for 5e Statblocks." + t("There was an error displaying the settings tab for 5e Statblocks.") ); } } generateAdvancedSettings(container: HTMLDivElement) { container.empty(); - new Setting(container).setHeading().setName("Advanced Settings"); + new Setting(container).setHeading().setName(t("Advanced Settings")); new Setting(container) - .setName("Try to Save Data Atomically") + .setName(t("Try to Save Data Atomically")) .setDesc( createFragment((e) => { e.createSpan({ - text: "This will cause to plugin to save data to a temporary file before saving the actual data file in an attempt to prevent data loss." + text: t("This will cause to plugin to save data to a temporary file before saving the actual data file in an attempt to prevent data loss.") }); e.createEl("br"); e.createSpan({ - text: "This can cause issues sometimes when using sync services." + text: t("This can cause issues sometimes when using sync services.") }); e.createEl("br"); const warning = e.createDiv(); @@ -95,7 +96,7 @@ export default class StatblockSettingTab extends PluginSettingTab { attr: { style: "color: var(--text-error)" }, - text: "This setting is currently disabled." + text: t("This setting is currently disabled.") }); }) ) @@ -111,7 +112,7 @@ export default class StatblockSettingTab extends PluginSettingTab { generateTopSettings(container: HTMLDivElement) { container.empty(); - new Setting(container).setHeading().setName("General Settings"); + new Setting(container).setHeading().setName(t("General Settings")); /* new Setting(container) .setName("Enable Export to PNG") .setDesc( @@ -133,20 +134,20 @@ export default class StatblockSettingTab extends PluginSettingTab { }) ); */ new Setting(container) - .setName("Integrate Dice Roller") + .setName(t("Integrate Dice Roller")) .setDesc( createFragment((e) => { if (this.plugin.diceRollerInstalled) { e.createSpan({ - text: "Add Dice Roller dice to statblocks by default. Use " + text: t("Add Dice Roller dice to statblocks by default. Use ") }); e.createEl("code", { text: "dice: false" }); e.createSpan({ - text: " to disable per-statblock." + text: t(" to disable per-statblock.") }); } else { e.createSpan({ - text: "This setting is only usable with the Dice Roller plugin enabled." + text: t("This setting is only usable with the Dice Roller plugin enabled.") }); } }) @@ -159,20 +160,20 @@ export default class StatblockSettingTab extends PluginSettingTab { }) ); new Setting(container) - .setName("Render Dice Rolls") + .setName(t("Render Dice Rolls")) .setDesc( createFragment((e) => { if (this.plugin.diceRollerInstalled) { e.createSpan({ - text: "Roll graphical dice inside statblocks by default. Use " + text: t("Roll graphical dice inside statblocks by default. Use ") }); e.createEl("code", { text: "render: false" }); e.createSpan({ - text: " to disable per-statblock." + text: t(" to disable per-statblock.") }); } else { e.createSpan({ - text: "This setting is only usable with the Dice Roller plugin enabled." + text: t("This setting is only usable with the Dice Roller plugin enabled.") }); } }) @@ -200,15 +201,15 @@ export default class StatblockSettingTab extends PluginSettingTab { }) ); new Setting(container) - .setName("Try to Render Wikilinks") + .setName(t("Try to Render Wikilinks")) .setDesc( createFragment((e) => { e.createSpan({ - text: "The plugin will attempt to detect wikilinks inside Statblocks." + text: t("The plugin will attempt to detect wikilinks inside Statblocks.") }); e.createEl("br"); e.createEl("strong", { - text: "Please note: these links will not be added to the graph." + text: t("Please note: these links will not be added to the graph.") }); }) ) @@ -221,11 +222,11 @@ export default class StatblockSettingTab extends PluginSettingTab { }) ); new Setting(container) - .setName("Enable 5e SRD") + .setName(t("Enable 5e SRD")) .setDesc( createFragment((e) => { e.createSpan({ - text: "Use the Dungeons & Dragons 5th Edition System Reference Document monsters." + text: t("Use the Dungeons & Dragons 5th Edition System Reference Document monsters.") }); }) ) @@ -247,18 +248,18 @@ export default class StatblockSettingTab extends PluginSettingTab { const additionalContainer = containerEl.createDiv( "statblock-additional-container" ); - new Setting(additionalContainer).setHeading().setName("Note Parsing"); + new Setting(additionalContainer).setHeading().setName(t("Note Parsing")); new Setting(additionalContainer) - .setName("Automatically Parse Frontmatter for Creatures") + .setName(t("Automatically Parse Frontmatter for Creatures")) .setDesc( createFragment((e) => { e.createSpan({ - text: "The plugin will watch the vault for creatures defined in note frontmatter." + text: t("The plugin will watch the vault for creatures defined in note frontmatter.") }); e.createEl("br"); e.createEl("br"); e.createSpan({ - text: `The "Parse Frontmatter for Creatures" command can also be used.` + text: t(`The \"Parse Frontmatter for Creatures\" command can also be used.`) }); }) ) @@ -274,11 +275,11 @@ export default class StatblockSettingTab extends PluginSettingTab { ); }); new Setting(additionalContainer) - .setName("Enable Debug Messages") + .setName(t("Enable Debug Messages")) .setDesc( createFragment((e) => { e.createSpan({ - text: "Debug messages will be displayed by the file parser." + text: t("Debug messages will be displayed by the file parser.") }); }) ) @@ -291,9 +292,9 @@ export default class StatblockSettingTab extends PluginSettingTab { ); let path: string; new Setting(additionalContainer) - .setName("Bestiary Folder") + .setName(t("Bestiary Folder")) .setDesc( - "The plugin will only parse notes inside these folders and their children." + t("The plugin will only parse notes inside these folders and their children.") ) .addText(async (text) => { let folders = this.app.vault @@ -347,7 +348,7 @@ export default class StatblockSettingTab extends PluginSettingTab { } generateLayouts(containerEl: HTMLDivElement) { containerEl.empty(); - new Setting(containerEl).setHeading().setName("Layouts"); + new Setting(containerEl).setHeading().setName(t("Layouts")); const statblockCreatorContainer = containerEl.createDiv( "statblock-additional-container" @@ -358,15 +359,15 @@ export default class StatblockSettingTab extends PluginSettingTab { .appendChild( createFragment((el) => { el.createSpan({ - text: "New statblock layouts can be created and managed here. A specific layout can be used for a creature using the " + text: t("New statblock layouts can be created and managed here. A specific layout can be used for a creature using the ") }); el.createEl("code", { text: "layout" }); - el.createSpan({ text: " parameter." }); + el.createSpan({ text: t(" parameter.") }); }) ); const importFile = new Setting(statblockCreatorContainer) - .setName("Import From JSON") - .setDesc("Import a custom layout from a JSON file."); + .setName(t("Import From JSON")) + .setDesc(t("Import a custom layout from a JSON file.")); const inputFile = createEl("input", { attr: { type: "file", @@ -392,14 +393,14 @@ export default class StatblockSettingTab extends PluginSettingTab { ); if (!layout) { reject( - new Error("Invalid layout imported") + new Error(t("Invalid layout imported")) ); return; } if (!layout?.name) { reject( new Error( - "Invalid layout imported: layout does not have a name" + t("Invalid layout imported: layout does not have a name") ) ); return; @@ -408,7 +409,7 @@ export default class StatblockSettingTab extends PluginSettingTab { if (!layout?.blocks) { reject( new Error( - "Invalid layout imported: no blocks defined in layout." + t("Invalid layout imported: no blocks defined in layout.") ) ); return; @@ -457,11 +458,11 @@ export default class StatblockSettingTab extends PluginSettingTab { b.onClick(() => inputFile.click()); }); new Setting(statblockCreatorContainer) - .setName("Add New Layout") + .setName(t("Add New Layout")) .addButton((b) => b .setIcon("plus-with-circle") - .setTooltip("Add New Layout") + .setTooltip(t("Add New Layout")) .onClick(() => { const modal = new CreateStatblockModal(this.plugin); modal.onClose = async () => { @@ -482,9 +483,9 @@ export default class StatblockSettingTab extends PluginSettingTab { const statblockAdditional = statblockCreatorContainer.createDiv("additional"); new Setting(statblockAdditional) - .setName("Default Layout") + .setName(t("Default Layout")) .setDesc( - "Change the default statblock layout used, if not specified." + t("Change the default statblock layout used, if not specified.") ) .addDropdown(async (d) => { for (const layout of this.plugin.manager.getAllLayouts()) { @@ -510,8 +511,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }); }); new Setting(statblockAdditional) - .setName("Show Advanced Options") - .setDesc("Show advanced options when editing layout blocks.") + .setName(t("Show Advanced Options")) + .setDesc(t("Show advanced options when editing layout blocks.")) .addToggle((t) => t .setValue(this.plugin.settings.showAdvanced) @@ -559,7 +560,7 @@ export default class StatblockSettingTab extends PluginSettingTab { if (this.plugin.manager.getAllDefaultLayouts().some((f) => f.removed)) { new Setting(layoutContainer) - .setName("Restore Default Layouts") + .setName(t("Restore Default Layouts")) .addButton((b) => { b.setIcon("rotate-ccw").onClick(async () => { for (const layout of Object.values( @@ -584,7 +585,7 @@ export default class StatblockSettingTab extends PluginSettingTab { .setName(layout.name) .addExtraButton((b) => { b.setIcon("pencil") - .setTooltip("Edit") + .setTooltip(t("Edit")) .onClick(() => { const modal = new CreateStatblockModal( this.plugin, @@ -627,7 +628,7 @@ export default class StatblockSettingTab extends PluginSettingTab { setting .addExtraButton((b) => { b.setIcon("duplicate-glyph") - .setTooltip("Create Copy") + .setTooltip(t("Create Copy")) .onClick(async () => { const dupe = this.getDuplicate(layout); this.plugin.settings.layouts.push(dupe); @@ -642,7 +643,7 @@ export default class StatblockSettingTab extends PluginSettingTab { }) .addExtraButton((b) => { b.setIcon("import-glyph") - .setTooltip("Export as JSON") + .setTooltip(t("Export as JSON")) .onClick(() => { const link = createEl("a"); const file = new Blob([JSON.stringify(layout)], { @@ -657,7 +658,7 @@ export default class StatblockSettingTab extends PluginSettingTab { }) .addExtraButton((b) => { b.setIcon("trash") - .setTooltip("Delete") + .setTooltip(t("Delete")) .onClick(async () => { layout.removed = true; this.plugin.settings.defaultLayouts[layout.id] = @@ -672,7 +673,7 @@ export default class StatblockSettingTab extends PluginSettingTab { .setName(layout.name) .addExtraButton((b) => { b.setIcon("pencil") - .setTooltip("Edit") + .setTooltip(t("Edit")) .onClick(() => { const modal = new CreateStatblockModal( this.plugin, @@ -708,7 +709,7 @@ export default class StatblockSettingTab extends PluginSettingTab { }) .addExtraButton((b) => { b.setIcon("duplicate-glyph") - .setTooltip("Create Copy") + .setTooltip(t("Create Copy")) .onClick(async () => { const dupe = this.getDuplicate(layout); this.plugin.settings.layouts.push(dupe); @@ -722,7 +723,7 @@ export default class StatblockSettingTab extends PluginSettingTab { }) .addExtraButton((b) => { b.setIcon("import-glyph") - .setTooltip("Export as JSON") + .setTooltip(t("Export as JSON")) .onClick(() => { const link = createEl("a"); const file = new Blob([JSON.stringify(layout)], { @@ -737,7 +738,7 @@ export default class StatblockSettingTab extends PluginSettingTab { }) .addExtraButton((b) => { b.setIcon("trash") - .setTooltip("Delete") + .setTooltip(t("Delete")) .onClick(async () => { this.plugin.settings.layouts = this.plugin.settings.layouts.filter( @@ -756,20 +757,20 @@ export default class StatblockSettingTab extends PluginSettingTab { containerEl.empty(); new Setting(containerEl) .setHeading() - .setName("Import Homebrew Creatures"); + .setName(t("Import Homebrew Creatures")); const importSettingsContainer = containerEl.createDiv( "statblock-additional-container" ); new Setting(importSettingsContainer).setDesc( - "Import creatures from creature files. Monsters are stored by name, so only the last creature by that name will be saved. This is destructive - any saved creature will be overwritten." + t("Import creatures from creature files. Monsters are stored by name, so only the last creature by that name will be saved. This is destructive - any saved creature will be overwritten.") ); const importAdditional = importSettingsContainer.createDiv("additional"); const importAppFile = new Setting(importAdditional) - .setName("Import DnDAppFile") - .setDesc("Only import content that you own."); + .setName(t("Import DnDAppFile")) + .setDesc(t("Only import content that you own.")); const inputAppFile = createEl("input", { attr: { type: "file", @@ -794,8 +795,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }; importAppFile.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip( - "Import DnDAppFile Data" + b.setButtonText(t("Choose File(s)")).setTooltip( + t("Import DnDAppFile Data") ); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(inputAppFile); @@ -803,8 +804,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }); const importImprovedInitiative = new Setting(importAdditional) - .setName("Import Improved Initiative Data") - .setDesc("Only import content that you own."); + .setName(t("Import Improved Initiative Data")) + .setDesc(t("Only import content that you own.")); const inputImprovedInitiative = createEl("input", { attr: { type: "file", @@ -829,8 +830,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }; importImprovedInitiative.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip( - "Import Improved Initiative Data" + b.setButtonText(t("Choose File(s)")).setTooltip( + t("Import Improved Initiative Data") ); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(inputImprovedInitiative); @@ -838,8 +839,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }); const importCritterDB = new Setting(importAdditional) - .setName("Import CritterDB Data") - .setDesc("Only import content that you own."); + .setName(t("Import CritterDB Data")) + .setDesc(t("Only import content that you own.")); const inputCritterDB = createEl("input", { attr: { type: "file", @@ -864,8 +865,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }; importCritterDB.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip( - "Import CritterDB Data" + b.setButtonText(t("Choose File(s)")).setTooltip( + t("Import CritterDB Data") ); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(inputCritterDB); @@ -873,8 +874,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }); const import5eTools = new Setting(importAdditional) - .setName("Import 5e.tools Data") - .setDesc("Only import content that you own."); + .setName(t("Import 5e.tools Data")) + .setDesc(t("Only import content that you own.")); const input5eTools = createEl("input", { attr: { type: "file", @@ -895,16 +896,16 @@ export default class StatblockSettingTab extends PluginSettingTab { }; import5eTools.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip( - "Import 5e.tools Data" + b.setButtonText(t("Choose File(s)")).setTooltip( + t("Import 5e.tools Data") ); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(input5eTools); b.onClick(() => input5eTools.click()); }); const importTetra = new Setting(importAdditional) - .setName("Import TetraCube Data") - .setDesc("Only import content that you own."); + .setName(t("Import TetraCube Data")) + .setDesc(t("Only import content that you own.")); const inputTetra = createEl("input", { attr: { type: "file", @@ -923,16 +924,16 @@ export default class StatblockSettingTab extends PluginSettingTab { this.display(); }; importTetra.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip( - "Import TetraCube Data" + b.setButtonText(t("Choose File(s)")).setTooltip( + t("Import TetraCube Data") ); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(inputTetra); b.onClick(() => inputTetra.click()); }); const importPF2EMonsterTools = new Setting(importAdditional) - .setName("Import PF2eMonsterTools Data") - .setDesc("Only import content that you own."); + .setName(t("Import PF2eMonsterTools Data")) + .setDesc(t("Only import content that you own.")); const inputPF2EMonsterTools = createEl("input", { attr: { type: "file", @@ -951,8 +952,8 @@ export default class StatblockSettingTab extends PluginSettingTab { this.display(); }; importPF2EMonsterTools.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip( - "Import PF2EMonsterTools Data" + b.setButtonText(t("Choose File(s)")).setTooltip( + t("Import PF2EMonsterTools Data") ); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(inputPF2EMonsterTools); @@ -960,8 +961,8 @@ export default class StatblockSettingTab extends PluginSettingTab { }); // import Pathbuilder const importPathbuilder = new Setting(importAdditional) - .setName("Import Pathbuilder Data") - .setDesc("Import a PC or NPC exported from Pathbuilder2e."); + .setName(t("Import Pathbuilder Data")) + .setDesc(t("Import a PC or NPC exported from Pathbuilder2e.")); const inputPathbuilder = createEl("input", { attr: { type: "file", @@ -980,8 +981,8 @@ export default class StatblockSettingTab extends PluginSettingTab { this.display(); }; importPathbuilder.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip( - "Import Pathbuilder Data" + b.setButtonText(t("Choose File(s)")).setTooltip( + t("Import Pathbuilder Data") ); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(inputPathbuilder); @@ -991,16 +992,16 @@ export default class StatblockSettingTab extends PluginSettingTab { const importGeneric = new Setting(importAdditional) - .setName("Import Generic Data") + .setName(t("Import Generic Data")) .setDesc( createFragment((e) => { e.createSpan({ - text: "Import generic JSON files. JSON objects will be imported " + text: t("Import generic JSON files. JSON objects will be imported ") }); e.createEl("strong", { text: "as-is" }); - e.createSpan({ text: " and all objects must have the " }); + e.createSpan({ text: t(" and all objects must have the ") }); e.createEl("code", { text: "name" }); - e.createSpan({ text: " property." }); + e.createSpan({ text: t(" property.") }); }) ); const inputGeneric = createEl("input", { @@ -1021,7 +1022,7 @@ export default class StatblockSettingTab extends PluginSettingTab { this.display(); }; importGeneric.addButton((b) => { - b.setButtonText("Choose File(s)").setTooltip("Import Generic Data"); + b.setButtonText(t("Choose File(s)")).setTooltip(t("Import Generic Data")); b.buttonEl.addClass("statblock-file-upload"); b.buttonEl.appendChild(inputGeneric); b.onClick(() => inputGeneric.click()); @@ -1029,12 +1030,12 @@ export default class StatblockSettingTab extends PluginSettingTab { } generateMonsters(containerEl: HTMLDivElement) { containerEl.empty(); - new Setting(containerEl).setHeading().setName("Bestiary"); + new Setting(containerEl).setHeading().setName(t("Bestiary")); const additionalContainer = containerEl.createDiv( "statblock-additional-container statblock-monsters" ); new Setting(additionalContainer) - .setName("Add Creature") + .setName(t("Add Creature")) .addButton((b) => { b.setIcon("plus-with-circle").onClick(() => { const modal = new EditMonsterModal(this.plugin); @@ -1069,7 +1070,7 @@ class CreateStatblockModal extends FantasyStatblockModal { constructor( public plugin: StatBlockPlugin, layout: Layout = { - name: "Layout", + name: t("Layout"), blocks: [], id: nanoid() } @@ -1085,7 +1086,7 @@ class CreateStatblockModal extends FantasyStatblockModal { } display() { - this.titleEl.createSpan({ text: "Create Layout" }); + this.titleEl.createSpan({ text: t("Create Layout") }); this.creator = new LayoutEditor({ target: this.contentEl, props: { @@ -1110,7 +1111,7 @@ class ConfirmModal extends FantasyStatblockModal { super(plugin); } onOpen() { - this.titleEl.setText("Are you sure?"); + this.titleEl.setText(t("Are you sure?")); this.contentEl.createEl("p", { text: `This will delete ${this.filtered} creatures. This cannot be undone.` }); @@ -1153,10 +1154,10 @@ class ConfirmImport extends FantasyStatblockModal { this.contentEl.empty(); this.contentEl.addClass("confirm-modal"); this.contentEl.createEl("p", { - text: "This Layout includes JavaScript blocks. JavaScript blocks can execute code in your vault, which could cause loss or corruption of data." + text: t("This Layout includes JavaScript blocks. JavaScript blocks can execute code in your vault, which could cause loss or corruption of data.") }); this.contentEl.createEl("p", { - text: "Are you sure you want to import this layout?" + text: t("Are you sure you want to import this layout?") }); const buttonContainerEl = this.contentEl.createDiv( @@ -1164,7 +1165,7 @@ class ConfirmImport extends FantasyStatblockModal { ); buttonContainerEl.createEl("a").createEl("small", { cls: "dont-ask", - text: "Import and don't ask again" + text: t("Import and don't ask again") }).onclick = async () => { this.confirmed = true; this.plugin.settings.alwaysImport = true; @@ -1173,7 +1174,7 @@ class ConfirmImport extends FantasyStatblockModal { const buttonEl = buttonContainerEl.createDiv("confirm-buttons"); new ButtonComponent(buttonEl) - .setButtonText("Import") + .setButtonText(t("Import")) .setCta() .onClick(() => { this.confirmed = true; @@ -1181,7 +1182,7 @@ class ConfirmImport extends FantasyStatblockModal { }); buttonEl.createEl("a").createEl("small", { cls: "dont-ask", - text: "Cancel" + text: t("Cancel") }).onclick = () => { this.close(); }; diff --git a/src/util/i18n.ts b/src/util/i18n.ts new file mode 100644 index 00000000..32aaecdc --- /dev/null +++ b/src/util/i18n.ts @@ -0,0 +1,22 @@ +import { moment } from "obsidian"; +import { _, register, init, getLocaleFromNavigator } from "svelte-i18n"; +import { get } from "svelte/store"; + +export function initI18n() { + const lang = moment.locale(); + + //register("en", () => import("./../data/translations/en.json")); + register("pt-br", () => import("./../data/translations/pt-br.json")); + + init({ + fallbackLocale: lang, + initialLocale: getLocaleFromNavigator(), + handleMissingMessage: ({ locale, id, defaultValue }) => { + if ("en" === lang) return; + console.log(`Missing translation: "${id}"`); + return defaultValue; + } + }); +} + +export const t = get(_); diff --git a/src/view/Statblock.svelte b/src/view/Statblock.svelte index cc9ffb40..b85b4dbf 100644 --- a/src/view/Statblock.svelte +++ b/src/view/Statblock.svelte @@ -21,6 +21,7 @@ import Bar from "./ui/Bar.svelte"; import ColumnContainer from "./ui/ColumnContainer.svelte"; + import { t } from "src/util/i18n"; import { slugifyLayoutForCss } from "src/util/util"; const dispatch = createEventDispatcher(); @@ -109,20 +110,20 @@ menu.addItem((item) => item .setIcon("save") - .setTitle("Save to Bestiary") + .setTitle(t("Save to Bestiary")) .setDisabled(!canSave) .onClick(() => dispatch("save")) ); menu.addItem((item) => { - item.setTitle("Copy YAML") + item.setTitle(t("Copy YAML")) .setIcon("code") .onClick(async () => { try { await navigator.clipboard.writeText(stringifyYaml(monster)); - new Notice("Creature YAML copied to clipboard"); + new Notice(t("Creature YAML copied to clipboard")); } catch (e: unknown) { new Notice( - `There was an issue copying the yaml:\n\n${(e as Error).message}` + `${t("There was an issue copying the yaml:")}\n\n${(e as Error).message}` ); } }); @@ -130,14 +131,14 @@ menu.addItem((item) => item .setIcon("image-down") - .setTitle("Export as PNG") + .setTitle(t("Export as PNG")) .onClick(() => dispatch("export")) ); if (canDice) menu.addItem((item) => item .setIcon("reset") - .setTitle("Reset Dice") + .setTitle(t("Reset Dice")) .onClick(() => { reset.set(true); reset.set(false); @@ -198,7 +199,7 @@ {/key} {:else} - Invalid monster. + {t("Invalid monster.")} {/if} {/key}
diff --git a/src/view/statblock.ts b/src/view/statblock.ts index f47b6cd1..e637c6e0 100644 --- a/src/view/statblock.ts +++ b/src/view/statblock.ts @@ -26,6 +26,7 @@ import type { StatblockItem } from "src/layouts/layout.types"; import { append } from "src/util/util"; +import { t } from "src/util/i18n"; import { Linkifier } from "src/parser/linkify"; import { Bestiary } from "src/bestiary/bestiary"; import copy from "fast-copy"; @@ -391,13 +392,13 @@ export default class StatBlockRenderer extends MarkdownRenderChild { Bestiary.hasCreature(this.monster.name) && !(await confirmWithModal( this.plugin.app, - "This will overwrite an existing monster in settings. Are you sure?" + t("This will overwrite an existing monster in settings. Are you sure?") )) ) return; this.plugin.saveMonster({ ...fastCopy(this.monster), - source: this.monster.source ?? "Homebrew", + source: this.monster.source ?? t("Homebrew"), layout: this.layout.name } as Monster); }); @@ -440,8 +441,8 @@ export async function confirmWithModal( app: App, text: string, buttons: { cta: string; secondary: string } = { - cta: "Yes", - secondary: "No" + cta: t("Yes"), + secondary: t("No") } ): Promise { return new Promise((resolve, reject) => { diff --git a/src/view/ui/Action.svelte b/src/view/ui/Action.svelte index 1c1a9cf9..1891f6a3 100644 --- a/src/view/ui/Action.svelte +++ b/src/view/ui/Action.svelte @@ -1,5 +1,6 @@ diff --git a/src/view/ui/Table.svelte b/src/view/ui/Table.svelte index 7f9ff9f7..5cba7d1f 100644 --- a/src/view/ui/Table.svelte +++ b/src/view/ui/Table.svelte @@ -1,6 +1,7 @@