From 999fff8ddce0d83523a64f2fa9e572cd4c409ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 14 Oct 2025 05:17:52 +0200 Subject: [PATCH] Work --- bun.lock | 15 +++ packages/webapp/package.json | 3 + packages/webapp/src/@types/i18next.d.ts | 8 ++ packages/webapp/src/i18n/en.ts | 5 + packages/webapp/src/i18n/fr.ts | 8 ++ packages/webapp/src/i18n/index.ts | 21 ++++ packages/webapp/src/index.css | 122 ++---------------------- packages/webapp/src/main.tsx | 1 + packages/webapp/src/routes/__root.tsx | 13 ++- 9 files changed, 82 insertions(+), 114 deletions(-) create mode 100644 packages/webapp/src/@types/i18next.d.ts create mode 100644 packages/webapp/src/i18n/en.ts create mode 100644 packages/webapp/src/i18n/fr.ts create mode 100644 packages/webapp/src/i18n/index.ts diff --git a/bun.lock b/bun.lock index de17e13..76f6f5e 100644 --- a/bun.lock +++ b/bun.lock @@ -58,9 +58,12 @@ "clsx": "^2.1.1", "effect": "^3.17.13", "effect-fc": "^0.1.3", + "i18next": "^25.6.0", + "i18next-browser-languagedetector": "^8.2.0", "lucide-react": "^0.545.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "react-i18next": "^16.0.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.13", }, @@ -133,6 +136,8 @@ "@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="], + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], @@ -683,6 +688,12 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "html-parse-stringify": ["html-parse-stringify@3.0.1", "", { "dependencies": { "void-elements": "3.1.0" } }, "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg=="], + + "i18next": ["i18next@25.6.0", "", { "dependencies": { "@babel/runtime": "^7.27.6" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw=="], + + "i18next-browser-languagedetector": ["i18next-browser-languagedetector@8.2.0", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g=="], + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], @@ -833,6 +844,8 @@ "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], + "react-i18next": ["react-i18next@16.0.1", "", { "dependencies": { "@babel/runtime": "^7.27.6", "html-parse-stringify": "^3.0.1" }, "peerDependencies": { "i18next": ">= 25.5.2", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-0S//bpYEkCPjzuVmxDf9Z6+Y+ArNvpAUk7eDL4qNCZXjDh6Z9j6MZ+NThU7kMCOsmYmDCun3GYEwkiOjjZo9Ug=="], + "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], @@ -937,6 +950,8 @@ "vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], + "void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="], + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], diff --git a/packages/webapp/package.json b/packages/webapp/package.json index 4c0b1a3..fc87491 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -25,9 +25,12 @@ "clsx": "^2.1.1", "effect": "^3.17.13", "effect-fc": "^0.1.3", + "i18next": "^25.6.0", + "i18next-browser-languagedetector": "^8.2.0", "lucide-react": "^0.545.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "react-i18next": "^16.0.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.13" }, diff --git a/packages/webapp/src/@types/i18next.d.ts b/packages/webapp/src/@types/i18next.d.ts new file mode 100644 index 0000000..987a982 --- /dev/null +++ b/packages/webapp/src/@types/i18next.d.ts @@ -0,0 +1,8 @@ +import type { resources } from "@/i18n" + + +declare module "i18next" { + interface CustomTypeOptions { + resources: typeof resources["en"] + } +} diff --git a/packages/webapp/src/i18n/en.ts b/packages/webapp/src/i18n/en.ts new file mode 100644 index 0000000..41565f8 --- /dev/null +++ b/packages/webapp/src/i18n/en.ts @@ -0,0 +1,5 @@ +export const en = { + nav: { + gitRepos: "Git repositories", + } +} diff --git a/packages/webapp/src/i18n/fr.ts b/packages/webapp/src/i18n/fr.ts new file mode 100644 index 0000000..41d056d --- /dev/null +++ b/packages/webapp/src/i18n/fr.ts @@ -0,0 +1,8 @@ +import type { en } from "./en" + + +export const fr = { + nav: { + gitRepos: "Dépôts Git", + } +} satisfies typeof en diff --git a/packages/webapp/src/i18n/index.ts b/packages/webapp/src/i18n/index.ts new file mode 100644 index 0000000..aae6326 --- /dev/null +++ b/packages/webapp/src/i18n/index.ts @@ -0,0 +1,21 @@ +import i18n from "i18next" +import LanguageDetector from "i18next-browser-languagedetector" +import { initReactI18next } from "react-i18next" +import { en } from "./en" +import { fr } from "./fr" + + +export const resources = { en, fr } as const + +await i18n + .use(initReactI18next) + .use(LanguageDetector) + .init({ + supportedLngs: ["en", "fr"], + resources, + interpolation: { + escapeValue: false + }, + }) + +export { i18n } diff --git a/packages/webapp/src/index.css b/packages/webapp/src/index.css index 14f55ae..8b8328a 100644 --- a/packages/webapp/src/index.css +++ b/packages/webapp/src/index.css @@ -3,86 +3,22 @@ @import "tw-animate-css"; @import "@fontsource/work-sans"; -@custom-variant dark (&:is(.dark *)); - :root { - --background: oklch(1 0 0); + --background: oklch(95.38% 0.0357 72.89); --secondary-background: oklch(100% 0 0); - --foreground: oklch(0.145 0 0); + --foreground: oklch(0% 0 0); --main-foreground: oklch(0% 0 0); --main: oklch(72.27% 0.1894 50.19); - --border: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); + --border: oklch(0% 0 0); + --ring: oklch(0% 0 0); --overlay: oklch(0% 0 0 / 0.8); --shadow: 4px 4px 0px 0px var(--border); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); + --chart-1: #FF7A05; + --chart-2: #0099FF; + --chart-3: #FFBF00; + --chart-4: #00D696; + --chart-5: #7A83FF; --chart-active-dot: #000; - --radius: 0.625rem; - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --input: oklch(0.922 0 0); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); -} - -.dark { - --background: oklch(0.145 0 0); - --secondary-background: oklch(23.93% 0 0); - --foreground: oklch(0.985 0 0); - --main-foreground: oklch(0% 0 0); - --main: oklch(67.56% 0.1796 49.61); - --border: oklch(1 0 0 / 10%); - --ring: oklch(0.556 0 0); - --shadow: 4px 4px 0px 0px var(--border); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --chart-active-dot: #fff; - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --input: oklch(1 0 0 / 15%); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); } @theme inline { @@ -108,53 +44,15 @@ --shadow-shadow: var(--shadow); --font-weight-base: 500; --font-weight-heading: 700; - --color-sidebar-ring: var(--sidebar-ring); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar: var(--sidebar); - --color-input: var(--input); - --color-destructive: var(--destructive); - --color-accent-foreground: var(--accent-foreground); - --color-accent: var(--accent); - --color-muted-foreground: var(--muted-foreground); - --color-muted: var(--muted); - --color-secondary-foreground: var(--secondary-foreground); - --color-secondary: var(--secondary); - --color-primary-foreground: var(--primary-foreground); - --color-primary: var(--primary); - --color-popover-foreground: var(--popover-foreground); - --color-popover: var(--popover); - --color-card-foreground: var(--card-foreground); - --color-card: var(--card); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); } @layer base { body { @apply text-foreground font-base bg-background; + font-family: "Work Sans", sans-serif; } h1, h2, h3, h4, h5, h6{ @apply font-heading; } } - -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -} - -body { - font-family: "Work Sans", sans-serif; -} diff --git a/packages/webapp/src/main.tsx b/packages/webapp/src/main.tsx index 4444c2f..7ffd7a1 100644 --- a/packages/webapp/src/main.tsx +++ b/packages/webapp/src/main.tsx @@ -1,6 +1,7 @@ import { createRouter, RouterProvider } from "@tanstack/react-router" import { StrictMode } from "react" import { createRoot } from "react-dom/client" +import "./i18n" import "./index.css" import { routeTree } from "./routeTree.gen" diff --git a/packages/webapp/src/routes/__root.tsx b/packages/webapp/src/routes/__root.tsx index 5a4766c..da4461f 100644 --- a/packages/webapp/src/routes/__root.tsx +++ b/packages/webapp/src/routes/__root.tsx @@ -1,5 +1,6 @@ import { createRootRoute, Outlet } from "@tanstack/react-router" import { TanStackRouterDevtools } from "@tanstack/react-router-devtools" +import { useTranslation } from "react-i18next" import { Button } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" @@ -9,9 +10,17 @@ export const Route = createRootRoute({ }) function RootComponent() { + const { t } = useTranslation() + return <> -
- +
+
+ + + +