mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-13 11:05:03 +00:00
React translations
This commit is contained in:
parent
5f862f55d9
commit
09f05ac8d0
2
frontend/dist/assets/browser-ponyfill-CwETHAJL.js
vendored
Normal file
2
frontend/dist/assets/browser-ponyfill-CwETHAJL.js
vendored
Normal file
File diff suppressed because one or more lines are too long
99
frontend/dist/assets/index-C7ZkpjP3.js
vendored
Normal file
99
frontend/dist/assets/index-C7ZkpjP3.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
99
frontend/dist/assets/index-DAILjWej.js
vendored
99
frontend/dist/assets/index-DAILjWej.js
vendored
File diff suppressed because one or more lines are too long
4
frontend/dist/index.html
vendored
4
frontend/dist/index.html
vendored
@ -13,8 +13,8 @@
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<title>Vite App</title>
|
||||
<script type="module" crossorigin src="/assets/index-DAILjWej.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DxdJ27yt.css">
|
||||
<script type="module" crossorigin src="/assets/index-C7ZkpjP3.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CEZrmp6h.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
1561
frontend/dist/locales/ar-AR/translation.json
vendored
Normal file
1561
frontend/dist/locales/ar-AR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/az-AZ/translation.json
vendored
Normal file
1561
frontend/dist/locales/az-AZ/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/bg-BG/translation.json
vendored
Normal file
1561
frontend/dist/locales/bg-BG/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/ca-CA/translation.json
vendored
Normal file
1561
frontend/dist/locales/ca-CA/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/cs-CZ/translation.json
vendored
Normal file
1561
frontend/dist/locales/cs-CZ/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/da-DK/translation.json
vendored
Normal file
1561
frontend/dist/locales/da-DK/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/de-DE/translation.json
vendored
Normal file
1561
frontend/dist/locales/de-DE/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/el-GR/translation.json
vendored
Normal file
1561
frontend/dist/locales/el-GR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1620
frontend/dist/locales/en-GB/translation.json
vendored
Normal file
1620
frontend/dist/locales/en-GB/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/en-US/translation.json
vendored
Normal file
1561
frontend/dist/locales/en-US/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
frontend/dist/locales/en/translation.json
vendored
Normal file
1
frontend/dist/locales/en/translation.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1561
frontend/dist/locales/es-ES/translation.json
vendored
Normal file
1561
frontend/dist/locales/es-ES/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/eu-ES/translation.json
vendored
Normal file
1561
frontend/dist/locales/eu-ES/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/fa-IR/translation.json
vendored
Normal file
1561
frontend/dist/locales/fa-IR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/fr-FR/translation.json
vendored
Normal file
1561
frontend/dist/locales/fr-FR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/ga-IE/translation.json
vendored
Normal file
1561
frontend/dist/locales/ga-IE/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/hi-IN/translation.json
vendored
Normal file
1561
frontend/dist/locales/hi-IN/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/hr-HR/translation.json
vendored
Normal file
1561
frontend/dist/locales/hr-HR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/hu-HU/translation.json
vendored
Normal file
1561
frontend/dist/locales/hu-HU/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/id-ID/translation.json
vendored
Normal file
1561
frontend/dist/locales/id-ID/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/it-IT/translation.json
vendored
Normal file
1561
frontend/dist/locales/it-IT/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/ja-JP/translation.json
vendored
Normal file
1561
frontend/dist/locales/ja-JP/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/ko-KR/translation.json
vendored
Normal file
1561
frontend/dist/locales/ko-KR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/ml-ML/translation.json
vendored
Normal file
1561
frontend/dist/locales/ml-ML/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/nl-NL/translation.json
vendored
Normal file
1561
frontend/dist/locales/nl-NL/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/no-NB/translation.json
vendored
Normal file
1561
frontend/dist/locales/no-NB/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/pl-PL/translation.json
vendored
Normal file
1561
frontend/dist/locales/pl-PL/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/pt-BR/translation.json
vendored
Normal file
1561
frontend/dist/locales/pt-BR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/pt-PT/translation.json
vendored
Normal file
1561
frontend/dist/locales/pt-PT/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/ro-RO/translation.json
vendored
Normal file
1561
frontend/dist/locales/ro-RO/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/ru-RU/translation.json
vendored
Normal file
1561
frontend/dist/locales/ru-RU/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/sk-SK/translation.json
vendored
Normal file
1561
frontend/dist/locales/sk-SK/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/sl-SI/translation.json
vendored
Normal file
1561
frontend/dist/locales/sl-SI/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/sr-LATN-RS/translation.json
vendored
Normal file
1561
frontend/dist/locales/sr-LATN-RS/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/sv-SE/translation.json
vendored
Normal file
1561
frontend/dist/locales/sv-SE/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/th-TH/translation.json
vendored
Normal file
1561
frontend/dist/locales/th-TH/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/tr-TR/translation.json
vendored
Normal file
1561
frontend/dist/locales/tr-TR/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/uk-UA/translation.json
vendored
Normal file
1561
frontend/dist/locales/uk-UA/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/vi-VN/translation.json
vendored
Normal file
1561
frontend/dist/locales/vi-VN/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/zh-BO/translation.json
vendored
Normal file
1561
frontend/dist/locales/zh-BO/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/zh-CN/translation.json
vendored
Normal file
1561
frontend/dist/locales/zh-CN/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/dist/locales/zh-TW/translation.json
vendored
Normal file
1561
frontend/dist/locales/zh-TW/translation.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
109
frontend/package-lock.json
generated
109
frontend/package-lock.json
generated
@ -22,9 +22,13 @@
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"axios": "^1.9.0",
|
||||
"i18next": "^25.2.1",
|
||||
"i18next-browser-languagedetector": "^8.1.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"pdfjs-dist": "^3.11.174",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^15.5.2",
|
||||
"react-router-dom": "^7.6.0",
|
||||
"tailwindcss": "^4.1.8",
|
||||
"web-vitals": "^2.1.4"
|
||||
@ -2693,6 +2697,14 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"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/css.escape": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
|
||||
@ -3353,6 +3365,14 @@
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/html-parse-stringify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||
"dependencies": {
|
||||
"void-elements": "3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
@ -3367,6 +3387,52 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "25.2.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.2.1.tgz",
|
||||
"integrity": "sha512-+UoXK5wh+VlE1Zy5p6MjcvctHXAhRwQKCxiJD8noKZzIXmnAX8gdHX5fLPA3MEVxEN4vbZkQFy8N0LyD9tUqPw==",
|
||||
"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.27.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-browser-languagedetector": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.1.0.tgz",
|
||||
"integrity": "sha512-mHZxNx1Lq09xt5kCauZ/4bsXOEA2pfpwSoU11/QTJB+pD94iONFwp+ohqi///PwiFvjFOxe1akYCdHyFo1ng5Q==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.2"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-http-backend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz",
|
||||
"integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
@ -4036,7 +4102,6 @@
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
@ -4056,22 +4121,19 @@
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
@ -4711,6 +4773,31 @@
|
||||
"react": ">= 16.8 || 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "15.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.2.tgz",
|
||||
"integrity": "sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.0",
|
||||
"html-parse-stringify": "^3.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"i18next": ">= 23.2.3",
|
||||
"react": ">= 16.8.0",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
|
||||
@ -5430,7 +5517,7 @@
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@ -5678,6 +5765,14 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/void-elements": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/web-vitals": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz",
|
||||
|
@ -18,9 +18,13 @@
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"axios": "^1.9.0",
|
||||
"i18next": "^25.2.1",
|
||||
"i18next-browser-languagedetector": "^8.1.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"pdfjs-dist": "^3.11.174",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^15.5.2",
|
||||
"react-router-dom": "^7.6.0",
|
||||
"tailwindcss": "^4.1.8",
|
||||
"web-vitals": "^2.1.4"
|
||||
|
1561
frontend/public/locales/ar-AR/translation.json
Normal file
1561
frontend/public/locales/ar-AR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/az-AZ/translation.json
Normal file
1561
frontend/public/locales/az-AZ/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/bg-BG/translation.json
Normal file
1561
frontend/public/locales/bg-BG/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/ca-CA/translation.json
Normal file
1561
frontend/public/locales/ca-CA/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/cs-CZ/translation.json
Normal file
1561
frontend/public/locales/cs-CZ/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/da-DK/translation.json
Normal file
1561
frontend/public/locales/da-DK/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/de-DE/translation.json
Normal file
1561
frontend/public/locales/de-DE/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/el-GR/translation.json
Normal file
1561
frontend/public/locales/el-GR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1620
frontend/public/locales/en-GB/translation.json
Normal file
1620
frontend/public/locales/en-GB/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/en-US/translation.json
Normal file
1561
frontend/public/locales/en-US/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1
frontend/public/locales/en/translation.json
Normal file
1
frontend/public/locales/en/translation.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1561
frontend/public/locales/es-ES/translation.json
Normal file
1561
frontend/public/locales/es-ES/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/eu-ES/translation.json
Normal file
1561
frontend/public/locales/eu-ES/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/fa-IR/translation.json
Normal file
1561
frontend/public/locales/fa-IR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/fr-FR/translation.json
Normal file
1561
frontend/public/locales/fr-FR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/ga-IE/translation.json
Normal file
1561
frontend/public/locales/ga-IE/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/hi-IN/translation.json
Normal file
1561
frontend/public/locales/hi-IN/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/hr-HR/translation.json
Normal file
1561
frontend/public/locales/hr-HR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/hu-HU/translation.json
Normal file
1561
frontend/public/locales/hu-HU/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/id-ID/translation.json
Normal file
1561
frontend/public/locales/id-ID/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/it-IT/translation.json
Normal file
1561
frontend/public/locales/it-IT/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/ja-JP/translation.json
Normal file
1561
frontend/public/locales/ja-JP/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/ko-KR/translation.json
Normal file
1561
frontend/public/locales/ko-KR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/ml-ML/translation.json
Normal file
1561
frontend/public/locales/ml-ML/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/nl-NL/translation.json
Normal file
1561
frontend/public/locales/nl-NL/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/no-NB/translation.json
Normal file
1561
frontend/public/locales/no-NB/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/pl-PL/translation.json
Normal file
1561
frontend/public/locales/pl-PL/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/pt-BR/translation.json
Normal file
1561
frontend/public/locales/pt-BR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/pt-PT/translation.json
Normal file
1561
frontend/public/locales/pt-PT/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/ro-RO/translation.json
Normal file
1561
frontend/public/locales/ro-RO/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/ru-RU/translation.json
Normal file
1561
frontend/public/locales/ru-RU/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/sk-SK/translation.json
Normal file
1561
frontend/public/locales/sk-SK/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/sl-SI/translation.json
Normal file
1561
frontend/public/locales/sl-SI/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/sr-LATN-RS/translation.json
Normal file
1561
frontend/public/locales/sr-LATN-RS/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/sv-SE/translation.json
Normal file
1561
frontend/public/locales/sv-SE/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/th-TH/translation.json
Normal file
1561
frontend/public/locales/th-TH/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/tr-TR/translation.json
Normal file
1561
frontend/public/locales/tr-TR/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/uk-UA/translation.json
Normal file
1561
frontend/public/locales/uk-UA/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/vi-VN/translation.json
Normal file
1561
frontend/public/locales/vi-VN/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/zh-BO/translation.json
Normal file
1561
frontend/public/locales/zh-BO/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/zh-CN/translation.json
Normal file
1561
frontend/public/locales/zh-CN/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
1561
frontend/public/locales/zh-TW/translation.json
Normal file
1561
frontend/public/locales/zh-TW/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,10 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Card, Group, Text, Stack, Image, Badge, Button, Box, Flex, ThemeIcon } from "@mantine/core";
|
||||
import { Dropzone, MIME_TYPES } from "@mantine/dropzone";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf";
|
||||
|
||||
import { GlobalWorkerOptions } from "pdfjs-dist";
|
||||
import { GlobalWorkerOptions, getDocument } from "pdfjs-dist";
|
||||
GlobalWorkerOptions.workerSrc = "/pdf.worker.js";
|
||||
|
||||
export interface FileWithUrl extends File {
|
||||
@ -63,6 +64,7 @@ interface FileCardProps {
|
||||
}
|
||||
|
||||
function FileCard({ file, onRemove, onDoubleClick }: FileCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const thumb = usePdfThumbnail(file);
|
||||
|
||||
return (
|
||||
@ -120,7 +122,7 @@ function FileCard({ file, onRemove, onDoubleClick }: FileCardProps) {
|
||||
onClick={onRemove}
|
||||
mt={4}
|
||||
>
|
||||
Remove
|
||||
{t("delete", "Remove")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
@ -142,6 +144,7 @@ const FileManager: React.FC<FileManagerProps> = ({
|
||||
setPdfFile,
|
||||
setCurrentView,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const handleDrop = (uploadedFiles: File[]) => {
|
||||
setFiles((prevFiles) => (allowMultiple ? [...prevFiles, ...uploadedFiles] : uploadedFiles));
|
||||
};
|
||||
@ -171,13 +174,13 @@ const FileManager: React.FC<FileManagerProps> = ({
|
||||
>
|
||||
<Group justify="center" gap="xl" style={{ pointerEvents: "none" }}>
|
||||
<Text size="md">
|
||||
Drag PDF files here or click to select
|
||||
{t("fileChooser.dragAndDropPDF", "Drag PDF files here or click to select")}
|
||||
</Text>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
{files.length === 0 ? (
|
||||
<Text c="dimmed" ta="center">
|
||||
No files uploaded yet.
|
||||
{t("noFileSelected", "No files uploaded yet.")}
|
||||
</Text>
|
||||
) : (
|
||||
<Box>
|
||||
|
71
frontend/src/components/LanguageSelector.module.css
Normal file
71
frontend/src/components/LanguageSelector.module.css
Normal file
@ -0,0 +1,71 @@
|
||||
/* Language selector grid responsive layout */
|
||||
.languageGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.languageItem {
|
||||
border-right: 2px solid var(--mantine-color-gray-3);
|
||||
}
|
||||
|
||||
.languageItem:nth-child(4n) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* Responsive breakpoints */
|
||||
@media (max-width: 600px) {
|
||||
.languageGrid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.languageItem:nth-child(4n) {
|
||||
border-right: 2px solid var(--mantine-color-gray-3);
|
||||
}
|
||||
|
||||
.languageItem:nth-child(2n) {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 601px) and (max-width: 900px) {
|
||||
.languageGrid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.languageItem:nth-child(4n) {
|
||||
border-right: 2px solid var(--mantine-color-gray-3);
|
||||
}
|
||||
|
||||
.languageItem:nth-child(3n) {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark theme support */
|
||||
[data-mantine-color-scheme="dark"] .languageItem {
|
||||
border-right-color: var(--mantine-color-dark-4);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .languageItem:nth-child(4n) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .languageItem:nth-child(2n) {
|
||||
border-right-color: var(--mantine-color-dark-4);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .languageItem:nth-child(3n) {
|
||||
border-right-color: var(--mantine-color-dark-4);
|
||||
}
|
||||
|
||||
/* Responsive text visibility */
|
||||
.languageText {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.languageText {
|
||||
display: inline;
|
||||
}
|
||||
}
|
125
frontend/src/components/LanguageSelector.tsx
Normal file
125
frontend/src/components/LanguageSelector.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Menu, Button, ScrollArea, useMantineTheme, useMantineColorScheme } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { supportedLanguages } from '../i18n';
|
||||
import LanguageIcon from '@mui/icons-material/Language';
|
||||
import styles from './LanguageSelector.module.css';
|
||||
|
||||
const LanguageSelector: React.FC = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
const languageOptions = Object.entries(supportedLanguages)
|
||||
.sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB))
|
||||
.map(([code, name]) => ({
|
||||
value: code,
|
||||
label: name,
|
||||
}));
|
||||
|
||||
const handleLanguageChange = (value: string) => {
|
||||
i18n.changeLanguage(value);
|
||||
setOpened(false);
|
||||
};
|
||||
|
||||
const currentLanguage = supportedLanguages[i18n.language as keyof typeof supportedLanguages] ||
|
||||
supportedLanguages['en-GB'];
|
||||
|
||||
return (
|
||||
<Menu
|
||||
opened={opened}
|
||||
onChange={setOpened}
|
||||
width={600}
|
||||
position="bottom-start"
|
||||
offset={8}
|
||||
>
|
||||
<Menu.Target>
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="sm"
|
||||
leftSection={<LanguageIcon style={{ fontSize: 18 }} />}
|
||||
styles={{
|
||||
root: {
|
||||
border: 'none',
|
||||
color: colorScheme === 'dark' ? theme.colors.gray[3] : theme.colors.gray[7],
|
||||
'&:hover': {
|
||||
backgroundColor: colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
||||
}
|
||||
},
|
||||
label: {
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className={styles.languageText}>
|
||||
{currentLanguage}
|
||||
</span>
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
|
||||
<Menu.Dropdown
|
||||
style={{
|
||||
padding: '12px',
|
||||
borderRadius: '8px',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
|
||||
border: colorScheme === 'dark' ? `1px solid ${theme.colors.dark[4]}` : `1px solid ${theme.colors.gray[3]}`,
|
||||
}}
|
||||
>
|
||||
<ScrollArea h={190} type="scroll">
|
||||
<div className={styles.languageGrid}>
|
||||
{languageOptions.map((option) => (
|
||||
<div
|
||||
key={option.value}
|
||||
className={styles.languageItem}
|
||||
>
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="sm"
|
||||
fullWidth
|
||||
onClick={() => handleLanguageChange(option.value)}
|
||||
styles={{
|
||||
root: {
|
||||
borderRadius: '4px',
|
||||
minHeight: '32px',
|
||||
padding: '4px 8px',
|
||||
justifyContent: 'flex-start',
|
||||
backgroundColor: option.value === i18n.language ? (
|
||||
colorScheme === 'dark' ? theme.colors.blue[8] : theme.colors.blue[1]
|
||||
) : 'transparent',
|
||||
color: option.value === i18n.language ? (
|
||||
colorScheme === 'dark' ? theme.colors.blue[2] : theme.colors.blue[7]
|
||||
) : (
|
||||
colorScheme === 'dark' ? theme.colors.gray[3] : theme.colors.gray[7]
|
||||
),
|
||||
'&:hover': {
|
||||
backgroundColor: option.value === i18n.language ? (
|
||||
colorScheme === 'dark' ? theme.colors.blue[7] : theme.colors.blue[2]
|
||||
) : (
|
||||
colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1]
|
||||
),
|
||||
}
|
||||
},
|
||||
label: {
|
||||
fontSize: '13px',
|
||||
fontWeight: option.value === i18n.language ? 600 : 400,
|
||||
textAlign: 'left',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageSelector;
|
@ -2,6 +2,7 @@ import React, { useState } from "react";
|
||||
import {
|
||||
Paper, Button, Group, Text, Stack, Center, Checkbox, ScrollArea, Box, Tooltip, ActionIcon, Notification
|
||||
} from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import UndoIcon from "@mui/icons-material/Undo";
|
||||
import RedoIcon from "@mui/icons-material/Redo";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
@ -28,6 +29,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
downloadUrl,
|
||||
setDownloadUrl,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [selectedPages, setSelectedPages] = useState<number[]>([]);
|
||||
const [status, setStatus] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@ -61,20 +63,20 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
};
|
||||
|
||||
// Example action handlers (replace with real API calls)
|
||||
const handleRotateLeft = () => setStatus("Rotated left: " + selectedPages.join(", "));
|
||||
const handleRotateRight = () => setStatus("Rotated right: " + selectedPages.join(", "));
|
||||
const handleDelete = () => setStatus("Deleted: " + selectedPages.join(", "));
|
||||
const handleMoveLeft = () => setStatus("Moved left: " + selectedPages.join(", "));
|
||||
const handleMoveRight = () => setStatus("Moved right: " + selectedPages.join(", "));
|
||||
const handleSplit = () => setStatus("Split at: " + selectedPages.join(", "));
|
||||
const handleInsertPageBreak = () => setStatus("Inserted page break at: " + selectedPages.join(", "));
|
||||
const handleAddFile = () => setStatus("Add file not implemented in demo");
|
||||
const handleRotateLeft = () => setStatus(t("pageEditor.rotatedLeft", "Rotated left: ") + selectedPages.join(", "));
|
||||
const handleRotateRight = () => setStatus(t("pageEditor.rotatedRight", "Rotated right: ") + selectedPages.join(", "));
|
||||
const handleDelete = () => setStatus(t("pageEditor.deleted", "Deleted: ") + selectedPages.join(", "));
|
||||
const handleMoveLeft = () => setStatus(t("pageEditor.movedLeft", "Moved left: ") + selectedPages.join(", "));
|
||||
const handleMoveRight = () => setStatus(t("pageEditor.movedRight", "Moved right: ") + selectedPages.join(", "));
|
||||
const handleSplit = () => setStatus(t("pageEditor.splitAt", "Split at: ") + selectedPages.join(", "));
|
||||
const handleInsertPageBreak = () => setStatus(t("pageEditor.insertedPageBreak", "Inserted page break at: ") + selectedPages.join(", "));
|
||||
const handleAddFile = () => setStatus(t("pageEditor.addFileNotImplemented", "Add file not implemented in demo"));
|
||||
|
||||
if (!file) {
|
||||
return (
|
||||
<Paper shadow="xs" radius="md" p="md">
|
||||
<Center>
|
||||
<Text color="dimmed">No PDF loaded. Please upload a PDF to edit.</Text>
|
||||
<Text color="dimmed">{t("pageEditor.noPdfLoaded", "No PDF loaded. Please upload a PDF to edit.")}</Text>
|
||||
</Center>
|
||||
</Paper>
|
||||
);
|
||||
@ -85,14 +87,14 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
<Group align="flex-start" gap="lg">
|
||||
{/* Sidebar */}
|
||||
<Stack w={180} gap="xs">
|
||||
<Text fw={600} size="lg">PDF Multitool</Text>
|
||||
<Button onClick={selectAll} fullWidth variant="light">Select All</Button>
|
||||
<Button onClick={deselectAll} fullWidth variant="light">Deselect All</Button>
|
||||
<Button onClick={handleUndo} leftSection={<UndoIcon fontSize="small" />} fullWidth disabled={undoStack.length === 0}>Undo</Button>
|
||||
<Button onClick={handleRedo} leftSection={<RedoIcon fontSize="small" />} fullWidth disabled={redoStack.length === 0}>Redo</Button>
|
||||
<Button onClick={handleAddFile} leftSection={<AddIcon fontSize="small" />} fullWidth>Add File</Button>
|
||||
<Button onClick={handleInsertPageBreak} leftSection={<ContentCutIcon fontSize="small" />} fullWidth>Insert Page Break</Button>
|
||||
<Button onClick={handleSplit} leftSection={<ContentCutIcon fontSize="small" />} fullWidth>Split</Button>
|
||||
<Text fw={600} size="lg">{t("pageEditor.title", "PDF Multitool")}</Text>
|
||||
<Button onClick={selectAll} fullWidth variant="light">{t("multiTool.selectAll", "Select All")}</Button>
|
||||
<Button onClick={deselectAll} fullWidth variant="light">{t("multiTool.deselectAll", "Deselect All")}</Button>
|
||||
<Button onClick={handleUndo} leftSection={<UndoIcon fontSize="small" />} fullWidth disabled={undoStack.length === 0}>{t("multiTool.undo", "Undo")}</Button>
|
||||
<Button onClick={handleRedo} leftSection={<RedoIcon fontSize="small" />} fullWidth disabled={redoStack.length === 0}>{t("multiTool.redo", "Redo")}</Button>
|
||||
<Button onClick={handleAddFile} leftSection={<AddIcon fontSize="small" />} fullWidth>{t("multiTool.addFile", "Add File")}</Button>
|
||||
<Button onClick={handleInsertPageBreak} leftSection={<ContentCutIcon fontSize="small" />} fullWidth>{t("multiTool.insertPageBreak", "Insert Page Break")}</Button>
|
||||
<Button onClick={handleSplit} leftSection={<ContentCutIcon fontSize="small" />} fullWidth>{t("multiTool.split", "Split")}</Button>
|
||||
<Button
|
||||
component="a"
|
||||
href={downloadUrl || "#"}
|
||||
@ -103,7 +105,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
variant="light"
|
||||
disabled={!downloadUrl}
|
||||
>
|
||||
Download All
|
||||
{t("multiTool.downloadAll", "Download All")}
|
||||
</Button>
|
||||
<Button
|
||||
component="a"
|
||||
@ -115,7 +117,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
variant="light"
|
||||
disabled={!downloadUrl || selectedPages.length === 0}
|
||||
>
|
||||
Download Selected
|
||||
{t("multiTool.downloadSelected", "Download Selected")}
|
||||
</Button>
|
||||
<Button
|
||||
color="red"
|
||||
@ -123,34 +125,34 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
onClick={() => setFile && setFile(null)}
|
||||
fullWidth
|
||||
>
|
||||
Close PDF
|
||||
{t("pageEditor.closePdf", "Close PDF")}
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
{/* Main multitool area */}
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Group mb="sm">
|
||||
<Tooltip label="Rotate Left">
|
||||
<Tooltip label={t("multiTool.rotateLeft", "Rotate Left")}>
|
||||
<ActionIcon onClick={handleRotateLeft} disabled={selectedPages.length === 0} color="blue" variant="light">
|
||||
<RotateLeftIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Rotate Right">
|
||||
<Tooltip label={t("multiTool.rotateRight", "Rotate Right")}>
|
||||
<ActionIcon onClick={handleRotateRight} disabled={selectedPages.length === 0} color="blue" variant="light">
|
||||
<RotateRightIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Delete">
|
||||
<Tooltip label={t("delete", "Delete")}>
|
||||
<ActionIcon onClick={handleDelete} disabled={selectedPages.length === 0} color="red" variant="light">
|
||||
<DeleteIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Move Left">
|
||||
<Tooltip label={t("multiTool.moveLeft", "Move Left")}>
|
||||
<ActionIcon onClick={handleMoveLeft} disabled={selectedPages.length === 0} color="gray" variant="light">
|
||||
<ArrowBackIosNewIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Move Right">
|
||||
<Tooltip label={t("multiTool.moveRight", "Move Right")}>
|
||||
<ActionIcon onClick={handleMoveRight} disabled={selectedPages.length === 0} color="gray" variant="light">
|
||||
<ArrowForwardIosIcon />
|
||||
</ActionIcon>
|
||||
@ -163,7 +165,7 @@ const PageEditor: React.FC<PageEditorProps> = ({
|
||||
<Checkbox
|
||||
checked={selectedPages.includes(page)}
|
||||
onChange={() => togglePage(page)}
|
||||
label={`Page ${page}`}
|
||||
label={t("page", "Page") + ` ${page}`}
|
||||
/>
|
||||
<Box
|
||||
w={60}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { Box, Text, Stack, Button, TextInput, Group } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Tool = {
|
||||
icon: React.ReactNode;
|
||||
@ -17,6 +18,7 @@ interface ToolPickerProps {
|
||||
}
|
||||
|
||||
const ToolPicker: React.FC<ToolPickerProps> = ({ selectedToolKey, onSelect, toolRegistry }) => {
|
||||
const { t } = useTranslation();
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const filteredTools = Object.entries(toolRegistry).filter(([_, { name }]) =>
|
||||
@ -26,7 +28,7 @@ const ToolPicker: React.FC<ToolPickerProps> = ({ selectedToolKey, onSelect, tool
|
||||
return (
|
||||
<Box >
|
||||
<TextInput
|
||||
placeholder="Search tools..."
|
||||
placeholder={t("toolPicker.searchPlaceholder", "Search tools...")}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
mb="md"
|
||||
@ -35,7 +37,7 @@ const ToolPicker: React.FC<ToolPickerProps> = ({ selectedToolKey, onSelect, tool
|
||||
<Stack align="flex-start">
|
||||
{filteredTools.length === 0 ? (
|
||||
<Text c="dimmed" size="sm">
|
||||
No tools found
|
||||
{t("toolPicker.noToolsFound", "No tools found")}
|
||||
</Text>
|
||||
) : (
|
||||
filteredTools.map(([id, { icon, name }]) => (
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Paper, Stack, Text, ScrollArea, Loader, Center, Button, Group, NumberInput, useMantineTheme } from "@mantine/core";
|
||||
import { getDocument, GlobalWorkerOptions } from "pdfjs-dist";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
|
||||
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
|
||||
import FirstPageIcon from "@mui/icons-material/FirstPage";
|
||||
@ -25,6 +26,7 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
sidebarsVisible,
|
||||
setSidebarsVisible,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
const [numPages, setNumPages] = useState<number>(0);
|
||||
const [pageImages, setPageImages] = useState<string[]>([]);
|
||||
@ -176,13 +178,13 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
{!pdfFile ? (
|
||||
<Center style={{ flex: 1 }}>
|
||||
<Stack align="center">
|
||||
<Text c="dimmed">No PDF loaded. Click to upload a PDF.</Text>
|
||||
<Text c="dimmed">{t("viewer.noPdfLoaded", "No PDF loaded. Click to upload a PDF.")}</Text>
|
||||
<Button
|
||||
component="label"
|
||||
variant="outline"
|
||||
color="blue"
|
||||
>
|
||||
Choose PDF
|
||||
{t("viewer.choosePdf", "Choose PDF")}
|
||||
<input
|
||||
type="file"
|
||||
accept="application/pdf"
|
||||
@ -209,7 +211,7 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
>
|
||||
<Stack gap="xl" align="center" >
|
||||
{pageImages.length === 0 && (
|
||||
<Text color="dimmed">No pages to display.</Text>
|
||||
<Text color="dimmed">{t("viewer.noPagesToDisplay", "No pages to display.")}</Text>
|
||||
)}
|
||||
{dualPage
|
||||
? Array.from({ length: Math.ceil(pageImages.length / 2) }).map((_, i) => (
|
||||
@ -372,7 +374,7 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
radius="xl"
|
||||
onClick={() => setDualPage(v => !v)}
|
||||
style={{ minWidth: 36 }}
|
||||
title={dualPage ? "Single Page View" : "Dual Page View"}
|
||||
title={dualPage ? t("viewer.singlePageView", "Single Page View") : t("viewer.dualPageView", "Dual Page View")}
|
||||
>
|
||||
{dualPage ? <DescriptionIcon fontSize="small" /> : <ViewWeekIcon fontSize="small" />}
|
||||
</Button>
|
||||
@ -383,7 +385,7 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
radius="xl"
|
||||
onClick={() => setSidebarsVisible(!sidebarsVisible)}
|
||||
style={{ minWidth: 36 }}
|
||||
title={sidebarsVisible ? "Hide Sidebars" : "Show Sidebars"}
|
||||
title={sidebarsVisible ? t("viewer.hideSidebars", "Hide Sidebars") : t("viewer.showSidebars", "Show Sidebars")}
|
||||
>
|
||||
<ViewSidebarIcon
|
||||
fontSize="small"
|
||||
@ -401,7 +403,7 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
radius="xl"
|
||||
onClick={() => setZoom(z => Math.max(0.1, z - 0.1))}
|
||||
style={{ minWidth: 32, padding: 0 }}
|
||||
title="Zoom out"
|
||||
title={t("viewer.zoomOut", "Zoom out")}
|
||||
>−</Button>
|
||||
<span style={{ minWidth: 40, textAlign: "center" }}>{Math.round(zoom * 100)}%</span>
|
||||
<Button
|
||||
@ -411,7 +413,7 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
radius="xl"
|
||||
onClick={() => setZoom(z => Math.min(5, z + 0.1))}
|
||||
style={{ minWidth: 32, padding: 0 }}
|
||||
title="Zoom in"
|
||||
title={t("viewer.zoomIn", "Zoom in")}
|
||||
>+</Button>
|
||||
</Group>
|
||||
</Paper>
|
||||
|
20
frontend/src/hooks/useTranslation.ts
Normal file
20
frontend/src/hooks/useTranslation.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// Re-export react-i18next hook with our custom types
|
||||
export { useTranslation } from 'react-i18next';
|
||||
|
||||
// You can add custom hooks here later if needed
|
||||
// For example, a hook that returns commonly used translations
|
||||
import { useTranslation as useI18nTranslation } from 'react-i18next';
|
||||
|
||||
export const useCommonTranslations = () => {
|
||||
const { t } = useI18nTranslation();
|
||||
|
||||
return {
|
||||
submit: t('genericSubmit'),
|
||||
selectPdf: t('pdfPrompt'),
|
||||
selectPdfs: t('multiPdfPrompt'),
|
||||
selectImages: t('imgPrompt'),
|
||||
loading: t('loading', 'Loading...'), // fallback if not found
|
||||
error: t('error._value', 'Error'),
|
||||
success: t('success', 'Success'),
|
||||
};
|
||||
};
|
87
frontend/src/i18n.ts
Normal file
87
frontend/src/i18n.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import Backend from 'i18next-http-backend';
|
||||
|
||||
// Define supported languages (based on your existing translations)
|
||||
export const supportedLanguages = {
|
||||
'en-GB': 'English (UK)',
|
||||
'en-US': 'English (US)',
|
||||
'ar-AR': 'العربية',
|
||||
'az-AZ': 'Azərbaycan Dili',
|
||||
'bg-BG': 'Български',
|
||||
'ca-CA': 'Català',
|
||||
'cs-CZ': 'Česky',
|
||||
'da-DK': 'Dansk',
|
||||
'de-DE': 'Deutsch',
|
||||
'el-GR': 'Ελληνικά',
|
||||
'es-ES': 'Español',
|
||||
'eu-ES': 'Euskara',
|
||||
'fa-IR': 'فارسی',
|
||||
'fr-FR': 'Français',
|
||||
'ga-IE': 'Gaeilge',
|
||||
'hi-IN': 'हिंदी',
|
||||
'hr-HR': 'Hrvatski',
|
||||
'hu-HU': 'Magyar',
|
||||
'id-ID': 'Bahasa Indonesia',
|
||||
'it-IT': 'Italiano',
|
||||
'ja-JP': '日本語',
|
||||
'ko-KR': '한국어',
|
||||
'ml-ML': 'മലയാളം',
|
||||
'nl-NL': 'Nederlands',
|
||||
'no-NB': 'Norsk',
|
||||
'pl-PL': 'Polski',
|
||||
'pt-BR': 'Português (Brasil)',
|
||||
'pt-PT': 'Português',
|
||||
'ro-RO': 'Română',
|
||||
'ru-RU': 'Русский',
|
||||
'sk-SK': 'Slovensky',
|
||||
'sl-SI': 'Slovenščina',
|
||||
'sr-LATN-RS': 'Srpski',
|
||||
'sv-SE': 'Svenska',
|
||||
'th-TH': 'ไทย',
|
||||
'tr-TR': 'Türkçe',
|
||||
'uk-UA': 'Українська',
|
||||
'vi-VN': 'Tiếng Việt',
|
||||
'zh-BO': 'བོད་ཡིག',
|
||||
'zh-CN': '简体中文',
|
||||
'zh-TW': '繁體中文',
|
||||
};
|
||||
|
||||
// RTL languages (based on your existing language.direction property)
|
||||
export const rtlLanguages = ['ar-AR', 'fa-IR'];
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
fallbackLng: 'en-GB',
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // React already escapes values
|
||||
},
|
||||
|
||||
backend: {
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||
},
|
||||
|
||||
detection: {
|
||||
order: ['localStorage', 'navigator', 'htmlTag'],
|
||||
caches: ['localStorage'],
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: false, // Set to false to avoid suspense issues with SSR
|
||||
},
|
||||
});
|
||||
|
||||
// Set document direction based on language
|
||||
i18n.on('languageChanged', (lng) => {
|
||||
const isRTL = rtlLanguages.includes(lng);
|
||||
document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
|
||||
document.documentElement.lang = lng;
|
||||
});
|
||||
|
||||
export default i18n;
|
@ -5,6 +5,7 @@ import { ColorSchemeScript, MantineProvider, mantineHtmlProps } from '@mantine/c
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import './i18n'; // Initialize i18next
|
||||
|
||||
|
||||
const container = document.getElementById('root');
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user