diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..6778438354 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "lokalise.i18n-ally" + ] +} \ No newline at end of file diff --git a/.vscode/i18n-ally-custom-framework.yml b/.vscode/i18n-ally-custom-framework.yml new file mode 100644 index 0000000000..aeba9668ce --- /dev/null +++ b/.vscode/i18n-ally-custom-framework.yml @@ -0,0 +1,30 @@ +# An array of strings which contain Language Ids defined by VS Code +# You can check available language ids here: https://code.visualstudio.com/docs/languages/identifiers +languageIds: + - javascript + - typescript + +# An array of RegExes to find the key usage. **The key should be captured in the first match group**. +# You should unescape RegEx strings in order to fit in the YAML file +# To help with this, you can use https://www.freeformatter.com/json-escape.html +usageMatchRegex: + # The following example shows how to detect `t("your.i18n.keys")` + # the `{key}` will be placed by a proper keypath matching regex, + # you can ignore it and use your own matching rules as well + - "[^\\w\\d]t\\(['\"`]({key})['\"`]" + +# A RegEx to set a custom scope range. This scope will be used as a prefix when detecting keys +# and works like how the i18next framework identifies the namespace scope from the +# useTranslation() hook. +# You should unescape RegEx strings in order to fit in the YAML file +# To help with this, you can use https://www.freeformatter.com/json-escape.html +scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]" + +# An array of strings containing refactor templates. +# The "$1" will be replaced by the keypath specified. +refactorTemplates: + - t("$1") + + +# If set to true, only enables this custom framework (will disable all built-in frameworks) +monopoly: true \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 1415c74fa5..01d9b230e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,10 @@ "editor.formatOnSave": false, "editor.defaultFormatter": "esbenp.prettier-vscode", "files.eol": "\n", - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "i18n-ally.sourceLanguage": "en", + "i18n-ally.keystyle": "nested", + "i18n-ally.localesPaths": [ + "./src/public/translations" + ], } diff --git a/package-lock.json b/package-lock.json index 89c0286699..bf5b43e1f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "trilium", - "version": "0.90.0-beta", + "version": "0.90.1-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "trilium", - "version": "0.90.0-beta", + "version": "0.90.1-beta", "license": "AGPL-3.0-only", "dependencies": { "@braintree/sanitize-url": "^7.1.0", @@ -42,6 +42,8 @@ "html2plaintext": "2.1.4", "http-proxy-agent": "7.0.2", "https-proxy-agent": "^7.0.5", + "i18next": "^23.12.2", + "i18next-http-backend": "^2.5.2", "image-type": "4.1.0", "ini": "^4.1.3", "is-animated": "2.0.2", @@ -4767,6 +4769,14 @@ "yarn": ">=1" } }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7891,6 +7901,36 @@ "ms": "^2.0.0" } }, + "node_modules/i18next": { + "version": "23.12.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.12.2.tgz", + "integrity": "sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-http-backend": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.2.tgz", + "integrity": "sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==", + "dependencies": { + "cross-fetch": "4.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", diff --git a/package.json b/package.json index 36597be6e6..9383b0a642 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,8 @@ "html2plaintext": "2.1.4", "http-proxy-agent": "7.0.2", "https-proxy-agent": "^7.0.5", + "i18next": "^23.12.2", + "i18next-http-backend": "^2.5.2", "image-type": "4.1.0", "ini": "^4.1.3", "is-animated": "2.0.2", diff --git a/src/public/app/services/i18n.js b/src/public/app/services/i18n.js new file mode 100644 index 0000000000..0c9a52a0da --- /dev/null +++ b/src/public/app/services/i18n.js @@ -0,0 +1,15 @@ +import library_loader from "./library_loader.js"; + +await library_loader.requireLibrary(library_loader.I18NEXT); + +await i18next + .use(i18nextHttpBackend) + .init({ + lng: "en", + debug: true, + backend: { + loadPath: `/${window.glob.assetPath}/translations/{{lng}}/{{ns}}.json` + } + }); + +export const t = i18next.t; \ No newline at end of file diff --git a/src/public/app/services/library_loader.js b/src/public/app/services/library_loader.js index 6860795ca5..ee19a55bf7 100644 --- a/src/public/app/services/library_loader.js +++ b/src/public/app/services/library_loader.js @@ -72,6 +72,13 @@ const MARKJS = { ] }; +const I18NEXT = { + js: [ + "node_modules/i18next/i18next.min.js", + "node_modules/i18next-http-backend/i18nextHttpBackend.min.js" + ] +}; + async function requireLibrary(library) { if (library.css) { library.css.map(cssUrl => requireCss(cssUrl)); @@ -129,5 +136,6 @@ export default { FORCE_GRAPH, MERMAID, EXCALIDRAW, - MARKJS + MARKJS, + I18NEXT } diff --git a/src/public/app/widgets/dialogs/about.js b/src/public/app/widgets/dialogs/about.js index 9bf98e098f..61241d66f3 100644 --- a/src/public/app/widgets/dialogs/about.js +++ b/src/public/app/widgets/dialogs/about.js @@ -1,5 +1,6 @@ import server from "../../services/server.js"; import utils from "../../services/utils.js"; +import { t } from "../../services/i18n.js"; import BasicWidget from "../basic_widget.js"; const TPL = ` @@ -7,7 +8,7 @@ const TPL = `