From 158e4ef0f30c7ee4a389655d9e900679f7992ea8 Mon Sep 17 00:00:00 2001 From: KarimTamani Date: Sat, 24 May 2025 20:26:47 +0100 Subject: [PATCH] Translate the database diagram to Frnech & Arabic --- src/components/menu/menu.tsx | 185 ++++++++---------- src/i18/index.ts | 2 +- src/i18/languages/ar.ts | 134 +++++++++---- src/i18/languages/en.ts | 66 +++++-- src/i18/languages/fr.ts | 137 +++++++++---- src/pages/database/database-page.tsx | 15 +- .../database/db-controller/db-controller.tsx | 4 +- .../tables-controller/tables-controller.tsx | 36 ++-- 8 files changed, 360 insertions(+), 219 deletions(-) diff --git a/src/components/menu/menu.tsx b/src/components/menu/menu.tsx index ddb2ec6..4361f1b 100644 --- a/src/components/menu/menu.tsx +++ b/src/components/menu/menu.tsx @@ -1,8 +1,9 @@ import { useTheme } from "next-themes"; import DropdownMenu, { MenuDropdownProps } from "./menu-dropdown"; -import { useMemo } from "react"; +import React, { useMemo } from "react"; import { menuItem } from "@heroui/react"; +import { useTranslation } from "react-i18next"; @@ -17,119 +18,87 @@ interface MenuProps { const Menu: React.FC = ({ }) => { - const { resolvedTheme, setTheme } = useTheme() - - + const { setTheme } = useTheme() + const { t } = useTranslation(); const menu: MenuDropdownProps[] = useMemo(() => [ { - title: "File", - - children: [{ - title: "New" - }, - { - title: "Open", - shortcut: "Ctnl + O" - }, { - title: "Save", - shortcut: "Ctnl + S", - divide: true - }, { - title: "Import", - divide: true, - children: [{ - title: ".json", - }, { - title: ".dbml" - }, { - title: "MySql" - }, { - title: "Postgresql" - }] - }, { - title: "Export SQL", - children: [{ - title: "Generic", - }, { - title: "MySql" - }, { - title: "Postgresql" - }] - }, { - title: "Export ORM Models", - divide: true - }, { - title: "Delete Project", - theme: "danger" - }] - }, { - title: "Edit", - clickHandler: () => { - console.log("hello wo") - }, - - children: [{ - title: "Undo", - isDisabled: true, - }, - { - title: "Redo", - - isDisabled: true, - }, { - title: "Clear", - - }] - }, { - title: "View", - children: [{ - title: "Hide Controller", - shortcut: "Ctnl + B", - divide: true - }, { - title: "Zoom on scroll", - divide: true, - children: [{ - title: "On" - }, { - title: "Off" - }], - - }, { - title: "Theme", - clickHandler: () => { - console.log("hello wo") + title: t("menu.file"), + children: [ + { title: t("menu.new") }, + { title: t("menu.open"), shortcut: "Ctnl + O" }, + { title: t("menu.save"), shortcut: "Ctnl + S", divide: true }, + { + title: t("menu.import"), + divide: true, + children: [ + { title: t("menu.json") }, + { title: t("menu.dbml") }, + { title: t("menu.mysql") }, + { title: t("menu.postgresql") }, + ], }, - children: [{ - title: "Light", - clickHandler: () => { - - setTheme("light"); - } - }, { - title: "Dark", - clickHandler: () => { - - setTheme("dark"); - } - }] - }] - }, { - title: "Help", - children: [{ - title: "Show Docs", - }, { - title: "Join Discord" - }] - } - ], []); - - + { + title: t("menu.export_sql"), + children: [ + { title: t("menu.generic") }, + { title: t("menu.mysql") }, + { title: t("menu.postgresql") }, + ], + }, + { title: t("menu.export_orm_models"), divide: true }, + { title: t("menu.delete_project"), theme: "danger" }, + ], + }, + { + title: t("menu.edit"), + clickHandler: () => console.log("hello wo"), + children: [ + { title: t("menu.undo"), isDisabled: true }, + { title: t("menu.redo"), isDisabled: true }, + { title: t("menu.clear") }, + ], + }, + { + title: t("menu.view"), + children: [ + { title: t("menu.hide_controller"), shortcut: "Ctnl + B", divide: true }, + { + title: t("menu.zoom_on_scroll"), + divide: true, + children: [ + { title: t("menu.on") }, + { title: t("menu.off") }, + ], + }, + { + title: t("menu.theme"), + clickHandler: () => console.log("hello wo"), + children: [ + { + title: t("menu.light"), + clickHandler: () => setTheme("light"), + }, + { + title: t("menu.dark"), + clickHandler: () => setTheme("dark"), + }, + ], + }, + ], + }, + { + title: t("menu.help"), + children: [ + { title: t("menu.show_docs") }, + { title: t("menu.join_discord") }, + ], + }, + ], [t]); return
{ - menu.map((menuItem , index ) => ( + menu.map((menuItem, index) => ( )) } @@ -137,4 +106,4 @@ const Menu: React.FC = ({ }) => { } -export default Menu; \ No newline at end of file +export default React.memo(Menu); \ No newline at end of file diff --git a/src/i18/index.ts b/src/i18/index.ts index 772a345..12a3e09 100644 --- a/src/i18/index.ts +++ b/src/i18/index.ts @@ -28,7 +28,7 @@ i18n.use(LanguageDetector) .use(initReactI18next) .init({ resources , - lng: 'en', // 👈 set default language to French + lng: 'en', interpolation: { escapeValue: false, diff --git a/src/i18/languages/ar.ts b/src/i18/languages/ar.ts index 23ce530..471855b 100644 --- a/src/i18/languages/ar.ts +++ b/src/i18/languages/ar.ts @@ -1,48 +1,112 @@ import { Language } from "../types"; - export const ar = { - translation: { sidebar: { tables: "الجداول", relationships: "العلاقات" }, - db_controller: { - filter : "تصفية" , - add_table : "إضافة جدول" , - add_field : "إضافة حقل" , - add_index : "إضافة فهرس" , - add_relationship : "إضافة علاقة" , - show_code : "عرض الكود" , - fields : "الحقول" , - indexes : "الفهارس" , - note : "ملاحظة" , - name : "الاسم" , - type : "النوع" , - primary_key : "المفتاح الأساسي" , - nullable : "يسمح بالقيمة الفارغة" , - select_fields : "اختر الحقول" , - unique : "فريد" , - table_note : "ملاحظة الجدول" , - collapse : "إخفاء الكل" , - primary_table : "الجدول الأساسي" , - referenced_table : "الجدول المشار إليه" , - cardinality :{ - name : "العلاقة" , - one_to_one : "واحد إلى واحد" , - one_to_many : "واحد إلى متعدد" , - many_to_one : "متعدد إلى واحد" , - many_to_many : "متعدد إلى متعدد" , - }, - delete : "حذف" , - } , - table: { - double_click: "انقر مرتين للتعديل" + modal: { + close: "إغلاق", + create: "إنشاء" }, - } + color_picker: { + default_color: "اللون الافتراضي" + }, + db_controller: { + filter: "تصفية", + add_table: "إضافة جدول", + add_field: "إضافة حقل", + add_index: "إضافة فهرس", + add_relationship: "إضافة علاقة", + show_code: "عرض الكود", + fields: "الحقول", + indexes: "الفهارس", + note: "ملاحظة", + name: "الاسم", + type: "النوع", + nullable: "يمكن أن يكون فارغًا", + select_fields: "اختر الحقول", + unique: "فريد", + table_note: "ملاحظة الجدول", + collapse: "طي الكل", -} + primary_key: "المفتاح الأساسي", + foreign_key: "المفتاح الخارجي", + + source_table: "جدول المصدر", + target_table: "جدول الهدف", + + select_table: "اختر الجدول", + select_field: "اختر الحقل", + + cardinality: { + name: "التعددية", + one_to_one: "واحد إلى واحد", + one_to_many: "واحد إلى متعدد", + many_to_one: "متعدد إلى واحد", + many_to_many: "متعدد إلى متعدد" + }, + delete: "حذف", + field_setting: "إعدادات الحقل", + table_actions: "إجراءات الحقل", + actions: "إجراءات", + + field_note: "ملاحظة الحقل", + delete_field: "حذف الحقل", + + create_relationship: "إنشاء علاقة", + relationship_error: "لإنشاء علاقة، يجب أن يكون نوع مفتاح المصدر ونوع المفتاح المرجعي متماثلين.", + + invalid_relationship: { + title: "علاقة غير صالحة", + description: "نوع مفتاح المصدر لا يتطابق مع نوع المفتاح المرجعي. يرجى التأكد من أن كلا المفتاحين لهما نفس نوع البيانات." + } + }, + table: { + double_click: "انقر مزدوجًا للتعديل" , + overlapping_tables: "الجداول المتداخلة", + }, + control_buttons: { + redo: "إعادة", + undo: "تراجع", + zoom_in: "تكبير", + zoom_out: "تصغير", + adjust_positions: "تعديل المواقع", + show_all: "عرض الكل" + }, + menu: { + file: "ملف", + new: "جديد", + open: "فتح", + save: "حفظ", + import: "استيراد", + json: ".json", + dbml: ".dbml", + mysql: "MySql", + postgresql: "Postgresql", + export_sql: "تصدير SQL", + generic: "عام", + export_orm_models: "تصدير ORM Models", + delete_project: "حذف المشروع", + edit: "تحرير", + undo: "تراجع", + redo: "إعادة", + clear: "مسح", + view: "عرض", + hide_controller: "إخفاء المتحكم", + zoom_on_scroll: "التكبير عند التمرير", + on: "تشغيل", + off: "إيقاف", + theme: "الثيم", + light: "فاتح", + dark: "داكن", + help: "مساعدة", + show_docs: "عرض المستندات", + join_discord: "الانضمام إلى ديسكورد" + } + } +}; export const arLanguage: Language = { name: 'Arabic', diff --git a/src/i18/languages/en.ts b/src/i18/languages/en.ts index 3f9d384..5f3db10 100644 --- a/src/i18/languages/en.ts +++ b/src/i18/languages/en.ts @@ -12,9 +12,9 @@ export const en = { close: "Close", create: "Create" }, - color_picker : { - default_color : "Default color" - } , + color_picker: { + default_color: "Default color" + }, db_controller: { filter: "Filter", add_table: "Add Table", @@ -51,29 +51,65 @@ export const en = { one_to_many: "One to Many", many_to_one: "Many to One", many_to_many: "Many to Many", - + }, delete: "Delete", field_setting: "Field Setting", table_actions: "Field Actions", - actions : "Actions" , - + actions: "Actions", + field_note: "Field note", delete_field: "Delete Field", create_relationship: "Create Relationship", relationship_error: "To create a relationship, the primary key and foreign key must be of the same type.", + + invalid_relationship: { + title: "Invalid Relationship", + description: "The source key type does not match the referenced key type. Please ensure both keys have the same data type." + } }, table: { - double_click: "Double click to edit" - } , - control_buttons : { - redo : "Redo" , - undo : "Undo" , - zoom_in : "Zoom In" , - zoom_out : "Zoom Out" , - adjust_positions : "Adjust Positions" , - show_all : "Show all" + double_click: "Double click to edit" , + overlapping_tables : "Overlapping Tables" + }, + control_buttons: { + redo: "Redo", + undo: "Undo", + zoom_in: "Zoom In", + zoom_out: "Zoom Out", + adjust_positions: "Adjust Positions", + show_all: "Show all" + }, + menu: { + file: "File", + new: "New", + open: "Open", + save: "Save", + import: "Import", + json: ".json", + dbml: ".dbml", + mysql: "MySql", + postgresql: "Postgresql", + export_sql: "Export SQL", + generic: "Generic", + export_orm_models: "Export ORM Models", + delete_project: "Delete Project", + edit: "Edit", + undo: "Undo", + redo: "Redo", + clear: "Clear", + view: "View", + hide_controller: "Hide Controller", + zoom_on_scroll: "Zoom on scroll", + on: "On", + off: "Off", + theme: "Theme", + light: "Light", + dark: "Dark", + help: "Help", + show_docs: "Show Docs", + join_discord: "Join Discord" } } diff --git a/src/i18/languages/fr.ts b/src/i18/languages/fr.ts index dd88493..8748602 100644 --- a/src/i18/languages/fr.ts +++ b/src/i18/languages/fr.ts @@ -4,45 +4,110 @@ import { Language } from "../types"; export const fr = { translation: { sidebar: { - tables: "Tableaux", + tables: "Tables", relationships: "Relations" - } , - db_controller : { - filter : "Filtrer" , - add_table : "Ajouter une table" , - add_field : "Ajouter un champ" , - add_index : "Ajouter un index" , - add_relationship : "Ajouter une relation" , - show_code : "Afficher le code" , - fields : "Champs" , - indexes : "Index" , - note : "Note" , - name : "Nom" , - type : "Type" , - primary_key : "Clé primaire" , - nullable : "Nullable" , - select_fields : "Sélectionner des champs" , - unique : "Unique" , - table_note : "Note de la table" , - collapse : "Tout réduire" , - primary_table : "Table primaire" , - referenced_table : "Table référencée" , - cardinality :{ - name : "Cardinalité" , - one_to_one : "Un à un" , - one_to_many : "Un à plusieurs" , - many_to_one : "Plusieurs à un" , - many_to_many : "Plusieurs à plusieurs" , - }, - delete : "Supprimer" , - } , - table: { - double_click: "Double-cliquez pour modifier" }, - - } + modal: { + close: "Fermer", + create: "Créer" + }, + color_picker: { + default_color: "Couleur par défaut" + }, + db_controller: { + filter: "Filtrer", + add_table: "Ajouter une Table", + add_field: "Ajouter un Champ", + add_index: "Ajouter un Index", + add_relationship: "Ajouter une Relation", + show_code: "Afficher le code", + fields: "Champs", + indexes: "Indexes", + note: "Note", + name: "Nom", + type: "Type", + nullable: "Nullable", + select_fields: "Sélectionner les champs", + unique: "Unique", + table_note: "Note de la Table", + collapse: "Tout réduire", -} + primary_key: "Clé primaire", + foreign_key: "Clé étrangère", + + source_table: "Table source", + target_table: "Table cible", + + select_table: "Sélectionner la table", + select_field: "Sélectionner le champ", + + cardinality: { + name: "Cardinalité", + one_to_one: "Un à Un", + one_to_many: "Un à Plusieurs", + many_to_one: "Plusieurs à Un", + many_to_many: "Plusieurs à Plusieurs" + }, + delete: "Supprimer", + field_setting: "Paramètres du champ", + table_actions: "Actions sur le champ", + actions: "Actions", + + field_note: "Note sur le champ", + delete_field: "Supprimer le champ", + + create_relationship: "Créer une Relation", + relationship_error: "Pour créer une relation, la primary key et la foreign key doivent être du même type.", + + invalid_relationship: { + title: "Relation invalide", + description: "Le type de la source key ne correspond pas au type de la referenced key. Veuillez vous assurer que les deux clés ont le même type de données." + } + }, + table: { + double_click: "Double-cliquez pour éditer" , + overlapping_tables: "Tables qui se chevauchent", + }, + control_buttons: { + redo: "Refaire", + undo: "Annuler", + zoom_in: "Zoom avant", + zoom_out: "Zoom arrière", + adjust_positions: "Ajuster les positions", + show_all: "Tout afficher" + }, + menu: { + file: "Fichier", + new: "Nouveau", + open: "Ouvrir", + save: "Enregistrer", + import: "Importer", + json: ".json", + dbml: ".dbml", + mysql: "MySql", + postgresql: "Postgresql", + export_sql: "Exporter SQL", + generic: "Générique", + export_orm_models: "Exporter les ORM Models", + delete_project: "Supprimer le projet", + edit: "Éditer", + undo: "Annuler", + redo: "Refaire", + clear: "Effacer", + view: "Affichage", + hide_controller: "Masquer le contrôleur", + zoom_on_scroll: "Zoom au défilement", + on: "Activé", + off: "Désactivé", + theme: "Thème", + light: "Clair", + dark: "Sombre", + help: "Aide", + show_docs: "Afficher la documentation", + join_discord: "Rejoindre Discord" + } + } +}; export const frLanguage: Language = { name: "French", diff --git a/src/pages/database/database-page.tsx b/src/pages/database/database-page.tsx index 9a69e1b..f2eac51 100644 --- a/src/pages/database/database-page.tsx +++ b/src/pages/database/database-page.tsx @@ -35,10 +35,11 @@ import DatabaseControlButtons from "./database-control-buttons"; import { FieldType } from "@/lib/schemas/field-schema"; import useHighlightedEdges from "@/hooks/use-highlighted-edges"; import useOverlappingNodes from "@/hooks/use-overlapping-nodes"; +import { useTranslation } from "react-i18next"; + -// Main functional component const DatabasePage: React.FC = () => { - + const { t } = useTranslation() ; // Extract database state and operations const { database, getField } = useDatabase(); const { updateTablePositions, deleteMultiTables, deleteMultiRelationships, createRelationship } = useDatabaseOperations(); @@ -83,14 +84,14 @@ const DatabasePage: React.FC = () => { } else { // Show error toast if invalid relationship addToast({ - title: "Invalid Relationship", - description: "Relationship should be between primary key and foreign key of the same time", + title: t("db_controller.invalid_relationship.title"), + description: t("db_controller.invalid_relationship.description"), color: "danger", }); } setIsConnectionInProgress(false); - }, [database]); + }, [database , t ]); // Called when nodes are updated (position changes or removed) const handleNodesChanges: OnNodesChange = useCallback(async (changes: NodeChange[]) => { @@ -196,7 +197,7 @@ const DatabasePage: React.FC = () => {
{ isOverlapping && @@ -216,7 +217,7 @@ const DatabasePage: React.FC = () => { - Overlapping Tables + {t("table.overlapping_tables")} } diff --git a/src/pages/database/db-controller/db-controller.tsx b/src/pages/database/db-controller/db-controller.tsx index bb418cb..481dcbe 100644 --- a/src/pages/database/db-controller/db-controller.tsx +++ b/src/pages/database/db-controller/db-controller.tsx @@ -1,3 +1,4 @@ +import React from "react" import { Outlet } from "react-router-dom" @@ -8,6 +9,7 @@ interface Props { const DBController : React.FC = ({ }) => { + console.log ("re-render db controller") return(
@@ -15,6 +17,6 @@ const DBController : React.FC = ({ }) => { ) } -export default DBController +export default React.memo(DBController) diff --git a/src/pages/database/db-controller/tables-controller/tables-controller.tsx b/src/pages/database/db-controller/tables-controller/tables-controller.tsx index 59f0483..349c89c 100644 --- a/src/pages/database/db-controller/tables-controller/tables-controller.tsx +++ b/src/pages/database/db-controller/tables-controller/tables-controller.tsx @@ -9,47 +9,47 @@ import { useDatabase, useDatabaseOperations } from "@/providers/database-provide import { TableInsertType, TableType } from "@/lib/schemas/table-schema"; import { v4 } from "uuid"; import { useDiagram } from "@/providers/diagram-provider/diagram-provider"; -import { useViewport } from "@xyflow/react"; +import { useReactFlow, useViewport } from "@xyflow/react"; interface Props { } -const PADDING_X = 40 ; -const PADDING_Y = 80 ; +const PADDING_X = 40; +const PADDING_Y = 80; const TablesController: React.FC = ({ }) => { const { database } = useDatabase(); const { createTable } = useDatabaseOperations(); + const { getViewport } = useReactFlow(); const { tables } = database; - const viewport = useViewport(); + //const viewport = useViewport(); const { t } = useTranslation(); const [selectedTable, setSelectedTable] = useState(new Set([])); const { focusedTableId } = useDiagram(); const addNewTable = useCallback(async () => { const newTableId: string = v4(); - + const viewport = getViewport(); const { x, y, zoom } = viewport; // Convert screen (0,0) to flow coordinates using viewport values const posX = -x / zoom + (PADDING_X / zoom); const posY = -y / zoom + (PADDING_Y / zoom); - await createTable({ id: newTableId, - name: `table_${tables.length + 1}`, - posX , - posY , - fields : [{ - id : v4() , - name : "id" , - isPrimary : true , - unique : true + name: `table_${tables.length + 1}`, + posX, + posY, + fields: [{ + id: v4(), + name: "id", + isPrimary: true, + unique: true }] } as TableInsertType); - + setSelectedTable(new Set([newTableId]) as any); - }, [tables, viewport]); + }, [tables, getViewport]); useEffect(() => { @@ -59,6 +59,10 @@ const TablesController: React.FC = ({ }) => { }, [focusedTableId]); const selectedTableId = selectedTable.values().next().value; + useEffect(() => { + + console.log("tables controller re-rendered") + }, [getViewport]); return (