Spaces:
Running
Running
Jiya3177 commited on
Commit ·
4f1e987
1
Parent(s): aaff8ef
feat: add frontend internationalization
Browse files- frontend/package-lock.json +103 -123
- frontend/package.json +3 -0
- frontend/src/app/layout.tsx +6 -3
- frontend/src/app/login/page.tsx +11 -9
- frontend/src/app/register/page.tsx +13 -11
- frontend/src/components/chat/ChatPanel.tsx +15 -11
- frontend/src/components/document/DocumentSidebar.tsx +15 -13
- frontend/src/components/layout/Header.tsx +32 -5
- frontend/src/components/providers/I18nProvider.tsx +22 -0
- frontend/src/lib/i18n.ts +316 -0
frontend/package-lock.json
CHANGED
|
@@ -11,12 +11,15 @@
|
|
| 11 |
"@base-ui/react": "^1.4.1",
|
| 12 |
"class-variance-authority": "^0.7.1",
|
| 13 |
"clsx": "^2.1.1",
|
|
|
|
|
|
|
| 14 |
"lucide-react": "^1.8.0",
|
| 15 |
"next": "16.2.4",
|
| 16 |
"pdfjs-dist": "^5.6.205",
|
| 17 |
"react": "19.2.4",
|
| 18 |
"react-dom": "19.2.4",
|
| 19 |
"react-dropzone": "^15.0.0",
|
|
|
|
| 20 |
"react-markdown": "^10.1.0",
|
| 21 |
"react-pdf": "^10.4.1",
|
| 22 |
"remark-gfm": "^4.0.1",
|
|
@@ -76,6 +79,7 @@
|
|
| 76 |
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
| 77 |
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
| 78 |
"license": "MIT",
|
|
|
|
| 79 |
"dependencies": {
|
| 80 |
"@babel/code-frame": "^7.29.0",
|
| 81 |
"@babel/generator": "^7.29.0",
|
|
@@ -669,6 +673,7 @@
|
|
| 669 |
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
| 670 |
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
| 671 |
"license": "MIT",
|
|
|
|
| 672 |
"engines": {
|
| 673 |
"node": ">=12"
|
| 674 |
},
|
|
@@ -1106,9 +1111,6 @@
|
|
| 1106 |
"cpu": [
|
| 1107 |
"arm"
|
| 1108 |
],
|
| 1109 |
-
"libc": [
|
| 1110 |
-
"glibc"
|
| 1111 |
-
],
|
| 1112 |
"license": "LGPL-3.0-or-later",
|
| 1113 |
"optional": true,
|
| 1114 |
"os": [
|
|
@@ -1125,9 +1127,6 @@
|
|
| 1125 |
"cpu": [
|
| 1126 |
"arm64"
|
| 1127 |
],
|
| 1128 |
-
"libc": [
|
| 1129 |
-
"glibc"
|
| 1130 |
-
],
|
| 1131 |
"license": "LGPL-3.0-or-later",
|
| 1132 |
"optional": true,
|
| 1133 |
"os": [
|
|
@@ -1144,9 +1143,6 @@
|
|
| 1144 |
"cpu": [
|
| 1145 |
"ppc64"
|
| 1146 |
],
|
| 1147 |
-
"libc": [
|
| 1148 |
-
"glibc"
|
| 1149 |
-
],
|
| 1150 |
"license": "LGPL-3.0-or-later",
|
| 1151 |
"optional": true,
|
| 1152 |
"os": [
|
|
@@ -1163,9 +1159,6 @@
|
|
| 1163 |
"cpu": [
|
| 1164 |
"riscv64"
|
| 1165 |
],
|
| 1166 |
-
"libc": [
|
| 1167 |
-
"glibc"
|
| 1168 |
-
],
|
| 1169 |
"license": "LGPL-3.0-or-later",
|
| 1170 |
"optional": true,
|
| 1171 |
"os": [
|
|
@@ -1182,9 +1175,6 @@
|
|
| 1182 |
"cpu": [
|
| 1183 |
"s390x"
|
| 1184 |
],
|
| 1185 |
-
"libc": [
|
| 1186 |
-
"glibc"
|
| 1187 |
-
],
|
| 1188 |
"license": "LGPL-3.0-or-later",
|
| 1189 |
"optional": true,
|
| 1190 |
"os": [
|
|
@@ -1201,9 +1191,6 @@
|
|
| 1201 |
"cpu": [
|
| 1202 |
"x64"
|
| 1203 |
],
|
| 1204 |
-
"libc": [
|
| 1205 |
-
"glibc"
|
| 1206 |
-
],
|
| 1207 |
"license": "LGPL-3.0-or-later",
|
| 1208 |
"optional": true,
|
| 1209 |
"os": [
|
|
@@ -1220,9 +1207,6 @@
|
|
| 1220 |
"cpu": [
|
| 1221 |
"arm64"
|
| 1222 |
],
|
| 1223 |
-
"libc": [
|
| 1224 |
-
"musl"
|
| 1225 |
-
],
|
| 1226 |
"license": "LGPL-3.0-or-later",
|
| 1227 |
"optional": true,
|
| 1228 |
"os": [
|
|
@@ -1239,9 +1223,6 @@
|
|
| 1239 |
"cpu": [
|
| 1240 |
"x64"
|
| 1241 |
],
|
| 1242 |
-
"libc": [
|
| 1243 |
-
"musl"
|
| 1244 |
-
],
|
| 1245 |
"license": "LGPL-3.0-or-later",
|
| 1246 |
"optional": true,
|
| 1247 |
"os": [
|
|
@@ -1258,9 +1239,6 @@
|
|
| 1258 |
"cpu": [
|
| 1259 |
"arm"
|
| 1260 |
],
|
| 1261 |
-
"libc": [
|
| 1262 |
-
"glibc"
|
| 1263 |
-
],
|
| 1264 |
"license": "Apache-2.0",
|
| 1265 |
"optional": true,
|
| 1266 |
"os": [
|
|
@@ -1283,9 +1261,6 @@
|
|
| 1283 |
"cpu": [
|
| 1284 |
"arm64"
|
| 1285 |
],
|
| 1286 |
-
"libc": [
|
| 1287 |
-
"glibc"
|
| 1288 |
-
],
|
| 1289 |
"license": "Apache-2.0",
|
| 1290 |
"optional": true,
|
| 1291 |
"os": [
|
|
@@ -1308,9 +1283,6 @@
|
|
| 1308 |
"cpu": [
|
| 1309 |
"ppc64"
|
| 1310 |
],
|
| 1311 |
-
"libc": [
|
| 1312 |
-
"glibc"
|
| 1313 |
-
],
|
| 1314 |
"license": "Apache-2.0",
|
| 1315 |
"optional": true,
|
| 1316 |
"os": [
|
|
@@ -1333,9 +1305,6 @@
|
|
| 1333 |
"cpu": [
|
| 1334 |
"riscv64"
|
| 1335 |
],
|
| 1336 |
-
"libc": [
|
| 1337 |
-
"glibc"
|
| 1338 |
-
],
|
| 1339 |
"license": "Apache-2.0",
|
| 1340 |
"optional": true,
|
| 1341 |
"os": [
|
|
@@ -1358,9 +1327,6 @@
|
|
| 1358 |
"cpu": [
|
| 1359 |
"s390x"
|
| 1360 |
],
|
| 1361 |
-
"libc": [
|
| 1362 |
-
"glibc"
|
| 1363 |
-
],
|
| 1364 |
"license": "Apache-2.0",
|
| 1365 |
"optional": true,
|
| 1366 |
"os": [
|
|
@@ -1383,9 +1349,6 @@
|
|
| 1383 |
"cpu": [
|
| 1384 |
"x64"
|
| 1385 |
],
|
| 1386 |
-
"libc": [
|
| 1387 |
-
"glibc"
|
| 1388 |
-
],
|
| 1389 |
"license": "Apache-2.0",
|
| 1390 |
"optional": true,
|
| 1391 |
"os": [
|
|
@@ -1408,9 +1371,6 @@
|
|
| 1408 |
"cpu": [
|
| 1409 |
"arm64"
|
| 1410 |
],
|
| 1411 |
-
"libc": [
|
| 1412 |
-
"musl"
|
| 1413 |
-
],
|
| 1414 |
"license": "Apache-2.0",
|
| 1415 |
"optional": true,
|
| 1416 |
"os": [
|
|
@@ -1433,9 +1393,6 @@
|
|
| 1433 |
"cpu": [
|
| 1434 |
"x64"
|
| 1435 |
],
|
| 1436 |
-
"libc": [
|
| 1437 |
-
"musl"
|
| 1438 |
-
],
|
| 1439 |
"license": "Apache-2.0",
|
| 1440 |
"optional": true,
|
| 1441 |
"os": [
|
|
@@ -1856,9 +1813,6 @@
|
|
| 1856 |
"cpu": [
|
| 1857 |
"arm64"
|
| 1858 |
],
|
| 1859 |
-
"libc": [
|
| 1860 |
-
"glibc"
|
| 1861 |
-
],
|
| 1862 |
"license": "MIT",
|
| 1863 |
"optional": true,
|
| 1864 |
"os": [
|
|
@@ -1879,9 +1833,6 @@
|
|
| 1879 |
"cpu": [
|
| 1880 |
"arm64"
|
| 1881 |
],
|
| 1882 |
-
"libc": [
|
| 1883 |
-
"musl"
|
| 1884 |
-
],
|
| 1885 |
"license": "MIT",
|
| 1886 |
"optional": true,
|
| 1887 |
"os": [
|
|
@@ -1902,9 +1853,6 @@
|
|
| 1902 |
"cpu": [
|
| 1903 |
"riscv64"
|
| 1904 |
],
|
| 1905 |
-
"libc": [
|
| 1906 |
-
"glibc"
|
| 1907 |
-
],
|
| 1908 |
"license": "MIT",
|
| 1909 |
"optional": true,
|
| 1910 |
"os": [
|
|
@@ -1925,9 +1873,6 @@
|
|
| 1925 |
"cpu": [
|
| 1926 |
"x64"
|
| 1927 |
],
|
| 1928 |
-
"libc": [
|
| 1929 |
-
"glibc"
|
| 1930 |
-
],
|
| 1931 |
"license": "MIT",
|
| 1932 |
"optional": true,
|
| 1933 |
"os": [
|
|
@@ -1948,9 +1893,6 @@
|
|
| 1948 |
"cpu": [
|
| 1949 |
"x64"
|
| 1950 |
],
|
| 1951 |
-
"libc": [
|
| 1952 |
-
"musl"
|
| 1953 |
-
],
|
| 1954 |
"license": "MIT",
|
| 1955 |
"optional": true,
|
| 1956 |
"os": [
|
|
@@ -2072,9 +2014,6 @@
|
|
| 2072 |
"cpu": [
|
| 2073 |
"arm64"
|
| 2074 |
],
|
| 2075 |
-
"libc": [
|
| 2076 |
-
"glibc"
|
| 2077 |
-
],
|
| 2078 |
"license": "MIT",
|
| 2079 |
"optional": true,
|
| 2080 |
"os": [
|
|
@@ -2091,9 +2030,6 @@
|
|
| 2091 |
"cpu": [
|
| 2092 |
"arm64"
|
| 2093 |
],
|
| 2094 |
-
"libc": [
|
| 2095 |
-
"musl"
|
| 2096 |
-
],
|
| 2097 |
"license": "MIT",
|
| 2098 |
"optional": true,
|
| 2099 |
"os": [
|
|
@@ -2110,9 +2046,6 @@
|
|
| 2110 |
"cpu": [
|
| 2111 |
"x64"
|
| 2112 |
],
|
| 2113 |
-
"libc": [
|
| 2114 |
-
"glibc"
|
| 2115 |
-
],
|
| 2116 |
"license": "MIT",
|
| 2117 |
"optional": true,
|
| 2118 |
"os": [
|
|
@@ -2129,9 +2062,6 @@
|
|
| 2129 |
"cpu": [
|
| 2130 |
"x64"
|
| 2131 |
],
|
| 2132 |
-
"libc": [
|
| 2133 |
-
"musl"
|
| 2134 |
-
],
|
| 2135 |
"license": "MIT",
|
| 2136 |
"optional": true,
|
| 2137 |
"os": [
|
|
@@ -2178,6 +2108,7 @@
|
|
| 2178 |
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
|
| 2179 |
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
|
| 2180 |
"license": "MIT",
|
|
|
|
| 2181 |
"engines": {
|
| 2182 |
"node": "^14.21.3 || >=16"
|
| 2183 |
},
|
|
@@ -2446,9 +2377,6 @@
|
|
| 2446 |
"arm64"
|
| 2447 |
],
|
| 2448 |
"dev": true,
|
| 2449 |
-
"libc": [
|
| 2450 |
-
"glibc"
|
| 2451 |
-
],
|
| 2452 |
"license": "MIT",
|
| 2453 |
"optional": true,
|
| 2454 |
"os": [
|
|
@@ -2466,9 +2394,6 @@
|
|
| 2466 |
"arm64"
|
| 2467 |
],
|
| 2468 |
"dev": true,
|
| 2469 |
-
"libc": [
|
| 2470 |
-
"musl"
|
| 2471 |
-
],
|
| 2472 |
"license": "MIT",
|
| 2473 |
"optional": true,
|
| 2474 |
"os": [
|
|
@@ -2486,9 +2411,6 @@
|
|
| 2486 |
"x64"
|
| 2487 |
],
|
| 2488 |
"dev": true,
|
| 2489 |
-
"libc": [
|
| 2490 |
-
"glibc"
|
| 2491 |
-
],
|
| 2492 |
"license": "MIT",
|
| 2493 |
"optional": true,
|
| 2494 |
"os": [
|
|
@@ -2506,9 +2428,6 @@
|
|
| 2506 |
"x64"
|
| 2507 |
],
|
| 2508 |
"dev": true,
|
| 2509 |
-
"libc": [
|
| 2510 |
-
"musl"
|
| 2511 |
-
],
|
| 2512 |
"license": "MIT",
|
| 2513 |
"optional": true,
|
| 2514 |
"os": [
|
|
@@ -2749,6 +2668,7 @@
|
|
| 2749 |
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
|
| 2750 |
"integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
|
| 2751 |
"license": "MIT",
|
|
|
|
| 2752 |
"dependencies": {
|
| 2753 |
"undici-types": "~6.21.0"
|
| 2754 |
}
|
|
@@ -2758,6 +2678,7 @@
|
|
| 2758 |
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
| 2759 |
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
| 2760 |
"license": "MIT",
|
|
|
|
| 2761 |
"dependencies": {
|
| 2762 |
"csstype": "^3.2.2"
|
| 2763 |
}
|
|
@@ -2844,6 +2765,7 @@
|
|
| 2844 |
"integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==",
|
| 2845 |
"dev": true,
|
| 2846 |
"license": "MIT",
|
|
|
|
| 2847 |
"dependencies": {
|
| 2848 |
"@typescript-eslint/scope-manager": "8.59.0",
|
| 2849 |
"@typescript-eslint/types": "8.59.0",
|
|
@@ -3206,9 +3128,6 @@
|
|
| 3206 |
"arm64"
|
| 3207 |
],
|
| 3208 |
"dev": true,
|
| 3209 |
-
"libc": [
|
| 3210 |
-
"glibc"
|
| 3211 |
-
],
|
| 3212 |
"license": "MIT",
|
| 3213 |
"optional": true,
|
| 3214 |
"os": [
|
|
@@ -3223,9 +3142,6 @@
|
|
| 3223 |
"arm64"
|
| 3224 |
],
|
| 3225 |
"dev": true,
|
| 3226 |
-
"libc": [
|
| 3227 |
-
"musl"
|
| 3228 |
-
],
|
| 3229 |
"license": "MIT",
|
| 3230 |
"optional": true,
|
| 3231 |
"os": [
|
|
@@ -3240,9 +3156,6 @@
|
|
| 3240 |
"ppc64"
|
| 3241 |
],
|
| 3242 |
"dev": true,
|
| 3243 |
-
"libc": [
|
| 3244 |
-
"glibc"
|
| 3245 |
-
],
|
| 3246 |
"license": "MIT",
|
| 3247 |
"optional": true,
|
| 3248 |
"os": [
|
|
@@ -3257,9 +3170,6 @@
|
|
| 3257 |
"riscv64"
|
| 3258 |
],
|
| 3259 |
"dev": true,
|
| 3260 |
-
"libc": [
|
| 3261 |
-
"glibc"
|
| 3262 |
-
],
|
| 3263 |
"license": "MIT",
|
| 3264 |
"optional": true,
|
| 3265 |
"os": [
|
|
@@ -3274,9 +3184,6 @@
|
|
| 3274 |
"riscv64"
|
| 3275 |
],
|
| 3276 |
"dev": true,
|
| 3277 |
-
"libc": [
|
| 3278 |
-
"musl"
|
| 3279 |
-
],
|
| 3280 |
"license": "MIT",
|
| 3281 |
"optional": true,
|
| 3282 |
"os": [
|
|
@@ -3291,9 +3198,6 @@
|
|
| 3291 |
"s390x"
|
| 3292 |
],
|
| 3293 |
"dev": true,
|
| 3294 |
-
"libc": [
|
| 3295 |
-
"glibc"
|
| 3296 |
-
],
|
| 3297 |
"license": "MIT",
|
| 3298 |
"optional": true,
|
| 3299 |
"os": [
|
|
@@ -3308,9 +3212,6 @@
|
|
| 3308 |
"x64"
|
| 3309 |
],
|
| 3310 |
"dev": true,
|
| 3311 |
-
"libc": [
|
| 3312 |
-
"glibc"
|
| 3313 |
-
],
|
| 3314 |
"license": "MIT",
|
| 3315 |
"optional": true,
|
| 3316 |
"os": [
|
|
@@ -3325,9 +3226,6 @@
|
|
| 3325 |
"x64"
|
| 3326 |
],
|
| 3327 |
"dev": true,
|
| 3328 |
-
"libc": [
|
| 3329 |
-
"musl"
|
| 3330 |
-
],
|
| 3331 |
"license": "MIT",
|
| 3332 |
"optional": true,
|
| 3333 |
"os": [
|
|
@@ -3412,6 +3310,7 @@
|
|
| 3412 |
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
| 3413 |
"dev": true,
|
| 3414 |
"license": "MIT",
|
|
|
|
| 3415 |
"bin": {
|
| 3416 |
"acorn": "bin/acorn"
|
| 3417 |
},
|
|
@@ -3866,6 +3765,7 @@
|
|
| 3866 |
}
|
| 3867 |
],
|
| 3868 |
"license": "MIT",
|
|
|
|
| 3869 |
"dependencies": {
|
| 3870 |
"baseline-browser-mapping": "^2.10.12",
|
| 3871 |
"caniuse-lite": "^1.0.30001782",
|
|
@@ -4917,6 +4817,7 @@
|
|
| 4917 |
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
|
| 4918 |
"dev": true,
|
| 4919 |
"license": "MIT",
|
|
|
|
| 4920 |
"dependencies": {
|
| 4921 |
"@eslint-community/eslint-utils": "^4.8.0",
|
| 4922 |
"@eslint-community/regexpp": "^4.12.1",
|
|
@@ -5102,6 +5003,7 @@
|
|
| 5102 |
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
| 5103 |
"dev": true,
|
| 5104 |
"license": "MIT",
|
|
|
|
| 5105 |
"dependencies": {
|
| 5106 |
"@rtsao/scc": "^1.1.0",
|
| 5107 |
"array-includes": "^3.1.9",
|
|
@@ -5401,6 +5303,7 @@
|
|
| 5401 |
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
| 5402 |
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
| 5403 |
"license": "MIT",
|
|
|
|
| 5404 |
"dependencies": {
|
| 5405 |
"accepts": "^2.0.0",
|
| 5406 |
"body-parser": "^2.2.1",
|
|
@@ -6172,10 +6075,20 @@
|
|
| 6172 |
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz",
|
| 6173 |
"integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==",
|
| 6174 |
"license": "MIT",
|
|
|
|
| 6175 |
"engines": {
|
| 6176 |
"node": ">=16.9.0"
|
| 6177 |
}
|
| 6178 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6179 |
"node_modules/html-url-attributes": {
|
| 6180 |
"version": "3.0.1",
|
| 6181 |
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
|
@@ -6228,6 +6141,44 @@
|
|
| 6228 |
"node": ">=18.18.0"
|
| 6229 |
}
|
| 6230 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6231 |
"node_modules/iconv-lite": {
|
| 6232 |
"version": "0.7.2",
|
| 6233 |
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
|
@@ -7288,9 +7239,6 @@
|
|
| 7288 |
"arm64"
|
| 7289 |
],
|
| 7290 |
"dev": true,
|
| 7291 |
-
"libc": [
|
| 7292 |
-
"glibc"
|
| 7293 |
-
],
|
| 7294 |
"license": "MPL-2.0",
|
| 7295 |
"optional": true,
|
| 7296 |
"os": [
|
|
@@ -7312,9 +7260,6 @@
|
|
| 7312 |
"arm64"
|
| 7313 |
],
|
| 7314 |
"dev": true,
|
| 7315 |
-
"libc": [
|
| 7316 |
-
"musl"
|
| 7317 |
-
],
|
| 7318 |
"license": "MPL-2.0",
|
| 7319 |
"optional": true,
|
| 7320 |
"os": [
|
|
@@ -7336,9 +7281,6 @@
|
|
| 7336 |
"x64"
|
| 7337 |
],
|
| 7338 |
"dev": true,
|
| 7339 |
-
"libc": [
|
| 7340 |
-
"glibc"
|
| 7341 |
-
],
|
| 7342 |
"license": "MPL-2.0",
|
| 7343 |
"optional": true,
|
| 7344 |
"os": [
|
|
@@ -7360,9 +7302,6 @@
|
|
| 7360 |
"x64"
|
| 7361 |
],
|
| 7362 |
"dev": true,
|
| 7363 |
-
"libc": [
|
| 7364 |
-
"musl"
|
| 7365 |
-
],
|
| 7366 |
"license": "MPL-2.0",
|
| 7367 |
"optional": true,
|
| 7368 |
"os": [
|
|
@@ -9521,6 +9460,7 @@
|
|
| 9521 |
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
| 9522 |
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
| 9523 |
"license": "MIT",
|
|
|
|
| 9524 |
"engines": {
|
| 9525 |
"node": ">=0.10.0"
|
| 9526 |
}
|
|
@@ -9530,6 +9470,7 @@
|
|
| 9530 |
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
| 9531 |
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
| 9532 |
"license": "MIT",
|
|
|
|
| 9533 |
"dependencies": {
|
| 9534 |
"scheduler": "^0.27.0"
|
| 9535 |
},
|
|
@@ -9554,6 +9495,33 @@
|
|
| 9554 |
"react": ">= 16.8 || 18.0.0"
|
| 9555 |
}
|
| 9556 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9557 |
"node_modules/react-is": {
|
| 9558 |
"version": "16.13.1",
|
| 9559 |
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
@@ -10810,6 +10778,7 @@
|
|
| 10810 |
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
| 10811 |
"dev": true,
|
| 10812 |
"license": "MIT",
|
|
|
|
| 10813 |
"engines": {
|
| 10814 |
"node": ">=12"
|
| 10815 |
},
|
|
@@ -11078,6 +11047,7 @@
|
|
| 11078 |
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
| 11079 |
"devOptional": true,
|
| 11080 |
"license": "Apache-2.0",
|
|
|
|
| 11081 |
"bin": {
|
| 11082 |
"tsc": "bin/tsc",
|
| 11083 |
"tsserver": "bin/tsserver"
|
|
@@ -11397,6 +11367,15 @@
|
|
| 11397 |
"url": "https://opencollective.com/unified"
|
| 11398 |
}
|
| 11399 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11400 |
"node_modules/warning": {
|
| 11401 |
"version": "4.0.3",
|
| 11402 |
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
|
@@ -11737,6 +11716,7 @@
|
|
| 11737 |
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
| 11738 |
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
| 11739 |
"license": "MIT",
|
|
|
|
| 11740 |
"funding": {
|
| 11741 |
"url": "https://github.com/sponsors/colinhacks"
|
| 11742 |
}
|
|
|
|
| 11 |
"@base-ui/react": "^1.4.1",
|
| 12 |
"class-variance-authority": "^0.7.1",
|
| 13 |
"clsx": "^2.1.1",
|
| 14 |
+
"i18next": "^26.3.0",
|
| 15 |
+
"i18next-browser-languagedetector": "^8.2.1",
|
| 16 |
"lucide-react": "^1.8.0",
|
| 17 |
"next": "16.2.4",
|
| 18 |
"pdfjs-dist": "^5.6.205",
|
| 19 |
"react": "19.2.4",
|
| 20 |
"react-dom": "19.2.4",
|
| 21 |
"react-dropzone": "^15.0.0",
|
| 22 |
+
"react-i18next": "^17.0.8",
|
| 23 |
"react-markdown": "^10.1.0",
|
| 24 |
"react-pdf": "^10.4.1",
|
| 25 |
"remark-gfm": "^4.0.1",
|
|
|
|
| 79 |
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
| 80 |
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
| 81 |
"license": "MIT",
|
| 82 |
+
"peer": true,
|
| 83 |
"dependencies": {
|
| 84 |
"@babel/code-frame": "^7.29.0",
|
| 85 |
"@babel/generator": "^7.29.0",
|
|
|
|
| 673 |
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
| 674 |
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
| 675 |
"license": "MIT",
|
| 676 |
+
"peer": true,
|
| 677 |
"engines": {
|
| 678 |
"node": ">=12"
|
| 679 |
},
|
|
|
|
| 1111 |
"cpu": [
|
| 1112 |
"arm"
|
| 1113 |
],
|
|
|
|
|
|
|
|
|
|
| 1114 |
"license": "LGPL-3.0-or-later",
|
| 1115 |
"optional": true,
|
| 1116 |
"os": [
|
|
|
|
| 1127 |
"cpu": [
|
| 1128 |
"arm64"
|
| 1129 |
],
|
|
|
|
|
|
|
|
|
|
| 1130 |
"license": "LGPL-3.0-or-later",
|
| 1131 |
"optional": true,
|
| 1132 |
"os": [
|
|
|
|
| 1143 |
"cpu": [
|
| 1144 |
"ppc64"
|
| 1145 |
],
|
|
|
|
|
|
|
|
|
|
| 1146 |
"license": "LGPL-3.0-or-later",
|
| 1147 |
"optional": true,
|
| 1148 |
"os": [
|
|
|
|
| 1159 |
"cpu": [
|
| 1160 |
"riscv64"
|
| 1161 |
],
|
|
|
|
|
|
|
|
|
|
| 1162 |
"license": "LGPL-3.0-or-later",
|
| 1163 |
"optional": true,
|
| 1164 |
"os": [
|
|
|
|
| 1175 |
"cpu": [
|
| 1176 |
"s390x"
|
| 1177 |
],
|
|
|
|
|
|
|
|
|
|
| 1178 |
"license": "LGPL-3.0-or-later",
|
| 1179 |
"optional": true,
|
| 1180 |
"os": [
|
|
|
|
| 1191 |
"cpu": [
|
| 1192 |
"x64"
|
| 1193 |
],
|
|
|
|
|
|
|
|
|
|
| 1194 |
"license": "LGPL-3.0-or-later",
|
| 1195 |
"optional": true,
|
| 1196 |
"os": [
|
|
|
|
| 1207 |
"cpu": [
|
| 1208 |
"arm64"
|
| 1209 |
],
|
|
|
|
|
|
|
|
|
|
| 1210 |
"license": "LGPL-3.0-or-later",
|
| 1211 |
"optional": true,
|
| 1212 |
"os": [
|
|
|
|
| 1223 |
"cpu": [
|
| 1224 |
"x64"
|
| 1225 |
],
|
|
|
|
|
|
|
|
|
|
| 1226 |
"license": "LGPL-3.0-or-later",
|
| 1227 |
"optional": true,
|
| 1228 |
"os": [
|
|
|
|
| 1239 |
"cpu": [
|
| 1240 |
"arm"
|
| 1241 |
],
|
|
|
|
|
|
|
|
|
|
| 1242 |
"license": "Apache-2.0",
|
| 1243 |
"optional": true,
|
| 1244 |
"os": [
|
|
|
|
| 1261 |
"cpu": [
|
| 1262 |
"arm64"
|
| 1263 |
],
|
|
|
|
|
|
|
|
|
|
| 1264 |
"license": "Apache-2.0",
|
| 1265 |
"optional": true,
|
| 1266 |
"os": [
|
|
|
|
| 1283 |
"cpu": [
|
| 1284 |
"ppc64"
|
| 1285 |
],
|
|
|
|
|
|
|
|
|
|
| 1286 |
"license": "Apache-2.0",
|
| 1287 |
"optional": true,
|
| 1288 |
"os": [
|
|
|
|
| 1305 |
"cpu": [
|
| 1306 |
"riscv64"
|
| 1307 |
],
|
|
|
|
|
|
|
|
|
|
| 1308 |
"license": "Apache-2.0",
|
| 1309 |
"optional": true,
|
| 1310 |
"os": [
|
|
|
|
| 1327 |
"cpu": [
|
| 1328 |
"s390x"
|
| 1329 |
],
|
|
|
|
|
|
|
|
|
|
| 1330 |
"license": "Apache-2.0",
|
| 1331 |
"optional": true,
|
| 1332 |
"os": [
|
|
|
|
| 1349 |
"cpu": [
|
| 1350 |
"x64"
|
| 1351 |
],
|
|
|
|
|
|
|
|
|
|
| 1352 |
"license": "Apache-2.0",
|
| 1353 |
"optional": true,
|
| 1354 |
"os": [
|
|
|
|
| 1371 |
"cpu": [
|
| 1372 |
"arm64"
|
| 1373 |
],
|
|
|
|
|
|
|
|
|
|
| 1374 |
"license": "Apache-2.0",
|
| 1375 |
"optional": true,
|
| 1376 |
"os": [
|
|
|
|
| 1393 |
"cpu": [
|
| 1394 |
"x64"
|
| 1395 |
],
|
|
|
|
|
|
|
|
|
|
| 1396 |
"license": "Apache-2.0",
|
| 1397 |
"optional": true,
|
| 1398 |
"os": [
|
|
|
|
| 1813 |
"cpu": [
|
| 1814 |
"arm64"
|
| 1815 |
],
|
|
|
|
|
|
|
|
|
|
| 1816 |
"license": "MIT",
|
| 1817 |
"optional": true,
|
| 1818 |
"os": [
|
|
|
|
| 1833 |
"cpu": [
|
| 1834 |
"arm64"
|
| 1835 |
],
|
|
|
|
|
|
|
|
|
|
| 1836 |
"license": "MIT",
|
| 1837 |
"optional": true,
|
| 1838 |
"os": [
|
|
|
|
| 1853 |
"cpu": [
|
| 1854 |
"riscv64"
|
| 1855 |
],
|
|
|
|
|
|
|
|
|
|
| 1856 |
"license": "MIT",
|
| 1857 |
"optional": true,
|
| 1858 |
"os": [
|
|
|
|
| 1873 |
"cpu": [
|
| 1874 |
"x64"
|
| 1875 |
],
|
|
|
|
|
|
|
|
|
|
| 1876 |
"license": "MIT",
|
| 1877 |
"optional": true,
|
| 1878 |
"os": [
|
|
|
|
| 1893 |
"cpu": [
|
| 1894 |
"x64"
|
| 1895 |
],
|
|
|
|
|
|
|
|
|
|
| 1896 |
"license": "MIT",
|
| 1897 |
"optional": true,
|
| 1898 |
"os": [
|
|
|
|
| 2014 |
"cpu": [
|
| 2015 |
"arm64"
|
| 2016 |
],
|
|
|
|
|
|
|
|
|
|
| 2017 |
"license": "MIT",
|
| 2018 |
"optional": true,
|
| 2019 |
"os": [
|
|
|
|
| 2030 |
"cpu": [
|
| 2031 |
"arm64"
|
| 2032 |
],
|
|
|
|
|
|
|
|
|
|
| 2033 |
"license": "MIT",
|
| 2034 |
"optional": true,
|
| 2035 |
"os": [
|
|
|
|
| 2046 |
"cpu": [
|
| 2047 |
"x64"
|
| 2048 |
],
|
|
|
|
|
|
|
|
|
|
| 2049 |
"license": "MIT",
|
| 2050 |
"optional": true,
|
| 2051 |
"os": [
|
|
|
|
| 2062 |
"cpu": [
|
| 2063 |
"x64"
|
| 2064 |
],
|
|
|
|
|
|
|
|
|
|
| 2065 |
"license": "MIT",
|
| 2066 |
"optional": true,
|
| 2067 |
"os": [
|
|
|
|
| 2108 |
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
|
| 2109 |
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
|
| 2110 |
"license": "MIT",
|
| 2111 |
+
"peer": true,
|
| 2112 |
"engines": {
|
| 2113 |
"node": "^14.21.3 || >=16"
|
| 2114 |
},
|
|
|
|
| 2377 |
"arm64"
|
| 2378 |
],
|
| 2379 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 2380 |
"license": "MIT",
|
| 2381 |
"optional": true,
|
| 2382 |
"os": [
|
|
|
|
| 2394 |
"arm64"
|
| 2395 |
],
|
| 2396 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 2397 |
"license": "MIT",
|
| 2398 |
"optional": true,
|
| 2399 |
"os": [
|
|
|
|
| 2411 |
"x64"
|
| 2412 |
],
|
| 2413 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 2414 |
"license": "MIT",
|
| 2415 |
"optional": true,
|
| 2416 |
"os": [
|
|
|
|
| 2428 |
"x64"
|
| 2429 |
],
|
| 2430 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 2431 |
"license": "MIT",
|
| 2432 |
"optional": true,
|
| 2433 |
"os": [
|
|
|
|
| 2668 |
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
|
| 2669 |
"integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
|
| 2670 |
"license": "MIT",
|
| 2671 |
+
"peer": true,
|
| 2672 |
"dependencies": {
|
| 2673 |
"undici-types": "~6.21.0"
|
| 2674 |
}
|
|
|
|
| 2678 |
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
| 2679 |
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
| 2680 |
"license": "MIT",
|
| 2681 |
+
"peer": true,
|
| 2682 |
"dependencies": {
|
| 2683 |
"csstype": "^3.2.2"
|
| 2684 |
}
|
|
|
|
| 2765 |
"integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==",
|
| 2766 |
"dev": true,
|
| 2767 |
"license": "MIT",
|
| 2768 |
+
"peer": true,
|
| 2769 |
"dependencies": {
|
| 2770 |
"@typescript-eslint/scope-manager": "8.59.0",
|
| 2771 |
"@typescript-eslint/types": "8.59.0",
|
|
|
|
| 3128 |
"arm64"
|
| 3129 |
],
|
| 3130 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3131 |
"license": "MIT",
|
| 3132 |
"optional": true,
|
| 3133 |
"os": [
|
|
|
|
| 3142 |
"arm64"
|
| 3143 |
],
|
| 3144 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3145 |
"license": "MIT",
|
| 3146 |
"optional": true,
|
| 3147 |
"os": [
|
|
|
|
| 3156 |
"ppc64"
|
| 3157 |
],
|
| 3158 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3159 |
"license": "MIT",
|
| 3160 |
"optional": true,
|
| 3161 |
"os": [
|
|
|
|
| 3170 |
"riscv64"
|
| 3171 |
],
|
| 3172 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3173 |
"license": "MIT",
|
| 3174 |
"optional": true,
|
| 3175 |
"os": [
|
|
|
|
| 3184 |
"riscv64"
|
| 3185 |
],
|
| 3186 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3187 |
"license": "MIT",
|
| 3188 |
"optional": true,
|
| 3189 |
"os": [
|
|
|
|
| 3198 |
"s390x"
|
| 3199 |
],
|
| 3200 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3201 |
"license": "MIT",
|
| 3202 |
"optional": true,
|
| 3203 |
"os": [
|
|
|
|
| 3212 |
"x64"
|
| 3213 |
],
|
| 3214 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3215 |
"license": "MIT",
|
| 3216 |
"optional": true,
|
| 3217 |
"os": [
|
|
|
|
| 3226 |
"x64"
|
| 3227 |
],
|
| 3228 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 3229 |
"license": "MIT",
|
| 3230 |
"optional": true,
|
| 3231 |
"os": [
|
|
|
|
| 3310 |
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
| 3311 |
"dev": true,
|
| 3312 |
"license": "MIT",
|
| 3313 |
+
"peer": true,
|
| 3314 |
"bin": {
|
| 3315 |
"acorn": "bin/acorn"
|
| 3316 |
},
|
|
|
|
| 3765 |
}
|
| 3766 |
],
|
| 3767 |
"license": "MIT",
|
| 3768 |
+
"peer": true,
|
| 3769 |
"dependencies": {
|
| 3770 |
"baseline-browser-mapping": "^2.10.12",
|
| 3771 |
"caniuse-lite": "^1.0.30001782",
|
|
|
|
| 4817 |
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
|
| 4818 |
"dev": true,
|
| 4819 |
"license": "MIT",
|
| 4820 |
+
"peer": true,
|
| 4821 |
"dependencies": {
|
| 4822 |
"@eslint-community/eslint-utils": "^4.8.0",
|
| 4823 |
"@eslint-community/regexpp": "^4.12.1",
|
|
|
|
| 5003 |
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
| 5004 |
"dev": true,
|
| 5005 |
"license": "MIT",
|
| 5006 |
+
"peer": true,
|
| 5007 |
"dependencies": {
|
| 5008 |
"@rtsao/scc": "^1.1.0",
|
| 5009 |
"array-includes": "^3.1.9",
|
|
|
|
| 5303 |
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
| 5304 |
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
| 5305 |
"license": "MIT",
|
| 5306 |
+
"peer": true,
|
| 5307 |
"dependencies": {
|
| 5308 |
"accepts": "^2.0.0",
|
| 5309 |
"body-parser": "^2.2.1",
|
|
|
|
| 6075 |
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz",
|
| 6076 |
"integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==",
|
| 6077 |
"license": "MIT",
|
| 6078 |
+
"peer": true,
|
| 6079 |
"engines": {
|
| 6080 |
"node": ">=16.9.0"
|
| 6081 |
}
|
| 6082 |
},
|
| 6083 |
+
"node_modules/html-parse-stringify": {
|
| 6084 |
+
"version": "3.0.1",
|
| 6085 |
+
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
| 6086 |
+
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
| 6087 |
+
"license": "MIT",
|
| 6088 |
+
"dependencies": {
|
| 6089 |
+
"void-elements": "3.1.0"
|
| 6090 |
+
}
|
| 6091 |
+
},
|
| 6092 |
"node_modules/html-url-attributes": {
|
| 6093 |
"version": "3.0.1",
|
| 6094 |
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
|
|
|
| 6141 |
"node": ">=18.18.0"
|
| 6142 |
}
|
| 6143 |
},
|
| 6144 |
+
"node_modules/i18next": {
|
| 6145 |
+
"version": "26.3.0",
|
| 6146 |
+
"resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.0.tgz",
|
| 6147 |
+
"integrity": "sha512-gHSgGpUXVmuqE2El1W61DmxeyeTlFfZgdJRWMo9jScAn5pu7TuTuiccb1zh3E2J9hEBVGJ23+96x0ieBhfuIHA==",
|
| 6148 |
+
"funding": [
|
| 6149 |
+
{
|
| 6150 |
+
"type": "individual",
|
| 6151 |
+
"url": "https://www.locize.com/i18next"
|
| 6152 |
+
},
|
| 6153 |
+
{
|
| 6154 |
+
"type": "individual",
|
| 6155 |
+
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
| 6156 |
+
},
|
| 6157 |
+
{
|
| 6158 |
+
"type": "individual",
|
| 6159 |
+
"url": "https://www.locize.com"
|
| 6160 |
+
}
|
| 6161 |
+
],
|
| 6162 |
+
"license": "MIT",
|
| 6163 |
+
"peer": true,
|
| 6164 |
+
"peerDependencies": {
|
| 6165 |
+
"typescript": "^5 || ^6"
|
| 6166 |
+
},
|
| 6167 |
+
"peerDependenciesMeta": {
|
| 6168 |
+
"typescript": {
|
| 6169 |
+
"optional": true
|
| 6170 |
+
}
|
| 6171 |
+
}
|
| 6172 |
+
},
|
| 6173 |
+
"node_modules/i18next-browser-languagedetector": {
|
| 6174 |
+
"version": "8.2.1",
|
| 6175 |
+
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.1.tgz",
|
| 6176 |
+
"integrity": "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==",
|
| 6177 |
+
"license": "MIT",
|
| 6178 |
+
"dependencies": {
|
| 6179 |
+
"@babel/runtime": "^7.23.2"
|
| 6180 |
+
}
|
| 6181 |
+
},
|
| 6182 |
"node_modules/iconv-lite": {
|
| 6183 |
"version": "0.7.2",
|
| 6184 |
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
|
|
|
| 7239 |
"arm64"
|
| 7240 |
],
|
| 7241 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 7242 |
"license": "MPL-2.0",
|
| 7243 |
"optional": true,
|
| 7244 |
"os": [
|
|
|
|
| 7260 |
"arm64"
|
| 7261 |
],
|
| 7262 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 7263 |
"license": "MPL-2.0",
|
| 7264 |
"optional": true,
|
| 7265 |
"os": [
|
|
|
|
| 7281 |
"x64"
|
| 7282 |
],
|
| 7283 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 7284 |
"license": "MPL-2.0",
|
| 7285 |
"optional": true,
|
| 7286 |
"os": [
|
|
|
|
| 7302 |
"x64"
|
| 7303 |
],
|
| 7304 |
"dev": true,
|
|
|
|
|
|
|
|
|
|
| 7305 |
"license": "MPL-2.0",
|
| 7306 |
"optional": true,
|
| 7307 |
"os": [
|
|
|
|
| 9460 |
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
| 9461 |
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
| 9462 |
"license": "MIT",
|
| 9463 |
+
"peer": true,
|
| 9464 |
"engines": {
|
| 9465 |
"node": ">=0.10.0"
|
| 9466 |
}
|
|
|
|
| 9470 |
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
| 9471 |
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
| 9472 |
"license": "MIT",
|
| 9473 |
+
"peer": true,
|
| 9474 |
"dependencies": {
|
| 9475 |
"scheduler": "^0.27.0"
|
| 9476 |
},
|
|
|
|
| 9495 |
"react": ">= 16.8 || 18.0.0"
|
| 9496 |
}
|
| 9497 |
},
|
| 9498 |
+
"node_modules/react-i18next": {
|
| 9499 |
+
"version": "17.0.8",
|
| 9500 |
+
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.8.tgz",
|
| 9501 |
+
"integrity": "sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==",
|
| 9502 |
+
"license": "MIT",
|
| 9503 |
+
"dependencies": {
|
| 9504 |
+
"@babel/runtime": "^7.29.2",
|
| 9505 |
+
"html-parse-stringify": "^3.0.1",
|
| 9506 |
+
"use-sync-external-store": "^1.6.0"
|
| 9507 |
+
},
|
| 9508 |
+
"peerDependencies": {
|
| 9509 |
+
"i18next": ">= 26.2.0",
|
| 9510 |
+
"react": ">= 16.8.0",
|
| 9511 |
+
"typescript": "^5 || ^6"
|
| 9512 |
+
},
|
| 9513 |
+
"peerDependenciesMeta": {
|
| 9514 |
+
"react-dom": {
|
| 9515 |
+
"optional": true
|
| 9516 |
+
},
|
| 9517 |
+
"react-native": {
|
| 9518 |
+
"optional": true
|
| 9519 |
+
},
|
| 9520 |
+
"typescript": {
|
| 9521 |
+
"optional": true
|
| 9522 |
+
}
|
| 9523 |
+
}
|
| 9524 |
+
},
|
| 9525 |
"node_modules/react-is": {
|
| 9526 |
"version": "16.13.1",
|
| 9527 |
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
|
|
| 10778 |
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
| 10779 |
"dev": true,
|
| 10780 |
"license": "MIT",
|
| 10781 |
+
"peer": true,
|
| 10782 |
"engines": {
|
| 10783 |
"node": ">=12"
|
| 10784 |
},
|
|
|
|
| 11047 |
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
| 11048 |
"devOptional": true,
|
| 11049 |
"license": "Apache-2.0",
|
| 11050 |
+
"peer": true,
|
| 11051 |
"bin": {
|
| 11052 |
"tsc": "bin/tsc",
|
| 11053 |
"tsserver": "bin/tsserver"
|
|
|
|
| 11367 |
"url": "https://opencollective.com/unified"
|
| 11368 |
}
|
| 11369 |
},
|
| 11370 |
+
"node_modules/void-elements": {
|
| 11371 |
+
"version": "3.1.0",
|
| 11372 |
+
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
| 11373 |
+
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
| 11374 |
+
"license": "MIT",
|
| 11375 |
+
"engines": {
|
| 11376 |
+
"node": ">=0.10.0"
|
| 11377 |
+
}
|
| 11378 |
+
},
|
| 11379 |
"node_modules/warning": {
|
| 11380 |
"version": "4.0.3",
|
| 11381 |
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
|
|
|
| 11716 |
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
| 11717 |
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
| 11718 |
"license": "MIT",
|
| 11719 |
+
"peer": true,
|
| 11720 |
"funding": {
|
| 11721 |
"url": "https://github.com/sponsors/colinhacks"
|
| 11722 |
}
|
frontend/package.json
CHANGED
|
@@ -12,12 +12,15 @@
|
|
| 12 |
"@base-ui/react": "^1.4.1",
|
| 13 |
"class-variance-authority": "^0.7.1",
|
| 14 |
"clsx": "^2.1.1",
|
|
|
|
|
|
|
| 15 |
"lucide-react": "^1.8.0",
|
| 16 |
"next": "16.2.4",
|
| 17 |
"pdfjs-dist": "^5.6.205",
|
| 18 |
"react": "19.2.4",
|
| 19 |
"react-dom": "19.2.4",
|
| 20 |
"react-dropzone": "^15.0.0",
|
|
|
|
| 21 |
"react-markdown": "^10.1.0",
|
| 22 |
"react-pdf": "^10.4.1",
|
| 23 |
"remark-gfm": "^4.0.1",
|
|
|
|
| 12 |
"@base-ui/react": "^1.4.1",
|
| 13 |
"class-variance-authority": "^0.7.1",
|
| 14 |
"clsx": "^2.1.1",
|
| 15 |
+
"i18next": "^26.3.0",
|
| 16 |
+
"i18next-browser-languagedetector": "^8.2.1",
|
| 17 |
"lucide-react": "^1.8.0",
|
| 18 |
"next": "16.2.4",
|
| 19 |
"pdfjs-dist": "^5.6.205",
|
| 20 |
"react": "19.2.4",
|
| 21 |
"react-dom": "19.2.4",
|
| 22 |
"react-dropzone": "^15.0.0",
|
| 23 |
+
"react-i18next": "^17.0.8",
|
| 24 |
"react-markdown": "^10.1.0",
|
| 25 |
"react-pdf": "^10.4.1",
|
| 26 |
"remark-gfm": "^4.0.1",
|
frontend/src/app/layout.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { Inter } from "next/font/google";
|
|
| 3 |
import "./globals.css";
|
| 4 |
import { AuthProvider } from "@/lib/auth";
|
| 5 |
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
|
|
| 6 |
|
| 7 |
const inter = Inter({
|
| 8 |
variable: "--font-sans",
|
|
@@ -26,9 +27,11 @@ export default function RootLayout({
|
|
| 26 |
<html lang="en" className={`${inter.variable} dark h-full antialiased`}>
|
| 27 |
<body className="min-h-full flex flex-col bg-background text-foreground">
|
| 28 |
<AuthProvider>
|
| 29 |
-
<
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
| 32 |
</AuthProvider>
|
| 33 |
</body>
|
| 34 |
</html>
|
|
|
|
| 3 |
import "./globals.css";
|
| 4 |
import { AuthProvider } from "@/lib/auth";
|
| 5 |
import { TooltipProvider } from "@/components/ui/tooltip";
|
| 6 |
+
import I18nProvider from "@/components/providers/I18nProvider";
|
| 7 |
|
| 8 |
const inter = Inter({
|
| 9 |
variable: "--font-sans",
|
|
|
|
| 27 |
<html lang="en" className={`${inter.variable} dark h-full antialiased`}>
|
| 28 |
<body className="min-h-full flex flex-col bg-background text-foreground">
|
| 29 |
<AuthProvider>
|
| 30 |
+
<I18nProvider>
|
| 31 |
+
<TooltipProvider>
|
| 32 |
+
{children}
|
| 33 |
+
</TooltipProvider>
|
| 34 |
+
</I18nProvider>
|
| 35 |
</AuthProvider>
|
| 36 |
</body>
|
| 37 |
</html>
|
frontend/src/app/login/page.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
import { useState } from "react";
|
| 4 |
import { useRouter } from "next/navigation";
|
| 5 |
import { useAuth } from "@/lib/auth";
|
|
|
|
| 6 |
import { Button } from "@/components/ui/button";
|
| 7 |
import { Input } from "@/components/ui/input";
|
| 8 |
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
@@ -11,6 +12,7 @@ import Link from "next/link";
|
|
| 11 |
|
| 12 |
export default function LoginPage() {
|
| 13 |
const { login } = useAuth();
|
|
|
|
| 14 |
const router = useRouter();
|
| 15 |
const [email, setEmail] = useState("");
|
| 16 |
const [password, setPassword] = useState("");
|
|
@@ -27,7 +29,7 @@ export default function LoginPage() {
|
|
| 27 |
await login(email, password);
|
| 28 |
router.replace("/dashboard");
|
| 29 |
} catch (err: unknown) {
|
| 30 |
-
const message = err instanceof Error ? err.message : "
|
| 31 |
setError(message);
|
| 32 |
} finally {
|
| 33 |
setLoading(false);
|
|
@@ -46,8 +48,8 @@ export default function LoginPage() {
|
|
| 46 |
<Brain className="w-6 h-6 text-primary" />
|
| 47 |
</div>
|
| 48 |
</div>
|
| 49 |
-
<CardTitle className="text-2xl font-bold">
|
| 50 |
-
<CardDescription>
|
| 51 |
</CardHeader>
|
| 52 |
|
| 53 |
<CardContent>
|
|
@@ -59,7 +61,7 @@ export default function LoginPage() {
|
|
| 59 |
)}
|
| 60 |
|
| 61 |
<div className="space-y-2">
|
| 62 |
-
<label className="text-sm font-medium">
|
| 63 |
<Input
|
| 64 |
id="login-email"
|
| 65 |
type="email"
|
|
@@ -72,7 +74,7 @@ export default function LoginPage() {
|
|
| 72 |
</div>
|
| 73 |
|
| 74 |
<div className="space-y-2">
|
| 75 |
-
<label className="text-sm font-medium">
|
| 76 |
<div className="relative">
|
| 77 |
<Input
|
| 78 |
id="login-password"
|
|
@@ -97,18 +99,18 @@ export default function LoginPage() {
|
|
| 97 |
{loading ? (
|
| 98 |
<span className="flex items-center gap-2">
|
| 99 |
<span className="w-4 h-4 border-2 border-primary-foreground/30 border-t-primary-foreground rounded-full animate-spin" />
|
| 100 |
-
|
| 101 |
</span>
|
| 102 |
) : (
|
| 103 |
-
"
|
| 104 |
)}
|
| 105 |
</Button>
|
| 106 |
</form>
|
| 107 |
|
| 108 |
<p className="text-center text-sm text-muted-foreground mt-6">
|
| 109 |
-
|
| 110 |
<Link href="/register" className="text-primary hover:underline font-medium">
|
| 111 |
-
|
| 112 |
</Link>
|
| 113 |
</p>
|
| 114 |
</CardContent>
|
|
|
|
| 3 |
import { useState } from "react";
|
| 4 |
import { useRouter } from "next/navigation";
|
| 5 |
import { useAuth } from "@/lib/auth";
|
| 6 |
+
import { useTranslation } from "react-i18next";
|
| 7 |
import { Button } from "@/components/ui/button";
|
| 8 |
import { Input } from "@/components/ui/input";
|
| 9 |
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
|
|
| 12 |
|
| 13 |
export default function LoginPage() {
|
| 14 |
const { login } = useAuth();
|
| 15 |
+
const { t } = useTranslation();
|
| 16 |
const router = useRouter();
|
| 17 |
const [email, setEmail] = useState("");
|
| 18 |
const [password, setPassword] = useState("");
|
|
|
|
| 29 |
await login(email, password);
|
| 30 |
router.replace("/dashboard");
|
| 31 |
} catch (err: unknown) {
|
| 32 |
+
const message = err instanceof Error ? err.message : t("login.fallbackError");
|
| 33 |
setError(message);
|
| 34 |
} finally {
|
| 35 |
setLoading(false);
|
|
|
|
| 48 |
<Brain className="w-6 h-6 text-primary" />
|
| 49 |
</div>
|
| 50 |
</div>
|
| 51 |
+
<CardTitle className="text-2xl font-bold">{t("login.title")}</CardTitle>
|
| 52 |
+
<CardDescription>{t("login.description")}</CardDescription>
|
| 53 |
</CardHeader>
|
| 54 |
|
| 55 |
<CardContent>
|
|
|
|
| 61 |
)}
|
| 62 |
|
| 63 |
<div className="space-y-2">
|
| 64 |
+
<label className="text-sm font-medium">{t("common.email")}</label>
|
| 65 |
<Input
|
| 66 |
id="login-email"
|
| 67 |
type="email"
|
|
|
|
| 74 |
</div>
|
| 75 |
|
| 76 |
<div className="space-y-2">
|
| 77 |
+
<label className="text-sm font-medium">{t("common.password")}</label>
|
| 78 |
<div className="relative">
|
| 79 |
<Input
|
| 80 |
id="login-password"
|
|
|
|
| 99 |
{loading ? (
|
| 100 |
<span className="flex items-center gap-2">
|
| 101 |
<span className="w-4 h-4 border-2 border-primary-foreground/30 border-t-primary-foreground rounded-full animate-spin" />
|
| 102 |
+
{t("login.submitting")}
|
| 103 |
</span>
|
| 104 |
) : (
|
| 105 |
+
t("login.submit")
|
| 106 |
)}
|
| 107 |
</Button>
|
| 108 |
</form>
|
| 109 |
|
| 110 |
<p className="text-center text-sm text-muted-foreground mt-6">
|
| 111 |
+
{t("login.noAccount")}{" "}
|
| 112 |
<Link href="/register" className="text-primary hover:underline font-medium">
|
| 113 |
+
{t("login.createOne")}
|
| 114 |
</Link>
|
| 115 |
</p>
|
| 116 |
</CardContent>
|
frontend/src/app/register/page.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
import { useState } from "react";
|
| 4 |
import { useRouter } from "next/navigation";
|
| 5 |
import { useAuth } from "@/lib/auth";
|
|
|
|
| 6 |
import { Button } from "@/components/ui/button";
|
| 7 |
import { Input } from "@/components/ui/input";
|
| 8 |
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
@@ -11,6 +12,7 @@ import Link from "next/link";
|
|
| 11 |
|
| 12 |
export default function RegisterPage() {
|
| 13 |
const { register } = useAuth();
|
|
|
|
| 14 |
const router = useRouter();
|
| 15 |
const [username, setUsername] = useState("");
|
| 16 |
const [email, setEmail] = useState("");
|
|
@@ -28,7 +30,7 @@ export default function RegisterPage() {
|
|
| 28 |
await register(username, email, password);
|
| 29 |
router.replace("/dashboard");
|
| 30 |
} catch (err: unknown) {
|
| 31 |
-
const message = err instanceof Error ? err.message : "
|
| 32 |
setError(message);
|
| 33 |
} finally {
|
| 34 |
setLoading(false);
|
|
@@ -46,8 +48,8 @@ export default function RegisterPage() {
|
|
| 46 |
<Brain className="w-6 h-6 text-primary" />
|
| 47 |
</div>
|
| 48 |
</div>
|
| 49 |
-
<CardTitle className="text-2xl font-bold">
|
| 50 |
-
<CardDescription>
|
| 51 |
</CardHeader>
|
| 52 |
|
| 53 |
<CardContent>
|
|
@@ -59,7 +61,7 @@ export default function RegisterPage() {
|
|
| 59 |
)}
|
| 60 |
|
| 61 |
<div className="space-y-2">
|
| 62 |
-
<label className="text-sm font-medium">
|
| 63 |
<Input
|
| 64 |
id="reg-username"
|
| 65 |
type="text"
|
|
@@ -73,7 +75,7 @@ export default function RegisterPage() {
|
|
| 73 |
</div>
|
| 74 |
|
| 75 |
<div className="space-y-2">
|
| 76 |
-
<label className="text-sm font-medium">
|
| 77 |
<Input
|
| 78 |
id="reg-email"
|
| 79 |
type="email"
|
|
@@ -86,12 +88,12 @@ export default function RegisterPage() {
|
|
| 86 |
</div>
|
| 87 |
|
| 88 |
<div className="space-y-2">
|
| 89 |
-
<label className="text-sm font-medium">
|
| 90 |
<div className="relative">
|
| 91 |
<Input
|
| 92 |
id="reg-password"
|
| 93 |
type={showPw ? "text" : "password"}
|
| 94 |
-
placeholder="
|
| 95 |
value={password}
|
| 96 |
onChange={(e) => setPassword(e.target.value)}
|
| 97 |
required
|
|
@@ -112,18 +114,18 @@ export default function RegisterPage() {
|
|
| 112 |
{loading ? (
|
| 113 |
<span className="flex items-center gap-2">
|
| 114 |
<span className="w-4 h-4 border-2 border-primary-foreground/30 border-t-primary-foreground rounded-full animate-spin" />
|
| 115 |
-
|
| 116 |
</span>
|
| 117 |
) : (
|
| 118 |
-
"
|
| 119 |
)}
|
| 120 |
</Button>
|
| 121 |
</form>
|
| 122 |
|
| 123 |
<p className="text-center text-sm text-muted-foreground mt-6">
|
| 124 |
-
|
| 125 |
<Link href="/login" className="text-primary hover:underline font-medium">
|
| 126 |
-
|
| 127 |
</Link>
|
| 128 |
</p>
|
| 129 |
</CardContent>
|
|
|
|
| 3 |
import { useState } from "react";
|
| 4 |
import { useRouter } from "next/navigation";
|
| 5 |
import { useAuth } from "@/lib/auth";
|
| 6 |
+
import { useTranslation } from "react-i18next";
|
| 7 |
import { Button } from "@/components/ui/button";
|
| 8 |
import { Input } from "@/components/ui/input";
|
| 9 |
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
|
|
| 12 |
|
| 13 |
export default function RegisterPage() {
|
| 14 |
const { register } = useAuth();
|
| 15 |
+
const { t } = useTranslation();
|
| 16 |
const router = useRouter();
|
| 17 |
const [username, setUsername] = useState("");
|
| 18 |
const [email, setEmail] = useState("");
|
|
|
|
| 30 |
await register(username, email, password);
|
| 31 |
router.replace("/dashboard");
|
| 32 |
} catch (err: unknown) {
|
| 33 |
+
const message = err instanceof Error ? err.message : t("register.fallbackError");
|
| 34 |
setError(message);
|
| 35 |
} finally {
|
| 36 |
setLoading(false);
|
|
|
|
| 48 |
<Brain className="w-6 h-6 text-primary" />
|
| 49 |
</div>
|
| 50 |
</div>
|
| 51 |
+
<CardTitle className="text-2xl font-bold">{t("register.title")}</CardTitle>
|
| 52 |
+
<CardDescription>{t("register.description")}</CardDescription>
|
| 53 |
</CardHeader>
|
| 54 |
|
| 55 |
<CardContent>
|
|
|
|
| 61 |
)}
|
| 62 |
|
| 63 |
<div className="space-y-2">
|
| 64 |
+
<label className="text-sm font-medium">{t("common.username")}</label>
|
| 65 |
<Input
|
| 66 |
id="reg-username"
|
| 67 |
type="text"
|
|
|
|
| 75 |
</div>
|
| 76 |
|
| 77 |
<div className="space-y-2">
|
| 78 |
+
<label className="text-sm font-medium">{t("common.email")}</label>
|
| 79 |
<Input
|
| 80 |
id="reg-email"
|
| 81 |
type="email"
|
|
|
|
| 88 |
</div>
|
| 89 |
|
| 90 |
<div className="space-y-2">
|
| 91 |
+
<label className="text-sm font-medium">{t("common.password")}</label>
|
| 92 |
<div className="relative">
|
| 93 |
<Input
|
| 94 |
id="reg-password"
|
| 95 |
type={showPw ? "text" : "password"}
|
| 96 |
+
placeholder={t("register.passwordPlaceholder")}
|
| 97 |
value={password}
|
| 98 |
onChange={(e) => setPassword(e.target.value)}
|
| 99 |
required
|
|
|
|
| 114 |
{loading ? (
|
| 115 |
<span className="flex items-center gap-2">
|
| 116 |
<span className="w-4 h-4 border-2 border-primary-foreground/30 border-t-primary-foreground rounded-full animate-spin" />
|
| 117 |
+
{t("register.submitting")}
|
| 118 |
</span>
|
| 119 |
) : (
|
| 120 |
+
t("register.submit")
|
| 121 |
)}
|
| 122 |
</Button>
|
| 123 |
</form>
|
| 124 |
|
| 125 |
<p className="text-center text-sm text-muted-foreground mt-6">
|
| 126 |
+
{t("register.hasAccount")}{" "}
|
| 127 |
<Link href="/login" className="text-primary hover:underline font-medium">
|
| 128 |
+
{t("register.signIn")}
|
| 129 |
</Link>
|
| 130 |
</p>
|
| 131 |
</CardContent>
|
frontend/src/components/chat/ChatPanel.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import { useState, useRef, useEffect } from "react";
|
|
|
|
| 4 |
import type { DocInfo } from "@/app/dashboard/page";
|
| 5 |
import { api, API_BASE } from "@/lib/api";
|
| 6 |
import { Button } from "@/components/ui/button";
|
|
@@ -31,6 +32,7 @@ interface Props {
|
|
| 31 |
}
|
| 32 |
|
| 33 |
export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
|
|
| 34 |
const [messages, setMessages] = useState<ChatMsg[]>([]);
|
| 35 |
const [input, setInput] = useState("");
|
| 36 |
const [streaming, setStreaming] = useState(false);
|
|
@@ -189,7 +191,9 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 189 |
m.id === assistantId
|
| 190 |
? {
|
| 191 |
...m,
|
| 192 |
-
content:
|
|
|
|
|
|
|
| 193 |
isStreaming: false,
|
| 194 |
}
|
| 195 |
: m
|
|
@@ -202,7 +206,7 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 202 |
};
|
| 203 |
|
| 204 |
const handleClear = async () => {
|
| 205 |
-
if (!activeDoc || !confirm("
|
| 206 |
try {
|
| 207 |
await api.delete(`/api/v1/chat/history/${activeDoc.id}`);
|
| 208 |
setMessages([]);
|
|
@@ -254,12 +258,12 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 254 |
<MessageSquare className="w-8 h-8 text-primary/60" />
|
| 255 |
</div>
|
| 256 |
<h3 className="text-lg font-semibold mb-1">
|
| 257 |
-
{activeDoc ? "
|
| 258 |
</h3>
|
| 259 |
<p className="text-sm text-muted-foreground text-center max-w-sm">
|
| 260 |
{activeDoc
|
| 261 |
-
?
|
| 262 |
-
: "
|
| 263 |
</p>
|
| 264 |
</div>
|
| 265 |
) : (
|
|
@@ -297,8 +301,8 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 297 |
onKeyDown={handleKeyDown}
|
| 298 |
placeholder={
|
| 299 |
activeDoc
|
| 300 |
-
?
|
| 301 |
-
: "
|
| 302 |
}
|
| 303 |
disabled={streaming}
|
| 304 |
className="min-h-[44px] max-h-32 resize-none bg-background/50 border-border/50"
|
|
@@ -328,7 +332,7 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 328 |
size="icon"
|
| 329 |
onClick={() => setShowExportMenu((v) => !v)}
|
| 330 |
className="h-[44px] w-[44px] text-muted-foreground hover:text-primary"
|
| 331 |
-
title="
|
| 332 |
>
|
| 333 |
<Download className="w-4 h-4" />
|
| 334 |
</Button>
|
|
@@ -340,7 +344,7 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 340 |
className="w-full flex items-center gap-2 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors text-left"
|
| 341 |
>
|
| 342 |
<span className="text-base">📝</span>
|
| 343 |
-
|
| 344 |
</button>
|
| 345 |
<button
|
| 346 |
id="export-txt-btn"
|
|
@@ -348,7 +352,7 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 348 |
className="w-full flex items-center gap-2 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors text-left"
|
| 349 |
>
|
| 350 |
<span className="text-base">📄</span>
|
| 351 |
-
|
| 352 |
</button>
|
| 353 |
</div>
|
| 354 |
)}
|
|
@@ -369,4 +373,4 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
|
| 369 |
</div>
|
| 370 |
</div>
|
| 371 |
);
|
| 372 |
-
}
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import { useState, useRef, useEffect } from "react";
|
| 4 |
+
import { useTranslation } from "react-i18next";
|
| 5 |
import type { DocInfo } from "@/app/dashboard/page";
|
| 6 |
import { api, API_BASE } from "@/lib/api";
|
| 7 |
import { Button } from "@/components/ui/button";
|
|
|
|
| 32 |
}
|
| 33 |
|
| 34 |
export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
|
| 35 |
+
const { t } = useTranslation();
|
| 36 |
const [messages, setMessages] = useState<ChatMsg[]>([]);
|
| 37 |
const [input, setInput] = useState("");
|
| 38 |
const [streaming, setStreaming] = useState(false);
|
|
|
|
| 191 |
m.id === assistantId
|
| 192 |
? {
|
| 193 |
...m,
|
| 194 |
+
content: t("chat.fallbackError", {
|
| 195 |
+
message: err instanceof Error ? err.message : "Unknown error",
|
| 196 |
+
}),
|
| 197 |
isStreaming: false,
|
| 198 |
}
|
| 199 |
: m
|
|
|
|
| 206 |
};
|
| 207 |
|
| 208 |
const handleClear = async () => {
|
| 209 |
+
if (!activeDoc || !confirm(t("chat.clearConfirm"))) return;
|
| 210 |
try {
|
| 211 |
await api.delete(`/api/v1/chat/history/${activeDoc.id}`);
|
| 212 |
setMessages([]);
|
|
|
|
| 258 |
<MessageSquare className="w-8 h-8 text-primary/60" />
|
| 259 |
</div>
|
| 260 |
<h3 className="text-lg font-semibold mb-1">
|
| 261 |
+
{activeDoc ? t("chat.askAboutDocument") : t("chat.selectDocument")}
|
| 262 |
</h3>
|
| 263 |
<p className="text-sm text-muted-foreground text-center max-w-sm">
|
| 264 |
{activeDoc
|
| 265 |
+
? t("chat.readyPrompt", { name: activeDoc.original_name })
|
| 266 |
+
: t("chat.uploadPrompt")}
|
| 267 |
</p>
|
| 268 |
</div>
|
| 269 |
) : (
|
|
|
|
| 301 |
onKeyDown={handleKeyDown}
|
| 302 |
placeholder={
|
| 303 |
activeDoc
|
| 304 |
+
? t("chat.askPlaceholder", { name: activeDoc.original_name })
|
| 305 |
+
: t("chat.selectPlaceholder")
|
| 306 |
}
|
| 307 |
disabled={streaming}
|
| 308 |
className="min-h-[44px] max-h-32 resize-none bg-background/50 border-border/50"
|
|
|
|
| 332 |
size="icon"
|
| 333 |
onClick={() => setShowExportMenu((v) => !v)}
|
| 334 |
className="h-[44px] w-[44px] text-muted-foreground hover:text-primary"
|
| 335 |
+
title={t("chat.exportTitle")}
|
| 336 |
>
|
| 337 |
<Download className="w-4 h-4" />
|
| 338 |
</Button>
|
|
|
|
| 344 |
className="w-full flex items-center gap-2 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors text-left"
|
| 345 |
>
|
| 346 |
<span className="text-base">📝</span>
|
| 347 |
+
{t("chat.markdown")}
|
| 348 |
</button>
|
| 349 |
<button
|
| 350 |
id="export-txt-btn"
|
|
|
|
| 352 |
className="w-full flex items-center gap-2 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors text-left"
|
| 353 |
>
|
| 354 |
<span className="text-base">📄</span>
|
| 355 |
+
{t("chat.plainText")}
|
| 356 |
</button>
|
| 357 |
</div>
|
| 358 |
)}
|
|
|
|
| 373 |
</div>
|
| 374 |
</div>
|
| 375 |
);
|
| 376 |
+
}
|
frontend/src/components/document/DocumentSidebar.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import { useState, useCallback } from "react";
|
|
|
|
| 4 |
import type { DocInfo } from "@/app/dashboard/page";
|
| 5 |
import { api } from "@/lib/api";
|
| 6 |
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
@@ -20,6 +21,7 @@ interface Props {
|
|
| 20 |
}
|
| 21 |
|
| 22 |
export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc, onDocumentsChange }: Props) {
|
|
|
|
| 23 |
const [uploading, setUploading] = useState(false);
|
| 24 |
const [uploadProgress, setUploadProgress] = useState(0);
|
| 25 |
const [uploadError, setUploadError] = useState("");
|
|
@@ -43,7 +45,7 @@ export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc
|
|
| 43 |
}
|
| 44 |
onDocumentsChange();
|
| 45 |
} catch (err) {
|
| 46 |
-
const message = err instanceof Error ? err.message : "
|
| 47 |
setUploadError(message);
|
| 48 |
} finally {
|
| 49 |
setUploading(false);
|
|
@@ -51,7 +53,7 @@ export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc
|
|
| 51 |
}
|
| 52 |
})();
|
| 53 |
},
|
| 54 |
-
[onDocumentsChange]
|
| 55 |
);
|
| 56 |
|
| 57 |
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
@@ -67,7 +69,7 @@ export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc
|
|
| 67 |
|
| 68 |
const handleDelete = async (docId: string, e: React.MouseEvent) => {
|
| 69 |
e.stopPropagation();
|
| 70 |
-
if (!confirm("
|
| 71 |
setDeleting(docId);
|
| 72 |
try {
|
| 73 |
await api.delete(`/api/v1/documents/${docId}`);
|
|
@@ -119,17 +121,17 @@ export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc
|
|
| 119 |
{uploading ? (
|
| 120 |
<div className="space-y-2">
|
| 121 |
<Loader2 className="w-5 h-5 mx-auto animate-spin text-primary" />
|
| 122 |
-
<p className="text-xs text-muted-foreground">
|
| 123 |
<Progress value={uploadProgress} className="h-1" />
|
| 124 |
</div>
|
| 125 |
) : (
|
| 126 |
<>
|
| 127 |
<Upload className="w-5 h-5 mx-auto text-muted-foreground mb-2" />
|
| 128 |
<p className="text-xs text-muted-foreground">
|
| 129 |
-
{isDragActive ? "
|
| 130 |
</p>
|
| 131 |
<p className="text-[10px] text-muted-foreground/60 mt-1">
|
| 132 |
-
|
| 133 |
</p>
|
| 134 |
</>
|
| 135 |
)}
|
|
@@ -139,7 +141,7 @@ export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc
|
|
| 139 |
{/* ── Documents List ──────────────────────────── */}
|
| 140 |
<div className="px-3 pt-3 pb-1">
|
| 141 |
<h3 className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-2">
|
| 142 |
-
|
| 143 |
</h3>
|
| 144 |
</div>
|
| 145 |
|
|
@@ -147,8 +149,8 @@ export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc
|
|
| 147 |
{documents.length === 0 ? (
|
| 148 |
<div className="text-center py-12">
|
| 149 |
<FolderOpen className="w-8 h-8 mx-auto text-muted-foreground/40 mb-3" />
|
| 150 |
-
<p className="text-sm text-muted-foreground">
|
| 151 |
-
<p className="text-xs text-muted-foreground/60 mt-1">
|
| 152 |
</div>
|
| 153 |
) : (
|
| 154 |
<div className="space-y-1 pb-3">
|
|
@@ -176,22 +178,22 @@ export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc
|
|
| 176 |
<>
|
| 177 |
<span className="text-[10px] text-muted-foreground">•</span>
|
| 178 |
<span className="text-[10px] text-muted-foreground">
|
| 179 |
-
{doc.page_count
|
| 180 |
</span>
|
| 181 |
<span className="text-[10px] text-muted-foreground">•</span>
|
| 182 |
<span className="text-[10px] text-muted-foreground">
|
| 183 |
-
{doc.chunk_count
|
| 184 |
</span>
|
| 185 |
</>
|
| 186 |
)}
|
| 187 |
{doc.status === "processing" && (
|
| 188 |
<Badge variant="secondary" className="text-[9px] h-4 px-1.5">
|
| 189 |
-
|
| 190 |
</Badge>
|
| 191 |
)}
|
| 192 |
{doc.status === "failed" && (
|
| 193 |
<Badge variant="destructive" className="text-[9px] h-4 px-1.5">
|
| 194 |
-
|
| 195 |
</Badge>
|
| 196 |
)}
|
| 197 |
</div>
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import { useState, useCallback } from "react";
|
| 4 |
+
import { useTranslation } from "react-i18next";
|
| 5 |
import type { DocInfo } from "@/app/dashboard/page";
|
| 6 |
import { api } from "@/lib/api";
|
| 7 |
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
|
|
| 21 |
}
|
| 22 |
|
| 23 |
export default function DocumentSidebar({ documents = [], activeDoc, onSelectDoc, onDocumentsChange }: Props) {
|
| 24 |
+
const { t } = useTranslation();
|
| 25 |
const [uploading, setUploading] = useState(false);
|
| 26 |
const [uploadProgress, setUploadProgress] = useState(0);
|
| 27 |
const [uploadError, setUploadError] = useState("");
|
|
|
|
| 45 |
}
|
| 46 |
onDocumentsChange();
|
| 47 |
} catch (err) {
|
| 48 |
+
const message = err instanceof Error ? err.message : t("documents.uploadFailed");
|
| 49 |
setUploadError(message);
|
| 50 |
} finally {
|
| 51 |
setUploading(false);
|
|
|
|
| 53 |
}
|
| 54 |
})();
|
| 55 |
},
|
| 56 |
+
[onDocumentsChange, t]
|
| 57 |
);
|
| 58 |
|
| 59 |
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
|
|
| 69 |
|
| 70 |
const handleDelete = async (docId: string, e: React.MouseEvent) => {
|
| 71 |
e.stopPropagation();
|
| 72 |
+
if (!confirm(t("documents.deleteConfirm"))) return;
|
| 73 |
setDeleting(docId);
|
| 74 |
try {
|
| 75 |
await api.delete(`/api/v1/documents/${docId}`);
|
|
|
|
| 121 |
{uploading ? (
|
| 122 |
<div className="space-y-2">
|
| 123 |
<Loader2 className="w-5 h-5 mx-auto animate-spin text-primary" />
|
| 124 |
+
<p className="text-xs text-muted-foreground">{t("documents.uploading")}</p>
|
| 125 |
<Progress value={uploadProgress} className="h-1" />
|
| 126 |
</div>
|
| 127 |
) : (
|
| 128 |
<>
|
| 129 |
<Upload className="w-5 h-5 mx-auto text-muted-foreground mb-2" />
|
| 130 |
<p className="text-xs text-muted-foreground">
|
| 131 |
+
{isDragActive ? t("documents.dropHere") : t("documents.dropOrClick")}
|
| 132 |
</p>
|
| 133 |
<p className="text-[10px] text-muted-foreground/60 mt-1">
|
| 134 |
+
{t("documents.uploadFormats")}
|
| 135 |
</p>
|
| 136 |
</>
|
| 137 |
)}
|
|
|
|
| 141 |
{/* ── Documents List ──────────────────────────── */}
|
| 142 |
<div className="px-3 pt-3 pb-1">
|
| 143 |
<h3 className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-2">
|
| 144 |
+
{t("documents.documentsTitle", { count: documents.length })}
|
| 145 |
</h3>
|
| 146 |
</div>
|
| 147 |
|
|
|
|
| 149 |
{documents.length === 0 ? (
|
| 150 |
<div className="text-center py-12">
|
| 151 |
<FolderOpen className="w-8 h-8 mx-auto text-muted-foreground/40 mb-3" />
|
| 152 |
+
<p className="text-sm text-muted-foreground">{t("documents.noDocuments")}</p>
|
| 153 |
+
<p className="text-xs text-muted-foreground/60 mt-1">{t("documents.getStarted")}</p>
|
| 154 |
</div>
|
| 155 |
) : (
|
| 156 |
<div className="space-y-1 pb-3">
|
|
|
|
| 178 |
<>
|
| 179 |
<span className="text-[10px] text-muted-foreground">•</span>
|
| 180 |
<span className="text-[10px] text-muted-foreground">
|
| 181 |
+
{t("documents.pagesShort", { count: doc.page_count })}
|
| 182 |
</span>
|
| 183 |
<span className="text-[10px] text-muted-foreground">•</span>
|
| 184 |
<span className="text-[10px] text-muted-foreground">
|
| 185 |
+
{t("documents.chunks", { count: doc.chunk_count })}
|
| 186 |
</span>
|
| 187 |
</>
|
| 188 |
)}
|
| 189 |
{doc.status === "processing" && (
|
| 190 |
<Badge variant="secondary" className="text-[9px] h-4 px-1.5">
|
| 191 |
+
{t("documents.processing")}
|
| 192 |
</Badge>
|
| 193 |
)}
|
| 194 |
{doc.status === "failed" && (
|
| 195 |
<Badge variant="destructive" className="text-[9px] h-4 px-1.5">
|
| 196 |
+
{t("documents.failed")}
|
| 197 |
</Badge>
|
| 198 |
)}
|
| 199 |
</div>
|
frontend/src/components/layout/Header.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import { useAuth } from "@/lib/auth";
|
|
|
|
| 4 |
import { useRouter } from "next/navigation";
|
| 5 |
import { Button } from "@/components/ui/button";
|
| 6 |
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
@@ -32,6 +33,7 @@ interface HeaderProps {
|
|
| 32 |
|
| 33 |
export default function Header({ sidebarOpen, onToggleSidebar, viewerOpen, onToggleViewer }: HeaderProps) {
|
| 34 |
const { user, logout } = useAuth();
|
|
|
|
| 35 |
const router = useRouter();
|
| 36 |
const [isDark, setIsDark] = useState(true);
|
| 37 |
|
|
@@ -52,11 +54,24 @@ export default function Header({ sidebarOpen, onToggleSidebar, viewerOpen, onTog
|
|
| 52 |
router.replace("/login");
|
| 53 |
};
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
return (
|
| 56 |
<header className="h-14 flex items-center justify-between px-4 border-b border-border/50 bg-card/50 backdrop-blur-md flex-shrink-0 z-50">
|
| 57 |
{/* Left */}
|
| 58 |
<div className="flex items-center gap-3">
|
| 59 |
-
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggleSidebar} title={sidebarOpen ? "
|
| 60 |
{sidebarOpen ? <PanelLeftClose className="w-4 h-4" /> : <PanelLeftOpen className="w-4 h-4" />}
|
| 61 |
</Button>
|
| 62 |
|
|
@@ -64,20 +79,32 @@ export default function Header({ sidebarOpen, onToggleSidebar, viewerOpen, onTog
|
|
| 64 |
<div className="w-7 h-7 rounded-lg bg-primary/15 flex items-center justify-center">
|
| 65 |
<Brain className="w-4 h-4 text-primary" />
|
| 66 |
</div>
|
| 67 |
-
<span className="font-semibold text-sm hidden sm:inline">
|
| 68 |
</div>
|
| 69 |
</div>
|
| 70 |
|
| 71 |
{/* Right */}
|
| 72 |
<div className="flex items-center gap-2">
|
| 73 |
-
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggleViewer} title={viewerOpen ? "
|
| 74 |
{viewerOpen ? <PanelRightClose className="w-4 h-4" /> : <PanelRightOpen className="w-4 h-4" />}
|
| 75 |
</Button>
|
| 76 |
|
| 77 |
-
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={toggleTheme} title={isDark ? "
|
| 78 |
{isDark ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
|
| 79 |
</Button>
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
<DropdownMenu>
|
| 82 |
<DropdownMenuTrigger className="flex items-center h-8 gap-2 px-2 rounded-md hover:bg-accent transition-colors cursor-pointer">
|
| 83 |
<Avatar className="w-6 h-6">
|
|
@@ -95,7 +122,7 @@ export default function Header({ sidebarOpen, onToggleSidebar, viewerOpen, onTog
|
|
| 95 |
<DropdownMenuSeparator />
|
| 96 |
<DropdownMenuItem className="text-destructive cursor-pointer" onClick={handleLogout}>
|
| 97 |
<LogOut className="w-4 h-4 mr-2" />
|
| 98 |
-
|
| 99 |
</DropdownMenuItem>
|
| 100 |
</DropdownMenuContent>
|
| 101 |
</DropdownMenu>
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
import { useAuth } from "@/lib/auth";
|
| 4 |
+
import { useTranslation } from "react-i18next";
|
| 5 |
import { useRouter } from "next/navigation";
|
| 6 |
import { Button } from "@/components/ui/button";
|
| 7 |
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
|
|
| 33 |
|
| 34 |
export default function Header({ sidebarOpen, onToggleSidebar, viewerOpen, onToggleViewer }: HeaderProps) {
|
| 35 |
const { user, logout } = useAuth();
|
| 36 |
+
const { t, i18n } = useTranslation();
|
| 37 |
const router = useRouter();
|
| 38 |
const [isDark, setIsDark] = useState(true);
|
| 39 |
|
|
|
|
| 54 |
router.replace("/login");
|
| 55 |
};
|
| 56 |
|
| 57 |
+
const languageLabel = (language: string) => {
|
| 58 |
+
switch (language) {
|
| 59 |
+
case "hi":
|
| 60 |
+
return t("common.hindi");
|
| 61 |
+
case "es":
|
| 62 |
+
return t("common.spanish");
|
| 63 |
+
case "fr":
|
| 64 |
+
return t("common.french");
|
| 65 |
+
default:
|
| 66 |
+
return t("common.english");
|
| 67 |
+
}
|
| 68 |
+
};
|
| 69 |
+
|
| 70 |
return (
|
| 71 |
<header className="h-14 flex items-center justify-between px-4 border-b border-border/50 bg-card/50 backdrop-blur-md flex-shrink-0 z-50">
|
| 72 |
{/* Left */}
|
| 73 |
<div className="flex items-center gap-3">
|
| 74 |
+
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggleSidebar} title={sidebarOpen ? t("header.closeSidebar") : t("header.openSidebar")}>
|
| 75 |
{sidebarOpen ? <PanelLeftClose className="w-4 h-4" /> : <PanelLeftOpen className="w-4 h-4" />}
|
| 76 |
</Button>
|
| 77 |
|
|
|
|
| 79 |
<div className="w-7 h-7 rounded-lg bg-primary/15 flex items-center justify-center">
|
| 80 |
<Brain className="w-4 h-4 text-primary" />
|
| 81 |
</div>
|
| 82 |
+
<span className="font-semibold text-sm hidden sm:inline">{t("common.appName")}</span>
|
| 83 |
</div>
|
| 84 |
</div>
|
| 85 |
|
| 86 |
{/* Right */}
|
| 87 |
<div className="flex items-center gap-2">
|
| 88 |
+
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggleViewer} title={viewerOpen ? t("header.closeViewer") : t("header.openViewer")}>
|
| 89 |
{viewerOpen ? <PanelRightClose className="w-4 h-4" /> : <PanelRightOpen className="w-4 h-4" />}
|
| 90 |
</Button>
|
| 91 |
|
| 92 |
+
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={toggleTheme} title={isDark ? t("header.lightMode") : t("header.darkMode")}>
|
| 93 |
{isDark ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
|
| 94 |
</Button>
|
| 95 |
|
| 96 |
+
<select
|
| 97 |
+
aria-label={t("common.language")}
|
| 98 |
+
value={i18n.resolvedLanguage || "en"}
|
| 99 |
+
onChange={(e) => void i18n.changeLanguage(e.target.value)}
|
| 100 |
+
className="h-8 rounded-md border border-border bg-background px-2 text-xs text-foreground"
|
| 101 |
+
>
|
| 102 |
+
<option value="en">{languageLabel("en")}</option>
|
| 103 |
+
<option value="hi">{languageLabel("hi")}</option>
|
| 104 |
+
<option value="es">{languageLabel("es")}</option>
|
| 105 |
+
<option value="fr">{languageLabel("fr")}</option>
|
| 106 |
+
</select>
|
| 107 |
+
|
| 108 |
<DropdownMenu>
|
| 109 |
<DropdownMenuTrigger className="flex items-center h-8 gap-2 px-2 rounded-md hover:bg-accent transition-colors cursor-pointer">
|
| 110 |
<Avatar className="w-6 h-6">
|
|
|
|
| 122 |
<DropdownMenuSeparator />
|
| 123 |
<DropdownMenuItem className="text-destructive cursor-pointer" onClick={handleLogout}>
|
| 124 |
<LogOut className="w-4 h-4 mr-2" />
|
| 125 |
+
{t("header.signOut")}
|
| 126 |
</DropdownMenuItem>
|
| 127 |
</DropdownMenuContent>
|
| 128 |
</DropdownMenu>
|
frontend/src/components/providers/I18nProvider.tsx
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client";
|
| 2 |
+
|
| 3 |
+
import { useEffect } from "react";
|
| 4 |
+
import { I18nextProvider } from "react-i18next";
|
| 5 |
+
import i18n from "@/lib/i18n";
|
| 6 |
+
|
| 7 |
+
export default function I18nProvider({ children }: { children: React.ReactNode }) {
|
| 8 |
+
useEffect(() => {
|
| 9 |
+
document.documentElement.lang = i18n.resolvedLanguage || "en";
|
| 10 |
+
|
| 11 |
+
const handleLanguageChanged = (language: string) => {
|
| 12 |
+
document.documentElement.lang = language;
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
i18n.on("languageChanged", handleLanguageChanged);
|
| 16 |
+
return () => {
|
| 17 |
+
i18n.off("languageChanged", handleLanguageChanged);
|
| 18 |
+
};
|
| 19 |
+
}, []);
|
| 20 |
+
|
| 21 |
+
return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
|
| 22 |
+
}
|
frontend/src/lib/i18n.ts
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client";
|
| 2 |
+
|
| 3 |
+
import i18n from "i18next";
|
| 4 |
+
import LanguageDetector from "i18next-browser-languagedetector";
|
| 5 |
+
import { initReactI18next } from "react-i18next";
|
| 6 |
+
|
| 7 |
+
const resources = {
|
| 8 |
+
en: {
|
| 9 |
+
translation: {
|
| 10 |
+
common: {
|
| 11 |
+
appName: "Document AI Analyst",
|
| 12 |
+
language: "Language",
|
| 13 |
+
english: "English",
|
| 14 |
+
hindi: "Hindi",
|
| 15 |
+
spanish: "Spanish",
|
| 16 |
+
french: "French",
|
| 17 |
+
email: "Email",
|
| 18 |
+
password: "Password",
|
| 19 |
+
username: "Username",
|
| 20 |
+
},
|
| 21 |
+
header: {
|
| 22 |
+
closeSidebar: "Close sidebar",
|
| 23 |
+
openSidebar: "Open sidebar",
|
| 24 |
+
closeViewer: "Close viewer",
|
| 25 |
+
openViewer: "Open viewer",
|
| 26 |
+
lightMode: "Light mode",
|
| 27 |
+
darkMode: "Dark mode",
|
| 28 |
+
signOut: "Sign out",
|
| 29 |
+
},
|
| 30 |
+
login: {
|
| 31 |
+
title: "Welcome back",
|
| 32 |
+
description: "Sign in to your Document AI Analyst account",
|
| 33 |
+
fallbackError: "Login failed",
|
| 34 |
+
submit: "Sign In",
|
| 35 |
+
submitting: "Signing in...",
|
| 36 |
+
noAccount: "Don't have an account?",
|
| 37 |
+
createOne: "Create one",
|
| 38 |
+
},
|
| 39 |
+
register: {
|
| 40 |
+
title: "Create Account",
|
| 41 |
+
description: "Start analyzing documents with AI",
|
| 42 |
+
fallbackError: "Registration failed",
|
| 43 |
+
passwordPlaceholder: "Minimum 6 characters",
|
| 44 |
+
submit: "Create Account",
|
| 45 |
+
submitting: "Creating account...",
|
| 46 |
+
hasAccount: "Already have an account?",
|
| 47 |
+
signIn: "Sign in",
|
| 48 |
+
},
|
| 49 |
+
chat: {
|
| 50 |
+
askAboutDocument: "Ask about your document",
|
| 51 |
+
selectDocument: "Select a document",
|
| 52 |
+
readyPrompt: "\"{{name}}\" is ready. Ask any question and get cited answers.",
|
| 53 |
+
uploadPrompt: "Upload and select a document from the sidebar to start chatting.",
|
| 54 |
+
fallbackError: "Failed to get response: {{message}}",
|
| 55 |
+
clearConfirm: "Clear all chat history for this document?",
|
| 56 |
+
askPlaceholder: "Ask about \"{{name}}\"...",
|
| 57 |
+
selectPlaceholder: "Select a document first...",
|
| 58 |
+
exportTitle: "Export chat history",
|
| 59 |
+
markdown: "Markdown (.md)",
|
| 60 |
+
plainText: "Plain Text (.txt)",
|
| 61 |
+
},
|
| 62 |
+
documents: {
|
| 63 |
+
uploadFailed: "Upload failed",
|
| 64 |
+
deleteConfirm: "Delete this document and all its data?",
|
| 65 |
+
uploading: "Uploading...",
|
| 66 |
+
dropHere: "Drop files here",
|
| 67 |
+
dropOrClick: "Drop files or click to upload",
|
| 68 |
+
uploadFormats: "PDF, DOCX, TXT, MD (max 50MB)",
|
| 69 |
+
documentsTitle: "Documents ({{count}})",
|
| 70 |
+
noDocuments: "No documents yet",
|
| 71 |
+
getStarted: "Upload a file to get started",
|
| 72 |
+
pagesShort: "{{count}} pg",
|
| 73 |
+
chunks: "{{count}} chunks",
|
| 74 |
+
processing: "Processing",
|
| 75 |
+
failed: "Failed",
|
| 76 |
+
},
|
| 77 |
+
},
|
| 78 |
+
},
|
| 79 |
+
hi: {
|
| 80 |
+
translation: {
|
| 81 |
+
common: {
|
| 82 |
+
appName: "डॉक्यूमेंट एआई एनालिस्ट",
|
| 83 |
+
language: "भाषा",
|
| 84 |
+
english: "अंग्रेज़ी",
|
| 85 |
+
hindi: "हिंदी",
|
| 86 |
+
spanish: "स्पेनिश",
|
| 87 |
+
french: "फ़्रेंच",
|
| 88 |
+
email: "ईमेल",
|
| 89 |
+
password: "पासवर्ड",
|
| 90 |
+
username: "उपयोगकर्ता नाम",
|
| 91 |
+
},
|
| 92 |
+
header: {
|
| 93 |
+
closeSidebar: "साइडबार बंद करें",
|
| 94 |
+
openSidebar: "साइडबार खोलें",
|
| 95 |
+
closeViewer: "व्यूअर बंद करें",
|
| 96 |
+
openViewer: "व्यूअर खोलें",
|
| 97 |
+
lightMode: "लाइट मोड",
|
| 98 |
+
darkMode: "डार्क मोड",
|
| 99 |
+
signOut: "साइन आउट",
|
| 100 |
+
},
|
| 101 |
+
login: {
|
| 102 |
+
title: "वापसी पर स्वागत है",
|
| 103 |
+
description: "अपने डॉक्यूमेंट एआई एनालिस्ट खाते में साइन इन करें",
|
| 104 |
+
fallbackError: "लॉगिन विफल",
|
| 105 |
+
submit: "साइन इन करें",
|
| 106 |
+
submitting: "साइन इन हो रहा है...",
|
| 107 |
+
noAccount: "क्या आपका खाता नहीं है?",
|
| 108 |
+
createOne: "एक बनाएं",
|
| 109 |
+
},
|
| 110 |
+
register: {
|
| 111 |
+
title: "खाता बनाएं",
|
| 112 |
+
description: "एआई के साथ दस्तावेज़ों का विश्लेषण शुरू करें",
|
| 113 |
+
fallbackError: "पंजीकरण विफल",
|
| 114 |
+
passwordPlaceholder: "कम से कम 6 अक्षर",
|
| 115 |
+
submit: "खाता बनाएं",
|
| 116 |
+
submitting: "खाता बनाया जा रहा है...",
|
| 117 |
+
hasAccount: "क्या पहले से खाता है?",
|
| 118 |
+
signIn: "साइन इन करें",
|
| 119 |
+
},
|
| 120 |
+
chat: {
|
| 121 |
+
askAboutDocument: "अपने दस्तावेज़ के बारे में पूछें",
|
| 122 |
+
selectDocument: "एक दस्तावेज़ चुनें",
|
| 123 |
+
readyPrompt: "\"{{name}}\" ���ैयार है। कोई भी प्रश्न पूछें और स्रोत सहित उत्तर पाएं।",
|
| 124 |
+
uploadPrompt: "चैट शुरू करने के लिए साइडबार से फ़ाइल अपलोड और चुनें।",
|
| 125 |
+
fallbackError: "जवाब प्राप्त नहीं हुआ: {{message}}",
|
| 126 |
+
clearConfirm: "क्या इस दस्तावेज़ का पूरा चैट इतिहास साफ़ करें?",
|
| 127 |
+
askPlaceholder: "\"{{name}}\" के बारे में पूछें...",
|
| 128 |
+
selectPlaceholder: "पहले एक दस्तावेज़ चुनें...",
|
| 129 |
+
exportTitle: "चैट इतिहास निर्यात करें",
|
| 130 |
+
markdown: "मार्कडाउन (.md)",
|
| 131 |
+
plainText: "सादा पाठ (.txt)",
|
| 132 |
+
},
|
| 133 |
+
documents: {
|
| 134 |
+
uploadFailed: "अपलोड विफल",
|
| 135 |
+
deleteConfirm: "क्या इस दस्तावेज़ और उसके सभी डेटा को हटाएं?",
|
| 136 |
+
uploading: "अपलोड हो रहा है...",
|
| 137 |
+
dropHere: "फ़ाइलें यहाँ छोड़ें",
|
| 138 |
+
dropOrClick: "फ़ाइलें छोड़ें या अपलोड के लिए क्लिक करें",
|
| 139 |
+
uploadFormats: "PDF, DOCX, TXT, MD (अधिकतम 50MB)",
|
| 140 |
+
documentsTitle: "दस्तावेज़ ({{count}})",
|
| 141 |
+
noDocuments: "अभी तक कोई दस्तावेज़ नहीं",
|
| 142 |
+
getStarted: "शुरू करने के लिए फ़ाइल अपलोड करें",
|
| 143 |
+
pagesShort: "{{count}} पेज",
|
| 144 |
+
chunks: "{{count}} खंड",
|
| 145 |
+
processing: "प्रोसेस हो रहा है",
|
| 146 |
+
failed: "विफल",
|
| 147 |
+
},
|
| 148 |
+
},
|
| 149 |
+
},
|
| 150 |
+
es: {
|
| 151 |
+
translation: {
|
| 152 |
+
common: {
|
| 153 |
+
appName: "Analista IA de Documentos",
|
| 154 |
+
language: "Idioma",
|
| 155 |
+
english: "Inglés",
|
| 156 |
+
hindi: "Hindi",
|
| 157 |
+
spanish: "Español",
|
| 158 |
+
french: "Francés",
|
| 159 |
+
email: "Correo electrónico",
|
| 160 |
+
password: "Contraseña",
|
| 161 |
+
username: "Nombre de usuario",
|
| 162 |
+
},
|
| 163 |
+
header: {
|
| 164 |
+
closeSidebar: "Cerrar barra lateral",
|
| 165 |
+
openSidebar: "Abrir barra lateral",
|
| 166 |
+
closeViewer: "Cerrar visor",
|
| 167 |
+
openViewer: "Abrir visor",
|
| 168 |
+
lightMode: "Modo claro",
|
| 169 |
+
darkMode: "Modo oscuro",
|
| 170 |
+
signOut: "Cerrar sesión",
|
| 171 |
+
},
|
| 172 |
+
login: {
|
| 173 |
+
title: "Bienvenido de nuevo",
|
| 174 |
+
description: "Inicia sesión en tu cuenta de Analista IA de Documentos",
|
| 175 |
+
fallbackError: "Error al iniciar sesión",
|
| 176 |
+
submit: "Iniciar sesión",
|
| 177 |
+
submitting: "Iniciando sesión...",
|
| 178 |
+
noAccount: "¿No tienes una cuenta?",
|
| 179 |
+
createOne: "Crear una",
|
| 180 |
+
},
|
| 181 |
+
register: {
|
| 182 |
+
title: "Crear cuenta",
|
| 183 |
+
description: "Empieza a analizar documentos con IA",
|
| 184 |
+
fallbackError: "Error de registro",
|
| 185 |
+
passwordPlaceholder: "Mínimo 6 caracteres",
|
| 186 |
+
submit: "Crear cuenta",
|
| 187 |
+
submitting: "Creando cuenta...",
|
| 188 |
+
hasAccount: "¿Ya tienes una cuenta?",
|
| 189 |
+
signIn: "Inicia sesión",
|
| 190 |
+
},
|
| 191 |
+
chat: {
|
| 192 |
+
askAboutDocument: "Pregunta sobre tu documento",
|
| 193 |
+
selectDocument: "Selecciona un documento",
|
| 194 |
+
readyPrompt: "\"{{name}}\" está listo. Haz cualquier pregunta y obtén respuestas con citas.",
|
| 195 |
+
uploadPrompt: "Sube y selecciona un documento de la barra lateral para comenzar a chatear.",
|
| 196 |
+
fallbackError: "No se pudo obtener respuesta: {{message}}",
|
| 197 |
+
clearConfirm: "¿Borrar todo el historial de chat de este documento?",
|
| 198 |
+
askPlaceholder: "Pregunta sobre \"{{name}}\"...",
|
| 199 |
+
selectPlaceholder: "Primero selecciona un documento...",
|
| 200 |
+
exportTitle: "Exportar historial del chat",
|
| 201 |
+
markdown: "Markdown (.md)",
|
| 202 |
+
plainText: "Texto plano (.txt)",
|
| 203 |
+
},
|
| 204 |
+
documents: {
|
| 205 |
+
uploadFailed: "Error de carga",
|
| 206 |
+
deleteConfirm: "¿Eliminar este documento y todos sus datos?",
|
| 207 |
+
uploading: "Subiendo...",
|
| 208 |
+
dropHere: "Suelta archivos aquí",
|
| 209 |
+
dropOrClick: "Suelta archivos o haz clic para subir",
|
| 210 |
+
uploadFormats: "PDF, DOCX, TXT, MD (máx. 50MB)",
|
| 211 |
+
documentsTitle: "Documentos ({{count}})",
|
| 212 |
+
noDocuments: "Aún no hay documentos",
|
| 213 |
+
getStarted: "Sube un archivo para comenzar",
|
| 214 |
+
pagesShort: "{{count}} pág",
|
| 215 |
+
chunks: "{{count}} fragmentos",
|
| 216 |
+
processing: "Procesando",
|
| 217 |
+
failed: "Falló",
|
| 218 |
+
},
|
| 219 |
+
},
|
| 220 |
+
},
|
| 221 |
+
fr: {
|
| 222 |
+
translation: {
|
| 223 |
+
common: {
|
| 224 |
+
appName: "Analyste IA de Documents",
|
| 225 |
+
language: "Langue",
|
| 226 |
+
english: "Anglais",
|
| 227 |
+
hindi: "Hindi",
|
| 228 |
+
spanish: "Espagnol",
|
| 229 |
+
french: "Français",
|
| 230 |
+
email: "E-mail",
|
| 231 |
+
password: "Mot de passe",
|
| 232 |
+
username: "Nom d'utilisateur",
|
| 233 |
+
},
|
| 234 |
+
header: {
|
| 235 |
+
closeSidebar: "Fermer la barre latérale",
|
| 236 |
+
openSidebar: "Ouvrir la barre latérale",
|
| 237 |
+
closeViewer: "Fermer le lecteur",
|
| 238 |
+
openViewer: "Ouvrir le lecteur",
|
| 239 |
+
lightMode: "Mode clair",
|
| 240 |
+
darkMode: "Mode sombre",
|
| 241 |
+
signOut: "Se déconnecter",
|
| 242 |
+
},
|
| 243 |
+
login: {
|
| 244 |
+
title: "Bon retour",
|
| 245 |
+
description: "Connectez-vous à votre compte Analyste IA de Documents",
|
| 246 |
+
fallbackError: "Échec de la connexion",
|
| 247 |
+
submit: "Se connecter",
|
| 248 |
+
submitting: "Connexion en cours...",
|
| 249 |
+
noAccount: "Vous n'avez pas de compte ?",
|
| 250 |
+
createOne: "En créer un",
|
| 251 |
+
},
|
| 252 |
+
register: {
|
| 253 |
+
title: "Créer un compte",
|
| 254 |
+
description: "Commencez à analyser des documents avec l'IA",
|
| 255 |
+
fallbackError: "Échec de l'inscription",
|
| 256 |
+
passwordPlaceholder: "6 caractères minimum",
|
| 257 |
+
submit: "Créer un compte",
|
| 258 |
+
submitting: "Création du compte...",
|
| 259 |
+
hasAccount: "Vous avez déjà un compte ?",
|
| 260 |
+
signIn: "Se connecter",
|
| 261 |
+
},
|
| 262 |
+
chat: {
|
| 263 |
+
askAboutDocument: "Posez une question sur votre document",
|
| 264 |
+
selectDocument: "Sélectionnez un document",
|
| 265 |
+
readyPrompt: "\"{{name}}\" est prêt. Posez n'importe quelle question et obtenez des réponses sourcées.",
|
| 266 |
+
uploadPrompt: "Importez puis sélectionnez un document dans la barre latérale pour commencer à discuter.",
|
| 267 |
+
fallbackError: "Impossible d'obtenir une réponse : {{message}}",
|
| 268 |
+
clearConfirm: "Effacer tout l'historique de discussion de ce document ?",
|
| 269 |
+
askPlaceholder: "Posez une question sur \"{{name}}\"...",
|
| 270 |
+
selectPlaceholder: "Sélectionnez d'abord un document...",
|
| 271 |
+
exportTitle: "Exporter l'historique du chat",
|
| 272 |
+
markdown: "Markdown (.md)",
|
| 273 |
+
plainText: "Texte brut (.txt)",
|
| 274 |
+
},
|
| 275 |
+
documents: {
|
| 276 |
+
uploadFailed: "Échec de l'envoi",
|
| 277 |
+
deleteConfirm: "Supprimer ce document et toutes ses données ?",
|
| 278 |
+
uploading: "Envoi en cours...",
|
| 279 |
+
dropHere: "Déposez les fichiers ici",
|
| 280 |
+
dropOrClick: "Déposez les fichiers ou cliquez pour téléverser",
|
| 281 |
+
uploadFormats: "PDF, DOCX, TXT, MD (max 50 Mo)",
|
| 282 |
+
documentsTitle: "Documents ({{count}})",
|
| 283 |
+
noDocuments: "Aucun document pour le moment",
|
| 284 |
+
getStarted: "Importez un fichier pour commencer",
|
| 285 |
+
pagesShort: "{{count}} p",
|
| 286 |
+
chunks: "{{count}} segments",
|
| 287 |
+
processing: "Traitement",
|
| 288 |
+
failed: "Échec",
|
| 289 |
+
},
|
| 290 |
+
},
|
| 291 |
+
},
|
| 292 |
+
} as const;
|
| 293 |
+
|
| 294 |
+
if (!i18n.isInitialized) {
|
| 295 |
+
void i18n
|
| 296 |
+
.use(LanguageDetector)
|
| 297 |
+
.use(initReactI18next)
|
| 298 |
+
.init({
|
| 299 |
+
resources,
|
| 300 |
+
fallbackLng: "en",
|
| 301 |
+
supportedLngs: ["en", "hi", "es", "fr"],
|
| 302 |
+
interpolation: {
|
| 303 |
+
escapeValue: false,
|
| 304 |
+
},
|
| 305 |
+
detection: {
|
| 306 |
+
order: ["localStorage", "navigator"],
|
| 307 |
+
caches: ["localStorage"],
|
| 308 |
+
lookupLocalStorage: "i18nextLng",
|
| 309 |
+
},
|
| 310 |
+
react: {
|
| 311 |
+
useSuspense: false,
|
| 312 |
+
},
|
| 313 |
+
});
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
export default i18n;
|