Merge pull request #248 from TriliumNext/feature/i18n_first_steps

i18n: First steps
This commit is contained in:
Elian Doran 2024-07-23 23:27:45 +03:00 committed by GitHub
commit b9a2be6808
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 147 additions and 12 deletions

5
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"recommendations": [
"lokalise.i18n-ally"
]
}

30
.vscode/i18n-ally-custom-framework.yml vendored Normal file
View File

@ -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

View File

@ -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"
],
}

44
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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;

View File

@ -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
}

View File

@ -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 = `
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title mr-auto">About TriliumNext Notes</h5>
<h5 class="modal-title mr-auto">${t("about.title")}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" style="margin-left: 0;">
<span aria-hidden="true">&times;</span>
@ -16,33 +17,33 @@ const TPL = `
<div class="modal-body">
<table class="table table-borderless">
<tr>
<th>Homepage:</th>
<th>${t("about.homepage")}</th>
<td><a href="https://github.com/TriliumNext/Notes" class="external">https://github.com/TriliumNext/Notes</a></td>
</tr>
<tr>
<th>App version:</th>
<th>${t("about.app_version")}</th>
<td class="app-version"></td>
</tr>
<tr>
<th>DB version:</th>
<th>${t("about.db_version")}</th>
<td class="db-version"></td>
</tr>
<tr>
<th>Sync version:</th>
<th>${t("about.sync_version")}</th>
<td class="sync-version"></td>
</tr>
<tr>
<th>Build date:</th>
<th>${t("about.build_date")}</th>
<td class="build-date"></td>
</tr>
<tr>
<th>Build revision:</th>
<th>${t("about.build_revision")}</th>
<td><a href="" class="build-revision external" target="_blank"></a></td>
</tr>
<tr>
<th>Data directory:</th>
<th>${t("about.data_directory")}</th>
<td class="data-directory"></td>
</tr>
</table>

View File

@ -0,0 +1,12 @@
{
"about": {
"title": "About TriliumNext Notes",
"homepage": "Homepage:",
"app_version": "App version:",
"db_version": "DB version:",
"sync_version": "Sync version:",
"build_date": "Build date:",
"build_revision": "Build revision:",
"data_directory": "Data directory:"
}
}

View File

@ -0,0 +1,12 @@
{
"about": {
"title": "Despre TriliumNext Notes",
"homepage": "Site web:",
"app_version": "Versiune aplicație:",
"db_version": "Versiune bază de date:",
"sync_version": "Versiune sincronizare:",
"build_date": "Data compilării:",
"build_revision": "Revizia compilării:",
"data_directory": "Directorul de date:"
}
}

View File

@ -69,6 +69,11 @@ function register(app: express.Application) {
app.use(`/${assetPath}/node_modules/split.js/dist/`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/split.js/dist/')));
app.use(`/${assetPath}/node_modules/panzoom/dist/`, persistentCacheStatic(path.join(srcRoot, '..', 'node_modules/panzoom/dist/')));
// i18n
app.use(`/${assetPath}/node_modules/i18next/`, persistentCacheStatic(path.join(srcRoot, "..", 'node_modules/i18next/')));
app.use(`/${assetPath}/node_modules/i18next-http-backend/`, persistentCacheStatic(path.join(srcRoot, "..", 'node_modules/i18next-http-backend/')));
app.use(`/${assetPath}/translations/`, persistentCacheStatic(path.join(srcRoot, "public", "translations/")));
}
export = {