From f432adb710a3da4afd76900fb34c2eed8b6a5154 Mon Sep 17 00:00:00 2001 From: sufyan Date: Thu, 15 Jan 2026 17:20:29 -0500 Subject: [PATCH 1/8] chore: set up drizze client on web client --- apps/web/package.json | 2 +- apps/web/src/lib/db/index.ts | 63 +- .../_search-layout/index.lazy.tsx | 22 +- .../src/routes/_authorized-layout/route.tsx | 2 +- packages/drizzle-user-db-schemas/package.json | 2 +- pnpm-lock.yaml | 645 +++++++++++++++++- 6 files changed, 697 insertions(+), 39 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 3872a158..8ca51d24 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -39,7 +39,7 @@ "better-auth": "1.4.9", "date-fns": "^3.6.0", "dompurify": "^3.3.0", - "drizzle-orm": "^0.45.1", + "drizzle-orm": "1.0.0-beta.10-4a43a22", "input-otp": "1.4.2", "jotai": "^2.7.0", "lucide-react": "^0.507.0", diff --git a/apps/web/src/lib/db/index.ts b/apps/web/src/lib/db/index.ts index 210a15ae..3dd04ac9 100644 --- a/apps/web/src/lib/db/index.ts +++ b/apps/web/src/lib/db/index.ts @@ -1,7 +1,9 @@ import type { SelectMigration } from "@bahar/drizzle-user-db-schemas"; +import * as schema from "@bahar/drizzle-user-db-schemas"; import { err, ok, tryCatch } from "@bahar/result"; import * as Sentry from "@sentry/react"; import { connect, type Database } from "@tursodatabase/sync-wasm/vite"; +import { drizzle } from "drizzle-orm/sqlite-proxy"; import { api } from "../api"; /** @@ -15,8 +17,18 @@ import { api } from "../api"; let db: Database | null = null; let dbInitPromise: ReturnType | null = null; +let drizzleDb: ReturnType> | null = null; + const LOCAL_DB_PATH_PREFIX = "bahar-local"; +export const getDrizzleDb = () => { + if (!drizzleDb) { + throw new Error("Database not initialized."); + } + + return drizzleDb; +}; + export const getDb = () => { if (!db) { throw new Error("Database not initialized."); @@ -39,11 +51,15 @@ export const ensureDb = async () => { }; export const resetDb = async () => { - if (!db) return; + if (db) { + await db.close(); + db = null; + dbInitPromise = null; + } - await db.close(); - db = null; - dbInitPromise = null; + if (drizzleDb) { + drizzleDb = null; + } }; /** @@ -54,11 +70,7 @@ export const resetDb = async () => { * the data will be in a worse state. */ export const deleteLocalDatabase = async () => { - if (db) { - await db.close(); - db = null; - dbInitPromise = null; - } + await resetDb(); // Delete all OPFS files starting with our prefix const opfsRoot = await navigator.storage.getDirectory(); @@ -160,6 +172,39 @@ const _initDbInternal = async () => { if (!connectionResult.ok) return connectionResult; db = connectionResult.value; + drizzleDb = drizzle( + async (sql, params, method) => { + if (!db) return { rows: [] }; + + try { + const stmt = db.prepare(sql); + + if (method === "run") { + await stmt.run(params); + return { rows: [] }; + } + + if (method === "all" || method === "values") { + const rows = (await stmt.all(params)) as Record[]; + return { rows: rows.map((row) => Object.values(row)) }; + } + + if (method === "get") { + const row = (await stmt.get(params)) as Record< + string, + unknown + > | null; + return { rows: row ? Object.values(row) : [] }; + } + + return { rows: [] }; + } catch (e) { + console.error("SQL Error: ", e); + throw e; + } + }, + { schema } + ); const dbPullResult = await tryCatch( async () => { diff --git a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx index 1ec1810a..e0155ca3 100644 --- a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx +++ b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx @@ -53,15 +53,9 @@ const Index = () => { return ( -
- {/* Dictionary Card */} +
- - {/* Decorative gradient background */} -
-
- - {/* Header */} +
@@ -139,7 +133,7 @@ const Index = () => { Review {!isPending && regularCount > 0 && ( - + {formatNumber(regularCount)} )} @@ -153,16 +147,16 @@ const Index = () => {
{/* Divider */} -
+
- {/* Content */} - - - + {/* TODO: Empty space */} +
+ + {/* Scroll to top button */} =20.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-tracing': 1.3.1 + '@typespec/ts-http-runtime': 0.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/abort-controller@2.1.2: + resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@azure/core-auth@1.10.1: + resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} + engines: {node: '>=20.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-util': 1.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/core-client@1.10.1: + resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} + engines: {node: '>=20.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/core-http-compat@2.3.1: + resolution: {integrity: sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==} + engines: {node: '>=20.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/core-lro@2.7.2: + resolution: {integrity: sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==} + engines: {node: '>=18.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/core-paging@1.6.2: + resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@azure/core-rest-pipeline@1.22.2: + resolution: {integrity: sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==} + engines: {node: '>=20.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + '@typespec/ts-http-runtime': 0.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/core-tracing@1.3.1: + resolution: {integrity: sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==} + engines: {node: '>=20.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@azure/core-util@1.13.1: + resolution: {integrity: sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==} + engines: {node: '>=20.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@typespec/ts-http-runtime': 0.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/identity@4.13.0: + resolution: {integrity: sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==} + engines: {node: '>=20.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + '@azure/msal-browser': 4.27.0 + '@azure/msal-node': 3.8.4 + open: 10.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/keyvault-common@2.0.0: + resolution: {integrity: sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==} + engines: {node: '>=18.0.0'} + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/keyvault-keys@4.10.0: + resolution: {integrity: sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==} + engines: {node: '>=18.0.0'} + dependencies: + '@azure-rest/core-client': 2.5.1 + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-http-compat': 2.3.1 + '@azure/core-lro': 2.7.2 + '@azure/core-paging': 1.6.2 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/keyvault-common': 2.0.0 + '@azure/logger': 1.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/logger@1.3.0: + resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} + engines: {node: '>=20.0.0'} + dependencies: + '@typespec/ts-http-runtime': 0.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@azure/msal-browser@4.27.0: + resolution: {integrity: sha512-bZ8Pta6YAbdd0o0PEaL1/geBsPrLEnyY/RDWqvF1PP9RUH8EMLvUMGoZFYS6jSlUan6KZ9IMTLCnwpWWpQRK/w==} + engines: {node: '>=0.8.0'} + dependencies: + '@azure/msal-common': 15.13.3 + dev: false + + /@azure/msal-common@15.13.3: + resolution: {integrity: sha512-shSDU7Ioecya+Aob5xliW9IGq1Ui8y4EVSdWGyI1Gbm4Vg61WpP95LuzcY214/wEjSn6w4PZYD4/iVldErHayQ==} + engines: {node: '>=0.8.0'} + dev: false + + /@azure/msal-node@3.8.4: + resolution: {integrity: sha512-lvuAwsDpPDE/jSuVQOBMpLbXuVuLsPNRwWCyK3/6bPlBk0fGWegqoZ0qjZclMWyQ2JNvIY3vHY7hoFmFmFQcOw==} + engines: {node: '>=16'} + dependencies: + '@azure/msal-common': 15.13.3 + jsonwebtoken: 9.0.3 + uuid: 8.3.2 + dev: false + /@babel/code-frame@7.10.4: resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} dependencies: @@ -3530,7 +3726,7 @@ packages: peerDependencies: elysia: '>=1.4.19' dependencies: - elysia: 1.4.19(@sinclair/typebox@0.34.45)(exact-mirror@0.2.5)(file-type@21.2.0)(openapi-types@12.1.3)(typescript@5.8.3) + elysia: 1.4.19(@sinclair/typebox@0.34.45)(exact-mirror@0.2.5)(file-type@21.2.0)(openapi-types@12.1.3)(typescript@5.9.2) dev: false /@emmetio/abbreviation@2.3.3: @@ -5529,7 +5725,7 @@ packages: react-hook-form: ^7.55.0 dependencies: '@standard-schema/utils': 0.3.0 - react-hook-form: 7.56.4(react@19.0.0) + react-hook-form: 7.56.4(react@19.1.0) dev: false /@img/colour@1.0.0: @@ -6268,6 +6464,10 @@ packages: '@jridgewell/sourcemap-codec': 1.5.5 dev: true + /@js-joda/core@5.6.5: + resolution: {integrity: sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==} + dev: false + /@libsql/client@0.10.0: resolution: {integrity: sha512-2ERn08T4XOVx34yBtUPq0RDjAdd9TJ5qNH/izugr208ml2F94mk92qC64kXyDVQINodWJvp3kAdq6P4zTtCZ7g==} dependencies: @@ -6449,6 +6649,7 @@ packages: transitivePeerDependencies: - supports-color - typescript + dev: true /@lingui/babel-plugin-lingui-macro@5.3.2(babel-plugin-macros@3.1.0)(typescript@5.9.2): resolution: {integrity: sha512-NdXrq8aZlPjN4jeA/LkSLNyx5vPGmrW+r2ywMNQDPQPVP28Hq8c3hF9SQc1t7hwBorGQ3qzIQ7i2Vm6Y8PnjQw==} @@ -6577,6 +6778,7 @@ packages: jiti: 1.21.7 transitivePeerDependencies: - typescript + dev: true /@lingui/conf@5.3.2(typescript@5.9.2): resolution: {integrity: sha512-c0Dfovr9BLuwAnY5GADxKcwBUQdVl0Jo/JUa3cumIXFhHzZGb78kfhCHjWWQdX8+WQD8qzSl/YkVDbxhcQJGmg==} @@ -6620,7 +6822,7 @@ packages: optional: true dependencies: '@babel/runtime': 7.26.7 - '@lingui/babel-plugin-lingui-macro': 5.3.2(babel-plugin-macros@3.1.0)(typescript@5.8.3) + '@lingui/babel-plugin-lingui-macro': 5.3.2(babel-plugin-macros@3.1.0)(typescript@5.9.2) '@lingui/message-utils': 5.3.2 babel-plugin-macros: 3.1.0 unraw: 3.0.0 @@ -10089,6 +10291,10 @@ packages: engines: {node: '>=12'} dev: true + /@tediousjs/connection-string@0.5.0: + resolution: {integrity: sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==} + dev: false + /@tokenizer/inflate@0.4.1: resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} engines: {node: '>=18'} @@ -10282,6 +10488,16 @@ packages: resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} dev: false + /@types/mssql@9.1.8: + resolution: {integrity: sha512-mt9h5jWj+DYE5jxnKaWSV/GqDf9FV52XYVk6T3XZF69noEe+JJV6MKirii48l81+cjmAkSq+qeKX+k61fHkYrQ==} + dependencies: + '@types/node': 20.10.6 + tarn: 3.0.2 + tedious: 19.2.0 + transitivePeerDependencies: + - supports-color + dev: false + /@types/mysql@2.15.27: resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} dependencies: @@ -10360,6 +10576,12 @@ packages: dependencies: csstype: 3.1.3 + /@types/readable-stream@4.0.23: + resolution: {integrity: sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==} + dependencies: + '@types/node': 20.10.6 + dev: false + /@types/resolve@1.20.2: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} dev: true @@ -10408,6 +10630,17 @@ packages: dependencies: '@types/yargs-parser': 21.0.3 + /@typespec/ts-http-runtime@0.3.2: + resolution: {integrity: sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==} + engines: {node: '>=20.0.0'} + dependencies: + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + dev: false + /@uidotdev/usehooks@2.4.1(react-dom@19.0.0)(react@19.0.0): resolution: {integrity: sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==} engines: {node: '>=16'} @@ -11426,7 +11659,7 @@ packages: zod: 4.2.1 dev: false - /better-auth@1.4.9(drizzle-orm@0.45.1)(react-dom@19.0.0)(react@19.0.0): + /better-auth@1.4.9(drizzle-orm@1.0.0-beta.10-4a43a22)(react-dom@19.0.0)(react@19.0.0): resolution: {integrity: sha512-usSdjuyTzZwIvM8fjF8YGhPncxV3MAg3dHUO9uPUnf0yklXUSYISiH1+imk6/Z+UBqsscyyPRnbIyjyK97p7YA==} peerDependencies: '@lynx-js/react': '*' @@ -11493,7 +11726,7 @@ packages: '@noble/hashes': 2.0.1 better-call: 1.1.7(zod@4.0.17) defu: 6.1.4 - drizzle-orm: 0.45.1(@libsql/client@0.10.0)(@opentelemetry/api@1.9.0)(@upstash/redis@1.36.0)(bun-types@1.3.5)(kysely@0.28.5) + drizzle-orm: 1.0.0-beta.10-4a43a22(@types/mssql@9.1.8)(mssql@11.0.1) jose: 6.1.3 kysely: 0.28.5 nanostores: 1.1.0 @@ -11553,6 +11786,15 @@ packages: inherits: 2.0.4 readable-stream: 3.6.2 + /bl@6.1.6: + resolution: {integrity: sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==} + dependencies: + '@types/readable-stream': 4.0.23 + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 4.7.0 + dev: false + /blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} dev: true @@ -11651,6 +11893,10 @@ packages: dependencies: node-int64: 0.4.0 + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -11660,11 +11906,25 @@ packages: base64-js: 1.5.1 ieee754: 1.2.1 + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /bun-types@1.3.5: resolution: {integrity: sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw==} dependencies: '@types/node': 20.10.6 + /bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + dependencies: + run-applescript: 7.1.0 + dev: false + /bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -12149,6 +12409,7 @@ packages: parse-json: 5.2.0 path-type: 4.0.0 typescript: 5.8.3 + dev: true /cosmiconfig@8.3.6(typescript@5.9.2): resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} @@ -12420,6 +12681,19 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + /default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + dev: false + + /default-browser@5.4.0: + resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + engines: {node: '>=18'} + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + dev: false + /defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: @@ -12438,6 +12712,11 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} + /define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + dev: false + /define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -12685,6 +12964,116 @@ packages: kysely: 0.28.5 dev: false + /drizzle-orm@1.0.0-beta.10-4a43a22(@types/mssql@9.1.8)(mssql@11.0.1): + resolution: {integrity: sha512-ipys1kZjtMwBBDMtdbIaIt/j06wZITxxPcxsU8Sh93zKIILnADnK1rACwHRrm0kVaAOpZSW19efVZs1LihFZpQ==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@effect/sql': ^0.48.5 + '@effect/sql-pg': ^0.49.7 + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@sqlitecloud/drivers': '>=1.0.653' + '@tidbcloud/serverless': '*' + '@tursodatabase/database': '>=0.2.1' + '@tursodatabase/database-common': '>=0.2.1' + '@tursodatabase/database-wasm': '>=0.2.1' + '@types/better-sqlite3': '*' + '@types/mssql': ^9.1.4 + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=9.3.0' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + mssql: ^11.0.1 + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@effect/sql': + optional: true + '@effect/sql-pg': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@sqlitecloud/drivers': + optional: true + '@tidbcloud/serverless': + optional: true + '@tursodatabase/database': + optional: true + '@tursodatabase/database-common': + optional: true + '@tursodatabase/database-wasm': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + dependencies: + '@types/mssql': 9.1.8 + mssql: 11.0.1 + dev: false + /drizzle-zod@0.8.3(drizzle-orm@0.45.1)(zod@4.0.17): resolution: {integrity: sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==} peerDependencies: @@ -12695,6 +13084,16 @@ packages: zod: 4.0.17 dev: false + /drizzle-zod@0.8.3(drizzle-orm@1.0.0-beta.10-4a43a22)(zod@4.0.17): + resolution: {integrity: sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==} + peerDependencies: + drizzle-orm: '>=0.36.0' + zod: ^3.25.0 || ^4.0.0 + dependencies: + drizzle-orm: 1.0.0-beta.10-4a43a22(@types/mssql@9.1.8)(mssql@11.0.1) + zod: 4.0.17 + dev: false + /dset@3.1.4: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} @@ -12712,6 +13111,12 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -12754,6 +13159,31 @@ packages: typescript: 5.8.3 dev: false + /elysia@1.4.19(@sinclair/typebox@0.34.45)(exact-mirror@0.2.5)(file-type@21.2.0)(openapi-types@12.1.3)(typescript@5.9.2): + resolution: {integrity: sha512-DZb9y8FnWyX5IuqY44SvqAV0DjJ15NeCWHrLdgXrKgTPDPsl3VNwWHqrEr9bmnOCpg1vh6QUvAX/tcxNj88jLA==} + peerDependencies: + '@sinclair/typebox': '>= 0.34.0 < 1' + '@types/bun': '>= 1.2.0' + exact-mirror: '>= 0.0.9' + file-type: '>= 20.0.0' + openapi-types: '>= 12.0.0' + typescript: '>= 5.0.0' + peerDependenciesMeta: + '@types/bun': + optional: true + typescript: + optional: true + dependencies: + '@sinclair/typebox': 0.34.45 + cookie: 1.1.1 + exact-mirror: 0.2.5(@sinclair/typebox@0.34.45) + fast-decode-uri-component: 1.0.1 + file-type: 21.2.0 + memoirist: 0.4.0 + openapi-types: 12.1.3 + typescript: 5.9.2 + dev: false + /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -13314,7 +13744,6 @@ packages: /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - dev: true /exact-mirror@0.2.5(@sinclair/typebox@0.34.45): resolution: {integrity: sha512-u8Wu2lO8nio5lKSJubOydsdNtQmH8ENba5m0nbQYmTvsjksXKYIS1nSShdDlO8Uem+kbo+N6eD5I03cpZ+QsRQ==} @@ -14586,6 +15015,16 @@ packages: - supports-color dev: true + /http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.3 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + dev: false + /https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -14629,7 +15068,13 @@ packages: engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 - dev: true + + /iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false /idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} @@ -15673,6 +16118,10 @@ packages: resolution: {integrity: sha512-NPrWuHFxFUknr1KqJRDgUQPexQF0uIJWjeT+2KjEePhitQxQEx5EJBG1lVn5/hc8aLycTpXrDOgPQ6Zq+EDiTA==} dev: false + /js-md4@0.3.2: + resolution: {integrity: sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==} + dev: false + /js-sha256@0.10.1: resolution: {integrity: sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==} @@ -15788,6 +16237,37 @@ packages: engines: {node: '>=0.10.0'} dev: true + /jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.3 + dev: false + + /jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + dev: false + /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -16101,6 +16581,34 @@ packages: /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + /lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} @@ -17210,6 +17718,21 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + /mssql@11.0.1: + resolution: {integrity: sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==} + engines: {node: '>=18'} + hasBin: true + dependencies: + '@tediousjs/connection-string': 0.5.0 + commander: 11.1.0 + debug: 4.4.3 + rfdc: 1.4.1 + tarn: 3.0.2 + tedious: 18.6.2 + transitivePeerDependencies: + - supports-color + dev: false + /muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} dev: true @@ -17245,6 +17768,10 @@ packages: engines: {node: ^20.0.0 || >=22.0.0} dev: false + /native-duplexpair@1.0.0: + resolution: {integrity: sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==} + dev: false + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -17465,6 +17992,16 @@ packages: regex-recursion: 6.0.2 dev: false + /open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + dependencies: + default-browser: 5.4.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + dev: false + /open@7.4.2: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} engines: {node: '>=8'} @@ -17932,6 +18469,11 @@ packages: resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==} dev: false + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: false + /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -18441,6 +18983,17 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + dev: false + /readdirp@3.5.0: resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} engines: {node: '>=8.10.0'} @@ -18844,6 +19397,10 @@ packages: engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true + /rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + dev: false + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} dependencies: @@ -18916,6 +19473,11 @@ packages: resolution: {integrity: sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==} dev: false + /run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + dev: false + /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -19428,6 +19990,10 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + /sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + dev: false + /stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} dependencies: @@ -19793,6 +20359,47 @@ packages: mkdirp: 3.0.1 yallist: 5.0.0 + /tarn@3.0.2: + resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} + engines: {node: '>=8.0.0'} + dev: false + + /tedious@18.6.2: + resolution: {integrity: sha512-g7jC56o3MzLkE3lHkaFe2ZdOVFBahq5bsB60/M4NYUbocw/MCrS89IOEQUFr+ba6pb8ZHczZ/VqCyYeYq0xBAg==} + engines: {node: '>=18'} + dependencies: + '@azure/core-auth': 1.10.1 + '@azure/identity': 4.13.0 + '@azure/keyvault-keys': 4.10.0 + '@js-joda/core': 5.6.5 + '@types/node': 20.10.6 + bl: 6.1.6 + iconv-lite: 0.6.3 + js-md4: 0.3.2 + native-duplexpair: 1.0.0 + sprintf-js: 1.1.3 + transitivePeerDependencies: + - supports-color + dev: false + + /tedious@19.2.0: + resolution: {integrity: sha512-2dDjX0KP54riDvJPiiIozv0WRS/giJb3/JG2lWpa2dgM0Gha7mLAxbTR3ltPkGzfoS6M3oDnhYnWuzeaZibHuQ==} + engines: {node: '>=18.17'} + dependencies: + '@azure/core-auth': 1.10.1 + '@azure/identity': 4.13.0 + '@azure/keyvault-keys': 4.10.0 + '@js-joda/core': 5.6.5 + '@types/node': 20.10.6 + bl: 6.1.6 + iconv-lite: 0.7.2 + js-md4: 0.3.2 + native-duplexpair: 1.0.0 + sprintf-js: 1.1.3 + transitivePeerDependencies: + - supports-color + dev: false + /temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -20619,6 +21226,11 @@ packages: resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} hasBin: true + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + /v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} @@ -21472,6 +22084,13 @@ packages: utf-8-validate: optional: true + /wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + dependencies: + is-wsl: 3.1.0 + dev: false + /xcode@3.0.1: resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} engines: {node: '>=10.0.0'} From 0e3b6ccddc379bd947ca27d8ccece6036e45407f Mon Sep 17 00:00:00 2001 From: sufyan Date: Thu, 15 Jan 2026 17:40:03 -0500 Subject: [PATCH 2/8] feat: update home page infinite list styling --- .../src/components/search/InfiniteScroll.tsx | 163 ++++++++++-------- .../_search-layout/index.lazy.tsx | 2 +- 2 files changed, 88 insertions(+), 77 deletions(-) diff --git a/apps/web/src/components/search/InfiniteScroll.tsx b/apps/web/src/components/search/InfiniteScroll.tsx index 499f3e30..b1add42b 100644 --- a/apps/web/src/components/search/InfiniteScroll.tsx +++ b/apps/web/src/components/search/InfiniteScroll.tsx @@ -1,6 +1,7 @@ import { cn } from "@bahar/design-system"; import type { SelectDictionaryEntry } from "@bahar/drizzle-user-db-schemas"; import { Button } from "@bahar/web-ui/components/button"; +import { Card, CardContent } from "@bahar/web-ui/components/card"; import { Trans, useLingui } from "@lingui/react/macro"; import { useQuery } from "@tanstack/react-query"; import { useNavigate } from "@tanstack/react-router"; @@ -16,7 +17,14 @@ import { SearchX, } from "lucide-react"; import { AnimatePresence, motion } from "motion/react"; -import { type FC, memo, useCallback, useEffect, useState } from "react"; +import { + type FC, + memo, + type ReactNode, + useCallback, + useEffect, + useState, +} from "react"; import { useInfiniteScroll } from "@/hooks/useSearch"; import { dictionaryEntriesTable } from "@/lib/db/operations/dictionary-entries"; import { Highlight } from "./Highlight"; @@ -40,6 +48,25 @@ const useGenderLabels = (): Record<"masculine" | "feminine", string> => { }; }; +const SecondaryIconButton = ({ + onClick, + children, +}: { + onClick?: () => void; + children: ReactNode; +}) => { + return ( + + ); +}; + const CopyButton: FC<{ text: string }> = memo(({ text }) => { const [copied, setCopied] = useState(false); @@ -50,18 +77,13 @@ const CopyButton: FC<{ text: string }> = memo(({ text }) => { }; return ( - + ); }); @@ -336,22 +358,20 @@ const WordCardContent: FC = memo( const document = hit.document; return ( -
-
+

@@ -359,57 +379,45 @@ const WordCardContent: FC = memo(
- + - +

-

+

-
- {/* Expanded details */} - - {isExpanded && ( - - - - )} - - - {/* Subtle accent line */} -
-
+ {/* Expanded details */} + + {isExpanded && ( + + + + )} + + + {/* Subtle accent line */} +
+ + ); } ); @@ -520,7 +528,7 @@ export const InfiniteScroll: FC = () => {
    {hits.map((hit) => ( -
  • +
  • { ))}
- {/* Loading indicator for infinite scroll */} - {hasMore && ( -
-
- {[0, 1, 2].map((i) => ( - - ))} -
-
- )} + {hasMore && } +
+ ); +}; + +const LoadingIndicator = () => { + return ( +
+
+ {[0, 1, 2].map((i) => ( + + ))} +
); }; diff --git a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx index e0155ca3..08c43e59 100644 --- a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx +++ b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx @@ -1,6 +1,6 @@ import { cn } from "@bahar/design-system"; import { Button } from "@bahar/web-ui/components/button"; -import { Card, CardContent } from "@bahar/web-ui/components/card"; +import { Card } from "@bahar/web-ui/components/card"; import { Plural, Trans } from "@lingui/react/macro"; import { useQuery } from "@tanstack/react-query"; import { createLazyFileRoute, Link } from "@tanstack/react-router"; From 87dbce53113d2184889b7d5888ec5f550df96fae Mon Sep 17 00:00:00 2001 From: sufyan Date: Fri, 16 Jan 2026 16:39:10 -0500 Subject: [PATCH 3/8] style: fix lint issues --- apps/web/src/components/features/decks/DeckDialogContent.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/features/decks/DeckDialogContent.tsx b/apps/web/src/components/features/decks/DeckDialogContent.tsx index de2e2e15..dd38eb51 100644 --- a/apps/web/src/components/features/decks/DeckDialogContent.tsx +++ b/apps/web/src/components/features/decks/DeckDialogContent.tsx @@ -171,7 +171,7 @@ export const DeckDialogContent = ({ if (!isEditing) { form.reset(); } - } catch (err) { + } catch (_err) { if (isEditing) { toast.error(t`There was an error updating the deck`, { description: t`Your deck was not updated. Please try again.`, @@ -309,7 +309,7 @@ export const DeckDialogContent = ({ /> - + {getTypeLabel(type)} From a1ec1676b1a687f07ff8400ac4c04b142bae3189 Mon Sep 17 00:00:00 2001 From: sufyan Date: Sun, 18 Jan 2026 13:19:17 -0500 Subject: [PATCH 4/8] feat: Implement tags filter and drizzle client on web --- apps/web/package.json | 1 + .../dictionary/filters/DictionaryFilters.tsx | 44 ++++++++ .../dictionary/filters/TagsFilter.tsx | 106 ++++++++++++++++++ .../src/components/search/InfiniteScroll.tsx | 45 ++++---- apps/web/src/hooks/useSearch.ts | 56 +++++++-- .../lib/db/operations/dictionary-entries.ts | 2 +- .../_search-layout/index.lazy.tsx | 13 ++- .../_search-layout/route.tsx | 7 ++ .../src/routes/_authorized-layout/route.tsx | 2 +- packages/search/src/database.ts | 28 +++-- packages/search/src/schema.ts | 2 +- packages/web-ui/package.json | 1 + .../web-ui/src/components/scroll-area.tsx | 45 ++++++++ pnpm-lock.yaml | 68 +++++++++++ 14 files changed, 370 insertions(+), 50 deletions(-) create mode 100644 apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx create mode 100644 apps/web/src/components/features/dictionary/filters/TagsFilter.tsx create mode 100644 packages/web-ui/src/components/scroll-area.tsx diff --git a/apps/web/package.json b/apps/web/package.json index 8ca51d24..9dd895b5 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -34,6 +34,7 @@ "@tanstack/react-query": "^5.82.2", "@tanstack/react-query-devtools": "^5.82.2", "@tanstack/react-router": "^1.119.0", + "@tanstack/zod-adapter": "^1.150.0", "@tursodatabase/sync-wasm": "^0.3.2", "@uidotdev/usehooks": "^2.4.1", "better-auth": "1.4.9", diff --git a/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx b/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx new file mode 100644 index 00000000..eebce482 --- /dev/null +++ b/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx @@ -0,0 +1,44 @@ +import { Button } from "@bahar/web-ui/components/button"; +import { Trans } from "@lingui/react/macro"; +import { useNavigate, useSearch } from "@tanstack/react-router"; +import { SlidersHorizontal } from "lucide-react"; +import { TagsFilter } from "@/components/features/dictionary/filters/TagsFilter"; + +export const DictionaryFilters = () => { + const navigate = useNavigate(); + const { tags: filteredTags } = useSearch({ + from: "/_authorized-layout/_search-layout", + }); + + const hasActiveFilters = !!filteredTags?.length && filteredTags.length > 0; + + const clearAllFilters = () => { + navigate({ to: "/" }); + }; + + return ( +
+
+
+ + + Filters + +
+ + + + {hasActiveFilters && ( + + )} +
+
+ ); +}; diff --git a/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx b/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx new file mode 100644 index 00000000..d94a5839 --- /dev/null +++ b/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx @@ -0,0 +1,106 @@ +import { cn } from "@bahar/design-system"; +import { Button } from "@bahar/web-ui/components/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@bahar/web-ui/components/command"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@bahar/web-ui/components/popover"; +import { t } from "@lingui/core/macro"; +import { Plural, Trans } from "@lingui/react/macro"; +import { useQuery } from "@tanstack/react-query"; +import { useNavigate, useSearch } from "@tanstack/react-router"; +import { CheckIcon, ChevronsUpDownIcon } from "lucide-react"; +import { useState } from "react"; +import { dictionaryEntriesTable } from "@/lib/db/operations/dictionary-entries"; + +export const TagsFilter = () => { + const [open, setOpen] = useState(false); + const { tags: filteredTags } = useSearch({ + from: "/_authorized-layout/_search-layout", + }); + const navigate = useNavigate(); + const { data: tags } = useQuery({ + queryFn: () => dictionaryEntriesTable.tags.query(""), + queryKey: [...dictionaryEntriesTable.tags.cacheOptions.queryKey], + }); + + return ( + + + + + + + + + + + + No tags found. + + + + {tags?.map(({ tag }) => ( + { + const shouldRemove = filteredTags?.some((t) => t === tag); + + if (shouldRemove) { + navigate({ + to: "/", + search: { + tags: filteredTags?.filter((t) => t !== tag), + }, + }); + } else { + navigate({ + to: "/", + search: { + tags: [...(filteredTags ?? []), tag], + }, + }); + } + }} + value={tag} + > + + {tag} + + ))} + + + + + + ); +}; diff --git a/apps/web/src/components/search/InfiniteScroll.tsx b/apps/web/src/components/search/InfiniteScroll.tsx index b1add42b..6df5c242 100644 --- a/apps/web/src/components/search/InfiniteScroll.tsx +++ b/apps/web/src/components/search/InfiniteScroll.tsx @@ -4,9 +4,13 @@ import { Button } from "@bahar/web-ui/components/button"; import { Card, CardContent } from "@bahar/web-ui/components/card"; import { Trans, useLingui } from "@lingui/react/macro"; import { useQuery } from "@tanstack/react-query"; -import { useNavigate } from "@tanstack/react-router"; -import { useDebounce, useMeasure, useWindowScroll } from "@uidotdev/usehooks"; -import { useAtomValue } from "jotai"; +import { useNavigate, useSearch } from "@tanstack/react-router"; +import { + useDebounce, + useMeasure, + useSet, + useWindowScroll, +} from "@uidotdev/usehooks"; import { BookOpenText, Check, @@ -28,7 +32,6 @@ import { import { useInfiniteScroll } from "@/hooks/useSearch"; import { dictionaryEntriesTable } from "@/lib/db/operations/dictionary-entries"; import { Highlight } from "./Highlight"; -import { searchQueryAtom } from "./state"; const useWordTypeLabels = (): Record => { const { t } = useLingui(); @@ -115,7 +118,6 @@ const ExpandedDetails: FC = memo(({ id, document }) => { return (
- {/* Type and Root row */}
{wordTypeLabels[document.type]} @@ -130,7 +132,6 @@ const ExpandedDetails: FC = memo(({ id, document }) => { )}
- {/* Definition */} {hasDefinition && (

@@ -140,7 +141,6 @@ const ExpandedDetails: FC = memo(({ id, document }) => {

)} - {/* Loading state for heavy fields */} {isLoading && (
@@ -150,7 +150,6 @@ const ExpandedDetails: FC = memo(({ id, document }) => {
)} - {/* Ism Morphology */} {ismMorphology && (

@@ -210,7 +209,6 @@ const ExpandedDetails: FC = memo(({ id, document }) => {

)} - {/* Verb Morphology */} {verbMorphology && (

@@ -288,7 +286,6 @@ const ExpandedDetails: FC = memo(({ id, document }) => {

)} - {/* Examples */} {hasExamples && (

@@ -317,7 +314,6 @@ const ExpandedDetails: FC = memo(({ id, document }) => {

)} - {/* Tags */} {hasTags && (

@@ -431,28 +427,29 @@ const WordCardContent: FC = memo( */ const PIXEL_HEIGHT_OFFSET = 800; -export const InfiniteScroll: FC = () => { +export const InfiniteScroll: FC<{ searchQuery?: string }> = ({ + searchQuery, +}) => { + const { tags } = useSearch({ from: "/_authorized-layout/_search-layout" }); const navigate = useNavigate(); - const searchQuery = useAtomValue(searchQueryAtom); const { results: { hits } = {}, showMore, hasMore, - } = useInfiniteScroll({ term: searchQuery }); + } = useInfiniteScroll({ + term: searchQuery, + filters: { tags }, + }); const [ref, { height }] = useMeasure(); const [{ y }] = useWindowScroll(); - const [expandedIds, setExpandedIds] = useState>(new Set()); + const expandedIds = useSet(); const toggleExpanded = useCallback((id: string) => { - setExpandedIds((prev) => { - const next = new Set(prev); - if (next.has(id)) { - next.delete(id); - } else { - next.add(id); - } - return next; - }); + if (expandedIds.has(id)) { + expandedIds.delete(id); + } else { + expandedIds.add(id); + } }, []); const handleNavigateEdit = useCallback( diff --git a/apps/web/src/hooks/useSearch.ts b/apps/web/src/hooks/useSearch.ts index 1d231add..afe588d7 100644 --- a/apps/web/src/hooks/useSearch.ts +++ b/apps/web/src/hooks/useSearch.ts @@ -1,4 +1,8 @@ -import { type SearchLanguage, searchDictionary } from "@bahar/search/database"; +import { + type SearchDictionaryOptions, + type SearchLanguage, + searchDictionary, +} from "@bahar/search/database"; import type { DictionaryDocument } from "@bahar/search/schema"; import type { InternalTypedDocument, Result, Results } from "@orama/orama"; import { atom, useAtom, useSetAtom } from "jotai"; @@ -8,10 +12,12 @@ import { detectLanguage } from "@/lib/utils"; const SEARCH_RESULTS_PER_PAGE = 20; -const searchResultsMetadataAtom = atom>, - "hits" -> | null>(null); +const searchResultsMetadataAtom = atom< + | (Omit>, "hits"> & { + searchTerm?: string; + }) + | null +>(null); const hitsAtom = atom< Result>[] | null @@ -33,13 +39,20 @@ export const useSearch = () => { const search = useCallback( ( - params: { term?: string; offset?: number } = {}, + params: { + term?: string; + offset?: number; + where?: SearchDictionaryOptions["where"]; + sortBy?: SearchDictionaryOptions["sortBy"]; + } = {}, language: SearchLanguage = "english" ) => { return searchDictionary(getOramaDb(), params.term ?? "", { limit: SEARCH_RESULTS_PER_PAGE, offset: params.offset, language, + where: params.where, + sortBy: params.sortBy, }) as Results>; }, [] @@ -85,7 +98,8 @@ export const useSearch = () => { ? ({ hits, ...searchResultsMetadata, - } as Results>) + } as Results> & + typeof searchResultsMetadata) : undefined, /** @@ -104,7 +118,15 @@ export const useSearch = () => { * Custom hook that wraps orama's search to implement infinite scrolling * functionality and exposes helper methods for interacting with the results. */ -export const useInfiniteScroll = (params: { term?: string } = {}) => { +export const useInfiniteScroll = ( + params: { + term?: string; + filters?: { + tags?: string[]; + }; + sortBy?: SearchDictionaryOptions["sortBy"]; + } = {} +) => { const { search } = useSearch(); const [hasMore, setHasMore] = useState(true); @@ -118,6 +140,14 @@ export const useInfiniteScroll = (params: { term?: string } = {}) => { // For checking if the search params have changed const paramsKey = JSON.stringify(params); + const whereFilter = useMemo(() => { + if (!params.filters?.tags?.length) return undefined; + + return { + tags: { containsAll: params.filters.tags }, + }; + }, [paramsKey]); + const searchQueryLanguage = useMemo[1]>(() => { const detectedLanuage = detectLanguage(params.term ?? ""); @@ -141,8 +171,10 @@ export const useInfiniteScroll = (params: { term?: string } = {}) => { const { hits } = search( { - ...params, offset, + sortBy: params.sortBy, + term: params.term, + where: whereFilter, }, searchQueryLanguage ); @@ -157,7 +189,9 @@ export const useInfiniteScroll = (params: { term?: string } = {}) => { useEffect(() => { const { hits, ...metadata } = search( { - ...params, + sortBy: params.sortBy, + term: params.term, + where: whereFilter, offset: 0, }, searchQueryLanguage @@ -165,7 +199,7 @@ export const useInfiniteScroll = (params: { term?: string } = {}) => { setOffset(0); setHits(hits); - setSearchResultsMetadata(metadata); + setSearchResultsMetadata({ ...metadata, searchTerm: params.term }); setHasMore(hits.length < metadata.count); }, [paramsKey, setOffset, setHits, setSearchResultsMetadata, search]); diff --git a/apps/web/src/lib/db/operations/dictionary-entries.ts b/apps/web/src/lib/db/operations/dictionary-entries.ts index 62a32d27..3a4fc716 100644 --- a/apps/web/src/lib/db/operations/dictionary-entries.ts +++ b/apps/web/src/lib/db/operations/dictionary-entries.ts @@ -59,7 +59,7 @@ export const dictionaryEntriesTable = { .prepare( `SELECT value as tag, COUNT(*) as count FROM dictionary_entries, json_each(tags) - WHERE value IS NOT NULL AND value LIKE '%' || ? || '%' + WHERE value IS NOT NULL ${searchTerm ? "AND value LIKE '%' || ? || '%'" : ""} GROUP BY value ORDER BY count DESC;` ) diff --git a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx index 08c43e59..816e6bad 100644 --- a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx +++ b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx @@ -5,11 +5,15 @@ import { Plural, Trans } from "@lingui/react/macro"; import { useQuery } from "@tanstack/react-query"; import { createLazyFileRoute, Link } from "@tanstack/react-router"; import { useWindowScroll, useWindowSize } from "@uidotdev/usehooks"; +import { useAtomValue } from "jotai"; import { ArrowUp, BookOpen, GraduationCap, PlusIcon } from "lucide-react"; import { motion } from "motion/react"; +import { useDeferredValue } from "react"; +import { DictionaryFilters } from "@/components/features/dictionary/filters/DictionaryFilters"; import { FlashcardDrawer } from "@/components/features/flashcards/FlashcardDrawer/FlashcardDrawer"; import { itemVariants, Page } from "@/components/Page"; import { InfiniteScroll } from "@/components/search/InfiniteScroll"; +import { searchQueryAtom } from "@/components/search/state"; import { useFormatNumber } from "@/hooks/useFormatNumber"; import { useSearch } from "@/hooks/useSearch"; import { @@ -21,6 +25,8 @@ import { settingsTable } from "@/lib/db/operations/settings"; const Index = () => { const { formatNumber, formatElapsedTime } = useFormatNumber(); const [{ y }, scrollTo] = useWindowScroll(); + const searchQuery = useAtomValue(searchQueryAtom); + const deferredSearchQuery = useDeferredValue(searchQuery); const { results } = useSearch(); const { height } = useWindowSize(); const { data: flashcardSettings } = useQuery({ @@ -149,13 +155,14 @@ const Index = () => { {/* Divider */}

- {/* TODO: Empty space */} -
+
+ +
- + {/* Scroll to top button */} { const { data } = authClient.useSession(); @@ -34,6 +40,7 @@ const AppLayout = () => { export const Route = createFileRoute("/_authorized-layout/_search-layout")({ component: AppLayout, + validateSearch: zodValidator(filtersSchema), beforeLoad: async ({ location }) => { const { data } = await authClient.getSession(); diff --git a/apps/web/src/routes/_authorized-layout/route.tsx b/apps/web/src/routes/_authorized-layout/route.tsx index c25d876f..29e66d59 100644 --- a/apps/web/src/routes/_authorized-layout/route.tsx +++ b/apps/web/src/routes/_authorized-layout/route.tsx @@ -1,6 +1,6 @@ import { plural, t } from "@lingui/core/macro"; -import * as Sentry from "@sentry/react"; import { Trans } from "@lingui/react/macro"; +import * as Sentry from "@sentry/react"; import { useQuery } from "@tanstack/react-query"; import { createFileRoute, diff --git a/packages/search/src/database.ts b/packages/search/src/database.ts index 55719c3c..e74e378f 100644 --- a/packages/search/src/database.ts +++ b/packages/search/src/database.ts @@ -9,6 +9,7 @@ import { insertMultiple, type Results, remove, + type SearchParamsFullText, search, update, } from "@orama/orama"; @@ -97,7 +98,7 @@ export const removeDocument = (db: DictionaryOrama, id: string) => { type SearchableProperties = keyof typeof dictionarySchema; /** - * Exact match fields - searched first with low tolerance + * Exact match fields */ export const EXACT_PROPERTIES: SearchableProperties[] = [ "word_exact", @@ -109,13 +110,12 @@ export const EXACT_PROPERTIES: SearchableProperties[] = [ ]; /** - * Normalized fields - searched with higher tolerance for fuzzy matching + * Normalized fields */ export const NORMALIZED_PROPERTIES: SearchableProperties[] = [ "word", "translation", "definition", - "tags", "morphology.ism.plurals", "morphology.ism.singular", "morphology.verb.masadir", @@ -138,6 +138,15 @@ export const NORMALIZED_BOOST = { export type SearchLanguage = "arabic" | "english"; +export type SearchDictionaryOptions = { + limit?: number; + offset?: number; + properties?: SearchableProperties[]; + language?: SearchLanguage; + where?: SearchParamsFullText["where"]; + sortBy?: SearchParamsFullText["sortBy"]; +}; + /** * Searches the Orama database using two-pass search for better relevance: * 1. First pass: exact fields with low tolerance (precise matching) @@ -147,12 +156,7 @@ export type SearchLanguage = "arabic" | "english"; export const searchDictionary = ( db: DictionaryOrama, term: string, - options?: { - limit?: number; - offset?: number; - properties?: SearchableProperties[]; - language?: SearchLanguage; - } + options?: SearchDictionaryOptions ) => { const limit = options?.limit ?? 10; const offset = options?.offset ?? 0; @@ -165,6 +169,8 @@ export const searchDictionary = ( term, limit, offset, + where: options?.where, + sortBy: options?.sortBy, }, language ); @@ -185,6 +191,8 @@ export const searchDictionary = ( limit: fetchLimit, properties: EXACT_PROPERTIES, tolerance: exactTolerance, + where: options?.where, + sortBy: options?.sortBy, }, language ) as SearchResults; @@ -200,6 +208,8 @@ export const searchDictionary = ( properties: NORMALIZED_PROPERTIES, boost: NORMALIZED_BOOST, tolerance: fuzzyTolerance, + where: options?.where, + sortBy: options?.sortBy, }, language ) as SearchResults; diff --git a/packages/search/src/schema.ts b/packages/search/src/schema.ts index 6b34e257..066654d1 100644 --- a/packages/search/src/schema.ts +++ b/packages/search/src/schema.ts @@ -22,7 +22,7 @@ export const dictionarySchema = { definition: "string", type: "enum", root: "string[]", - tags: "string[]", + tags: "enum[]", "morphology.ism.singular": "string", "morphology.ism.plurals": "string[]", "morphology.verb.past_tense": "string", diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index 28bfda68..93446060 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -21,6 +21,7 @@ "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-popover": "^1.1.14", "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.0", diff --git a/packages/web-ui/src/components/scroll-area.tsx b/packages/web-ui/src/components/scroll-area.tsx new file mode 100644 index 00000000..9b399ce1 --- /dev/null +++ b/packages/web-ui/src/components/scroll-area.tsx @@ -0,0 +1,45 @@ +import { cn } from "@bahar/design-system"; +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; +import * as React from "react"; + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; + +export { ScrollArea, ScrollBar }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 190df087..11dcfd70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -464,6 +464,9 @@ importers: '@tanstack/react-router': specifier: ^1.119.0 version: 1.120.13(react-dom@19.0.0)(react@19.0.0) + '@tanstack/zod-adapter': + specifier: ^1.150.0 + version: 1.150.0(@tanstack/react-router@1.120.13)(zod@4.0.17) '@tursodatabase/sync-wasm': specifier: ^0.3.2 version: 0.3.2 @@ -709,6 +712,9 @@ importers: '@radix-ui/react-radio-group': specifier: ^1.3.7 version: 1.3.7(react-dom@19.1.0)(react@19.1.0) + '@radix-ui/react-scroll-area': + specifier: ^1.2.10 + version: 1.2.10(react-dom@19.1.0)(react@19.1.0) '@radix-ui/react-select': specifier: ^2.2.5 version: 2.2.5(react-dom@19.1.0)(react@19.1.0) @@ -7527,6 +7533,10 @@ packages: /@radix-ui/primitive@1.1.2: resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + /@radix-ui/primitive@1.1.3: + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + dev: false + /@radix-ui/react-accordion@1.2.11(react-dom@19.1.0)(react@19.1.0): resolution: {integrity: sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==} peerDependencies: @@ -8011,6 +8021,25 @@ packages: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + /@radix-ui/react-presence@1.1.5(react-dom@19.1.0)(react@19.1.0): + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + dev: false + /@radix-ui/react-primitive@2.1.0(@types/react@19.1.17)(react-dom@19.1.0)(react@19.1.0): resolution: {integrity: sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==} peerDependencies: @@ -8100,6 +8129,32 @@ packages: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + /@radix-ui/react-scroll-area@1.2.10(react-dom@19.1.0)(react@19.1.0): + resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.17)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(react-dom@19.1.0)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.17)(react-dom@19.1.0)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + dev: false + /@radix-ui/react-select@2.2.5(react-dom@19.1.0)(react@19.1.0): resolution: {integrity: sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==} peerDependencies: @@ -10291,6 +10346,17 @@ packages: engines: {node: '>=12'} dev: true + /@tanstack/zod-adapter@1.150.0(@tanstack/react-router@1.120.13)(zod@4.0.17): + resolution: {integrity: sha512-MdkmF6tqxxskG3K3LHVN+PAj8m7kHMhlArDcI4Mi+Fx5CG3BCJcpYGv4aZJhvL0CxHw3N6qyn6SIzG9FraXhWA==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': '>=1.43.2' + zod: ^3.23.8 + dependencies: + '@tanstack/react-router': 1.120.13(react-dom@19.0.0)(react@19.0.0) + zod: 4.0.17 + dev: false + /@tediousjs/connection-string@0.5.0: resolution: {integrity: sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==} dev: false @@ -15296,6 +15362,7 @@ packages: /is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true dev: false /is-extglob@2.1.1: @@ -15341,6 +15408,7 @@ packages: /is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} + hasBin: true dependencies: is-docker: 3.0.0 dev: false From 9368ebe8b3d40d5462b3abc2778ecdb2bb131312 Mon Sep 17 00:00:00 2001 From: sufyan Date: Sun, 18 Jan 2026 15:35:20 -0500 Subject: [PATCH 5/8] feat: implement sorting --- apps/web/src/components/TagsCombobox.tsx | 74 +++++---- .../dictionary/filters/DictionaryFilters.tsx | 139 +++++++++++++++-- .../dictionary/filters/TagsFilter.tsx | 10 +- .../src/components/search/InfiniteScroll.tsx | 7 +- apps/web/src/hooks/db/index.ts | 2 +- apps/web/src/hooks/{ => search}/useSearch.ts | 19 ++- .../lib/db/operations/dictionary-entries.ts | 2 +- .../_app-layout/dictionary/edit/$wordId.tsx | 30 ++-- .../_app-layout/settings/route.lazy.tsx | 2 +- .../_search-layout/index.lazy.tsx | 4 +- .../_search-layout/route.tsx | 2 + .../src/routes/_authorized-layout/route.tsx | 2 +- packages/i18n/locales/ar.po | 142 ++++++++++++------ packages/i18n/locales/ar.ts | 2 +- packages/i18n/locales/en.po | 138 +++++++++++------ packages/i18n/locales/en.ts | 2 +- packages/search/src/database.ts | 34 +---- packages/search/src/schema.ts | 9 -- packages/search/src/tokenizer.ts | 19 +-- 19 files changed, 419 insertions(+), 220 deletions(-) rename apps/web/src/hooks/{ => search}/useSearch.ts (92%) diff --git a/apps/web/src/components/TagsCombobox.tsx b/apps/web/src/components/TagsCombobox.tsx index aa5e10d2..5005608d 100644 --- a/apps/web/src/components/TagsCombobox.tsx +++ b/apps/web/src/components/TagsCombobox.tsx @@ -2,6 +2,7 @@ import { cn } from "@bahar/design-system"; import { useDebounce } from "@uidotdev/usehooks"; import { Check, ChevronDown, X } from "lucide-react"; import { + type FC, type KeyboardEvent, type ReactNode, startTransition, @@ -11,6 +12,46 @@ import { useState, } from "react"; +export const TagPill: FC<{ + disabled?: boolean; + onClick?: () => void; + tagValue: string; +}> = ({ tagValue, disabled, onClick }) => { + return ( + + + ); +}; + export interface TagsComboboxProps { /** Selected tag values */ value: string[]; @@ -210,7 +251,6 @@ export function TagsCombobox({ return (
- {/* Anchor - Tags + Input */}
({ }} > {value.map((tagValue) => ( - + tagValue={tagValue} + /> ))} { + switch (option) { + case "relevance": + return Relevance; + case "updatedAt": + return Recently updated; + case "createdAt": + return Recently added; + } +}; + +const sortOptions: SortOption[] = ["relevance", "updatedAt", "createdAt"]; export const DictionaryFilters = () => { const navigate = useNavigate(); - const { tags: filteredTags } = useSearch({ + const { tags: filteredTags, sort } = useSearch({ from: "/_authorized-layout/_search-layout", }); @@ -18,24 +43,114 @@ export const DictionaryFilters = () => { return (
-
-
- - - Filters - -
+
+
+
+ + + Filters + +
+ + +
+ +
+

+ Tags +

+ + + + {filteredTags?.length ? ( +
    + {filteredTags.map((tag) => ( + { + const shouldRemove = filteredTags?.some((t) => t === tag); - + if (shouldRemove) { + const newTags = filteredTags?.filter((t) => t !== tag); + + navigate({ + to: "/", + search: { + tags: newTags?.length ? newTags : undefined, + sort, + }, + }); + } else { + navigate({ + to: "/", + search: { + tags: [...(filteredTags ?? []), tag], + sort, + }, + }); + } + }} + tagValue={tag} + /> + ))} +
+ ) : null} +
+ +
+
+ + + Sort by + +
+ + +
+ +
+ +
{hasActiveFilters && ( )}
diff --git a/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx b/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx index d94a5839..09a14fff 100644 --- a/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx +++ b/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx @@ -28,7 +28,7 @@ export const TagsFilter = () => { }); const navigate = useNavigate(); const { data: tags } = useQuery({ - queryFn: () => dictionaryEntriesTable.tags.query(""), + queryFn: () => dictionaryEntriesTable.tags.query(), queryKey: [...dictionaryEntriesTable.tags.cacheOptions.queryKey], }); @@ -37,7 +37,7 @@ export const TagsFilter = () => { - + @@ -71,10 +71,12 @@ export const TagsFilter = () => { const shouldRemove = filteredTags?.some((t) => t === tag); if (shouldRemove) { + const newTags = filteredTags?.filter((t) => t !== tag); + navigate({ to: "/", search: { - tags: filteredTags?.filter((t) => t !== tag), + tags: newTags?.length ? newTags : undefined, }, }); } else { diff --git a/apps/web/src/components/search/InfiniteScroll.tsx b/apps/web/src/components/search/InfiniteScroll.tsx index 6df5c242..7c6d50d9 100644 --- a/apps/web/src/components/search/InfiniteScroll.tsx +++ b/apps/web/src/components/search/InfiniteScroll.tsx @@ -29,7 +29,7 @@ import { useEffect, useState, } from "react"; -import { useInfiniteScroll } from "@/hooks/useSearch"; +import { useInfiniteScroll } from "@/hooks/search/useSearch"; import { dictionaryEntriesTable } from "@/lib/db/operations/dictionary-entries"; import { Highlight } from "./Highlight"; @@ -430,7 +430,9 @@ const PIXEL_HEIGHT_OFFSET = 800; export const InfiniteScroll: FC<{ searchQuery?: string }> = ({ searchQuery, }) => { - const { tags } = useSearch({ from: "/_authorized-layout/_search-layout" }); + const { tags, sort } = useSearch({ + from: "/_authorized-layout/_search-layout", + }); const navigate = useNavigate(); const { results: { hits } = {}, @@ -439,6 +441,7 @@ export const InfiniteScroll: FC<{ searchQuery?: string }> = ({ } = useInfiniteScroll({ term: searchQuery, filters: { tags }, + sort, }); const [ref, { height }] = useMeasure(); const [{ y }] = useWindowScroll(); diff --git a/apps/web/src/hooks/db/index.ts b/apps/web/src/hooks/db/index.ts index a1ba31f5..30ccc276 100644 --- a/apps/web/src/hooks/db/index.ts +++ b/apps/web/src/hooks/db/index.ts @@ -7,7 +7,7 @@ import { dictionaryEntriesTable } from "@/lib/db/operations/dictionary-entries"; import { flashcardsTable } from "@/lib/db/operations/flashcards"; import { queryClient } from "@/lib/query"; import { getOramaDb, toOramaDocument } from "@/lib/search"; -import { useSearch } from "../useSearch"; +import { useSearch } from "../search/useSearch"; /** * Hook for adding a new word to the local database and the search index. diff --git a/apps/web/src/hooks/useSearch.ts b/apps/web/src/hooks/search/useSearch.ts similarity index 92% rename from apps/web/src/hooks/useSearch.ts rename to apps/web/src/hooks/search/useSearch.ts index afe588d7..9c28e955 100644 --- a/apps/web/src/hooks/useSearch.ts +++ b/apps/web/src/hooks/search/useSearch.ts @@ -10,6 +10,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { getOramaDb } from "@/lib/search"; import { detectLanguage } from "@/lib/utils"; +export const SORT_OPTIONS = ["relevance", "updatedAt", "createdAt"] as const; const SEARCH_RESULTS_PER_PAGE = 20; const searchResultsMetadataAtom = atom< @@ -124,7 +125,7 @@ export const useInfiniteScroll = ( filters?: { tags?: string[]; }; - sortBy?: SearchDictionaryOptions["sortBy"]; + sort?: (typeof SORT_OPTIONS)[number]; } = {} ) => { const { search } = useSearch(); @@ -148,6 +149,18 @@ export const useInfiniteScroll = ( }; }, [paramsKey]); + const sortBy = useMemo(() => { + if (!params.sort || params.sort === "relevance") return undefined; + + return { + property: + params.sort === "createdAt" + ? "created_at_timestamp_ms" + : "updated_at_timestamp_ms", + order: "DESC", + }; + }, [paramsKey]); + const searchQueryLanguage = useMemo[1]>(() => { const detectedLanuage = detectLanguage(params.term ?? ""); @@ -172,7 +185,7 @@ export const useInfiniteScroll = ( const { hits } = search( { offset, - sortBy: params.sortBy, + sortBy, term: params.term, where: whereFilter, }, @@ -189,7 +202,7 @@ export const useInfiniteScroll = ( useEffect(() => { const { hits, ...metadata } = search( { - sortBy: params.sortBy, + sortBy, term: params.term, where: whereFilter, offset: 0, diff --git a/apps/web/src/lib/db/operations/dictionary-entries.ts b/apps/web/src/lib/db/operations/dictionary-entries.ts index 3a4fc716..9612b773 100644 --- a/apps/web/src/lib/db/operations/dictionary-entries.ts +++ b/apps/web/src/lib/db/operations/dictionary-entries.ts @@ -50,7 +50,7 @@ export const dictionaryEntriesTable = { }, tags: { query: async ( - searchTerm: string + searchTerm?: string ): Promise<{ tag: string; count: number }[]> => { try { const db = await ensureDb(); diff --git a/apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx b/apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx index 4212cba9..8ee465cf 100644 --- a/apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx +++ b/apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx @@ -22,7 +22,12 @@ import { Form } from "@bahar/web-ui/components/form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Trans, useLingui } from "@lingui/react/macro"; import { useMutation, useQuery } from "@tanstack/react-query"; -import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; +import { + createFileRoute, + Link, + useNavigate, + useRouter, +} from "@tanstack/react-router"; import { ChevronLeft, ChevronRight } from "lucide-react"; import { type FC, useEffect } from "react"; import { type SubmitHandler, useForm } from "react-hook-form"; @@ -190,25 +195,26 @@ const Breadcrumbs: FC<{ className?: string; word: string }> = ({ const BackButton = () => { const dir = useDir(); + const router = useRouter(); return ( ); }; diff --git a/apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx b/apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx index 24a11a26..5755416e 100644 --- a/apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx +++ b/apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx @@ -31,7 +31,7 @@ import { InputFile } from "@/components/InputFile"; import { LanguageMenu } from "@/components/LanguageMenu"; import { Page } from "@/components/Page"; import { ColorThemeMenu, ThemeMenu } from "@/components/ThemeMenu"; -import { useSearch } from "@/hooks/useSearch"; +import { useSearch } from "@/hooks/search/useSearch"; import { authClient } from "@/lib/auth-client"; import { deleteLocalDatabase, ensureDb } from "@/lib/db"; import { transformForExport } from "@/lib/db/export"; diff --git a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx index 816e6bad..fd64d1f1 100644 --- a/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx +++ b/apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx @@ -14,8 +14,8 @@ import { FlashcardDrawer } from "@/components/features/flashcards/FlashcardDrawe import { itemVariants, Page } from "@/components/Page"; import { InfiniteScroll } from "@/components/search/InfiniteScroll"; import { searchQueryAtom } from "@/components/search/state"; +import { useSearch } from "@/hooks/search/useSearch"; import { useFormatNumber } from "@/hooks/useFormatNumber"; -import { useSearch } from "@/hooks/useSearch"; import { DEFAULT_BACKLOG_THRESHOLD_DAYS, flashcardsTable, @@ -153,7 +153,7 @@ const Index = () => {
{/* Divider */} -
+ {/*
*/}
diff --git a/apps/web/src/routes/_authorized-layout/_search-layout/route.tsx b/apps/web/src/routes/_authorized-layout/_search-layout/route.tsx index 903e4574..e8083ad9 100644 --- a/apps/web/src/routes/_authorized-layout/_search-layout/route.tsx +++ b/apps/web/src/routes/_authorized-layout/_search-layout/route.tsx @@ -4,12 +4,14 @@ import { z } from "zod"; import { DesktopNavigation } from "@/components/DesktopNavigation"; import { MobileHeader } from "@/components/MobileHeader"; import { SearchInput } from "@/components/search/SearchInput"; +import { SORT_OPTIONS } from "@/hooks/search/useSearch"; import { authClient } from "@/lib/auth-client"; import { settingsTable } from "@/lib/db/operations/settings"; import { queryClient } from "@/lib/query"; const filtersSchema = z.object({ tags: z.array(z.string()).optional(), + sort: z.enum(SORT_OPTIONS).optional(), }); const AppLayout = () => { diff --git a/apps/web/src/routes/_authorized-layout/route.tsx b/apps/web/src/routes/_authorized-layout/route.tsx index 29e66d59..28adf57d 100644 --- a/apps/web/src/routes/_authorized-layout/route.tsx +++ b/apps/web/src/routes/_authorized-layout/route.tsx @@ -19,7 +19,7 @@ import { ErrorMessage } from "@/components/errors/ErrorMessage"; import { MobileHeader } from "@/components/MobileHeader"; import { SchemaOutdatedBanner } from "@/components/SchemaOutdatedBanner"; import { SyncIndicator } from "@/components/SyncIndicator"; -import { useSearch } from "@/hooks/useSearch"; +import { useSearch } from "@/hooks/search/useSearch"; import { authClient } from "@/lib/auth-client"; import { ensureDb, initDb } from "@/lib/db"; import { DisplayError } from "@/lib/db/errors"; diff --git a/packages/i18n/locales/ar.po b/packages/i18n/locales/ar.po index e75d188e..ff966055 100644 --- a/packages/i18n/locales/ar.po +++ b/packages/i18n/locales/ar.po @@ -13,6 +13,11 @@ msgstr "" "Plural-Forms: \n" "X-Generator: Poedit 3.4.4\n" +#. placeholder {0}: filteredTags.length +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:45 +msgid "{0, plural, one {# tag selected} other {# tags selected}}" +msgstr "{0, plural, one {علامة واحدة محددة} two {علامتان محددتان} few {# علامات محددة} many {# علامة محددة} other {# علامة محددة}}" + #. placeholder {0}: cards.length #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/FlashcardDrawer.tsx:333 msgid "{0, plural, one {card left} other {cards left}}" @@ -110,7 +115,7 @@ msgstr "أضف كلمة جديدة إلى معجمك" #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:137 #: ../../apps/mobile/src/app/(search)/(home)/edit-word/[id].tsx:138 msgid "Add a tag and press enter" -msgstr "أضف وسمًا واضغط Enter" +msgstr "أضف علامة واضغط Enter" #: ../../apps/web/src/components/features/dictionary/add/TagsFormSection.tsx:82 msgid "Add all" @@ -158,7 +163,7 @@ msgstr "أضف صيغة جمع" msgid "Add Plural" msgstr "أضف جمع" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:509 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:518 msgid "Add some to get started!" msgstr "أضف شيئًا لتبدأ!" @@ -172,7 +177,7 @@ msgstr "أضف علامة {inputValue}" #: ../../apps/web/src/components/features/dictionary/add/TagsFormSection.tsx:60 msgid "Add tag {value}" -msgstr "إضافة الوسم {value}" +msgstr "إضافة العلامة {value}" #: ../../apps/web/src/components/features/dictionary/add/TagsFormSection.tsx:48 msgid "Add tags to your word." @@ -207,7 +212,7 @@ msgstr "تعريف عربية للكلمة." msgid "An English translation of the sentence." msgstr "ترجمة إنجليزية للجملة." -#: ../../apps/web/src/components/features/dictionary/add/BasicDetailsFormSection.tsx:72 +#: ../../apps/web/src/components/features/dictionary/add/BasicDetailsFormSection.tsx:77 msgid "An English translation of the word." msgstr "ترجمة إنجليزية للكلمة." @@ -287,7 +292,7 @@ msgstr "الكلمة بالعربية" msgid "Are you sure you want to delete \"{0}\"?" msgstr "هل أنت متأكد من حذف \"{0}\"؟" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:135 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:140 msgid "Are you sure you want to delete this word? It will be removed from your dictionary and its flashcards will be deleted.<0/><1/><2>This action cannot be undone." msgstr "هل أنت متأكد أنك تريد حذف هذه الكلمة؟ سيتم إزالتها من معجمك وسيتم حذف البطاقات بها.<0/><1/><2>لا يمكن التراجع عن هذا الإجراء." @@ -299,16 +304,16 @@ msgstr "هل أنت متأكد من حذف هذه الكلمة؟ لا يمكن msgid "Are you sure you want to delete your dictionary? All your words will be deleted permanently. This action cannot be undone. Please make sure to export your dictionary before deleting it." msgstr "هل أنت متأكد من رغبتك في حذف معجمك؟ سيحذف جميع كلماتك بشكل دائم. لا يمكن التراجع عن هذا الإجراء. صدر معجمك قبل حذفه من فضلك." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:83 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:88 msgid "Are you sure you want to reset this flashcard's progress? The word entry in the dictionary will not be modified, but its corresponding flashcards will be treated as new ones.<0/><1/><2>This action cannot be undone." msgstr "هل أنت متأكد أنك تريد إعادة تعيين تقدم بطاقة هذه؟ لن يتم تعديل إدخال الكلمة في المعجم، ولكن سيتم التعامل مع بطاقات بها على أنها بطاقات جديدة.<0/><1/><2>لا يمكن التراجع عن هذا الإجراء." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:209 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:216 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:74 msgid "Back" msgstr "رجوع" -#: ../../apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx:187 +#: ../../apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx:188 msgid "Back to top" msgstr "العودة إلى الأعلى" @@ -382,6 +387,10 @@ msgstr "اختر ملف" msgid "Clear" msgstr "امسح" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:153 +msgid "Clear all filters" +msgstr "مسح كل المرشّحات" + #: ../../apps/web/src/components/features/settings/FlashcardSettingsCardSection.tsx:242 msgid "Clear backlog" msgstr "امسح المتراكمات" @@ -515,7 +524,7 @@ msgstr "تتيح لك المجموعات إنشاء مجموعات فرعية م msgid "Default" msgstr "افتراضي" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:115 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:138 #: ../../apps/web/src/components/features/dictionary/add/AdditionalDetailsFormSection.tsx:81 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:117 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:444 @@ -525,8 +534,8 @@ msgstr "التعريف" #: ../../apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx:466 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx:489 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:124 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:156 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:129 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:161 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/decks/route.lazy.tsx:207 #: ../../apps/mobile/src/app/(search)/decks.tsx:285 #: ../../apps/mobile/src/app/(search)/(home)/edit-word/[id].tsx:393 @@ -549,7 +558,7 @@ msgstr "حذف المعجم" msgid "Delete Everything" msgstr "حذف كل شيء" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:131 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:136 msgid "Delete this word?" msgstr "حذف هذه الكلمة؟" @@ -599,7 +608,7 @@ msgstr "اغلق" msgid "Don't show" msgstr "لا تعرض" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:154 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:175 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:84 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:146 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:633 @@ -612,7 +621,7 @@ msgstr "المثنى" msgid "e.g. ك ت ب" msgstr "مثال: ك ت ب" -#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:64 +#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:62 msgid "Easy" msgstr "سهل" @@ -620,7 +629,7 @@ msgstr "سهل" msgid "Edit" msgstr "عدَل" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:183 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:188 msgid "Edit {word}" msgstr "حرّر {word}" @@ -693,7 +702,7 @@ msgstr "المثال {0}" msgid "Example usages of the word in different contexts." msgstr "استخدامات مثالية للكلمة في سياقات مختلفة." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:273 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:292 #: ../../apps/web/src/components/features/dictionary/add/AdditionalDetailsFormSection.tsx:154 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:262 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:490 @@ -726,7 +735,7 @@ msgstr "تصدير معجمك" msgid "Exporting dictionary with flaschards will save your flashcard progress along with all the content in your dictionary. Use this option if you want to make a backup of your data.<0/><1/>Exporting your dictionary without flashcards will not save any flashcard progress. Use this option if you want to share your dictionary.<2/><3/><4>Warning: Importing your file without flashcards will reset all of your flashcards." msgstr "تصدير المعجم مع البطاقات سيحفظ تقدم البطاقات بالإضافة إلى كل المحتوى في معجمك. استخدم هذا الخيار إذا كنت ترغب في إنشاء نسخة احتياطية من بياناتك.<0/><1/>تصدير المعجم بدون البطاقات لن يحفظ أي تقدم للبطاقات. استخدم هذا الخيار إذا كنت ترغب في مشاركة معجمك.<2/><3/><4>تحذير: استيراد ملفك بدون البطاقات سيؤدي إلى إعادة تعيين جميع البطاقات." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:31 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:42 #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/TagBadgesList.tsx:17 #: ../../apps/web/src/components/features/dictionary/add/CategoryFormSection.tsx:136 #: ../../apps/web/src/components/features/decks/DeckDialogContent.tsx:69 @@ -794,7 +803,7 @@ msgstr "فشل في تحديث إعدادات البطاقة." msgid "Failed to update settings" msgstr "فشل تحديث الإعدادات" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:324 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:330 msgid "Failed to update the word!" msgstr "فشل في تحديث الكلمة!" @@ -807,7 +816,7 @@ msgstr "فشل تحديث الكلمة" msgid "Failed to upload migration" msgstr "فشل تحميل الترحيل" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:39 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:50 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:230 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:46 msgid "Feminine" @@ -819,6 +828,14 @@ msgstr "مؤنّث" msgid "Fi'l" msgstr "فعل" +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:51 +msgid "Filter by tags..." +msgstr "رشّح حسب العلامات..." + +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:51 +msgid "Filters" +msgstr "الترشيح" + #: ../../apps/web/src/components/features/settings/FlashcardSettingsCardSection.tsx:123 msgid "Flashcard settings updated!" msgstr "تم تحديث إعدادات البطاقة!" @@ -840,14 +857,14 @@ msgstr "يتبع إعدادات النظام" msgid "For example, the word" msgstr "مثلا الكلمة" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:240 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:260 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:232 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:803 #: ../../apps/mobile/src/app/(search)/(home)/edit-word/[id].tsx:913 msgid "Form" msgstr "الوزن" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:180 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:201 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:211 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:172 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:700 @@ -865,7 +882,7 @@ msgstr "GitHub" msgid "Go back" msgstr "ارجع" -#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:55 +#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:53 msgid "Good" msgstr "حسن" @@ -878,7 +895,7 @@ msgstr "أحسنت في المتراكمات! لا يزال لديك {0} مرا msgid "Great work! Come back later for more reviews." msgstr "عمل رائع! عد لاحقًا لمزيد من المراجعات." -#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:45 +#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:44 msgid "Hard" msgstr "صعب" @@ -908,7 +925,7 @@ msgstr "إخفاء التفاصيل" msgid "Hint" msgstr "تلميح" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:174 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:179 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:39 #: ../../apps/web/src/components/MobileHeader.tsx:154 #: ../../apps/web/src/components/DesktopNavigation.tsx:53 @@ -943,7 +960,7 @@ msgstr "إذا لزم الأمر." msgid "If you're experiencing sync issues or data not loading correctly, you can reset your local data and re-sync from the server." msgstr "إذا كنت تواجه مشاكل في المزامنة أو عدم تحميل البيانات بشكل صحيح، يمكنك إعادة تعيين بياناتك المحلية وإعادة المزامنة من الخادم." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:227 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:247 #: ../../apps/web/src/components/features/dictionary/add/VerbMorphologyCardSection.tsx:103 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:219 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:786 @@ -1019,7 +1036,7 @@ msgstr "اللغة" msgid "Light" msgstr "فاتح" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:126 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:148 msgid "Loading details..." msgstr "جارٍ تحميل التفاصيل..." @@ -1059,7 +1076,7 @@ msgstr "إدارة معجمك." msgid "Masadir" msgstr "مصادر" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:38 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:49 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:227 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:45 msgid "Masculine" @@ -1096,8 +1113,8 @@ msgstr "تم تسجيل الترحيل بنجاح." #~ msgid "More than {0} cards to review" #~ msgstr "أكثر من {0} بطاقة للمراجعة" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:135 -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:195 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:156 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:215 #: ../../apps/web/src/components/features/dictionary/add/MorphologyFormSection.tsx:52 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:127 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:187 @@ -1137,16 +1154,20 @@ msgstr "لا توجد مجموعات بعد" msgid "No file chosen" msgstr "لم يتم اختيار ملف" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:486 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:495 #: ../../apps/mobile/src/components/dictionary/DictionaryList.tsx:55 msgid "No results found" msgstr "لم يتم العثور على نتائج" +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:63 +msgid "No tags found." +msgstr "لم يتم العثور على علامات." + #: ../../apps/web/src/components/ThemeMenu.tsx:87 msgid "Nord" msgstr "نورد" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:28 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:39 #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/TagBadgesList.tsx:11 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:35 msgid "Noun" @@ -1162,7 +1183,7 @@ msgstr "صرف الاسم" msgid "Or continue with" msgstr "أو استمر مع" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:30 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:41 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:37 msgid "Particle" msgstr "حرف" @@ -1176,7 +1197,7 @@ msgstr "اسم مفعول" msgid "Passive Participle" msgstr "اسم مفعول" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:201 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:221 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:193 msgid "Past" msgstr "الماضي" @@ -1217,7 +1238,7 @@ msgstr "حاول مرة أخرى أو أعد تحميل الصفحة." msgid "Please try again." msgstr "حاول مرّة أخرى." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:167 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:188 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:159 msgid "Plural" msgstr "الجمع" @@ -1253,7 +1274,7 @@ msgstr "حروف الجر" msgid "Prepositions (huroof) that can be used with the verb to change its meaning." msgstr "الحروف التي يمكن استخدامها مع الفعل لتغيير معناه." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:214 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:234 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:206 msgid "Present" msgstr "المضارع" @@ -1267,10 +1288,22 @@ msgstr "المضارع" msgid "Present Tense" msgstr "المضارع" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:26 +msgid "Recently added" +msgstr "المضافة حديثًا" + +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:24 +msgid "Recently updated" +msgstr "المحدّثة حديثًا" + #: ../../apps/web/src/components/features/dictionary/add/TagsFormSection.tsx:68 msgid "Recently used" msgstr "المستخدمة مؤخرًا" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:22 +msgid "Relevance" +msgstr "الصلة" + #: ../../apps/web/src/components/errors/ErrorMessage.tsx:142 msgid "Reload" msgstr "أعد التحميل" @@ -1295,12 +1328,12 @@ msgstr "أعد جدولة جميع البطاقات المتراكمة بتقي msgid "Reset and re-sync" msgstr "إعادة التعيين والمزامنة" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:72 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:106 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:77 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:111 msgid "Reset flashcard" msgstr "أعد البطاقة" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:79 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:84 msgid "Reset flashcard progress?" msgstr "إعادة تعيين تقدم البطاقة؟" @@ -1352,8 +1385,8 @@ msgstr "الجذر" msgid "Root Letters" msgstr "الحروف الأصلية" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:350 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:379 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:356 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:385 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:199 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:226 #: ../../apps/web/src/components/features/settings/FlashcardSettingsCardSection.tsx:232 @@ -1380,6 +1413,10 @@ msgstr "ترحيلات المخطط" msgid "Search for a tag..." msgstr "ابحث عن علامة..." +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:59 +msgid "Search tag..." +msgstr "ابحث عن علامة..." + #: ../../apps/web/src/components/search/SearchInput.tsx:63 #: ../../apps/mobile/src/app/(search)/_layout.tsx:97 msgid "Search..." @@ -1455,7 +1492,7 @@ msgstr "اعرض بطاقات من الإنجليزية إلى العربية." msgid "Show reverse flashcards" msgstr "إظهار البطاقات العكسية" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:141 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:162 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:60 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:133 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:616 @@ -1471,6 +1508,10 @@ msgstr "تعذر تحميل بعض إدخالات المعجم" msgid "Something went wrong" msgstr "حدث خطأ ما" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:104 +msgid "Sort by" +msgstr "ترتيب حسب" + #: ../../apps/web/src/components/features/settings/AdminSettingsCardSection.tsx:100 msgid "SQL Script" msgstr "نص SQL" @@ -1499,11 +1540,11 @@ msgstr "تم التصدير بنجاح!" msgid "Successfully imported!" msgstr "تم الاستيراد بنجاح!" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:101 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:106 msgid "Successfully reset the flashcard." msgstr "تمت إعادة تعيين البطاقة بنجاح." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:316 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:322 msgid "Successfully updated the word!" msgstr "حدثت الكلمة بنجاح!" @@ -1517,7 +1558,8 @@ msgstr "حدثت الكلمة بنجاح!" msgid "System" msgstr "نظام" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:302 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:320 +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:60 #: ../../apps/web/src/components/features/dictionary/add/TagsFormSection.tsx:44 #: ../../apps/web/src/components/features/decks/DeckDialogContent.tsx:229 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:291 @@ -1582,7 +1624,7 @@ msgstr "الحروف الجذرية للكلمة. يمكن فصل المدخلا msgid "The word has been added to your dictionary." msgstr "تمت إضافة الكلمة إلى معجمك." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:317 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:323 msgid "The word has been updated." msgstr "حدثت الكلمة." @@ -1634,7 +1676,7 @@ msgstr "حدث خطأ أثناء تحديث المجموعة" msgid "There was an error updating your flashcard settings." msgstr "حدث خطأ أثناء تحديث إعدادات بطاقاتك." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:325 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:331 msgid "There was an error updating your word. Please try again." msgstr "حدث خطأ أثناء تحديث كلمتك. حاول مرة أخرى." @@ -1721,7 +1763,7 @@ msgstr "معرب" #~ msgid "Troubleshooting" #~ msgstr "استكشاف الأخطاء" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:489 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:498 #: ../../apps/mobile/src/components/dictionary/DictionaryList.tsx:58 msgid "Try a different search term" msgstr "جرب مصطلح بحث مختلف" @@ -1759,7 +1801,7 @@ msgstr "يجب أن تكون القيمة {0} أو أكبر" msgid "Value must be {0} or less" msgstr "يجب أن تكون القيمة {0} أو أقل" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:29 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:40 #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/TagBadgesList.tsx:13 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:36 msgid "Verb" @@ -1778,7 +1820,7 @@ msgstr "صرف الفعل" msgid "Verbal noun forms (masadir) of the verb." msgstr "مصادر للفعل." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:255 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:275 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:244 msgid "Verbal nouns" msgstr "المصادر" @@ -1852,7 +1894,7 @@ msgstr "لديك {0} بطاقات متراكمة عندما تكون جاهزً msgid "You have no decks yet." msgstr "ليس لديك أي مجموعات بعد." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:506 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:515 msgid "You have no words in your dictionary yet." msgstr "ليس لديك أي كلمات في قاموسك بعد." diff --git a/packages/i18n/locales/ar.ts b/packages/i18n/locales/ar.ts index e114f272..411e87c6 100644 --- a/packages/i18n/locales/ar.ts +++ b/packages/i18n/locales/ar.ts @@ -1 +1 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"5rgLzy\":[[\"0\",\"plural\",{\"one\":[\"بطاقة متبقية\"],\"other\":[\"بطاقات متبقية\"]}]],\"/hTM9k\":[[\"0\"],\" / \",[\"1\"],\" دفعات\"],\"mHye52\":[[\"0\"],\" / \",[\"1\"],\" بطاقات\"],\"baIPh3\":[[\"0\"],\" بطاقات مستحقة\"],\"beWT44\":[\"تمت إعادة جدولة \",[\"0\"],\" بطاقات.\"],\"JJcBCm\":[[\"0\"],\" إجمالي\"],\"JtTuS0\":[[\"hydrationSkippedCount\",\"plural\",{\"one\":[\"تم تخطي إدخال واحد بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخال الذي لم يتم تحميله.\"],\"two\":[\"تم تخطي إدخالين بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"],\"few\":[\"تم تخطي \",\"#\",\" إدخالات بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"],\"many\":[\"تم تخطي \",\"#\",\" إدخالًا بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"],\"other\":[\"تم تخطي \",\"#\",\" إدخال بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"]}]],\"UGMDWm\":[\"تم تخطي \",[\"skippedCount\"],\" إدخالات بسبب تلف البيانات.\"],\"gp8eDB\":[[\"totalHits\",\"plural\",{\"one\":[\"نتيجة\"],\"two\":[\"نتيجتان\"],\"few\":[\"نتائج\"],\"many\":[\"نتيجة\"],\"other\":[\"نتيجة\"]}]],\"QSRKsm\":[[\"totalResults\",\"plural\",{\"one\":[[\"totalResults\"],\" نتيجة\"],\"two\":[[\"totalResults\"],\" نتيجتان\"],\"few\":[[\"totalResults\"],\" نتائج\"],\"many\":[[\"totalResults\"],\" نتيجة\"],\"other\":[[\"totalResults\"],\" نتيجة\"]}]],\"gD09hk\":[[\"val\",\"plural\",{\"one\":[\"لا يمكن تجاوز حرف واحد\"],\"two\":[\"لا يمكن تجاوز حرفين\"],\"few\":[\"لا يمكن تجاوز \",\"#\",\" حروف\"],\"many\":[\"لا يمكن تجاوز \",\"#\",\" حرفا\"],\"other\":[\"لا يمكن تجاوز \",\"#\",\" حرف\"]}]],\"I1sa5o\":[[\"val\",\"plural\",{\"one\":[\"يجب أن يكون على الأقل واحد حرف\"],\"two\":[\"يجب أن يكون على الأقل حرفين\"],\"few\":[\"يجب أن يكون على الأقل \",\"#\",\" حروف\"],\"many\":[\"يجب أن يكون على الأقل \",\"#\",\" حرفا\"],\"other\":[\"يجب أن يكون على الأقل \",\"#\",\" حرف\"]}]],\"WWcbHw\":[\"غير فعل أو حرف.\"],\"ZQW2PY\":[\"حرف أو عطف.\"],\"0iy5O2\":[\"فعل.\"],\"7L01XJ\":[\"أعمال\"],\"DQqXHw\":[\"اسم فاعل\"],\"AwJc6G\":[\"اسم فاعل\"],\"rJwPkp\":[\"أضف ترحيل SQL جديد.\"],\"t89Ong\":[\"أضف كلمة جديدة\"],\"L18Pbl\":[\"أضف كلمة جديدة إلى معجمك\"],\"TYPFtT\":[\"أضف وسمًا واضغط Enter\"],\"iJMhp0\":[\"إضافة الكل\"],\"nPm6bB\":[\"أضف ضدا\"],\"jVmrwy\":[\"أضف ضد\"],\"1x4W9u\":[\"أضف مثلا\"],\"nI3A8B\":[\"أضف مثال\"],\"gLlFHX\":[\"أضف حرف\"],\"aLEtgY\":[\"أضف حرف\"],\"Tcbo6a\":[\"أضف مصدر\"],\"vKbErK\":[\"أضف صيغة جمع\"],\"SNF/r2\":[\"أضف جمع\"],\"cR1WUS\":[\"أضف شيئًا لتبدأ!\"],\"Oft+Pb\":[\"أضف بعض الكلمات للبدء!\"],\"Xls3A3\":[\"أضف علامة \",[\"inputValue\"]],\"VMjLu6\":[\"إضافة الوسم \",[\"value\"]],\"uF1GKn\":[\"أضف علامات إلى كلمتك.\"],\"bDXmnN\":[\"أضف كلمة\"],\"p59pEv\":[\"تفاصيل إضافية\"],\"b/7DUT\":[\"مرة أخرى\"],\"KK8UsT\":[\"انتهيت لليوم!\"],\"7Qi3W5\":[\"تعريف عربية للكلمة.\"],\"Y/gBMP\":[\"ترجمة إنجليزية للجملة.\"],\"jalxln\":[\"ترجمة إنجليزية للكلمة.\"],\"hyC+qY\":[\"حدث خطأ غير متوقع. يمكنك محاولة إعادة تحميل الصفحة لإصلاحه.\"],\"4xBHUg\":[\"الجواب\"],\"vQXp82\":[\"الضد \",[\"0\"]],\"o9dJs3\":[\"كلمة مضادة\"],\"Hvs4LI\":[\"أضداد\"],\"p1Kh9a\":[\"أية تفاصيل إضافية عن هذه الصيغة مثلا تكرار الاستخدام أو السياق.\"],\"9kni2/\":[\"أية صيغ جمع للكلمة.\"],\"2PPE7k\":[\"سيستبدل كل كلمات التي لديها نفس المعرف.\"],\"cnAqww\":[\"لغة التطبيق\"],\"aAIQg2\":[\"المظهر\"],\"8HV3WN\":[\"العربية\"],\"LNIQwr\":[\"التعريف بالعربية\"],\"uKmc5K\":[\"الجملة بالعربية\"],\"wxIhw3\":[\"صيغة الفعل العربي\"],\"071WZA\":[\"الكلمة بالعربية\"],\"pkD36F\":[\"هل أنت متأكد من حذف \\\"\",[\"0\"],\"\\\"؟\"],\"DOiV7q\":[\"هل أنت متأكد أنك تريد حذف هذه الكلمة؟ سيتم إزالتها من معجمك وسيتم حذف البطاقات بها.<0/><1/><2>لا يمكن التراجع عن هذا الإجراء.\"],\"2cZh44\":[\"هل أنت متأكد من حذف هذه الكلمة؟ لا يمكن التراجع عن هذا الإجراء.\"],\"ikBPhc\":[\"هل أنت متأكد من رغبتك في حذف معجمك؟ سيحذف جميع كلماتك بشكل دائم. لا يمكن التراجع عن هذا الإجراء. صدر معجمك قبل حذفه من فضلك.\"],\"IJr17L\":[\"هل أنت متأكد أنك تريد إعادة تعيين تقدم بطاقة هذه؟ لن يتم تعديل إدخال الكلمة في المعجم، ولكن سيتم التعامل مع بطاقات بها على أنها بطاقات جديدة.<0/><1/><2>لا يمكن التراجع عن هذا الإجراء.\"],\"iH8pgl\":[\"رجوع\"],\"0MgdN+\":[\"العودة إلى الأعلى\"],\"KNKCTb\":[\"المتراكمة\"],\"Fg+fNW\":[\"تم مسح المتراكمات!\"],\"vMebS2\":[\"بحر\"],\"EOUool\":[\"تفاصيل أساسية\"],\"cSCbv3\":[\"بيتا\"],\"OKWk8n\":[\"بتسجيل الدخول، فإنك توافق على <0>سياسة الخصوصية\"],\"dEgA5A\":[\"ألغ\"],\"oySfhX\":[\"بطاقات مستحقة اليوم أو مؤخرًا\"],\"uV52e1\":[\"بطاقات متأخرة أكثر من ٧ أيام\"],\"zAwyRS\":[\"بطاقات للمراجعة اليوم\"],\"K7tIrx\":[\"الباب\"],\"qWRWG1\":[\"السبب:\"],\"+yPBXI\":[\"اختر ملف\"],\"xCJdfg\":[\"امسح\"],\"saoOio\":[\"امسح المتراكمات\"],\"Oo39Ga\":[\"جارٍ المسح...\"],\"mpby9d\":[\"اتصل بالدعم\"],\"M73whl\":[\"السياق\"],\"xGVfLh\":[\"استمر\"],\"RvVi9c\":[\"استمرّ مع بريد الإلكتروني\"],\"NMo6Pj\":[\"تحكم في مكان ظهور الأضداد أثناء المراجعة\"],\"PiH3UR\":[\"تم النسخ!\"],\"he3ygx\":[\"انسخ\"],\"hYgDIe\":[\"إنشاء\"],\"oJc/nD\":[\"أنشئ مجموعة لتنظيم مراجعات بطاقاتك\"],\"wxDqxy\":[\"إنشاء مجموعة جديدة\"],\"oQH8Kl\":[\"أنشئ مجموعة جديدة باختيار الفلاتر. ستكون المجموعة مكونة من البطاقات التي تطابق هذه الفلاتر.\"],\"l7kxcE\":[\"أنشئ مجموعة\"],\"g/Uxjl\":[\"إنشاء مجموعة\"],\"1m75Ii\":[\"أنشئ مجموعتك الأولى\"],\"PQ2zPy\":[\"خصص كيفيّة ظهور البطاقات.\"],\"P+gzBQ\":[\"قم بتخصيص كيف يظهر التطبيق بالنسبة لك.\"],\"ZQKLI1\":[\"منطقة الخطر\"],\"pvnfJD\":[\"غامق\"],\"Wf+eYa\":[\"استكشاف الأخطاء\"],\"LUfCYM\":[\"تم إنشاء المجموعة\"],\"sHVysr\":[\"تم حذف المجموعة\"],\"UzOd8J\":[\"اسم المجموعة\"],\"iJA/GX\":[\"تم إنشاء المجموعة بنجاح!\"],\"M+Nngk\":[\"تم حذف المجموعة بنجاح!\"],\"NVQRbB\":[\"تم تحديث المجموعة بنجاح!\"],\"WWDML8\":[\"تم تحديث المجموعة\"],\"9TyPfF\":[\"المجموعات\"],\"iUt5VS\":[\"تتيح لك المجموعات إنشاء مجموعات فرعية من بطاقاتك التعليمية الموجودة باستخدام الفلاتر على قاموسك، والتي يمكنك دراستها بشكل فردي.\"],\"ovBPCi\":[\"افتراضي\"],\"MbRyzp\":[\"التعريف\"],\"cnGeoo\":[\"احذف\"],\"CYf/wo\":[\"حذف جميع البيانات\"],\"G6sE0d\":[\"حذف المجموعة\"],\"fWNrfJ\":[\"حذف المعجم\"],\"Xpcev7\":[\"حذف كل شيء\"],\"pe67wL\":[\"حذف هذه الكلمة؟\"],\"VEQ9bX\":[\"حذف الكلمة\"],\"/GZNuU\":[\"هل تريد أن تحذف معجمك؟\"],\"Nu4oKW\":[\"الوصف\"],\"URmyfc\":[\"تفاصيل\"],\"wjSthp\":[\"التفاصيل:\"],\"VrH1k+\":[\"معجم\"],\"RPYrAz\":[\"تم حذف المعجم\"],\"460RzB\":[\"ممنوع من الصرف\"],\"bzSI52\":[\"ألغ\"],\"1QfxQT\":[\"اغلق\"],\"Nwbs0q\":[\"لا تعرض\"],\"xifE4+\":[\"المثنى\"],\"jSxAVI\":[\"مثال: ك ت ب\"],\"Pqhtap\":[\"سهل\"],\"ePK91l\":[\"عدَل\"],\"nZQfan\":[\"حرّر \",[\"word\"]],\"4cP5gy\":[\"تعديل المجموعة\"],\"AN/oWt\":[\"تعديل الكلمة\"],\"IQ6Efp\":[\"عدّل مجموعتك\"],\"FEct+T\":[\"عدّل مجموعتك من خلال اختيار الفلاتر. ستتكون المجموعة من البطاقات التي تطابق الفلاتر.\"],\"O3oNi5\":[\"البريد الإلكتروني\"],\"lYGfRP\":[\"الإنجليزية\"],\"7MHe6P\":[\"الترجمة بالإنجليزية\"],\"Ac92c9\":[\"أدخل اسم المجموعة\"],\"ot6UAQ\":[\"أدخل وصفًا لترحيلك\"],\"shwNsK\":[\"أدخل اسم المجموعة\"],\"BTF0Gj\":[\"ادخل رمزك المكون من ٦ أرقام من بريدك الإلكتروني\"],\"ojjQlT\":[\"أدخل نص ترحيل SQL هنا\"],\"CA1DlQ\":[\"العنصر \",[\"arrayIndex\"]],\"Af6M/a\":[\"المثال \",[\"0\"]],\"oea1as\":[\"استخدامات مثالية للكلمة في سياقات مختلفة.\"],\"hFthoo\":[\"أمثلة\"],\"GS+Mus\":[\"صدر\"],\"Mk2MqX\":[\"اكتمل التصدير مع وجود مشاكل\"],\"XydOn5\":[\"فشل التصدير!\"],\"FAMOg+\":[\"تصدير مع البطاقات\"],\"HynDdB\":[\"تصدير معجمك\"],\"cQ+n9e\":[\"تصدير المعجم مع البطاقات سيحفظ تقدم البطاقات بالإضافة إلى كل المحتوى في معجمك. استخدم هذا الخيار إذا كنت ترغب في إنشاء نسخة احتياطية من بياناتك.<0/><1/>تصدير المعجم بدون البطاقات لن يحفظ أي تقدم للبطاقات. استخدم هذا الخيار إذا كنت ترغب في مشاركة معجمك.<2/><3/><4>تحذير: استيراد ملفك بدون البطاقات سيؤدي إلى إعادة تعيين جميع البطاقات.\"],\"1JBTZV\":[\"عبارة\"],\"GiPSsF\":[\"فشل إضافة الكلمة\"],\"zABpXD\":[\"فشل إضافة الكلمة!\"],\"NcBoUU\":[\"فشل تطبيق الترحيل على قاعدة البيانات المحلية.\"],\"eN1Tfn\":[\"فشل مسح المتراكمات\"],\"7hUTxx\":[\"فشل إنشاء المجموعة\"],\"Fr4iT6\":[\"فشل حذف المجموعة\"],\"GCd4Bz\":[\"فشل حذف المعجم\"],\"KLMMnR\":[\"فشل حذف الكلمة\"],\"cncTzY\":[\"فشل الحذف!\"],\"usb0iv\":[\"فشل في إعادة تعيين البيانات المحلية\"],\"dCD/3a\":[\"فشل في استرجاع البيانات من قاعدة البيانات المحلية.\"],\"btG7GG\":[\"فشل استرجاع معلومات قاعدة البيانات.\"],\"CBm+/w\":[\"فشل تحديث المجموعة\"],\"gw2VAF\":[\"فشل في تحديث إعدادات البطاقة.\"],\"tOcErW\":[\"فشل تحديث الإعدادات\"],\"X/iINj\":[\"فشل في تحديث الكلمة!\"],\"cnYJdR\":[\"فشل تحديث الكلمة\"],\"ixyKje\":[\"فشل تحميل الترحيل\"],\"1B52uC\":[\"مؤنّث\"],\"TUSnpz\":[\"فعل\"],\"wG1Flg\":[\"تم تحديث إعدادات البطاقة!\"],\"EiJpdz\":[\"بطاقات\"],\"hpt1Ta\":[\"يتبع لغة جهازك\"],\"tR/FIC\":[\"يتبع إعدادات النظام\"],\"q1hBIe\":[\"مثلا الكلمة\"],\"QRAWTO\":[\"الوزن\"],\"5iyJje\":[\"جنس\"],\"RkXlPZ\":[\"GitHub\"],\"CKyk7Q\":[\"ارجع\"],\"OEEQKT\":[\"حسن\"],\"VOwQ6g\":[\"أحسنت في المتراكمات! لا يزال لديك \",[\"0\"],\" مراجعات عادية.\"],\"Xv6Pek\":[\"عمل رائع! عد لاحقًا لمزيد من المراجعات.\"],\"g5Fk5U\":[\"صعب\"],\"h4MDwq\":[\"حرف\"],\"IzmuJh\":[\"الحرف \",[\"0\"]],\"D+zLDD\":[\"مخفي\"],\"VMlRqi\":[\"إخفاء التفاصيل\"],\"f+BFfG\":[\"تلميح\"],\"i0qMbr\":[\"منزل\"],\"q0alPy\":[\"كيف ينبغي عرض الأضداد في بطاقات؟\"],\"af2WMF\":[\"طريقة تصنف الكلمة حسب نوعها أو سياقها.\"],\"qGZ1pF\":[\"حروف\"],\"7fIUBE\":[\"المعرف:\"],\"mU4ZuB\":[\"إذا لزم الأمر.\"],\"eQI7A0\":[\"إذا كنت تواجه مشاكل في المزامنة أو عدم تحميل البيانات بشكل صحيح، يمكنك إعادة تعيين بياناتك المحلية وإعادة المزامنة من الخادم.\"],\"wqQIcr\":[\"الأمر\"],\"l3s5ri\":[\"استورد\"],\"5LwmgO\":[\"فشل الاستيراد!\"],\"X85Lgt\":[\"تضمين بطاقات الإنجليزية إلى العربية\"],\"O1SPgK\":[\"نوع الملف غير صحيح\"],\"bjwx3z\":[\"مبنى\"],\"d7G5eB\":[\"إعراب\"],\"IaZUGL\":[\"معلومات مثلا الجذر أو المعنى أو الأمثلة.\"],\"FP8ELf\":[\"الصيغة الزمنية غير صالحة\"],\"LpHyjF\":[\"صيغة النص غير صالحة\"],\"xVH/JG\":[\"نوع غير صالح. المتوقع \",[\"0\"]],\"lBmlgJ\":[\"نوع غير صالح. المتوقع \",[\"0\"],\"، المستلم \",[\"1\"]],\"5R5T/G\":[\"قيمة غير صالحة. يجب أن تكون إحدى القيم التالية: \",[\"0\"]],\"SK5zz1\":[\"اسم\"],\"vXIe7J\":[\"اللغة\"],\"1njn7W\":[\"فاتح\"],\"CZOrLB\":[\"جارٍ تحميل التفاصيل...\"],\"LioGSK\":[\"جارٍ تحميل معجمك...\"],\"Z3FXyt\":[\"جارٍ التحميل...\"],\"1bAISV\":[\"تمت إعادة تعيين البيانات المحلية بنجاح!\"],\"Od5n57\":[\"قم بتسجيل الدخول إلى حسابك الحالي أو قم بالتسجيل للحصول على حساب جديد\"],\"nOhz3x\":[\"تسجيل الخروج\"],\"c1DM+0\":[\"إدارة مجموعاتك ودراستها.\"],\"QUTgGW\":[\"إدارة معجمك.\"],\"U60v+9\":[\"مصادر\"],\"IPJKNz\":[\"مذكر\"],\"dLgiXX\":[\"مصدر\"],\"SXYWd/\":[\"المصدر \",[\"0\"]],\"6URaYg\":[\"المعنى\"],\"MYTK2T\":[\"المعنى مع هذا الحرف\"],\"zucql+\":[\"القائمة\"],\"qGioCB\":[\"تم تسجيل الترحيل بنجاح.\"],\"QI9AZD\":[\"أكثر من \",[\"0\"],\" بطاقة للمراجعة\"],\"yf87+x\":[\"الصرف\"],\"60xV9Y\":[\"الصرف ينطبق فقط على الأسماء والأفعال. سيتم إخفاء هذا القسم إذا تم تعيين نوع الكلمة إلى أي شيء آخر.\"],\"qhwV2C\":[\"حسابي\"],\"6YtxFj\":[\"اسم\"],\"TizFpt\":[\"قائمة التنقل\"],\"TQ5Es1\":[\"لا توجد بطاقات متراكمة للمسح.\"],\"WAQMrN\":[\"لا توجد بطاقات مستحقة\"],\"TkUliY\":[\"لا توجد مجموعات بعد\"],\"7+IHTZ\":[\"لم يتم اختيار ملف\"],\"AxPAXW\":[\"لم يتم العثور على نتائج\"],\"Utp0Ef\":[\"نورد\"],\"tTu2yx\":[\"اسم\"],\"KfoKzr\":[\"صرف الاسم\"],\"zW+FpA\":[\"أو استمر مع\"],\"PMqWHy\":[\"حرف\"],\"UIYada\":[\"اسم مفعول\"],\"9wi5te\":[\"اسم مفعول\"],\"Ff0Dor\":[\"الماضي\"],\"KkgkHx\":[\"الماضي\"],\"uiVVIw\":[\"الماضي\"],\"VHWIM2\":[\"حذف جميع بياناتك نهائيًا بما في ذلك الكلمات والبطاقات والمجموعات.\"],\"1OB0cV\":[\"يرجى الاتصال <0>بالدعم مع التفاصيل التالية:\"],\"HsEwwb\":[\"الرجاء إدخال اسم المجموعة\"],\"nqOcNX\":[\"اختر ملف JSON لمعجمك من فضلك.\"],\"AXJfF/\":[\"حاول مرة أخرى أو أعد تحميل الصفحة.\"],\"fuwKpE\":[\"حاول مرّة أخرى.\"],\"BPig2P\":[\"الجمع\"],\"K5KP2J\":[\"صيغة الجمع \",[\"0\"]],\"zt7UFB\":[\"صيغة الجمع\"],\"u5SHG2\":[\"صيغ جمع\"],\"go9eTo\":[\"حرف\"],\"JuM71B\":[\"حروف الجر\"],\"0Of985\":[\"الحروف التي يمكن استخدامها مع الفعل لتغيير معناه.\"],\"eZb+lw\":[\"المضارع\"],\"24cT5U\":[\"المضارع\"],\"6Kwq+n\":[\"المضارع\"],\"uabU9X\":[\"المستخدمة مؤخرًا\"],\"HpK/8d\":[\"أعد التحميل\"],\"t/YqKh\":[\"احذف\"],\"8dg+Yo\":[\"حقل مطلوب\"],\"+7Cebq\":[\"أعد جدولة جميع البطاقات المتراكمة بتقييمها كـ \\\"صعب\\\".\"],\"ly/6zq\":[\"إعادة التعيين والمزامنة\"],\"vWTWiR\":[\"أعد البطاقة\"],\"7HeQ9b\":[\"إعادة تعيين تقدم البطاقة؟\"],\"2ma9vI\":[\"إعادة تعيين البيانات المحلية\"],\"lLVyir\":[\"إعادة تعيين البيانات المحلية والمزامنة؟\"],\"K/RS6y\":[\"جارٍ إعادة تعيين البيانات المحلية...\"],\"QFtnWs\":[\"بطاقات العكسية\"],\"5k0NLb\":[\"مراجعة\"],\"nqtnG9\":[\"ادرس البطاقات\"],\"HtzuX3\":[\"راجع البطاقات (\",[\"0\"],\" متراكمة)\"],\"TbJrZ9\":[\"راجع البطاقات بالتكرار المتباعد\"],\"05OIsb\":[\"راجع بطاقاتك العربية وقيم فهمك باستخدام خيارات مرة أخرى أو صعب أو جيد أو سهل لتعديل الجدولة.\"],\"cbGyYK\":[\"الجذر\"],\"qMVAMv\":[\"الحروف الأصلية\"],\"tfDRzk\":[\"احفظ\"],\"y3aU20\":[\"حفظ التغييرات\"],\"IUwGEM\":[\"حفظ التغييرات\"],\"bf6lG0\":[\"ترحيلات المخطط\"],\"BnCS55\":[\"ابحث عن علامة...\"],\"YIix5Y\":[\"ابحث...\"],\"Du+zn+\":[\"يبحث...\"],\"hid6Sx\":[\"اختر جنسا\"],\"NdMnnc\":[\"اختر إعرابا\"],\"V/vqsf\":[\"حدد أنواع الكلمات التي تريد تضمينها في المجموعة.\"],\"tXQ9KI\":[\"اختر الجنس\"],\"FDpH/H\":[\"جملة\"],\"iw3JO4\":[\"افصل الحروف بمسافات أو فواصل\"],\"Tz0i8g\":[\"الإعدادات\"],\"l8nxCn\":[\"عرض بعد كشف الإجابة\"],\"r0Xcmy\":[\"اعرض الجواب\"],\"vDNaTX\":[\"إظهار الأضداد\"],\"1H95wk\":[\"عرض كتلميح\"],\"BowKyI\":[\"عرض التفاصيل\"],\"C5SZax\":[\"اعرض بطاقات من الإنجليزية إلى العربية.\"],\"xzH3m9\":[\"إظهار البطاقات العكسية\"],\"maCaRp\":[\"مفرد\"],\"vK/oE0\":[\"تعذر تحميل بعض إدخالات المعجم\"],\"nwtY4N\":[\"حدث خطأ ما\"],\"r20wDW\":[\"نص SQL\"],\"ykSlaQ\":[\"لا يزال لا يعمل؟\"],\"pKr7ta\":[\"أدرس\"],\"+Env0D\":[\"تم إضافة الكلمة بنجاح!\"],\"NJp0hH\":[\"نجح الحذف!\"],\"z6XClS\":[\"تم التصدير بنجاح!\"],\"1uuPpZ\":[\"تم الاستيراد بنجاح!\"],\"/5ZZnV\":[\"تمت إعادة تعيين البطاقة بنجاح.\"],\"oEWC7j\":[\"حدثت الكلمة بنجاح!\"],\"QgnNyZ\":[\"خطأ في المزامنة\"],\"D+NlUC\":[\"نظام\"],\"OYHzN1\":[\"علامات\"],\"hz39Yb\":[\"هذا الشفرة غير صالح.\"],\"90dpv/\":[\"هذا البريد الإلكتروني غير صالح.\"],\"7X8sXq\":[\"هذا ليس JSON صالح.\"],\"P7iz9S\":[\"قد يكون التطبيق مفتوحًا في علامة تبويب أخرى. حاول إغلاق علامات التبويب الأخرى وتحديث هذه الصفحة.\"],\"Bm2B8s\":[\"سياق الجملة مثلا رسمي أو عامي.\"],\"MMs+Zk\":[\"تم حذف المجموعة \\\"\",[\"0\"],\"\\\".\"],\"Gpyta0\":[\"صيغة الفعل مثلا I, II, III, IV, هكذا و هكذا...\"],\"OE9TJJ\":[\"صيغة الفعل بالعربية مثلا فعل, أفعل, استفعل, هكذا و هكذا...\"],\"4cDTK/\":[\"قاعدة البيانات المحلية مقفلة بواسطة جلسة أخرى.\"],\"EVX5FZ\":[\"معنى الفعل إذا استعمل مع الحرف.\"],\"bdFFEw\":[\"التصريف.\"],\"nIdeZ6\":[\"الحروف الجذرية للكلمة. يمكن فصل المدخلات بفواصل أو مسافات أو لا تحتوي على محدد مثلا فعل أو ف، ع، ل أو ف ع ل.\"],\"AvErlv\":[\"تمت إضافة الكلمة إلى معجمك.\"],\"LbpyqF\":[\"حدثت الكلمة.\"],\"FEr96N\":[\"المظهر\"],\"uTlsP3\":[\"حدث خطأ أثناء إضافة كلمتك. حاول مرة اخرى.\"],\"boDfXQ\":[\"حدث خطأ أثناء مسح المتراكمات.\"],\"IOwnXs\":[\"حدث خطأ أثناء إنشاء المجموعة\"],\"AObMB6\":[\"حدث خطأ أثناء حذف معجمك. حاول مرة أخرى لاحقا من فضلك.\"],\"QPU+ph\":[\"حدث خطأ أثناء تصدير معجمك. حاول مرة أخرى لاحقا من فضلك.\"],\"+0R2Vl\":[\"حدث خطأ أثناء استيراد لمعجمك. حاول مرة أخرى لاحقا.\"],\"GrwZZY\":[\"حدث خطأ في مزامنة بياناتك. إذا استمرت المشكلة، حاول إعادة تعيين البيانات المحلية من الإعدادات.\"],\"1GLBeM\":[\"حدث خطأ في مزامنة بياناتك. حاول إعادة تعيين البيانات المحلية من الإعدادات.\"],\"Xs1nsK\":[\"حدث خطأ في مزامنة بياناتك. بياناتك المحلية لا تزال متاحة.\"],\"aUu2Qy\":[\"حدث خطأ أثناء تحديث المجموعة\"],\"jObkch\":[\"حدث خطأ أثناء تحديث إعدادات بطاقاتك.\"],\"DvuiAU\":[\"حدث خطأ أثناء تحديث كلمتك. حاول مرة أخرى.\"],\"n//cii\":[\"حدث خطأ أثناء تحميل ترحيل SQL الخاص بك\"],\"p0aTgS\":[\"حدث خطأ أثناء فهرسة بياناتك في محرك البحث.\"],\"7Eqx1H\":[\"حدث خطأ غير متوقع. حاول مرة أخرى من فضلك.\"],\"1hR1ca\":[\"هناك مشكلة مؤقتة في تحميل حسابك. حاول إعادة تحميل الصفحة من فضلك.\"],\"UwqoLj\":[\"هذه الخانة لازم.\"],\"oM4aHs\":[\"هذا حالة غير حساسة.\"],\"GoNjj1\":[\"هذا هو اسم مجموعتك.\"],\"e92j12\":[\"هذا هو إعراب الكلمة.\"],\"fxunD6\":[\"سيؤدي هذا إلى حذف النسخة المحلية من بياناتك المخزنة في هذا المتصفح وتنزيل نسخة جديدة من الخادم. لن تتأثر بياناتك على الخادم. استخدم هذا إذا كنت تواجه أخطاء في المزامنة أو ترى بيانات قديمة.\"],\"lpK0Kp\":[\"سيؤدي هذا إلى حذف جميع كلماتك وبطاقاتك ومجموعاتك نهائيًا. لا يمكن التراجع عن هذا الإجراء.\"],\"NVF43p\":[\"الوقت:\"],\"i1FCIq\":[\"للمراجعة\"],\"1Q2HRv\":[\"تبديل القائمة\"],\"inGj5T\":[\"ليلة طوكيو\"],\"72c5Qo\":[\"الإجمالي\"],\"wFcvZJ\":[\"الترجمة\"],\"/Onh8p\":[\"معرب\"],\"qlwLcm\":[\"استكشاف الأخطاء\"],\"jKoZME\":[\"جرب مصطلح بحث مختلف\"],\"+zy2Nq\":[\"الجنس\"],\"WtTYSX\":[\"أقسام\"],\"xlZRtS\":[\"آه! حدث خطأ ما!\"],\"6njDZ+\":[\"غير قادر على الاتصال بقاعدة البيانات البعيدة.\"],\"WRB75e\":[\"خطأ غير معروف.\"],\"1ZR0xe\":[\"يجب أن تكون القيمة \",[\"0\"],\" أو أكبر\"],\"e5Ml90\":[\"يجب أن تكون القيمة \",[\"0\"],\" أو أقل\"],\"bwa0RV\":[\"فعل\"],\"LnBspw\":[\"صيغة الفعل\"],\"esQ1ry\":[\"صرف الفعل\"],\"WjcFrD\":[\"مصادر للفعل.\"],\"2oIAhB\":[\"المصادر\"],\"s6NYcb\":[\"المصادر\"],\"B4iZJX\":[\"انظر كل الألفاظ في معجمك الشخصي.\"],\"VvvGs4\":[\"لا يمكننا الوصول إلى خوادمنا الآن. تحقق من اتصالك وحاول مرة أخرى.\"],\"dHoQb8\":[\"نواجه مشكلة في إعداد حسابك. حاول مرة أخرى من فضلك.\"],\"eqpeYp\":[\"مرحباً بكم في بحر!\"],\"dZiBrj\":[\"الكلمة\"],\"MJ0gHH\":[\"تمت إضافة الكلمة بنجاح\"],\"wazgbb\":[\"تم حذف الكلمة بنجاح\"],\"2WVQNU\":[\"الكلمة غير موجودة\"],\"Xwf/Kn\":[\"تم تحديث الكلمة بنجاح\"],\"FVyDlC\":[\"الكلمات التي تحتوي على أي من هذه العلامات ستُدرج في المجموعة.\"],\"7niJN/\":[\"الكلمات التي لها معنى عكسي.\"],\"nTIiPJ\":[\"ليس لديك إذن لتحميل ترحيلات المخطط.\"],\"4eom/r\":[\"لديك \",[\"0\"],\" بطاقات متراكمة عندما تكون جاهزًا.\"],\"alITRb\":[\"ليس لديك أي مجموعات بعد.\"],\"XOUb7A\":[\"ليس لديك أي كلمات في قاموسك بعد.\"],\"/dColV\":[\"حسابك يحتاج إلى صيانة. تم إخطارنا. حاول مرة أخرى لاحقا من فضلك.\"],\"a8jRIt\":[\"تمت إعادة مزامنة بياناتك من الخادم.\"],\"uRXiIT\":[\"لم يتم إنشاء مجموعتك. يرجى المحاولة مرة أخرى.\"],\"oz/nIQ\":[\"لم يتم تحديث مجموعتك. يرجى المحاولة مرة أخرى.\"],\"L1VLmw\":[\"معجمك\"],\"SB7dXw\":[\"حذف معجمك.\"],\"aQiurv\":[\"تم تحميل معجمك.\"],\"YAeum9\":[\"تم تحديث معجمك!\"],\"C0Fx9N\":[\"معجمك فارغ\"],\"3ma3fF\":[\"معجمك غير صالح. صوب الأخطاء و استورد مرة أخرى من فضلك.\"],\"4bRwmE\":[\"كلمة «محمد» هي معرب.\"],\"kkC1Qd\":[\"كلمة «موسى» هي مبنى.\"],\"jFIhM0\":[\"كلمة «يوسف» هي ممنوع من الصرف.\"]}")as Messages; \ No newline at end of file +/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"Wg0uLA\":[[\"0\",\"plural\",{\"one\":[\"علامة واحدة محددة\"],\"two\":[\"علامتان محددتان\"],\"few\":[\"#\",\" علامات محددة\"],\"many\":[\"#\",\" علامة محددة\"],\"other\":[\"#\",\" علامة محددة\"]}]],\"5rgLzy\":[[\"0\",\"plural\",{\"one\":[\"بطاقة متبقية\"],\"other\":[\"بطاقات متبقية\"]}]],\"/hTM9k\":[[\"0\"],\" / \",[\"1\"],\" دفعات\"],\"mHye52\":[[\"0\"],\" / \",[\"1\"],\" بطاقات\"],\"baIPh3\":[[\"0\"],\" بطاقات مستحقة\"],\"beWT44\":[\"تمت إعادة جدولة \",[\"0\"],\" بطاقات.\"],\"JJcBCm\":[[\"0\"],\" إجمالي\"],\"JtTuS0\":[[\"hydrationSkippedCount\",\"plural\",{\"one\":[\"تم تخطي إدخال واحد بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخال الذي لم يتم تحميله.\"],\"two\":[\"تم تخطي إدخالين بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"],\"few\":[\"تم تخطي \",\"#\",\" إدخالات بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"],\"many\":[\"تم تخطي \",\"#\",\" إدخالًا بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"],\"other\":[\"تم تخطي \",\"#\",\" إدخال بسبب مشاكل في البيانات. بياناتك محفوظة، لكن لن تتمكن من البحث عن الإدخالات التي لم يتم تحميلها.\"]}]],\"UGMDWm\":[\"تم تخطي \",[\"skippedCount\"],\" إدخالات بسبب تلف البيانات.\"],\"gp8eDB\":[[\"totalHits\",\"plural\",{\"one\":[\"نتيجة\"],\"two\":[\"نتيجتان\"],\"few\":[\"نتائج\"],\"many\":[\"نتيجة\"],\"other\":[\"نتيجة\"]}]],\"QSRKsm\":[[\"totalResults\",\"plural\",{\"one\":[[\"totalResults\"],\" نتيجة\"],\"two\":[[\"totalResults\"],\" نتيجتان\"],\"few\":[[\"totalResults\"],\" نتائج\"],\"many\":[[\"totalResults\"],\" نتيجة\"],\"other\":[[\"totalResults\"],\" نتيجة\"]}]],\"gD09hk\":[[\"val\",\"plural\",{\"one\":[\"لا يمكن تجاوز حرف واحد\"],\"two\":[\"لا يمكن تجاوز حرفين\"],\"few\":[\"لا يمكن تجاوز \",\"#\",\" حروف\"],\"many\":[\"لا يمكن تجاوز \",\"#\",\" حرفا\"],\"other\":[\"لا يمكن تجاوز \",\"#\",\" حرف\"]}]],\"I1sa5o\":[[\"val\",\"plural\",{\"one\":[\"يجب أن يكون على الأقل واحد حرف\"],\"two\":[\"يجب أن يكون على الأقل حرفين\"],\"few\":[\"يجب أن يكون على الأقل \",\"#\",\" حروف\"],\"many\":[\"يجب أن يكون على الأقل \",\"#\",\" حرفا\"],\"other\":[\"يجب أن يكون على الأقل \",\"#\",\" حرف\"]}]],\"WWcbHw\":[\"غير فعل أو حرف.\"],\"ZQW2PY\":[\"حرف أو عطف.\"],\"0iy5O2\":[\"فعل.\"],\"7L01XJ\":[\"أعمال\"],\"DQqXHw\":[\"اسم فاعل\"],\"AwJc6G\":[\"اسم فاعل\"],\"rJwPkp\":[\"أضف ترحيل SQL جديد.\"],\"t89Ong\":[\"أضف كلمة جديدة\"],\"L18Pbl\":[\"أضف كلمة جديدة إلى معجمك\"],\"TYPFtT\":[\"أضف علامة واضغط Enter\"],\"iJMhp0\":[\"إضافة الكل\"],\"nPm6bB\":[\"أضف ضدا\"],\"jVmrwy\":[\"أضف ضد\"],\"1x4W9u\":[\"أضف مثلا\"],\"nI3A8B\":[\"أضف مثال\"],\"gLlFHX\":[\"أضف حرف\"],\"aLEtgY\":[\"أضف حرف\"],\"Tcbo6a\":[\"أضف مصدر\"],\"vKbErK\":[\"أضف صيغة جمع\"],\"SNF/r2\":[\"أضف جمع\"],\"cR1WUS\":[\"أضف شيئًا لتبدأ!\"],\"Oft+Pb\":[\"أضف بعض الكلمات للبدء!\"],\"Xls3A3\":[\"أضف علامة \",[\"inputValue\"]],\"VMjLu6\":[\"إضافة العلامة \",[\"value\"]],\"uF1GKn\":[\"أضف علامات إلى كلمتك.\"],\"bDXmnN\":[\"أضف كلمة\"],\"p59pEv\":[\"تفاصيل إضافية\"],\"b/7DUT\":[\"مرة أخرى\"],\"KK8UsT\":[\"انتهيت لليوم!\"],\"7Qi3W5\":[\"تعريف عربية للكلمة.\"],\"Y/gBMP\":[\"ترجمة إنجليزية للجملة.\"],\"jalxln\":[\"ترجمة إنجليزية للكلمة.\"],\"hyC+qY\":[\"حدث خطأ غير متوقع. يمكنك محاولة إعادة تحميل الصفحة لإصلاحه.\"],\"4xBHUg\":[\"الجواب\"],\"vQXp82\":[\"الضد \",[\"0\"]],\"o9dJs3\":[\"كلمة مضادة\"],\"Hvs4LI\":[\"أضداد\"],\"p1Kh9a\":[\"أية تفاصيل إضافية عن هذه الصيغة مثلا تكرار الاستخدام أو السياق.\"],\"9kni2/\":[\"أية صيغ جمع للكلمة.\"],\"2PPE7k\":[\"سيستبدل كل كلمات التي لديها نفس المعرف.\"],\"cnAqww\":[\"لغة التطبيق\"],\"aAIQg2\":[\"المظهر\"],\"8HV3WN\":[\"العربية\"],\"LNIQwr\":[\"التعريف بالعربية\"],\"uKmc5K\":[\"الجملة بالعربية\"],\"wxIhw3\":[\"صيغة الفعل العربي\"],\"071WZA\":[\"الكلمة بالعربية\"],\"pkD36F\":[\"هل أنت متأكد من حذف \\\"\",[\"0\"],\"\\\"؟\"],\"DOiV7q\":[\"هل أنت متأكد أنك تريد حذف هذه الكلمة؟ سيتم إزالتها من معجمك وسيتم حذف البطاقات بها.<0/><1/><2>لا يمكن التراجع عن هذا الإجراء.\"],\"2cZh44\":[\"هل أنت متأكد من حذف هذه الكلمة؟ لا يمكن التراجع عن هذا الإجراء.\"],\"ikBPhc\":[\"هل أنت متأكد من رغبتك في حذف معجمك؟ سيحذف جميع كلماتك بشكل دائم. لا يمكن التراجع عن هذا الإجراء. صدر معجمك قبل حذفه من فضلك.\"],\"IJr17L\":[\"هل أنت متأكد أنك تريد إعادة تعيين تقدم بطاقة هذه؟ لن يتم تعديل إدخال الكلمة في المعجم، ولكن سيتم التعامل مع بطاقات بها على أنها بطاقات جديدة.<0/><1/><2>لا يمكن التراجع عن هذا الإجراء.\"],\"iH8pgl\":[\"رجوع\"],\"0MgdN+\":[\"العودة إلى الأعلى\"],\"KNKCTb\":[\"المتراكمة\"],\"Fg+fNW\":[\"تم مسح المتراكمات!\"],\"vMebS2\":[\"بحر\"],\"EOUool\":[\"تفاصيل أساسية\"],\"cSCbv3\":[\"بيتا\"],\"OKWk8n\":[\"بتسجيل الدخول، فإنك توافق على <0>سياسة الخصوصية\"],\"dEgA5A\":[\"ألغ\"],\"oySfhX\":[\"بطاقات مستحقة اليوم أو مؤخرًا\"],\"uV52e1\":[\"بطاقات متأخرة أكثر من ٧ أيام\"],\"zAwyRS\":[\"بطاقات للمراجعة اليوم\"],\"K7tIrx\":[\"الباب\"],\"qWRWG1\":[\"السبب:\"],\"+yPBXI\":[\"اختر ملف\"],\"xCJdfg\":[\"امسح\"],\"ot7qsv\":[\"مسح كل المرشّحات\"],\"saoOio\":[\"امسح المتراكمات\"],\"Oo39Ga\":[\"جارٍ المسح...\"],\"mpby9d\":[\"اتصل بالدعم\"],\"M73whl\":[\"السياق\"],\"xGVfLh\":[\"استمر\"],\"RvVi9c\":[\"استمرّ مع بريد الإلكتروني\"],\"NMo6Pj\":[\"تحكم في مكان ظهور الأضداد أثناء المراجعة\"],\"PiH3UR\":[\"تم النسخ!\"],\"he3ygx\":[\"انسخ\"],\"hYgDIe\":[\"إنشاء\"],\"oJc/nD\":[\"أنشئ مجموعة لتنظيم مراجعات بطاقاتك\"],\"wxDqxy\":[\"إنشاء مجموعة جديدة\"],\"oQH8Kl\":[\"أنشئ مجموعة جديدة باختيار الفلاتر. ستكون المجموعة مكونة من البطاقات التي تطابق هذه الفلاتر.\"],\"l7kxcE\":[\"أنشئ مجموعة\"],\"g/Uxjl\":[\"إنشاء مجموعة\"],\"1m75Ii\":[\"أنشئ مجموعتك الأولى\"],\"PQ2zPy\":[\"خصص كيفيّة ظهور البطاقات.\"],\"P+gzBQ\":[\"قم بتخصيص كيف يظهر التطبيق بالنسبة لك.\"],\"ZQKLI1\":[\"منطقة الخطر\"],\"pvnfJD\":[\"غامق\"],\"Wf+eYa\":[\"استكشاف الأخطاء\"],\"LUfCYM\":[\"تم إنشاء المجموعة\"],\"sHVysr\":[\"تم حذف المجموعة\"],\"UzOd8J\":[\"اسم المجموعة\"],\"iJA/GX\":[\"تم إنشاء المجموعة بنجاح!\"],\"M+Nngk\":[\"تم حذف المجموعة بنجاح!\"],\"NVQRbB\":[\"تم تحديث المجموعة بنجاح!\"],\"WWDML8\":[\"تم تحديث المجموعة\"],\"9TyPfF\":[\"المجموعات\"],\"iUt5VS\":[\"تتيح لك المجموعات إنشاء مجموعات فرعية من بطاقاتك التعليمية الموجودة باستخدام الفلاتر على قاموسك، والتي يمكنك دراستها بشكل فردي.\"],\"ovBPCi\":[\"افتراضي\"],\"MbRyzp\":[\"التعريف\"],\"cnGeoo\":[\"احذف\"],\"CYf/wo\":[\"حذف جميع البيانات\"],\"G6sE0d\":[\"حذف المجموعة\"],\"fWNrfJ\":[\"حذف المعجم\"],\"Xpcev7\":[\"حذف كل شيء\"],\"pe67wL\":[\"حذف هذه الكلمة؟\"],\"VEQ9bX\":[\"حذف الكلمة\"],\"/GZNuU\":[\"هل تريد أن تحذف معجمك؟\"],\"Nu4oKW\":[\"الوصف\"],\"URmyfc\":[\"تفاصيل\"],\"wjSthp\":[\"التفاصيل:\"],\"VrH1k+\":[\"معجم\"],\"RPYrAz\":[\"تم حذف المعجم\"],\"460RzB\":[\"ممنوع من الصرف\"],\"bzSI52\":[\"ألغ\"],\"1QfxQT\":[\"اغلق\"],\"Nwbs0q\":[\"لا تعرض\"],\"xifE4+\":[\"المثنى\"],\"jSxAVI\":[\"مثال: ك ت ب\"],\"Pqhtap\":[\"سهل\"],\"ePK91l\":[\"عدَل\"],\"nZQfan\":[\"حرّر \",[\"word\"]],\"4cP5gy\":[\"تعديل المجموعة\"],\"AN/oWt\":[\"تعديل الكلمة\"],\"IQ6Efp\":[\"عدّل مجموعتك\"],\"FEct+T\":[\"عدّل مجموعتك من خلال اختيار الفلاتر. ستتكون المجموعة من البطاقات التي تطابق الفلاتر.\"],\"O3oNi5\":[\"البريد الإلكتروني\"],\"lYGfRP\":[\"الإنجليزية\"],\"7MHe6P\":[\"الترجمة بالإنجليزية\"],\"Ac92c9\":[\"أدخل اسم المجموعة\"],\"ot6UAQ\":[\"أدخل وصفًا لترحيلك\"],\"shwNsK\":[\"أدخل اسم المجموعة\"],\"BTF0Gj\":[\"ادخل رمزك المكون من ٦ أرقام من بريدك الإلكتروني\"],\"ojjQlT\":[\"أدخل نص ترحيل SQL هنا\"],\"CA1DlQ\":[\"العنصر \",[\"arrayIndex\"]],\"Af6M/a\":[\"المثال \",[\"0\"]],\"oea1as\":[\"استخدامات مثالية للكلمة في سياقات مختلفة.\"],\"hFthoo\":[\"أمثلة\"],\"GS+Mus\":[\"صدر\"],\"Mk2MqX\":[\"اكتمل التصدير مع وجود مشاكل\"],\"XydOn5\":[\"فشل التصدير!\"],\"FAMOg+\":[\"تصدير مع البطاقات\"],\"HynDdB\":[\"تصدير معجمك\"],\"cQ+n9e\":[\"تصدير المعجم مع البطاقات سيحفظ تقدم البطاقات بالإضافة إلى كل المحتوى في معجمك. استخدم هذا الخيار إذا كنت ترغب في إنشاء نسخة احتياطية من بياناتك.<0/><1/>تصدير المعجم بدون البطاقات لن يحفظ أي تقدم للبطاقات. استخدم هذا الخيار إذا كنت ترغب في مشاركة معجمك.<2/><3/><4>تحذير: استيراد ملفك بدون البطاقات سيؤدي إلى إعادة تعيين جميع البطاقات.\"],\"1JBTZV\":[\"عبارة\"],\"GiPSsF\":[\"فشل إضافة الكلمة\"],\"zABpXD\":[\"فشل إضافة الكلمة!\"],\"NcBoUU\":[\"فشل تطبيق الترحيل على قاعدة البيانات المحلية.\"],\"eN1Tfn\":[\"فشل مسح المتراكمات\"],\"7hUTxx\":[\"فشل إنشاء المجموعة\"],\"Fr4iT6\":[\"فشل حذف المجموعة\"],\"GCd4Bz\":[\"فشل حذف المعجم\"],\"KLMMnR\":[\"فشل حذف الكلمة\"],\"cncTzY\":[\"فشل الحذف!\"],\"usb0iv\":[\"فشل في إعادة تعيين البيانات المحلية\"],\"dCD/3a\":[\"فشل في استرجاع البيانات من قاعدة البيانات المحلية.\"],\"btG7GG\":[\"فشل استرجاع معلومات قاعدة البيانات.\"],\"CBm+/w\":[\"فشل تحديث المجموعة\"],\"gw2VAF\":[\"فشل في تحديث إعدادات البطاقة.\"],\"tOcErW\":[\"فشل تحديث الإعدادات\"],\"X/iINj\":[\"فشل في تحديث الكلمة!\"],\"cnYJdR\":[\"فشل تحديث الكلمة\"],\"ixyKje\":[\"فشل تحميل الترحيل\"],\"1B52uC\":[\"مؤنّث\"],\"TUSnpz\":[\"فعل\"],\"YCAFdN\":[\"رشّح حسب العلامات...\"],\"cSev+j\":[\"الترشيح\"],\"wG1Flg\":[\"تم تحديث إعدادات البطاقة!\"],\"EiJpdz\":[\"بطاقات\"],\"hpt1Ta\":[\"يتبع لغة جهازك\"],\"tR/FIC\":[\"يتبع إعدادات النظام\"],\"q1hBIe\":[\"مثلا الكلمة\"],\"QRAWTO\":[\"الوزن\"],\"5iyJje\":[\"جنس\"],\"RkXlPZ\":[\"GitHub\"],\"CKyk7Q\":[\"ارجع\"],\"OEEQKT\":[\"حسن\"],\"VOwQ6g\":[\"أحسنت في المتراكمات! لا يزال لديك \",[\"0\"],\" مراجعات عادية.\"],\"Xv6Pek\":[\"عمل رائع! عد لاحقًا لمزيد من المراجعات.\"],\"g5Fk5U\":[\"صعب\"],\"h4MDwq\":[\"حرف\"],\"IzmuJh\":[\"الحرف \",[\"0\"]],\"D+zLDD\":[\"مخفي\"],\"VMlRqi\":[\"إخفاء التفاصيل\"],\"f+BFfG\":[\"تلميح\"],\"i0qMbr\":[\"منزل\"],\"q0alPy\":[\"كيف ينبغي عرض الأضداد في بطاقات؟\"],\"af2WMF\":[\"طريقة تصنف الكلمة حسب نوعها أو سياقها.\"],\"qGZ1pF\":[\"حروف\"],\"7fIUBE\":[\"المعرف:\"],\"mU4ZuB\":[\"إذا لزم الأمر.\"],\"eQI7A0\":[\"إذا كنت تواجه مشاكل في المزامنة أو عدم تحميل البيانات بشكل صحيح، يمكنك إعادة تعيين بياناتك المحلية وإعادة المزامنة من الخادم.\"],\"wqQIcr\":[\"الأمر\"],\"l3s5ri\":[\"استورد\"],\"5LwmgO\":[\"فشل الاستيراد!\"],\"X85Lgt\":[\"تضمين بطاقات الإنجليزية إلى العربية\"],\"O1SPgK\":[\"نوع الملف غير صحيح\"],\"bjwx3z\":[\"مبنى\"],\"d7G5eB\":[\"إعراب\"],\"IaZUGL\":[\"معلومات مثلا الجذر أو المعنى أو الأمثلة.\"],\"FP8ELf\":[\"الصيغة الزمنية غير صالحة\"],\"LpHyjF\":[\"صيغة النص غير صالحة\"],\"xVH/JG\":[\"نوع غير صالح. المتوقع \",[\"0\"]],\"lBmlgJ\":[\"نوع غير صالح. المتوقع \",[\"0\"],\"، المستلم \",[\"1\"]],\"5R5T/G\":[\"قيمة غير صالحة. يجب أن تكون إحدى القيم التالية: \",[\"0\"]],\"SK5zz1\":[\"اسم\"],\"vXIe7J\":[\"اللغة\"],\"1njn7W\":[\"فاتح\"],\"CZOrLB\":[\"جارٍ تحميل التفاصيل...\"],\"LioGSK\":[\"جارٍ تحميل معجمك...\"],\"Z3FXyt\":[\"جارٍ التحميل...\"],\"1bAISV\":[\"تمت إعادة تعيين البيانات المحلية بنجاح!\"],\"Od5n57\":[\"قم بتسجيل الدخول إلى حسابك الحالي أو قم بالتسجيل للحصول على حساب جديد\"],\"nOhz3x\":[\"تسجيل الخروج\"],\"c1DM+0\":[\"إدارة مجموعاتك ودراستها.\"],\"QUTgGW\":[\"إدارة معجمك.\"],\"U60v+9\":[\"مصادر\"],\"IPJKNz\":[\"مذكر\"],\"dLgiXX\":[\"مصدر\"],\"SXYWd/\":[\"المصدر \",[\"0\"]],\"6URaYg\":[\"المعنى\"],\"MYTK2T\":[\"المعنى مع هذا الحرف\"],\"zucql+\":[\"القائمة\"],\"qGioCB\":[\"تم تسجيل الترحيل بنجاح.\"],\"QI9AZD\":[\"أكثر من \",[\"0\"],\" بطاقة للمراجعة\"],\"yf87+x\":[\"الصرف\"],\"60xV9Y\":[\"الصرف ينطبق فقط على الأسماء والأفعال. سيتم إخفاء هذا القسم إذا تم تعيين نوع الكلمة إلى أي شيء آخر.\"],\"qhwV2C\":[\"حسابي\"],\"6YtxFj\":[\"اسم\"],\"TizFpt\":[\"قائمة التنقل\"],\"TQ5Es1\":[\"لا توجد بطاقات متراكمة للمسح.\"],\"WAQMrN\":[\"لا توجد بطاقات مستحقة\"],\"TkUliY\":[\"لا توجد مجموعات بعد\"],\"7+IHTZ\":[\"لم يتم اختيار ملف\"],\"AxPAXW\":[\"لم يتم العثور على نتائج\"],\"vLuosE\":[\"لم يتم العثور على علامات.\"],\"Utp0Ef\":[\"نورد\"],\"tTu2yx\":[\"اسم\"],\"KfoKzr\":[\"صرف الاسم\"],\"zW+FpA\":[\"أو استمر مع\"],\"PMqWHy\":[\"حرف\"],\"UIYada\":[\"اسم مفعول\"],\"9wi5te\":[\"اسم مفعول\"],\"Ff0Dor\":[\"الماضي\"],\"KkgkHx\":[\"الماضي\"],\"uiVVIw\":[\"الماضي\"],\"VHWIM2\":[\"حذف جميع بياناتك نهائيًا بما في ذلك الكلمات والبطاقات والمجموعات.\"],\"1OB0cV\":[\"يرجى الاتصال <0>بالدعم مع التفاصيل التالية:\"],\"HsEwwb\":[\"الرجاء إدخال اسم المجموعة\"],\"nqOcNX\":[\"اختر ملف JSON لمعجمك من فضلك.\"],\"AXJfF/\":[\"حاول مرة أخرى أو أعد تحميل الصفحة.\"],\"fuwKpE\":[\"حاول مرّة أخرى.\"],\"BPig2P\":[\"الجمع\"],\"K5KP2J\":[\"صيغة الجمع \",[\"0\"]],\"zt7UFB\":[\"صيغة الجمع\"],\"u5SHG2\":[\"صيغ جمع\"],\"go9eTo\":[\"حرف\"],\"JuM71B\":[\"حروف الجر\"],\"0Of985\":[\"الحروف التي يمكن استخدامها مع الفعل لتغيير معناه.\"],\"eZb+lw\":[\"المضارع\"],\"24cT5U\":[\"المضارع\"],\"6Kwq+n\":[\"المضارع\"],\"0DYtlD\":[\"المضافة حديثًا\"],\"X5P6yv\":[\"المحدّثة حديثًا\"],\"uabU9X\":[\"المستخدمة مؤخرًا\"],\"7dZnmw\":[\"الصلة\"],\"HpK/8d\":[\"أعد التحميل\"],\"t/YqKh\":[\"احذف\"],\"8dg+Yo\":[\"حقل مطلوب\"],\"+7Cebq\":[\"أعد جدولة جميع البطاقات المتراكمة بتقييمها كـ \\\"صعب\\\".\"],\"ly/6zq\":[\"إعادة التعيين والمزامنة\"],\"vWTWiR\":[\"أعد البطاقة\"],\"7HeQ9b\":[\"إعادة تعيين تقدم البطاقة؟\"],\"2ma9vI\":[\"إعادة تعيين البيانات المحلية\"],\"lLVyir\":[\"إعادة تعيين البيانات المحلية والمزامنة؟\"],\"K/RS6y\":[\"جارٍ إعادة تعيين البيانات المحلية...\"],\"QFtnWs\":[\"بطاقات العكسية\"],\"5k0NLb\":[\"مراجعة\"],\"nqtnG9\":[\"ادرس البطاقات\"],\"HtzuX3\":[\"راجع البطاقات (\",[\"0\"],\" متراكمة)\"],\"TbJrZ9\":[\"راجع البطاقات بالتكرار المتباعد\"],\"05OIsb\":[\"راجع بطاقاتك العربية وقيم فهمك باستخدام خيارات مرة أخرى أو صعب أو جيد أو سهل لتعديل الجدولة.\"],\"cbGyYK\":[\"الجذر\"],\"qMVAMv\":[\"الحروف الأصلية\"],\"tfDRzk\":[\"احفظ\"],\"y3aU20\":[\"حفظ التغييرات\"],\"IUwGEM\":[\"حفظ التغييرات\"],\"bf6lG0\":[\"ترحيلات المخطط\"],\"BnCS55\":[\"ابحث عن علامة...\"],\"VV6Aru\":[\"ابحث عن علامة...\"],\"YIix5Y\":[\"ابحث...\"],\"Du+zn+\":[\"يبحث...\"],\"hid6Sx\":[\"اختر جنسا\"],\"NdMnnc\":[\"اختر إعرابا\"],\"V/vqsf\":[\"حدد أنواع الكلمات التي تريد تضمينها في المجموعة.\"],\"tXQ9KI\":[\"اختر الجنس\"],\"FDpH/H\":[\"جملة\"],\"iw3JO4\":[\"افصل الحروف بمسافات أو فواصل\"],\"Tz0i8g\":[\"الإعدادات\"],\"l8nxCn\":[\"عرض بعد كشف الإجابة\"],\"r0Xcmy\":[\"اعرض الجواب\"],\"vDNaTX\":[\"إظهار الأضداد\"],\"1H95wk\":[\"عرض كتلميح\"],\"BowKyI\":[\"عرض التفاصيل\"],\"C5SZax\":[\"اعرض بطاقات من الإنجليزية إلى العربية.\"],\"xzH3m9\":[\"إظهار البطاقات العكسية\"],\"maCaRp\":[\"مفرد\"],\"vK/oE0\":[\"تعذر تحميل بعض إدخالات المعجم\"],\"nwtY4N\":[\"حدث خطأ ما\"],\"/HgF9q\":[\"ترتيب حسب\"],\"r20wDW\":[\"نص SQL\"],\"ykSlaQ\":[\"لا يزال لا يعمل؟\"],\"pKr7ta\":[\"أدرس\"],\"+Env0D\":[\"تم إضافة الكلمة بنجاح!\"],\"NJp0hH\":[\"نجح الحذف!\"],\"z6XClS\":[\"تم التصدير بنجاح!\"],\"1uuPpZ\":[\"تم الاستيراد بنجاح!\"],\"/5ZZnV\":[\"تمت إعادة تعيين البطاقة بنجاح.\"],\"oEWC7j\":[\"حدثت الكلمة بنجاح!\"],\"QgnNyZ\":[\"خطأ في المزامنة\"],\"D+NlUC\":[\"نظام\"],\"OYHzN1\":[\"علامات\"],\"hz39Yb\":[\"هذا الشفرة غير صالح.\"],\"90dpv/\":[\"هذا البريد الإلكتروني غير صالح.\"],\"7X8sXq\":[\"هذا ليس JSON صالح.\"],\"P7iz9S\":[\"قد يكون التطبيق مفتوحًا في علامة تبويب أخرى. حاول إغلاق علامات التبويب الأخرى وتحديث هذه الصفحة.\"],\"Bm2B8s\":[\"سياق الجملة مثلا رسمي أو عامي.\"],\"MMs+Zk\":[\"تم حذف المجموعة \\\"\",[\"0\"],\"\\\".\"],\"Gpyta0\":[\"صيغة الفعل مثلا I, II, III, IV, هكذا و هكذا...\"],\"OE9TJJ\":[\"صيغة الفعل بالعربية مثلا فعل, أفعل, استفعل, هكذا و هكذا...\"],\"4cDTK/\":[\"قاعدة البيانات المحلية مقفلة بواسطة جلسة أخرى.\"],\"EVX5FZ\":[\"معنى الفعل إذا استعمل مع الحرف.\"],\"bdFFEw\":[\"التصريف.\"],\"nIdeZ6\":[\"الحروف الجذرية للكلمة. يمكن فصل المدخلات بفواصل أو مسافات أو لا تحتوي على محدد مثلا فعل أو ف، ع، ل أو ف ع ل.\"],\"AvErlv\":[\"تمت إضافة الكلمة إلى معجمك.\"],\"LbpyqF\":[\"حدثت الكلمة.\"],\"FEr96N\":[\"المظهر\"],\"uTlsP3\":[\"حدث خطأ أثناء إضافة كلمتك. حاول مرة اخرى.\"],\"boDfXQ\":[\"حدث خطأ أثناء مسح المتراكمات.\"],\"IOwnXs\":[\"حدث خطأ أثناء إنشاء المجموعة\"],\"AObMB6\":[\"حدث خطأ أثناء حذف معجمك. حاول مرة أخرى لاحقا من فضلك.\"],\"QPU+ph\":[\"حدث خطأ أثناء تصدير معجمك. حاول مرة أخرى لاحقا من فضلك.\"],\"+0R2Vl\":[\"حدث خطأ أثناء استيراد لمعجمك. حاول مرة أخرى لاحقا.\"],\"GrwZZY\":[\"حدث خطأ في مزامنة بياناتك. إذا استمرت المشكلة، حاول إعادة تعيين البيانات المحلية من الإعدادات.\"],\"1GLBeM\":[\"حدث خطأ في مزامنة بياناتك. حاول إعادة تعيين البيانات المحلية من الإعدادات.\"],\"Xs1nsK\":[\"حدث خطأ في مزامنة بياناتك. بياناتك المحلية لا تزال متاحة.\"],\"aUu2Qy\":[\"حدث خطأ أثناء تحديث المجموعة\"],\"jObkch\":[\"حدث خطأ أثناء تحديث إعدادات بطاقاتك.\"],\"DvuiAU\":[\"حدث خطأ أثناء تحديث كلمتك. حاول مرة أخرى.\"],\"n//cii\":[\"حدث خطأ أثناء تحميل ترحيل SQL الخاص بك\"],\"p0aTgS\":[\"حدث خطأ أثناء فهرسة بياناتك في محرك البحث.\"],\"7Eqx1H\":[\"حدث خطأ غير متوقع. حاول مرة أخرى من فضلك.\"],\"1hR1ca\":[\"هناك مشكلة مؤقتة في تحميل حسابك. حاول إعادة تحميل الصفحة من فضلك.\"],\"UwqoLj\":[\"هذه الخانة لازم.\"],\"oM4aHs\":[\"هذا حالة غير حساسة.\"],\"GoNjj1\":[\"هذا هو اسم مجموعتك.\"],\"e92j12\":[\"هذا هو إعراب الكلمة.\"],\"fxunD6\":[\"سيؤدي هذا إلى حذف النسخة المحلية من بياناتك المخزنة في هذا المتصفح وتنزيل نسخة جديدة من الخادم. لن تتأثر بياناتك على الخادم. استخدم هذا إذا كنت تواجه أخطاء في المزامنة أو ترى بيانات قديمة.\"],\"lpK0Kp\":[\"سيؤدي هذا إلى حذف جميع كلماتك وبطاقاتك ومجموعاتك نهائيًا. لا يمكن التراجع عن هذا الإجراء.\"],\"NVF43p\":[\"الوقت:\"],\"i1FCIq\":[\"للمراجعة\"],\"1Q2HRv\":[\"تبديل القائمة\"],\"inGj5T\":[\"ليلة طوكيو\"],\"72c5Qo\":[\"الإجمالي\"],\"wFcvZJ\":[\"الترجمة\"],\"/Onh8p\":[\"معرب\"],\"qlwLcm\":[\"استكشاف الأخطاء\"],\"jKoZME\":[\"جرب مصطلح بحث مختلف\"],\"+zy2Nq\":[\"الجنس\"],\"WtTYSX\":[\"أقسام\"],\"xlZRtS\":[\"آه! حدث خطأ ما!\"],\"6njDZ+\":[\"غير قادر على الاتصال بقاعدة البيانات البعيدة.\"],\"WRB75e\":[\"خطأ غير معروف.\"],\"1ZR0xe\":[\"يجب أن تكون القيمة \",[\"0\"],\" أو أكبر\"],\"e5Ml90\":[\"يجب أن تكون القيمة \",[\"0\"],\" أو أقل\"],\"bwa0RV\":[\"فعل\"],\"LnBspw\":[\"صيغة الفعل\"],\"esQ1ry\":[\"صرف الفعل\"],\"WjcFrD\":[\"مصادر للفعل.\"],\"2oIAhB\":[\"المصادر\"],\"s6NYcb\":[\"المصادر\"],\"B4iZJX\":[\"انظر كل الألفاظ في معجمك الشخصي.\"],\"VvvGs4\":[\"لا يمكننا الوصول إلى خوادمنا الآن. تحقق من اتصالك وحاول مرة أخرى.\"],\"dHoQb8\":[\"نواجه مشكلة في إعداد حسابك. حاول مرة أخرى من فضلك.\"],\"eqpeYp\":[\"مرحباً بكم في بحر!\"],\"dZiBrj\":[\"الكلمة\"],\"MJ0gHH\":[\"تمت إضافة الكلمة بنجاح\"],\"wazgbb\":[\"تم حذف الكلمة بنجاح\"],\"2WVQNU\":[\"الكلمة غير موجودة\"],\"Xwf/Kn\":[\"تم تحديث الكلمة بنجاح\"],\"FVyDlC\":[\"الكلمات التي تحتوي على أي من هذه العلامات ستُدرج في المجموعة.\"],\"7niJN/\":[\"الكلمات التي لها معنى عكسي.\"],\"nTIiPJ\":[\"ليس لديك إذن لتحميل ترحيلات المخطط.\"],\"4eom/r\":[\"لديك \",[\"0\"],\" بطاقات متراكمة عندما تكون جاهزًا.\"],\"alITRb\":[\"ليس لديك أي مجموعات بعد.\"],\"XOUb7A\":[\"ليس لديك أي كلمات في قاموسك بعد.\"],\"/dColV\":[\"حسابك يحتاج إلى صيانة. تم إخطارنا. حاول مرة أخرى لاحقا من فضلك.\"],\"a8jRIt\":[\"تمت إعادة مزامنة بياناتك من الخادم.\"],\"uRXiIT\":[\"لم يتم إنشاء مجموعتك. يرجى المحاولة مرة أخرى.\"],\"oz/nIQ\":[\"لم يتم تحديث مجموعتك. يرجى المحاولة مرة أخرى.\"],\"L1VLmw\":[\"معجمك\"],\"SB7dXw\":[\"حذف معجمك.\"],\"aQiurv\":[\"تم تحميل معجمك.\"],\"YAeum9\":[\"تم تحديث معجمك!\"],\"C0Fx9N\":[\"معجمك فارغ\"],\"3ma3fF\":[\"معجمك غير صالح. صوب الأخطاء و استورد مرة أخرى من فضلك.\"],\"4bRwmE\":[\"كلمة «محمد» هي معرب.\"],\"kkC1Qd\":[\"كلمة «موسى» هي مبنى.\"],\"jFIhM0\":[\"كلمة «يوسف» هي ممنوع من الصرف.\"]}")as Messages; \ No newline at end of file diff --git a/packages/i18n/locales/en.po b/packages/i18n/locales/en.po index 1651243d..3f9bd3fa 100644 --- a/packages/i18n/locales/en.po +++ b/packages/i18n/locales/en.po @@ -13,6 +13,11 @@ msgstr "" "Plural-Forms: \n" "X-Generator: Poedit 3.4.4\n" +#. placeholder {0}: filteredTags.length +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:45 +msgid "{0, plural, one {# tag selected} other {# tags selected}}" +msgstr "{0, plural, one {# tag selected} other {# tags selected}}" + #. placeholder {0}: cards.length #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/FlashcardDrawer.tsx:333 msgid "{0, plural, one {card left} other {cards left}}" @@ -158,7 +163,7 @@ msgstr "Add plural" msgid "Add Plural" msgstr "Add Plural" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:509 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:518 msgid "Add some to get started!" msgstr "Add some to get started!" @@ -207,7 +212,7 @@ msgstr "An Arabic definition of the word." msgid "An English translation of the sentence." msgstr "An English translation of the sentence." -#: ../../apps/web/src/components/features/dictionary/add/BasicDetailsFormSection.tsx:72 +#: ../../apps/web/src/components/features/dictionary/add/BasicDetailsFormSection.tsx:77 msgid "An English translation of the word." msgstr "An English translation of the word." @@ -287,7 +292,7 @@ msgstr "Arabic word" msgid "Are you sure you want to delete \"{0}\"?" msgstr "Are you sure you want to delete \"{0}\"?" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:135 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:140 msgid "Are you sure you want to delete this word? It will be removed from your dictionary and its flashcards will be deleted.<0/><1/><2>This action cannot be undone." msgstr "Are you sure you want to delete this word? It will be removed from your dictionary and its flashcards will be deleted.<0/><1/><2>This action cannot be undone." @@ -299,16 +304,16 @@ msgstr "Are you sure you want to delete this word? This action cannot be undone. msgid "Are you sure you want to delete your dictionary? All your words will be deleted permanently. This action cannot be undone. Please make sure to export your dictionary before deleting it." msgstr "Are you sure you want to delete your dictionary? All your words will be deleted permanently. This action cannot be undone. Please make sure to export your dictionary before deleting it." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:83 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:88 msgid "Are you sure you want to reset this flashcard's progress? The word entry in the dictionary will not be modified, but its corresponding flashcards will be treated as new ones.<0/><1/><2>This action cannot be undone." msgstr "Are you sure you want to reset this flashcard's progress? The word entry in the dictionary will not be modified, but its corresponding flashcards will be treated as new ones.<0/><1/><2>This action cannot be undone." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:209 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:216 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:74 msgid "Back" msgstr "Back" -#: ../../apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx:187 +#: ../../apps/web/src/routes/_authorized-layout/_search-layout/index.lazy.tsx:188 msgid "Back to top" msgstr "Back to top" @@ -382,6 +387,10 @@ msgstr "Choose file" msgid "Clear" msgstr "Clear" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:153 +msgid "Clear all filters" +msgstr "Clear all filters" + #: ../../apps/web/src/components/features/settings/FlashcardSettingsCardSection.tsx:242 msgid "Clear backlog" msgstr "Clear backlog" @@ -515,7 +524,7 @@ msgstr "Decks allow you to create subsets of your existing flashcards by using f msgid "Default" msgstr "Default" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:115 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:138 #: ../../apps/web/src/components/features/dictionary/add/AdditionalDetailsFormSection.tsx:81 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:117 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:444 @@ -525,8 +534,8 @@ msgstr "Definition" #: ../../apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx:466 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/settings/route.lazy.tsx:489 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:124 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:156 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:129 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:161 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/decks/route.lazy.tsx:207 #: ../../apps/mobile/src/app/(search)/decks.tsx:285 #: ../../apps/mobile/src/app/(search)/(home)/edit-word/[id].tsx:393 @@ -549,7 +558,7 @@ msgstr "Delete Dictionary" msgid "Delete Everything" msgstr "Delete Everything" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:131 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:136 msgid "Delete this word?" msgstr "Delete this word?" @@ -599,7 +608,7 @@ msgstr "Dismiss" msgid "Don't show" msgstr "Don't show" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:154 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:175 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:84 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:146 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:633 @@ -612,7 +621,7 @@ msgstr "Dual" msgid "e.g. ك ت ب" msgstr "e.g. ك ت ب" -#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:64 +#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:62 msgid "Easy" msgstr "Easy" @@ -620,7 +629,7 @@ msgstr "Easy" msgid "Edit" msgstr "Edit" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:183 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:188 msgid "Edit {word}" msgstr "Edit {word}" @@ -693,7 +702,7 @@ msgstr "Example {0}" msgid "Example usages of the word in different contexts." msgstr "Example usages of the word in different contexts." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:273 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:292 #: ../../apps/web/src/components/features/dictionary/add/AdditionalDetailsFormSection.tsx:154 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:262 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:490 @@ -726,7 +735,7 @@ msgstr "Export your dictionary" msgid "Exporting dictionary with flaschards will save your flashcard progress along with all the content in your dictionary. Use this option if you want to make a backup of your data.<0/><1/>Exporting your dictionary without flashcards will not save any flashcard progress. Use this option if you want to share your dictionary.<2/><3/><4>Warning: Importing your file without flashcards will reset all of your flashcards." msgstr "Exporting dictionary with flaschards will save your flashcard progress along with all the content in your dictionary. Use this option if you want to make a backup of your data.<0/><1/>Exporting your dictionary without flashcards will not save any flashcard progress. Use this option if you want to share your dictionary.<2/><3/><4>Warning: Importing your file without flashcards will reset all of your flashcards." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:31 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:42 #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/TagBadgesList.tsx:17 #: ../../apps/web/src/components/features/dictionary/add/CategoryFormSection.tsx:136 #: ../../apps/web/src/components/features/decks/DeckDialogContent.tsx:69 @@ -794,7 +803,7 @@ msgstr "Failed to update flashcard settings." msgid "Failed to update settings" msgstr "Failed to update settings" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:324 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:330 msgid "Failed to update the word!" msgstr "Failed to update the word!" @@ -807,7 +816,7 @@ msgstr "Failed to update word" msgid "Failed to upload migration" msgstr "Failed to upload migration" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:39 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:50 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:230 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:46 msgid "Feminine" @@ -819,6 +828,14 @@ msgstr "Feminine" msgid "Fi'l" msgstr "Fi'l" +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:51 +msgid "Filter by tags..." +msgstr "Filter by tags..." + +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:51 +msgid "Filters" +msgstr "Filters" + #: ../../apps/web/src/components/features/settings/FlashcardSettingsCardSection.tsx:123 msgid "Flashcard settings updated!" msgstr "Flashcard settings updated!" @@ -840,14 +857,14 @@ msgstr "Follows your system settings" msgid "For example, the word" msgstr "For example, the word" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:240 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:260 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:232 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:803 #: ../../apps/mobile/src/app/(search)/(home)/edit-word/[id].tsx:913 msgid "Form" msgstr "Form" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:180 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:201 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:211 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:172 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:700 @@ -865,7 +882,7 @@ msgstr "GitHub" msgid "Go back" msgstr "Go back" -#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:55 +#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:53 msgid "Good" msgstr "Good" @@ -878,7 +895,7 @@ msgstr "Great work on the backlog! You still have {0} regular reviews." msgid "Great work! Come back later for more reviews." msgstr "Great work! Come back later for more reviews." -#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:45 +#: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/GradeOption.tsx:44 msgid "Hard" msgstr "Hard" @@ -908,7 +925,7 @@ msgstr "Hide details" msgid "Hint" msgstr "Hint" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:174 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:179 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:39 #: ../../apps/web/src/components/MobileHeader.tsx:154 #: ../../apps/web/src/components/DesktopNavigation.tsx:53 @@ -943,7 +960,7 @@ msgstr "if needed." msgid "If you're experiencing sync issues or data not loading correctly, you can reset your local data and re-sync from the server." msgstr "If you're experiencing sync issues or data not loading correctly, you can reset your local data and re-sync from the server." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:227 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:247 #: ../../apps/web/src/components/features/dictionary/add/VerbMorphologyCardSection.tsx:103 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:219 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:786 @@ -1019,7 +1036,7 @@ msgstr "Language" msgid "Light" msgstr "Light" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:126 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:148 msgid "Loading details..." msgstr "Loading details..." @@ -1059,7 +1076,7 @@ msgstr "Manage your dictionary." msgid "Masadir" msgstr "Masadir" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:38 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:49 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:227 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:45 msgid "Masculine" @@ -1096,8 +1113,8 @@ msgstr "Migration successfully registered." #~ msgid "More than {0} cards to review" #~ msgstr "More than {0} cards to review" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:135 -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:195 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:156 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:215 #: ../../apps/web/src/components/features/dictionary/add/MorphologyFormSection.tsx:52 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:127 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:187 @@ -1137,16 +1154,20 @@ msgstr "No decks yet" msgid "No file chosen" msgstr "No file chosen" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:486 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:495 #: ../../apps/mobile/src/components/dictionary/DictionaryList.tsx:55 msgid "No results found" msgstr "No results found" +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:63 +msgid "No tags found." +msgstr "No tags found." + #: ../../apps/web/src/components/ThemeMenu.tsx:87 msgid "Nord" msgstr "Nord" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:28 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:39 #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/TagBadgesList.tsx:11 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:35 msgid "Noun" @@ -1162,7 +1183,7 @@ msgstr "Noun Morphology" msgid "Or continue with" msgstr "Or continue with" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:30 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:41 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:37 msgid "Particle" msgstr "Particle" @@ -1176,7 +1197,7 @@ msgstr "Passive participle" msgid "Passive Participle" msgstr "Passive Participle" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:201 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:221 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:193 msgid "Past" msgstr "Past" @@ -1217,7 +1238,7 @@ msgstr "Please try again or reload the page." msgid "Please try again." msgstr "Please try again." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:167 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:188 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:159 msgid "Plural" msgstr "Plural" @@ -1253,7 +1274,7 @@ msgstr "Prepositions (Huroof)" msgid "Prepositions (huroof) that can be used with the verb to change its meaning." msgstr "Prepositions (huroof) that can be used with the verb to change its meaning." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:214 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:234 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:206 msgid "Present" msgstr "Present" @@ -1267,10 +1288,22 @@ msgstr "Present tense" msgid "Present Tense" msgstr "Present Tense" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:26 +msgid "Recently added" +msgstr "Recently added" + +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:24 +msgid "Recently updated" +msgstr "Recently updated" + #: ../../apps/web/src/components/features/dictionary/add/TagsFormSection.tsx:68 msgid "Recently used" msgstr "Recently used" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:22 +msgid "Relevance" +msgstr "Relevance" + #: ../../apps/web/src/components/errors/ErrorMessage.tsx:142 msgid "Reload" msgstr "Reload" @@ -1295,12 +1328,12 @@ msgstr "Reschedule all backlog cards by grading them as \"Hard\"." msgid "Reset and re-sync" msgstr "Reset and re-sync" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:72 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:106 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:77 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:111 msgid "Reset flashcard" msgstr "Reset flashcard" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:79 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:84 msgid "Reset flashcard progress?" msgstr "Reset flashcard progress?" @@ -1352,8 +1385,8 @@ msgstr "Root letters" msgid "Root Letters" msgstr "Root Letters" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:350 -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:379 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:356 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:385 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:199 #: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/add/route.lazy.tsx:226 #: ../../apps/web/src/components/features/settings/FlashcardSettingsCardSection.tsx:232 @@ -1380,6 +1413,10 @@ msgstr "Schema Migrations" msgid "Search for a tag..." msgstr "Search for a tag..." +#: ../../apps/web/src/components/features/dictionary/filters/TagsFilter.tsx:59 +msgid "Search tag..." +msgstr "Search tag..." + #: ../../apps/web/src/components/search/SearchInput.tsx:63 #: ../../apps/mobile/src/app/(search)/_layout.tsx:97 msgid "Search..." @@ -1455,7 +1492,7 @@ msgstr "Show English to Arabic flashcards." msgid "Show reverse flashcards" msgstr "Show reverse flashcards" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:141 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:162 #: ../../apps/web/src/components/features/dictionary/add/IsmMorphologyCardSection.tsx:60 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:133 #: ../../apps/mobile/src/app/(search)/(home)/add-word.tsx:616 @@ -1471,6 +1508,10 @@ msgstr "Some dictionary entries couldn't be loaded" msgid "Something went wrong" msgstr "Something went wrong" +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:104 +msgid "Sort by" +msgstr "Sort by" + #: ../../apps/web/src/components/features/settings/AdminSettingsCardSection.tsx:100 msgid "SQL Script" msgstr "SQL Script" @@ -1499,11 +1540,11 @@ msgstr "Successfully exported!" msgid "Successfully imported!" msgstr "Successfully imported!" -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:101 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:106 msgid "Successfully reset the flashcard." msgstr "Successfully reset the flashcard." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:316 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:322 msgid "Successfully updated the word!" msgstr "Successfully updated the word!" @@ -1517,7 +1558,8 @@ msgstr "Successfully updated the word!" msgid "System" msgstr "System" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:302 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:320 +#: ../../apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx:60 #: ../../apps/web/src/components/features/dictionary/add/TagsFormSection.tsx:44 #: ../../apps/web/src/components/features/decks/DeckDialogContent.tsx:229 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:291 @@ -1582,7 +1624,7 @@ msgstr "The root letters of the word. The input can be separated by commas, spac msgid "The word has been added to your dictionary." msgstr "The word has been added to your dictionary." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:317 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:323 msgid "The word has been updated." msgstr "The word has been updated." @@ -1634,7 +1676,7 @@ msgstr "There was an error updating the deck" msgid "There was an error updating your flashcard settings." msgstr "There was an error updating your flashcard settings." -#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:325 +#: ../../apps/web/src/routes/_authorized-layout/_app-layout/dictionary/edit/$wordId.tsx:331 msgid "There was an error updating your word. Please try again." msgstr "There was an error updating your word. Please try again." @@ -1721,7 +1763,7 @@ msgstr "Triptote" #~ msgid "Troubleshooting" #~ msgstr "Troubleshooting" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:489 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:498 #: ../../apps/mobile/src/components/dictionary/DictionaryList.tsx:58 msgid "Try a different search term" msgstr "Try a different search term" @@ -1759,7 +1801,7 @@ msgstr "Value must be {0} or greater" msgid "Value must be {0} or less" msgstr "Value must be {0} or less" -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:29 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:40 #: ../../apps/web/src/components/features/flashcards/FlashcardDrawer/TagBadgesList.tsx:13 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:36 msgid "Verb" @@ -1778,7 +1820,7 @@ msgstr "Verb Morphology" msgid "Verbal noun forms (masadir) of the verb." msgstr "Verbal noun forms (masadir) of the verb." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:255 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:275 #: ../../apps/mobile/src/components/dictionary/DictionaryEntryCard.tsx:244 msgid "Verbal nouns" msgstr "Verbal nouns" @@ -1852,7 +1894,7 @@ msgstr "You have {0} cards in your backlog when you're ready." msgid "You have no decks yet." msgstr "You have no decks yet." -#: ../../apps/web/src/components/search/InfiniteScroll.tsx:506 +#: ../../apps/web/src/components/search/InfiniteScroll.tsx:515 msgid "You have no words in your dictionary yet." msgstr "You have no words in your dictionary yet." diff --git a/packages/i18n/locales/en.ts b/packages/i18n/locales/en.ts index cfcbd97a..c29c2c6f 100644 --- a/packages/i18n/locales/en.ts +++ b/packages/i18n/locales/en.ts @@ -1 +1 @@ -/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"5rgLzy\":[[\"0\",\"plural\",{\"one\":[\"card left\"],\"other\":[\"cards left\"]}]],\"/hTM9k\":[[\"0\"],\" / \",[\"1\"],\" batches\"],\"mHye52\":[[\"0\"],\" / \",[\"1\"],\" cards\"],\"baIPh3\":[[\"0\"],\" cards due\"],\"beWT44\":[[\"0\"],\" cards have been rescheduled.\"],\"JJcBCm\":[[\"0\"],\" total\"],\"JtTuS0\":[[\"hydrationSkippedCount\",\"plural\",{\"one\":[\"#\",\" entry was skipped due to data issues. Your data is preserved, but you won't be able to search for the entry that wasn't loaded.\"],\"other\":[\"#\",\" entries were skipped due to data issues. Your data is preserved, but you won't be able to search for any entries that weren't loaded.\"]}]],\"UGMDWm\":[[\"skippedCount\"],\" entries were skipped due to data corruption.\"],\"gp8eDB\":[[\"totalHits\",\"plural\",{\"one\":[\"result\"],\"other\":[\"results\"]}]],\"QSRKsm\":[[\"totalResults\",\"plural\",{\"one\":[[\"totalResults\"],\" result\"],\"other\":[[\"totalResults\"],\" results\"]}]],\"gD09hk\":[[\"val\",\"plural\",{\"one\":[\"Cannot exceed \",\"#\",\" character\"],\"other\":[\"Cannot exceed \",\"#\",\" characters\"]}]],\"I1sa5o\":[[\"val\",\"plural\",{\"one\":[\"Must be at least \",\"#\",\" character\"],\"other\":[\"Must be at least \",\"#\",\" characters\"]}]],\"WWcbHw\":[\"A noun, adjective, adverb or pronoun, and anything that is not a verb or preposition.\"],\"ZQW2PY\":[\"A preposition or conjunction.\"],\"0iy5O2\":[\"A verb.\"],\"7L01XJ\":[\"Actions\"],\"DQqXHw\":[\"Active participle\"],\"AwJc6G\":[\"Active Participle\"],\"rJwPkp\":[\"Add a new SQL migration.\"],\"t89Ong\":[\"Add a new word\"],\"L18Pbl\":[\"Add a new word to your dictionary\"],\"TYPFtT\":[\"Add a tag and press enter\"],\"iJMhp0\":[\"Add all\"],\"nPm6bB\":[\"Add antonym\"],\"jVmrwy\":[\"Add Antonym\"],\"1x4W9u\":[\"Add example\"],\"nI3A8B\":[\"Add Example\"],\"gLlFHX\":[\"Add harf\"],\"aLEtgY\":[\"Add Harf\"],\"Tcbo6a\":[\"Add Masdar\"],\"vKbErK\":[\"Add plural\"],\"SNF/r2\":[\"Add Plural\"],\"cR1WUS\":[\"Add some to get started!\"],\"Oft+Pb\":[\"Add some words to get started!\"],\"Xls3A3\":[\"Add tag \",[\"inputValue\"]],\"VMjLu6\":[\"Add tag \",[\"value\"]],\"uF1GKn\":[\"Add tags to your word.\"],\"bDXmnN\":[\"Add word\"],\"p59pEv\":[\"Additional Details\"],\"b/7DUT\":[\"Again\"],\"KK8UsT\":[\"All done for today!\"],\"7Qi3W5\":[\"An Arabic definition of the word.\"],\"Y/gBMP\":[\"An English translation of the sentence.\"],\"jalxln\":[\"An English translation of the word.\"],\"hyC+qY\":[\"An unexpected error occurred. You can try reloading the page to fix it.\"],\"4xBHUg\":[\"Answer\"],\"vQXp82\":[\"Antonym \",[\"0\"]],\"o9dJs3\":[\"Antonym word\"],\"Hvs4LI\":[\"Antonyms\"],\"p1Kh9a\":[\"Any additional details about this form such as frequency of usage or context.\"],\"9kni2/\":[\"Any plural forms of the word.\"],\"2PPE7k\":[\"Any words that have the same ID will be overwritten.\"],\"cnAqww\":[\"App language\"],\"aAIQg2\":[\"Appearance\"],\"8HV3WN\":[\"Arabic\"],\"LNIQwr\":[\"Arabic definition\"],\"uKmc5K\":[\"Arabic sentence\"],\"wxIhw3\":[\"Arabic verb form\"],\"071WZA\":[\"Arabic word\"],\"pkD36F\":[\"Are you sure you want to delete \\\"\",[\"0\"],\"\\\"?\"],\"DOiV7q\":[\"Are you sure you want to delete this word? It will be removed from your dictionary and its flashcards will be deleted.<0/><1/><2>This action cannot be undone.\"],\"2cZh44\":[\"Are you sure you want to delete this word? This action cannot be undone.\"],\"ikBPhc\":[\"Are you sure you want to delete your dictionary? All your words will be deleted permanently. This action cannot be undone. Please make sure to export your dictionary before deleting it.\"],\"IJr17L\":[\"Are you sure you want to reset this flashcard's progress? The word entry in the dictionary will not be modified, but its corresponding flashcards will be treated as new ones.<0/><1/><2>This action cannot be undone.\"],\"iH8pgl\":[\"Back\"],\"0MgdN+\":[\"Back to top\"],\"KNKCTb\":[\"Backlog\"],\"Fg+fNW\":[\"Backlog cleared!\"],\"vMebS2\":[\"Bahar\"],\"EOUool\":[\"Basic Details\"],\"cSCbv3\":[\"BETA\"],\"OKWk8n\":[\"By signing in, you agree to our <0>Privacy Policy\"],\"dEgA5A\":[\"Cancel\"],\"oySfhX\":[\"Cards due today or recently\"],\"uV52e1\":[\"Cards overdue by more than 7 days\"],\"zAwyRS\":[\"Cards to review today\"],\"K7tIrx\":[\"Category\"],\"qWRWG1\":[\"Cause:\"],\"+yPBXI\":[\"Choose file\"],\"xCJdfg\":[\"Clear\"],\"saoOio\":[\"Clear backlog\"],\"Oo39Ga\":[\"Clearing...\"],\"mpby9d\":[\"Contact support\"],\"M73whl\":[\"Context\"],\"xGVfLh\":[\"Continue\"],\"RvVi9c\":[\"Continue with Email\"],\"NMo6Pj\":[\"Control where antonyms appear during review\"],\"PiH3UR\":[\"Copied!\"],\"he3ygx\":[\"Copy\"],\"hYgDIe\":[\"Create\"],\"oJc/nD\":[\"Create a deck to organize your flashcard reviews\"],\"wxDqxy\":[\"Create a new deck\"],\"oQH8Kl\":[\"Create a new deck by choosing filters. The deck will be composed of the cards that match the filters.\"],\"l7kxcE\":[\"Create deck\"],\"g/Uxjl\":[\"Create Deck\"],\"1m75Ii\":[\"Create your first deck\"],\"PQ2zPy\":[\"Customize how flashcards appear.\"],\"P+gzBQ\":[\"Customize how the application looks for you.\"],\"ZQKLI1\":[\"Danger Zone\"],\"pvnfJD\":[\"Dark\"],\"Wf+eYa\":[\"Debugging\"],\"LUfCYM\":[\"Deck created\"],\"sHVysr\":[\"Deck deleted\"],\"UzOd8J\":[\"Deck Name\"],\"iJA/GX\":[\"Deck successfully created!\"],\"M+Nngk\":[\"Deck successfully deleted!\"],\"NVQRbB\":[\"Deck successfully updated!\"],\"WWDML8\":[\"Deck updated\"],\"9TyPfF\":[\"Decks\"],\"iUt5VS\":[\"Decks allow you to create subsets of your existing flashcards by using filters on your dictionary which you can then study individually.\"],\"ovBPCi\":[\"Default\"],\"MbRyzp\":[\"Definition\"],\"cnGeoo\":[\"Delete\"],\"CYf/wo\":[\"Delete All Data\"],\"G6sE0d\":[\"Delete Deck\"],\"fWNrfJ\":[\"Delete Dictionary\"],\"Xpcev7\":[\"Delete Everything\"],\"pe67wL\":[\"Delete this word?\"],\"VEQ9bX\":[\"Delete word\"],\"/GZNuU\":[\"Delete your dictionary?\"],\"Nu4oKW\":[\"Description\"],\"URmyfc\":[\"Details\"],\"wjSthp\":[\"Details:\"],\"VrH1k+\":[\"Dictionary\"],\"RPYrAz\":[\"Dictionary deleted\"],\"460RzB\":[\"Diptote\"],\"bzSI52\":[\"Discard\"],\"1QfxQT\":[\"Dismiss\"],\"Nwbs0q\":[\"Don't show\"],\"xifE4+\":[\"Dual\"],\"jSxAVI\":[\"e.g. ك ت ب\"],\"Pqhtap\":[\"Easy\"],\"ePK91l\":[\"Edit\"],\"nZQfan\":[\"Edit \",[\"word\"]],\"4cP5gy\":[\"Edit Deck\"],\"AN/oWt\":[\"Edit word\"],\"IQ6Efp\":[\"Edit your deck\"],\"FEct+T\":[\"Edit your deck by choosing filters. The deck will be composed of the cards that match the filters.\"],\"O3oNi5\":[\"Email\"],\"lYGfRP\":[\"English\"],\"7MHe6P\":[\"English translation\"],\"Ac92c9\":[\"Enter a deck name\"],\"ot6UAQ\":[\"Enter a description for your migration\"],\"shwNsK\":[\"Enter deck name\"],\"BTF0Gj\":[\"Enter your 6-digit code from your email\"],\"ojjQlT\":[\"Enter your SQL migration script here\"],\"CA1DlQ\":[\"Entry \",[\"arrayIndex\"]],\"Af6M/a\":[\"Example \",[\"0\"]],\"oea1as\":[\"Example usages of the word in different contexts.\"],\"hFthoo\":[\"Examples\"],\"GS+Mus\":[\"Export\"],\"Mk2MqX\":[\"Export completed with issues\"],\"XydOn5\":[\"Export failed!\"],\"FAMOg+\":[\"Export with flashcards\"],\"HynDdB\":[\"Export your dictionary\"],\"cQ+n9e\":[\"Exporting dictionary with flaschards will save your flashcard progress along with all the content in your dictionary. Use this option if you want to make a backup of your data.<0/><1/>Exporting your dictionary without flashcards will not save any flashcard progress. Use this option if you want to share your dictionary.<2/><3/><4>Warning: Importing your file without flashcards will reset all of your flashcards.\"],\"1JBTZV\":[\"Expression\"],\"GiPSsF\":[\"Failed to add word\"],\"zABpXD\":[\"Failed to add word!\"],\"NcBoUU\":[\"Failed to apply migration to local database.\"],\"eN1Tfn\":[\"Failed to clear backlog\"],\"7hUTxx\":[\"Failed to create deck\"],\"Fr4iT6\":[\"Failed to delete deck\"],\"GCd4Bz\":[\"Failed to delete dictionary\"],\"KLMMnR\":[\"Failed to delete word\"],\"cncTzY\":[\"Failed to delete!\"],\"usb0iv\":[\"Failed to reset local data\"],\"dCD/3a\":[\"Failed to retrieve data from local database.\"],\"btG7GG\":[\"Failed to retrieve database information.\"],\"CBm+/w\":[\"Failed to update deck\"],\"gw2VAF\":[\"Failed to update flashcard settings.\"],\"tOcErW\":[\"Failed to update settings\"],\"X/iINj\":[\"Failed to update the word!\"],\"cnYJdR\":[\"Failed to update word\"],\"ixyKje\":[\"Failed to upload migration\"],\"1B52uC\":[\"Feminine\"],\"TUSnpz\":[\"Fi'l\"],\"wG1Flg\":[\"Flashcard settings updated!\"],\"EiJpdz\":[\"Flashcards\"],\"hpt1Ta\":[\"Follows your device language\"],\"tR/FIC\":[\"Follows your system settings\"],\"q1hBIe\":[\"For example, the word\"],\"QRAWTO\":[\"Form\"],\"5iyJje\":[\"Gender\"],\"RkXlPZ\":[\"GitHub\"],\"CKyk7Q\":[\"Go back\"],\"OEEQKT\":[\"Good\"],\"VOwQ6g\":[\"Great work on the backlog! You still have \",[\"0\"],\" regular reviews.\"],\"Xv6Pek\":[\"Great work! Come back later for more reviews.\"],\"g5Fk5U\":[\"Hard\"],\"h4MDwq\":[\"Harf\"],\"IzmuJh\":[\"Harf \",[\"0\"]],\"D+zLDD\":[\"Hidden\"],\"VMlRqi\":[\"Hide details\"],\"f+BFfG\":[\"Hint\"],\"i0qMbr\":[\"Home\"],\"q0alPy\":[\"How should antonyms be shown in flashcards?\"],\"af2WMF\":[\"How the word is categorized based on its type or context.\"],\"qGZ1pF\":[\"Huroof\"],\"7fIUBE\":[\"ID:\"],\"mU4ZuB\":[\"if needed.\"],\"eQI7A0\":[\"If you're experiencing sync issues or data not loading correctly, you can reset your local data and re-sync from the server.\"],\"wqQIcr\":[\"Imperative\"],\"l3s5ri\":[\"Import\"],\"5LwmgO\":[\"Import failed!\"],\"X85Lgt\":[\"Include English to Arabic flashcards\"],\"O1SPgK\":[\"Incorrect file type\"],\"bjwx3z\":[\"Indeclinable\"],\"d7G5eB\":[\"Inflection\"],\"IaZUGL\":[\"Information such as the word's root, meaning, and examples.\"],\"FP8ELf\":[\"Invalid datetime format\"],\"LpHyjF\":[\"Invalid string format\"],\"xVH/JG\":[\"Invalid type. Expected \",[\"0\"]],\"lBmlgJ\":[\"Invalid type. Expected \",[\"0\"],\", received \",[\"1\"]],\"5R5T/G\":[\"Invalid value. Expected one of: \",[\"0\"]],\"SK5zz1\":[\"Ism\"],\"vXIe7J\":[\"Language\"],\"1njn7W\":[\"Light\"],\"CZOrLB\":[\"Loading details...\"],\"LioGSK\":[\"Loading your dictionary...\"],\"Z3FXyt\":[\"Loading...\"],\"1bAISV\":[\"Local data reset successfully!\"],\"Od5n57\":[\"Log in to your existing account or sign up for a new one\"],\"nOhz3x\":[\"Logout\"],\"c1DM+0\":[\"Manage your decks and study them.\"],\"QUTgGW\":[\"Manage your dictionary.\"],\"U60v+9\":[\"Masadir\"],\"IPJKNz\":[\"Masculine\"],\"dLgiXX\":[\"Masdar\"],\"SXYWd/\":[\"Masdar \",[\"0\"]],\"6URaYg\":[\"Meaning\"],\"MYTK2T\":[\"Meaning with this preposition\"],\"zucql+\":[\"Menu\"],\"qGioCB\":[\"Migration successfully registered.\"],\"QI9AZD\":[\"More than \",[\"0\"],\" cards to review\"],\"yf87+x\":[\"Morphology\"],\"60xV9Y\":[\"Morphology only applies to isms and fi'ls. This section will be hidden if the type of the word is set to anything else.\"],\"qhwV2C\":[\"My Account\"],\"6YtxFj\":[\"Name\"],\"TizFpt\":[\"Navigation menu\"],\"TQ5Es1\":[\"No backlog cards to clear.\"],\"WAQMrN\":[\"No cards due\"],\"TkUliY\":[\"No decks yet\"],\"7+IHTZ\":[\"No file chosen\"],\"AxPAXW\":[\"No results found\"],\"Utp0Ef\":[\"Nord\"],\"tTu2yx\":[\"Noun\"],\"KfoKzr\":[\"Noun Morphology\"],\"zW+FpA\":[\"Or continue with\"],\"PMqWHy\":[\"Particle\"],\"UIYada\":[\"Passive participle\"],\"9wi5te\":[\"Passive Participle\"],\"Ff0Dor\":[\"Past\"],\"KkgkHx\":[\"Past tense\"],\"uiVVIw\":[\"Past Tense\"],\"VHWIM2\":[\"Permanently delete all your data including words, flashcards, and decks.\"],\"1OB0cV\":[\"Please contact <0>support with the following details:\"],\"HsEwwb\":[\"Please enter a deck name\"],\"nqOcNX\":[\"Please select a JSON file with your dictionary.\"],\"AXJfF/\":[\"Please try again or reload the page.\"],\"fuwKpE\":[\"Please try again.\"],\"BPig2P\":[\"Plural\"],\"K5KP2J\":[\"Plural \",[\"0\"]],\"zt7UFB\":[\"Plural form\"],\"u5SHG2\":[\"Plurals\"],\"go9eTo\":[\"Preposition\"],\"JuM71B\":[\"Prepositions (Huroof)\"],\"0Of985\":[\"Prepositions (huroof) that can be used with the verb to change its meaning.\"],\"eZb+lw\":[\"Present\"],\"24cT5U\":[\"Present tense\"],\"6Kwq+n\":[\"Present Tense\"],\"uabU9X\":[\"Recently used\"],\"HpK/8d\":[\"Reload\"],\"t/YqKh\":[\"Remove\"],\"8dg+Yo\":[\"Required field\"],\"+7Cebq\":[\"Reschedule all backlog cards by grading them as \\\"Hard\\\".\"],\"ly/6zq\":[\"Reset and re-sync\"],\"vWTWiR\":[\"Reset flashcard\"],\"7HeQ9b\":[\"Reset flashcard progress?\"],\"2ma9vI\":[\"Reset local data\"],\"lLVyir\":[\"Reset local data and re-sync?\"],\"K/RS6y\":[\"Resetting local data...\"],\"QFtnWs\":[\"Reverse flashcards\"],\"5k0NLb\":[\"Review\"],\"nqtnG9\":[\"Review flashcards\"],\"HtzuX3\":[\"Review flashcards (\",[\"0\"],\" in backlog)\"],\"TbJrZ9\":[\"Review flashcards with spaced repetition\"],\"05OIsb\":[\"Review your Arabic flashcards and grade your understanding with Again, Hard, Good, or Easy options to adjust scheduling.\"],\"cbGyYK\":[\"Root letters\"],\"qMVAMv\":[\"Root Letters\"],\"tfDRzk\":[\"Save\"],\"y3aU20\":[\"Save changes\"],\"IUwGEM\":[\"Save Changes\"],\"bf6lG0\":[\"Schema Migrations\"],\"BnCS55\":[\"Search for a tag...\"],\"YIix5Y\":[\"Search...\"],\"Du+zn+\":[\"Searching...\"],\"hid6Sx\":[\"Select gender\"],\"NdMnnc\":[\"Select inflection\"],\"V/vqsf\":[\"Select the types of words that you want to include in the deck.\"],\"tXQ9KI\":[\"Select type\"],\"FDpH/H\":[\"Sentence\"],\"iw3JO4\":[\"Separate letters with spaces or commas\"],\"Tz0i8g\":[\"Settings\"],\"l8nxCn\":[\"Show after revealing the answer\"],\"r0Xcmy\":[\"Show answer\"],\"vDNaTX\":[\"Show antonyms\"],\"1H95wk\":[\"Show as a hint\"],\"BowKyI\":[\"Show details\"],\"C5SZax\":[\"Show English to Arabic flashcards.\"],\"xzH3m9\":[\"Show reverse flashcards\"],\"maCaRp\":[\"Singular\"],\"vK/oE0\":[\"Some dictionary entries couldn't be loaded\"],\"nwtY4N\":[\"Something went wrong\"],\"r20wDW\":[\"SQL Script\"],\"ykSlaQ\":[\"Still not working?\"],\"pKr7ta\":[\"Study\"],\"+Env0D\":[\"Successfully added word!\"],\"NJp0hH\":[\"Successfully deleted!\"],\"z6XClS\":[\"Successfully exported!\"],\"1uuPpZ\":[\"Successfully imported!\"],\"/5ZZnV\":[\"Successfully reset the flashcard.\"],\"oEWC7j\":[\"Successfully updated the word!\"],\"QgnNyZ\":[\"Sync error\"],\"D+NlUC\":[\"System\"],\"OYHzN1\":[\"Tags\"],\"hz39Yb\":[\"That code is invalid.\"],\"90dpv/\":[\"That email is invalid.\"],\"7X8sXq\":[\"That is not valid JSON.\"],\"P7iz9S\":[\"The app may be open in another tab. Try closing other tabs and refreshing this page.\"],\"Bm2B8s\":[\"The context of the sentence, ex. formal or colloquial.\"],\"MMs+Zk\":[\"The deck \\\"\",[\"0\"],\"\\\" has been deleted.\"],\"Gpyta0\":[\"The form of the verb ex. I, II, III, IV, etc...\"],\"OE9TJJ\":[\"The form of the verb in Arabic ex. فعل, أفعل, استفعل, etc...\"],\"4cDTK/\":[\"The local database is locked by another session.\"],\"EVX5FZ\":[\"The meaning of the verb when used with the harf.\"],\"bdFFEw\":[\"The morphological breakdown of the word.\"],\"nIdeZ6\":[\"The root letters of the word. The input can be separated by commas, spaces or have no delimeter ex. فعل or ف, ع, ل or ف ع ل.\"],\"AvErlv\":[\"The word has been added to your dictionary.\"],\"LbpyqF\":[\"The word has been updated.\"],\"FEr96N\":[\"Theme\"],\"uTlsP3\":[\"There was an error adding your word. Please try again.\"],\"boDfXQ\":[\"There was an error clearing your backlog.\"],\"IOwnXs\":[\"There was an error creating the deck\"],\"AObMB6\":[\"There was an error deleting your dictionary. Please try again later.\"],\"QPU+ph\":[\"There was an error exporting your dictionary. Please try again later.\"],\"+0R2Vl\":[\"There was an error importing your dictionary. Please try again later.\"],\"GrwZZY\":[\"There was an error syncing your data. If this persists, try resetting local data in Settings.\"],\"1GLBeM\":[\"There was an error syncing your data. Please try resetting local data in Settings.\"],\"Xs1nsK\":[\"There was an error syncing your data. Your local data is still available.\"],\"aUu2Qy\":[\"There was an error updating the deck\"],\"jObkch\":[\"There was an error updating your flashcard settings.\"],\"DvuiAU\":[\"There was an error updating your word. Please try again.\"],\"n//cii\":[\"There was an error uploading your SQL migration\"],\"p0aTgS\":[\"There was an error when indexing your data in the search engine.\"],\"7Eqx1H\":[\"There was an unexpected error. Please try again.\"],\"1hR1ca\":[\"There's a temporary issue loading your account. Please try reloading the page.\"],\"UwqoLj\":[\"This field is required.\"],\"oM4aHs\":[\"This is case insensitive.\"],\"GoNjj1\":[\"This is the name of your deck.\"],\"e92j12\":[\"This refers to how many case endings the word can take.\"],\"fxunD6\":[\"This will delete the local copy of your data stored in this browser and download a fresh copy from the server. Your data on the server will not be affected. Use this if you're experiencing sync errors or seeing outdated data.\"],\"lpK0Kp\":[\"This will permanently delete all your words, flashcards, and decks. This action cannot be undone.\"],\"NVF43p\":[\"Time:\"],\"i1FCIq\":[\"To review\"],\"1Q2HRv\":[\"Toggle Menu\"],\"inGj5T\":[\"Tokyo Night\"],\"72c5Qo\":[\"Total\"],\"wFcvZJ\":[\"Translation\"],\"/Onh8p\":[\"Triptote\"],\"qlwLcm\":[\"Troubleshooting\"],\"jKoZME\":[\"Try a different search term\"],\"+zy2Nq\":[\"Type\"],\"WtTYSX\":[\"Types\"],\"xlZRtS\":[\"Uh oh! Something went wrong :/\"],\"6njDZ+\":[\"Unable to connect to remote database.\"],\"WRB75e\":[\"Unknown error.\"],\"1ZR0xe\":[\"Value must be \",[\"0\"],\" or greater\"],\"e5Ml90\":[\"Value must be \",[\"0\"],\" or less\"],\"bwa0RV\":[\"Verb\"],\"LnBspw\":[\"Verb form\"],\"esQ1ry\":[\"Verb Morphology\"],\"WjcFrD\":[\"Verbal noun forms (masadir) of the verb.\"],\"2oIAhB\":[\"Verbal nouns\"],\"s6NYcb\":[\"Verbal Nouns (Masadir)\"],\"B4iZJX\":[\"View all the words in your personal dictionary.\"],\"VvvGs4\":[\"We can't reach our servers right now. Check your connection and try again.\"],\"dHoQb8\":[\"We're having trouble with your account setup. Please try again.\"],\"eqpeYp\":[\"Welcome to Bahar!\"],\"dZiBrj\":[\"Word\"],\"MJ0gHH\":[\"Word added successfully\"],\"wazgbb\":[\"Word deleted successfully\"],\"2WVQNU\":[\"Word not found\"],\"Xwf/Kn\":[\"Word updated successfully\"],\"FVyDlC\":[\"Words that have any of these tags will be included in the deck.\"],\"7niJN/\":[\"Words that have the opposite meaning.\"],\"nTIiPJ\":[\"You do not have access to upload schema migrations.\"],\"4eom/r\":[\"You have \",[\"0\"],\" cards in your backlog when you're ready.\"],\"alITRb\":[\"You have no decks yet.\"],\"XOUb7A\":[\"You have no words in your dictionary yet.\"],\"/dColV\":[\"Your account needs maintenance. We've been notified. Please try again later.\"],\"a8jRIt\":[\"Your data has been re-synced from the server.\"],\"uRXiIT\":[\"Your deck was not created. Please try again.\"],\"oz/nIQ\":[\"Your deck was not updated. Please try again.\"],\"L1VLmw\":[\"Your Dictionary\"],\"SB7dXw\":[\"Your dictionary has been deleted.\"],\"aQiurv\":[\"Your dictionary has been downloaded.\"],\"YAeum9\":[\"Your dictionary has been updated!\"],\"C0Fx9N\":[\"Your dictionary is empty\"],\"3ma3fF\":[\"Your dictionary is not valid. Please fix the errors and upload it again.\"],\"4bRwmE\":[\"محمد is a triptote;\"],\"kkC1Qd\":[\"موسى is indeclinable.\"],\"jFIhM0\":[\"يوسف is a diptote;\"]}")as Messages; \ No newline at end of file +/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"Wg0uLA\":[[\"0\",\"plural\",{\"one\":[\"#\",\" tag selected\"],\"other\":[\"#\",\" tags selected\"]}]],\"5rgLzy\":[[\"0\",\"plural\",{\"one\":[\"card left\"],\"other\":[\"cards left\"]}]],\"/hTM9k\":[[\"0\"],\" / \",[\"1\"],\" batches\"],\"mHye52\":[[\"0\"],\" / \",[\"1\"],\" cards\"],\"baIPh3\":[[\"0\"],\" cards due\"],\"beWT44\":[[\"0\"],\" cards have been rescheduled.\"],\"JJcBCm\":[[\"0\"],\" total\"],\"JtTuS0\":[[\"hydrationSkippedCount\",\"plural\",{\"one\":[\"#\",\" entry was skipped due to data issues. Your data is preserved, but you won't be able to search for the entry that wasn't loaded.\"],\"other\":[\"#\",\" entries were skipped due to data issues. Your data is preserved, but you won't be able to search for any entries that weren't loaded.\"]}]],\"UGMDWm\":[[\"skippedCount\"],\" entries were skipped due to data corruption.\"],\"gp8eDB\":[[\"totalHits\",\"plural\",{\"one\":[\"result\"],\"other\":[\"results\"]}]],\"QSRKsm\":[[\"totalResults\",\"plural\",{\"one\":[[\"totalResults\"],\" result\"],\"other\":[[\"totalResults\"],\" results\"]}]],\"gD09hk\":[[\"val\",\"plural\",{\"one\":[\"Cannot exceed \",\"#\",\" character\"],\"other\":[\"Cannot exceed \",\"#\",\" characters\"]}]],\"I1sa5o\":[[\"val\",\"plural\",{\"one\":[\"Must be at least \",\"#\",\" character\"],\"other\":[\"Must be at least \",\"#\",\" characters\"]}]],\"WWcbHw\":[\"A noun, adjective, adverb or pronoun, and anything that is not a verb or preposition.\"],\"ZQW2PY\":[\"A preposition or conjunction.\"],\"0iy5O2\":[\"A verb.\"],\"7L01XJ\":[\"Actions\"],\"DQqXHw\":[\"Active participle\"],\"AwJc6G\":[\"Active Participle\"],\"rJwPkp\":[\"Add a new SQL migration.\"],\"t89Ong\":[\"Add a new word\"],\"L18Pbl\":[\"Add a new word to your dictionary\"],\"TYPFtT\":[\"Add a tag and press enter\"],\"iJMhp0\":[\"Add all\"],\"nPm6bB\":[\"Add antonym\"],\"jVmrwy\":[\"Add Antonym\"],\"1x4W9u\":[\"Add example\"],\"nI3A8B\":[\"Add Example\"],\"gLlFHX\":[\"Add harf\"],\"aLEtgY\":[\"Add Harf\"],\"Tcbo6a\":[\"Add Masdar\"],\"vKbErK\":[\"Add plural\"],\"SNF/r2\":[\"Add Plural\"],\"cR1WUS\":[\"Add some to get started!\"],\"Oft+Pb\":[\"Add some words to get started!\"],\"Xls3A3\":[\"Add tag \",[\"inputValue\"]],\"VMjLu6\":[\"Add tag \",[\"value\"]],\"uF1GKn\":[\"Add tags to your word.\"],\"bDXmnN\":[\"Add word\"],\"p59pEv\":[\"Additional Details\"],\"b/7DUT\":[\"Again\"],\"KK8UsT\":[\"All done for today!\"],\"7Qi3W5\":[\"An Arabic definition of the word.\"],\"Y/gBMP\":[\"An English translation of the sentence.\"],\"jalxln\":[\"An English translation of the word.\"],\"hyC+qY\":[\"An unexpected error occurred. You can try reloading the page to fix it.\"],\"4xBHUg\":[\"Answer\"],\"vQXp82\":[\"Antonym \",[\"0\"]],\"o9dJs3\":[\"Antonym word\"],\"Hvs4LI\":[\"Antonyms\"],\"p1Kh9a\":[\"Any additional details about this form such as frequency of usage or context.\"],\"9kni2/\":[\"Any plural forms of the word.\"],\"2PPE7k\":[\"Any words that have the same ID will be overwritten.\"],\"cnAqww\":[\"App language\"],\"aAIQg2\":[\"Appearance\"],\"8HV3WN\":[\"Arabic\"],\"LNIQwr\":[\"Arabic definition\"],\"uKmc5K\":[\"Arabic sentence\"],\"wxIhw3\":[\"Arabic verb form\"],\"071WZA\":[\"Arabic word\"],\"pkD36F\":[\"Are you sure you want to delete \\\"\",[\"0\"],\"\\\"?\"],\"DOiV7q\":[\"Are you sure you want to delete this word? It will be removed from your dictionary and its flashcards will be deleted.<0/><1/><2>This action cannot be undone.\"],\"2cZh44\":[\"Are you sure you want to delete this word? This action cannot be undone.\"],\"ikBPhc\":[\"Are you sure you want to delete your dictionary? All your words will be deleted permanently. This action cannot be undone. Please make sure to export your dictionary before deleting it.\"],\"IJr17L\":[\"Are you sure you want to reset this flashcard's progress? The word entry in the dictionary will not be modified, but its corresponding flashcards will be treated as new ones.<0/><1/><2>This action cannot be undone.\"],\"iH8pgl\":[\"Back\"],\"0MgdN+\":[\"Back to top\"],\"KNKCTb\":[\"Backlog\"],\"Fg+fNW\":[\"Backlog cleared!\"],\"vMebS2\":[\"Bahar\"],\"EOUool\":[\"Basic Details\"],\"cSCbv3\":[\"BETA\"],\"OKWk8n\":[\"By signing in, you agree to our <0>Privacy Policy\"],\"dEgA5A\":[\"Cancel\"],\"oySfhX\":[\"Cards due today or recently\"],\"uV52e1\":[\"Cards overdue by more than 7 days\"],\"zAwyRS\":[\"Cards to review today\"],\"K7tIrx\":[\"Category\"],\"qWRWG1\":[\"Cause:\"],\"+yPBXI\":[\"Choose file\"],\"xCJdfg\":[\"Clear\"],\"ot7qsv\":[\"Clear all filters\"],\"saoOio\":[\"Clear backlog\"],\"Oo39Ga\":[\"Clearing...\"],\"mpby9d\":[\"Contact support\"],\"M73whl\":[\"Context\"],\"xGVfLh\":[\"Continue\"],\"RvVi9c\":[\"Continue with Email\"],\"NMo6Pj\":[\"Control where antonyms appear during review\"],\"PiH3UR\":[\"Copied!\"],\"he3ygx\":[\"Copy\"],\"hYgDIe\":[\"Create\"],\"oJc/nD\":[\"Create a deck to organize your flashcard reviews\"],\"wxDqxy\":[\"Create a new deck\"],\"oQH8Kl\":[\"Create a new deck by choosing filters. The deck will be composed of the cards that match the filters.\"],\"l7kxcE\":[\"Create deck\"],\"g/Uxjl\":[\"Create Deck\"],\"1m75Ii\":[\"Create your first deck\"],\"PQ2zPy\":[\"Customize how flashcards appear.\"],\"P+gzBQ\":[\"Customize how the application looks for you.\"],\"ZQKLI1\":[\"Danger Zone\"],\"pvnfJD\":[\"Dark\"],\"Wf+eYa\":[\"Debugging\"],\"LUfCYM\":[\"Deck created\"],\"sHVysr\":[\"Deck deleted\"],\"UzOd8J\":[\"Deck Name\"],\"iJA/GX\":[\"Deck successfully created!\"],\"M+Nngk\":[\"Deck successfully deleted!\"],\"NVQRbB\":[\"Deck successfully updated!\"],\"WWDML8\":[\"Deck updated\"],\"9TyPfF\":[\"Decks\"],\"iUt5VS\":[\"Decks allow you to create subsets of your existing flashcards by using filters on your dictionary which you can then study individually.\"],\"ovBPCi\":[\"Default\"],\"MbRyzp\":[\"Definition\"],\"cnGeoo\":[\"Delete\"],\"CYf/wo\":[\"Delete All Data\"],\"G6sE0d\":[\"Delete Deck\"],\"fWNrfJ\":[\"Delete Dictionary\"],\"Xpcev7\":[\"Delete Everything\"],\"pe67wL\":[\"Delete this word?\"],\"VEQ9bX\":[\"Delete word\"],\"/GZNuU\":[\"Delete your dictionary?\"],\"Nu4oKW\":[\"Description\"],\"URmyfc\":[\"Details\"],\"wjSthp\":[\"Details:\"],\"VrH1k+\":[\"Dictionary\"],\"RPYrAz\":[\"Dictionary deleted\"],\"460RzB\":[\"Diptote\"],\"bzSI52\":[\"Discard\"],\"1QfxQT\":[\"Dismiss\"],\"Nwbs0q\":[\"Don't show\"],\"xifE4+\":[\"Dual\"],\"jSxAVI\":[\"e.g. ك ت ب\"],\"Pqhtap\":[\"Easy\"],\"ePK91l\":[\"Edit\"],\"nZQfan\":[\"Edit \",[\"word\"]],\"4cP5gy\":[\"Edit Deck\"],\"AN/oWt\":[\"Edit word\"],\"IQ6Efp\":[\"Edit your deck\"],\"FEct+T\":[\"Edit your deck by choosing filters. The deck will be composed of the cards that match the filters.\"],\"O3oNi5\":[\"Email\"],\"lYGfRP\":[\"English\"],\"7MHe6P\":[\"English translation\"],\"Ac92c9\":[\"Enter a deck name\"],\"ot6UAQ\":[\"Enter a description for your migration\"],\"shwNsK\":[\"Enter deck name\"],\"BTF0Gj\":[\"Enter your 6-digit code from your email\"],\"ojjQlT\":[\"Enter your SQL migration script here\"],\"CA1DlQ\":[\"Entry \",[\"arrayIndex\"]],\"Af6M/a\":[\"Example \",[\"0\"]],\"oea1as\":[\"Example usages of the word in different contexts.\"],\"hFthoo\":[\"Examples\"],\"GS+Mus\":[\"Export\"],\"Mk2MqX\":[\"Export completed with issues\"],\"XydOn5\":[\"Export failed!\"],\"FAMOg+\":[\"Export with flashcards\"],\"HynDdB\":[\"Export your dictionary\"],\"cQ+n9e\":[\"Exporting dictionary with flaschards will save your flashcard progress along with all the content in your dictionary. Use this option if you want to make a backup of your data.<0/><1/>Exporting your dictionary without flashcards will not save any flashcard progress. Use this option if you want to share your dictionary.<2/><3/><4>Warning: Importing your file without flashcards will reset all of your flashcards.\"],\"1JBTZV\":[\"Expression\"],\"GiPSsF\":[\"Failed to add word\"],\"zABpXD\":[\"Failed to add word!\"],\"NcBoUU\":[\"Failed to apply migration to local database.\"],\"eN1Tfn\":[\"Failed to clear backlog\"],\"7hUTxx\":[\"Failed to create deck\"],\"Fr4iT6\":[\"Failed to delete deck\"],\"GCd4Bz\":[\"Failed to delete dictionary\"],\"KLMMnR\":[\"Failed to delete word\"],\"cncTzY\":[\"Failed to delete!\"],\"usb0iv\":[\"Failed to reset local data\"],\"dCD/3a\":[\"Failed to retrieve data from local database.\"],\"btG7GG\":[\"Failed to retrieve database information.\"],\"CBm+/w\":[\"Failed to update deck\"],\"gw2VAF\":[\"Failed to update flashcard settings.\"],\"tOcErW\":[\"Failed to update settings\"],\"X/iINj\":[\"Failed to update the word!\"],\"cnYJdR\":[\"Failed to update word\"],\"ixyKje\":[\"Failed to upload migration\"],\"1B52uC\":[\"Feminine\"],\"TUSnpz\":[\"Fi'l\"],\"YCAFdN\":[\"Filter by tags...\"],\"cSev+j\":[\"Filters\"],\"wG1Flg\":[\"Flashcard settings updated!\"],\"EiJpdz\":[\"Flashcards\"],\"hpt1Ta\":[\"Follows your device language\"],\"tR/FIC\":[\"Follows your system settings\"],\"q1hBIe\":[\"For example, the word\"],\"QRAWTO\":[\"Form\"],\"5iyJje\":[\"Gender\"],\"RkXlPZ\":[\"GitHub\"],\"CKyk7Q\":[\"Go back\"],\"OEEQKT\":[\"Good\"],\"VOwQ6g\":[\"Great work on the backlog! You still have \",[\"0\"],\" regular reviews.\"],\"Xv6Pek\":[\"Great work! Come back later for more reviews.\"],\"g5Fk5U\":[\"Hard\"],\"h4MDwq\":[\"Harf\"],\"IzmuJh\":[\"Harf \",[\"0\"]],\"D+zLDD\":[\"Hidden\"],\"VMlRqi\":[\"Hide details\"],\"f+BFfG\":[\"Hint\"],\"i0qMbr\":[\"Home\"],\"q0alPy\":[\"How should antonyms be shown in flashcards?\"],\"af2WMF\":[\"How the word is categorized based on its type or context.\"],\"qGZ1pF\":[\"Huroof\"],\"7fIUBE\":[\"ID:\"],\"mU4ZuB\":[\"if needed.\"],\"eQI7A0\":[\"If you're experiencing sync issues or data not loading correctly, you can reset your local data and re-sync from the server.\"],\"wqQIcr\":[\"Imperative\"],\"l3s5ri\":[\"Import\"],\"5LwmgO\":[\"Import failed!\"],\"X85Lgt\":[\"Include English to Arabic flashcards\"],\"O1SPgK\":[\"Incorrect file type\"],\"bjwx3z\":[\"Indeclinable\"],\"d7G5eB\":[\"Inflection\"],\"IaZUGL\":[\"Information such as the word's root, meaning, and examples.\"],\"FP8ELf\":[\"Invalid datetime format\"],\"LpHyjF\":[\"Invalid string format\"],\"xVH/JG\":[\"Invalid type. Expected \",[\"0\"]],\"lBmlgJ\":[\"Invalid type. Expected \",[\"0\"],\", received \",[\"1\"]],\"5R5T/G\":[\"Invalid value. Expected one of: \",[\"0\"]],\"SK5zz1\":[\"Ism\"],\"vXIe7J\":[\"Language\"],\"1njn7W\":[\"Light\"],\"CZOrLB\":[\"Loading details...\"],\"LioGSK\":[\"Loading your dictionary...\"],\"Z3FXyt\":[\"Loading...\"],\"1bAISV\":[\"Local data reset successfully!\"],\"Od5n57\":[\"Log in to your existing account or sign up for a new one\"],\"nOhz3x\":[\"Logout\"],\"c1DM+0\":[\"Manage your decks and study them.\"],\"QUTgGW\":[\"Manage your dictionary.\"],\"U60v+9\":[\"Masadir\"],\"IPJKNz\":[\"Masculine\"],\"dLgiXX\":[\"Masdar\"],\"SXYWd/\":[\"Masdar \",[\"0\"]],\"6URaYg\":[\"Meaning\"],\"MYTK2T\":[\"Meaning with this preposition\"],\"zucql+\":[\"Menu\"],\"qGioCB\":[\"Migration successfully registered.\"],\"QI9AZD\":[\"More than \",[\"0\"],\" cards to review\"],\"yf87+x\":[\"Morphology\"],\"60xV9Y\":[\"Morphology only applies to isms and fi'ls. This section will be hidden if the type of the word is set to anything else.\"],\"qhwV2C\":[\"My Account\"],\"6YtxFj\":[\"Name\"],\"TizFpt\":[\"Navigation menu\"],\"TQ5Es1\":[\"No backlog cards to clear.\"],\"WAQMrN\":[\"No cards due\"],\"TkUliY\":[\"No decks yet\"],\"7+IHTZ\":[\"No file chosen\"],\"AxPAXW\":[\"No results found\"],\"vLuosE\":[\"No tags found.\"],\"Utp0Ef\":[\"Nord\"],\"tTu2yx\":[\"Noun\"],\"KfoKzr\":[\"Noun Morphology\"],\"zW+FpA\":[\"Or continue with\"],\"PMqWHy\":[\"Particle\"],\"UIYada\":[\"Passive participle\"],\"9wi5te\":[\"Passive Participle\"],\"Ff0Dor\":[\"Past\"],\"KkgkHx\":[\"Past tense\"],\"uiVVIw\":[\"Past Tense\"],\"VHWIM2\":[\"Permanently delete all your data including words, flashcards, and decks.\"],\"1OB0cV\":[\"Please contact <0>support with the following details:\"],\"HsEwwb\":[\"Please enter a deck name\"],\"nqOcNX\":[\"Please select a JSON file with your dictionary.\"],\"AXJfF/\":[\"Please try again or reload the page.\"],\"fuwKpE\":[\"Please try again.\"],\"BPig2P\":[\"Plural\"],\"K5KP2J\":[\"Plural \",[\"0\"]],\"zt7UFB\":[\"Plural form\"],\"u5SHG2\":[\"Plurals\"],\"go9eTo\":[\"Preposition\"],\"JuM71B\":[\"Prepositions (Huroof)\"],\"0Of985\":[\"Prepositions (huroof) that can be used with the verb to change its meaning.\"],\"eZb+lw\":[\"Present\"],\"24cT5U\":[\"Present tense\"],\"6Kwq+n\":[\"Present Tense\"],\"0DYtlD\":[\"Recently added\"],\"X5P6yv\":[\"Recently updated\"],\"uabU9X\":[\"Recently used\"],\"7dZnmw\":[\"Relevance\"],\"HpK/8d\":[\"Reload\"],\"t/YqKh\":[\"Remove\"],\"8dg+Yo\":[\"Required field\"],\"+7Cebq\":[\"Reschedule all backlog cards by grading them as \\\"Hard\\\".\"],\"ly/6zq\":[\"Reset and re-sync\"],\"vWTWiR\":[\"Reset flashcard\"],\"7HeQ9b\":[\"Reset flashcard progress?\"],\"2ma9vI\":[\"Reset local data\"],\"lLVyir\":[\"Reset local data and re-sync?\"],\"K/RS6y\":[\"Resetting local data...\"],\"QFtnWs\":[\"Reverse flashcards\"],\"5k0NLb\":[\"Review\"],\"nqtnG9\":[\"Review flashcards\"],\"HtzuX3\":[\"Review flashcards (\",[\"0\"],\" in backlog)\"],\"TbJrZ9\":[\"Review flashcards with spaced repetition\"],\"05OIsb\":[\"Review your Arabic flashcards and grade your understanding with Again, Hard, Good, or Easy options to adjust scheduling.\"],\"cbGyYK\":[\"Root letters\"],\"qMVAMv\":[\"Root Letters\"],\"tfDRzk\":[\"Save\"],\"y3aU20\":[\"Save changes\"],\"IUwGEM\":[\"Save Changes\"],\"bf6lG0\":[\"Schema Migrations\"],\"BnCS55\":[\"Search for a tag...\"],\"VV6Aru\":[\"Search tag...\"],\"YIix5Y\":[\"Search...\"],\"Du+zn+\":[\"Searching...\"],\"hid6Sx\":[\"Select gender\"],\"NdMnnc\":[\"Select inflection\"],\"V/vqsf\":[\"Select the types of words that you want to include in the deck.\"],\"tXQ9KI\":[\"Select type\"],\"FDpH/H\":[\"Sentence\"],\"iw3JO4\":[\"Separate letters with spaces or commas\"],\"Tz0i8g\":[\"Settings\"],\"l8nxCn\":[\"Show after revealing the answer\"],\"r0Xcmy\":[\"Show answer\"],\"vDNaTX\":[\"Show antonyms\"],\"1H95wk\":[\"Show as a hint\"],\"BowKyI\":[\"Show details\"],\"C5SZax\":[\"Show English to Arabic flashcards.\"],\"xzH3m9\":[\"Show reverse flashcards\"],\"maCaRp\":[\"Singular\"],\"vK/oE0\":[\"Some dictionary entries couldn't be loaded\"],\"nwtY4N\":[\"Something went wrong\"],\"/HgF9q\":[\"Sort by\"],\"r20wDW\":[\"SQL Script\"],\"ykSlaQ\":[\"Still not working?\"],\"pKr7ta\":[\"Study\"],\"+Env0D\":[\"Successfully added word!\"],\"NJp0hH\":[\"Successfully deleted!\"],\"z6XClS\":[\"Successfully exported!\"],\"1uuPpZ\":[\"Successfully imported!\"],\"/5ZZnV\":[\"Successfully reset the flashcard.\"],\"oEWC7j\":[\"Successfully updated the word!\"],\"QgnNyZ\":[\"Sync error\"],\"D+NlUC\":[\"System\"],\"OYHzN1\":[\"Tags\"],\"hz39Yb\":[\"That code is invalid.\"],\"90dpv/\":[\"That email is invalid.\"],\"7X8sXq\":[\"That is not valid JSON.\"],\"P7iz9S\":[\"The app may be open in another tab. Try closing other tabs and refreshing this page.\"],\"Bm2B8s\":[\"The context of the sentence, ex. formal or colloquial.\"],\"MMs+Zk\":[\"The deck \\\"\",[\"0\"],\"\\\" has been deleted.\"],\"Gpyta0\":[\"The form of the verb ex. I, II, III, IV, etc...\"],\"OE9TJJ\":[\"The form of the verb in Arabic ex. فعل, أفعل, استفعل, etc...\"],\"4cDTK/\":[\"The local database is locked by another session.\"],\"EVX5FZ\":[\"The meaning of the verb when used with the harf.\"],\"bdFFEw\":[\"The morphological breakdown of the word.\"],\"nIdeZ6\":[\"The root letters of the word. The input can be separated by commas, spaces or have no delimeter ex. فعل or ف, ع, ل or ف ع ل.\"],\"AvErlv\":[\"The word has been added to your dictionary.\"],\"LbpyqF\":[\"The word has been updated.\"],\"FEr96N\":[\"Theme\"],\"uTlsP3\":[\"There was an error adding your word. Please try again.\"],\"boDfXQ\":[\"There was an error clearing your backlog.\"],\"IOwnXs\":[\"There was an error creating the deck\"],\"AObMB6\":[\"There was an error deleting your dictionary. Please try again later.\"],\"QPU+ph\":[\"There was an error exporting your dictionary. Please try again later.\"],\"+0R2Vl\":[\"There was an error importing your dictionary. Please try again later.\"],\"GrwZZY\":[\"There was an error syncing your data. If this persists, try resetting local data in Settings.\"],\"1GLBeM\":[\"There was an error syncing your data. Please try resetting local data in Settings.\"],\"Xs1nsK\":[\"There was an error syncing your data. Your local data is still available.\"],\"aUu2Qy\":[\"There was an error updating the deck\"],\"jObkch\":[\"There was an error updating your flashcard settings.\"],\"DvuiAU\":[\"There was an error updating your word. Please try again.\"],\"n//cii\":[\"There was an error uploading your SQL migration\"],\"p0aTgS\":[\"There was an error when indexing your data in the search engine.\"],\"7Eqx1H\":[\"There was an unexpected error. Please try again.\"],\"1hR1ca\":[\"There's a temporary issue loading your account. Please try reloading the page.\"],\"UwqoLj\":[\"This field is required.\"],\"oM4aHs\":[\"This is case insensitive.\"],\"GoNjj1\":[\"This is the name of your deck.\"],\"e92j12\":[\"This refers to how many case endings the word can take.\"],\"fxunD6\":[\"This will delete the local copy of your data stored in this browser and download a fresh copy from the server. Your data on the server will not be affected. Use this if you're experiencing sync errors or seeing outdated data.\"],\"lpK0Kp\":[\"This will permanently delete all your words, flashcards, and decks. This action cannot be undone.\"],\"NVF43p\":[\"Time:\"],\"i1FCIq\":[\"To review\"],\"1Q2HRv\":[\"Toggle Menu\"],\"inGj5T\":[\"Tokyo Night\"],\"72c5Qo\":[\"Total\"],\"wFcvZJ\":[\"Translation\"],\"/Onh8p\":[\"Triptote\"],\"qlwLcm\":[\"Troubleshooting\"],\"jKoZME\":[\"Try a different search term\"],\"+zy2Nq\":[\"Type\"],\"WtTYSX\":[\"Types\"],\"xlZRtS\":[\"Uh oh! Something went wrong :/\"],\"6njDZ+\":[\"Unable to connect to remote database.\"],\"WRB75e\":[\"Unknown error.\"],\"1ZR0xe\":[\"Value must be \",[\"0\"],\" or greater\"],\"e5Ml90\":[\"Value must be \",[\"0\"],\" or less\"],\"bwa0RV\":[\"Verb\"],\"LnBspw\":[\"Verb form\"],\"esQ1ry\":[\"Verb Morphology\"],\"WjcFrD\":[\"Verbal noun forms (masadir) of the verb.\"],\"2oIAhB\":[\"Verbal nouns\"],\"s6NYcb\":[\"Verbal Nouns (Masadir)\"],\"B4iZJX\":[\"View all the words in your personal dictionary.\"],\"VvvGs4\":[\"We can't reach our servers right now. Check your connection and try again.\"],\"dHoQb8\":[\"We're having trouble with your account setup. Please try again.\"],\"eqpeYp\":[\"Welcome to Bahar!\"],\"dZiBrj\":[\"Word\"],\"MJ0gHH\":[\"Word added successfully\"],\"wazgbb\":[\"Word deleted successfully\"],\"2WVQNU\":[\"Word not found\"],\"Xwf/Kn\":[\"Word updated successfully\"],\"FVyDlC\":[\"Words that have any of these tags will be included in the deck.\"],\"7niJN/\":[\"Words that have the opposite meaning.\"],\"nTIiPJ\":[\"You do not have access to upload schema migrations.\"],\"4eom/r\":[\"You have \",[\"0\"],\" cards in your backlog when you're ready.\"],\"alITRb\":[\"You have no decks yet.\"],\"XOUb7A\":[\"You have no words in your dictionary yet.\"],\"/dColV\":[\"Your account needs maintenance. We've been notified. Please try again later.\"],\"a8jRIt\":[\"Your data has been re-synced from the server.\"],\"uRXiIT\":[\"Your deck was not created. Please try again.\"],\"oz/nIQ\":[\"Your deck was not updated. Please try again.\"],\"L1VLmw\":[\"Your Dictionary\"],\"SB7dXw\":[\"Your dictionary has been deleted.\"],\"aQiurv\":[\"Your dictionary has been downloaded.\"],\"YAeum9\":[\"Your dictionary has been updated!\"],\"C0Fx9N\":[\"Your dictionary is empty\"],\"3ma3fF\":[\"Your dictionary is not valid. Please fix the errors and upload it again.\"],\"4bRwmE\":[\"محمد is a triptote;\"],\"kkC1Qd\":[\"موسى is indeclinable.\"],\"jFIhM0\":[\"يوسف is a diptote;\"]}")as Messages; \ No newline at end of file diff --git a/packages/search/src/database.ts b/packages/search/src/database.ts index e74e378f..5b71188f 100644 --- a/packages/search/src/database.ts +++ b/packages/search/src/database.ts @@ -97,22 +97,7 @@ export const removeDocument = (db: DictionaryOrama, id: string) => { type SearchableProperties = keyof typeof dictionarySchema; -/** - * Exact match fields - */ -export const EXACT_PROPERTIES: SearchableProperties[] = [ - "word_exact", - "morphology.ism.singular_exact", - "morphology.ism.plurals_exact", - "morphology.verb.past_tense_exact", - "morphology.verb.present_tense_exact", - "morphology.verb.masadir_exact", -]; - -/** - * Normalized fields - */ -export const NORMALIZED_PROPERTIES: SearchableProperties[] = [ +export const PROPERTIES: SearchableProperties[] = [ "word", "translation", "definition", @@ -126,7 +111,7 @@ export const NORMALIZED_PROPERTIES: SearchableProperties[] = [ /** * Boost configuration for normalized field search */ -export const NORMALIZED_BOOST = { +export const BOOST = { word: 10, translation: 10, "morphology.ism.plurals": 10, @@ -148,9 +133,7 @@ export type SearchDictionaryOptions = { }; /** - * Searches the Orama database using two-pass search for better relevance: - * 1. First pass: exact fields with low tolerance (precise matching) - * 2. Second pass: normalized fields with higher tolerance (fuzzy matching) + * Searches the Orama database using two-pass search for better relevance. * Results are merged with exact matches prioritized. */ export const searchDictionary = ( @@ -181,18 +164,17 @@ export const searchDictionary = ( // Fetch enough results to cover offset + limit for proper pagination const fetchLimit = offset + limit; - // Pass 1: Exact match search (tolerance 0-1) - const exactTolerance = termLen <= 4 ? 0 : 1; + // Pass 1: Exact match search const exactResults = search( db, { term, mode: "fulltext", limit: fetchLimit, - properties: EXACT_PROPERTIES, - tolerance: exactTolerance, + properties: PROPERTIES, where: options?.where, sortBy: options?.sortBy, + exact: true, }, language ) as SearchResults; @@ -205,8 +187,8 @@ export const searchDictionary = ( term, mode: "fulltext", limit: fetchLimit, - properties: NORMALIZED_PROPERTIES, - boost: NORMALIZED_BOOST, + properties: PROPERTIES, + boost: BOOST, tolerance: fuzzyTolerance, where: options?.where, sortBy: options?.sortBy, diff --git a/packages/search/src/schema.ts b/packages/search/src/schema.ts index 066654d1..6e92f72c 100644 --- a/packages/search/src/schema.ts +++ b/packages/search/src/schema.ts @@ -16,7 +16,6 @@ export const dictionarySchema = { created_at_timestamp_ms: "number", updated_at_timestamp_ms: "number", - // Normalized fields (stemmed, diacritics stripped, hamza/weak letters normalized) word: "string", translation: "string", definition: "string", @@ -28,14 +27,6 @@ export const dictionarySchema = { "morphology.verb.past_tense": "string", "morphology.verb.present_tense": "string", "morphology.verb.masadir": "string[]", - - // Exact fields (only diacritics stripped, no other normalization) - word_exact: "string", - "morphology.ism.singular_exact": "string", - "morphology.ism.plurals_exact": "string[]", - "morphology.verb.past_tense_exact": "string", - "morphology.verb.present_tense_exact": "string", - "morphology.verb.masadir_exact": "string[]", } as const; /** diff --git a/packages/search/src/tokenizer.ts b/packages/search/src/tokenizer.ts index f4f4c8cd..6f5bbbcf 100644 --- a/packages/search/src/tokenizer.ts +++ b/packages/search/src/tokenizer.ts @@ -4,7 +4,7 @@ import { tokenizer as defaultTokenizer } from "@orama/orama/components"; import { stemmer as arabicStemmer } from "@orama/stemmers/arabic"; -import { normalizeArabicForSearch, stripArabicDiacritics } from "./arabic"; +import { normalizeArabicForSearch } from "./arabic"; export type OramaLanguage = "arabic" | "english"; @@ -45,18 +45,6 @@ export const englishTokenizer = defaultTokenizer.createTokenizer({ */ const ENGLISH_PROPS = ["translation"]; -/** - * Properties that should use exact Arabic tokenization (no normalization beyond diacritics) - */ -const EXACT_PROPS = [ - "word_exact", - "morphology.ism.singular_exact", - "morphology.ism.plurals_exact", - "morphology.verb.past_tense_exact", - "morphology.verb.present_tense_exact", - "morphology.verb.masadir_exact", -]; - /** * Multi-language tokenizer that delegates to Arabic or English tokenizers * based on the property name and language parameter. @@ -69,11 +57,6 @@ export const multiLanguageTokenizer = { normalizationCache: new Map(), tokenize(raw: string, language: string, prop?: string): string[] { - if (prop && EXACT_PROPS.includes(prop)) { - const diacriticsStripped = stripArabicDiacritics(raw); - return arabicExactTokenizer.tokenize(diacriticsStripped, "arabic", prop); - } - if (prop && ENGLISH_PROPS.includes(prop)) { return englishTokenizer.tokenize(raw, "english", prop); } From 1c401eefbb650b397c2e9663220e87ed739c872f Mon Sep 17 00:00:00 2001 From: sufyan Date: Mon, 19 Jan 2026 08:20:15 -0500 Subject: [PATCH 6/8] fix: issues with sorting and tags filter together --- .../dictionary/filters/DictionaryFilters.tsx | 26 ++++++++++++------- .../dictionary/filters/TagsFilter.tsx | 10 ++++--- .../web-ui/src/components/scroll-area.tsx | 4 +-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx b/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx index 5c63390f..f493d03b 100644 --- a/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx +++ b/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx @@ -11,6 +11,7 @@ import { Separator } from "@bahar/web-ui/components/separator"; import { Trans } from "@lingui/react/macro"; import { useNavigate, useSearch } from "@tanstack/react-router"; import { ArrowDownUp, FunnelXIcon, SlidersHorizontal } from "lucide-react"; +import { useMemo } from "react"; import { TagsFilter } from "@/components/features/dictionary/filters/TagsFilter"; import { TagPill } from "@/components/TagsCombobox"; @@ -35,7 +36,12 @@ export const DictionaryFilters = () => { from: "/_authorized-layout/_search-layout", }); - const hasActiveFilters = !!filteredTags?.length && filteredTags.length > 0; + const hasActiveFilters = useMemo(() => { + const hasTagsFilter = !!filteredTags?.length && filteredTags.length > 0; + const hasSortFilter = sort !== undefined && sort !== "relevance"; + + return hasTagsFilter || hasSortFilter; + }, [filteredTags, sort]); const clearAllFilters = () => { navigate({ to: "/" }); @@ -75,18 +81,18 @@ export const DictionaryFilters = () => { navigate({ to: "/", - search: { + search: (prev) => ({ + ...prev, tags: newTags?.length ? newTags : undefined, - sort, - }, + }), }); } else { navigate({ to: "/", - search: { + search: (prev) => ({ + ...prev, tags: [...(filteredTags ?? []), tag], - sort, - }, + }), }); } }} @@ -113,10 +119,10 @@ export const DictionaryFilters = () => { onValueChange={(value: SortOption) => { navigate({ to: "/", - search: { - tags: filteredTags?.length ? filteredTags : undefined, + search: (prev) => ({ + ...prev, sort: value === "relevance" ? undefined : value, - }, + }), }); }} value={sort ?? "relevance"} diff --git a/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx b/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx index 09a14fff..38302fc4 100644 --- a/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx +++ b/apps/web/src/components/features/dictionary/filters/TagsFilter.tsx @@ -75,16 +75,18 @@ export const TagsFilter = () => { navigate({ to: "/", - search: { + search: (prev) => ({ + ...prev, tags: newTags?.length ? newTags : undefined, - }, + }), }); } else { navigate({ to: "/", - search: { + search: (prev) => ({ + ...prev, tags: [...(filteredTags ?? []), tag], - }, + }), }); } }} diff --git a/packages/web-ui/src/components/scroll-area.tsx b/packages/web-ui/src/components/scroll-area.tsx index 9b399ce1..7b4a7e3e 100644 --- a/packages/web-ui/src/components/scroll-area.tsx +++ b/packages/web-ui/src/components/scroll-area.tsx @@ -28,9 +28,9 @@ const ScrollBar = React.forwardRef< className={cn( "flex touch-none select-none transition-colors", orientation === "vertical" && - "h-full w-2.5 border-l border-l-transparent p-[1px]", + "h-full w-2.5 border-l border-l-transparent p-px", orientation === "horizontal" && - "h-2.5 flex-col border-t border-t-transparent p-[1px]", + "h-2.5 flex-col border-t border-t-transparent p-px", className )} orientation={orientation} From 1b65e8b6e2c07a2b0d44db24d6907105a615b386 Mon Sep 17 00:00:00 2001 From: sufyan Date: Mon, 19 Jan 2026 08:25:09 -0500 Subject: [PATCH 7/8] fix: use rtl select component when using arabic --- .../features/dictionary/filters/DictionaryFilters.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx b/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx index f493d03b..12290f3f 100644 --- a/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx +++ b/apps/web/src/components/features/dictionary/filters/DictionaryFilters.tsx @@ -14,6 +14,7 @@ import { ArrowDownUp, FunnelXIcon, SlidersHorizontal } from "lucide-react"; import { useMemo } from "react"; import { TagsFilter } from "@/components/features/dictionary/filters/TagsFilter"; import { TagPill } from "@/components/TagsCombobox"; +import { useDir } from "@/hooks/useDir"; type SortOption = "relevance" | "updatedAt" | "createdAt"; @@ -32,6 +33,7 @@ const sortOptions: SortOption[] = ["relevance", "updatedAt", "createdAt"]; export const DictionaryFilters = () => { const navigate = useNavigate(); + const dir = useDir(); const { tags: filteredTags, sort } = useSearch({ from: "/_authorized-layout/_search-layout", }); @@ -116,6 +118,7 @@ export const DictionaryFilters = () => {