rtetley commited on
Commit
e3cb6ac
·
verified ·
1 Parent(s): 81579e9

feat(lang): logic for internationalization

Browse files

Mechanism is now in place, must fill in translations.

frontend/eslint.config.js CHANGED
@@ -24,6 +24,10 @@ export default [
24
  'react-refresh': reactRefresh,
25
  },
26
  rules: {
 
 
 
 
27
  ...js.configs.recommended.rules,
28
  ...react.configs.recommended.rules,
29
  ...react.configs['jsx-runtime'].rules,
 
24
  'react-refresh': reactRefresh,
25
  },
26
  rules: {
27
+ "@typescript-eslint/no-unused-vars": [
28
+ "error",
29
+ { varsIgnorePattern: "^i18n$" },
30
+ ],
31
  ...js.configs.recommended.rules,
32
  ...react.configs.recommended.rules,
33
  ...react.configs['jsx-runtime'].rules,
frontend/package.json CHANGED
@@ -41,6 +41,9 @@
41
  "eslint-plugin-react-hooks": "^5.0.0",
42
  "eslint-plugin-react-refresh": "^0.4.16",
43
  "globals": "^15.14.0",
44
- "vite": "^6.0.5"
 
 
 
45
  }
46
  }
 
41
  "eslint-plugin-react-hooks": "^5.0.0",
42
  "eslint-plugin-react-refresh": "^0.4.16",
43
  "globals": "^15.14.0",
44
+ "i18nifty": "^3.2.3",
45
+ "typescript": "^5.7.3",
46
+ "vite": "^6.0.5",
47
+ "vite-tsconfig-paths": "^5.1.4"
48
  }
49
  }
frontend/src/App.jsx CHANGED
@@ -6,19 +6,18 @@ import {
6
  useSearchParams,
7
  useLocation,
8
  } from "react-router-dom";
9
- import { ThemeProvider } from "@mui/material/styles";
10
  import { Box, CssBaseline } from "@mui/material";
11
- // import Navigation from "./components/Navigation/Navigation";
12
  import Navigation from "./components/Navigation/Navigation";
13
  import LeaderboardPage from "./pages/LeaderboardPage/LeaderboardPage";
14
  import AddModelPage from "./pages/AddModelPage/AddModelPage";
15
  import QuotePage from "./pages/QuotePage/QuotePage";
16
  import VoteModelPage from "./pages/VoteModelPage/VoteModelPage";
17
  import { Header } from "@codegouvfr/react-dsfr/Header";
18
- import { Footer } from "@codegouvfr/react-dsfr/Footer";
19
  import MuiDsfrThemeProvider from "@codegouvfr/react-dsfr/mui";
20
  import { headerFooterDisplayItem } from "@codegouvfr/react-dsfr/Display";
21
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
 
22
  import LeaderboardProvider from "./pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext";
23
  const queryClient = new QueryClient({
24
  defaultOptions: {
@@ -101,7 +100,8 @@ function App() {
101
  }}
102
  quickAccessItems={[
103
  // other quick access items...
104
- headerFooterDisplayItem
 
105
  ]}
106
  id="fr-header-simple-header-with-service-title-and-tagline"
107
  serviceTagline="Tableau de référence pour les grands modèles de langages en français"
@@ -125,43 +125,7 @@ function App() {
125
  <Route path="/vote" element={<VoteModelPage />} />
126
  </Routes>
127
  </Box>
128
- <Footer
129
- accessibility="fully compliant"
130
- contentDescription="
131
- L'évaluation des systèmes d'IA est un enjeu stratégique sur lequel la France s'est historiquement démarquée.
132
-
133
- Ce classement, ou leaderboard, s'inspire directement de l'Open LLM Leaderboard et permet de comparer différents modèles d'IA génératifs à l'aide de jeux de données spécifiquement adaptés aux environnements et à la culture francophones.
134
- "
135
- partnersLogos={{
136
- sub: [
137
- {
138
- alt: 'Logo Inria',
139
- href: '#',
140
- imgUrl: '/inr_logo_rouge.png'
141
- },
142
- {
143
- alt: 'Logo CNRS',
144
- href: '#',
145
- imgUrl: '/LOGO_CNRS_BLEU.png'
146
- },
147
- {
148
- alt: 'Logo LNE',
149
- href: '#',
150
- imgUrl: '/logo-lne.svgz'
151
- },
152
- {
153
- alt: 'Logo DGE',
154
- href: '#',
155
- imgUrl: '/logo_DGE.png'
156
- },
157
- {
158
- alt: 'Logo huggingface',
159
- href: '#',
160
- imgUrl: '/hf-logo-with-title.svg'
161
- }
162
- ]
163
- }}
164
- />
165
  </Box>
166
  </LeaderboardProvider>
167
  </Router>
 
6
  useSearchParams,
7
  useLocation,
8
  } from "react-router-dom";
 
9
  import { Box, CssBaseline } from "@mui/material";
 
10
  import Navigation from "./components/Navigation/Navigation";
11
  import LeaderboardPage from "./pages/LeaderboardPage/LeaderboardPage";
12
  import AddModelPage from "./pages/AddModelPage/AddModelPage";
13
  import QuotePage from "./pages/QuotePage/QuotePage";
14
  import VoteModelPage from "./pages/VoteModelPage/VoteModelPage";
15
  import { Header } from "@codegouvfr/react-dsfr/Header";
16
+ import Footer from "./components/Footer/Footer";
17
  import MuiDsfrThemeProvider from "@codegouvfr/react-dsfr/mui";
18
  import { headerFooterDisplayItem } from "@codegouvfr/react-dsfr/Display";
19
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
20
+ import { LanguageSelect } from "./components/LanguageSelect/LanguageSelect";
21
  import LeaderboardProvider from "./pages/LeaderboardPage/components/Leaderboard/context/LeaderboardContext";
22
  const queryClient = new QueryClient({
23
  defaultOptions: {
 
100
  }}
101
  quickAccessItems={[
102
  // other quick access items...
103
+ headerFooterDisplayItem,
104
+ <LanguageSelect />
105
  ]}
106
  id="fr-header-simple-header-with-service-title-and-tagline"
107
  serviceTagline="Tableau de référence pour les grands modèles de langages en français"
 
125
  <Route path="/vote" element={<VoteModelPage />} />
126
  </Routes>
127
  </Box>
128
+ <Footer />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  </Box>
130
  </LeaderboardProvider>
131
  </Router>
frontend/src/components/Footer/Footer.jsx DELETED
@@ -1,30 +0,0 @@
1
- import React from "react";
2
- import { Box, Typography, Link } from "@mui/material";
3
-
4
- const Footer = () => {
5
- return (
6
- <Box
7
- component="footer"
8
- sx={{
9
- width: "100%",
10
- py: 4,
11
- textAlign: "center",
12
- }}
13
- >
14
- <Typography variant="body2" color="text.secondary" sx={{ mx: 4 }}>
15
- © 2024 Hugging Face - Open LLM Leaderboard - Made with 🤗 by the HF team
16
- -{" "}
17
- <Link
18
- href="https://huggingface.co"
19
- target="_blank"
20
- rel="noopener noreferrer"
21
- color="inherit"
22
- >
23
- huggingface.co
24
- </Link>
25
- </Typography>
26
- </Box>
27
- );
28
- };
29
-
30
- export default Footer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/Footer/Footer.tsx ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {addFooterTranslations, Footer} from "@codegouvfr/react-dsfr/Footer";
2
+
3
+ // import { Box, Typography, Link } from "@mui/material";
4
+
5
+ const footer = () => {
6
+ <Footer
7
+ accessibility="fully compliant"
8
+ contentDescription="
9
+ L'évaluation des systèmes d'IA est un enjeu stratégique sur lequel la France s'est historiquement démarquée.
10
+
11
+ Ce classement, ou leaderboard, s'inspire directement de l'Open LLM Leaderboard et permet de comparer différents modèles d'IA génératifs à l'aide de jeux de données spécifiquement adaptés aux environnements et à la culture francophones.
12
+ "
13
+ partnersLogos={{
14
+ sub: [
15
+ {
16
+ alt: 'Logo Inria',
17
+ href: '#',
18
+ imgUrl: '/inr_logo_rouge.png'
19
+ },
20
+ {
21
+ alt: 'Logo CNRS',
22
+ href: '#',
23
+ imgUrl: '/LOGO_CNRS_BLEU.png'
24
+ },
25
+ {
26
+ alt: 'Logo LNE',
27
+ href: '#',
28
+ imgUrl: '/logo-lne.svgz'
29
+ },
30
+ {
31
+ alt: 'Logo DGE',
32
+ href: '#',
33
+ imgUrl: '/logo_DGE.png'
34
+ },
35
+ {
36
+ alt: 'Logo huggingface',
37
+ href: '#',
38
+ imgUrl: '/hf-logo-with-title.svg'
39
+ }
40
+ ]
41
+ }}
42
+ />
43
+ }
44
+
45
+ addFooterTranslations({
46
+ lang: "en",
47
+ messages: {
48
+ accessibility: "Accessibility",
49
+ "fully compliant": "Partially compliant",
50
+
51
+ }
52
+ });
53
+
54
+
55
+ addFooterTranslations({
56
+ lang: "fr",
57
+ messages: {
58
+ accessibility: "Accesibilité",
59
+ "fully compliant": "Partielle",
60
+
61
+ }
62
+ });
63
+
64
+ // const Footer = () => {
65
+ // return (
66
+ // <Box
67
+ // component="footer"
68
+ // sx={{
69
+ // width: "100%",
70
+ // py: 4,
71
+ // textAlign: "center",
72
+ // }}
73
+ // >
74
+ // <Typography variant="body2" color="text.secondary" sx={{ mx: 4 }}>
75
+ // © 2024 Hugging Face - Open LLM Leaderboard - Made with 🤗 by the HF team
76
+ // -{" "}
77
+ // <Link
78
+ // href="https://huggingface.co"
79
+ // target="_blank"
80
+ // rel="noopener noreferrer"
81
+ // color="inherit"
82
+ // >
83
+ // huggingface.co
84
+ // </Link>
85
+ // </Typography>
86
+ // </Box>
87
+ // );
88
+ // };
89
+
90
+
91
+ export default footer;
frontend/src/components/LanguageSelect/LanguageSelect.jsx ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';import {
2
+ LanguageSelect as LanguageSelect_base,
3
+ addLanguageSelectTranslations
4
+ } from "@codegouvfr/react-dsfr/LanguageSelect";
5
+
6
+ import { useLang, languages} from "i18n";
7
+
8
+ // NOTE: This component can be used inside or outside of the Header component.
9
+ export function LanguageSelect(props) {
10
+
11
+ const { id } = props;
12
+
13
+ const { lang, setLang } = useLang();
14
+
15
+ return (
16
+ <LanguageSelect_base
17
+ id={id}
18
+ supportedLangs={languages}
19
+ lang={lang} // "en" or "fr"
20
+ setLang={setLang}
21
+ fullNameByLang={{
22
+ en: "English",
23
+ fr: "Français"
24
+ }}
25
+ />
26
+ );
27
+
28
+ }
29
+
30
+ languages.forEach(lang =>
31
+ addLanguageSelectTranslations({
32
+ lang: lang,
33
+ messages: {
34
+ "select language": (() => {
35
+ switch (lang) {
36
+ case "en": return "Select language";
37
+ /* spell-checker: disable */
38
+ case "fr": return "Choisir la langue";
39
+ /* spell-checker: enable */
40
+ }
41
+ })()
42
+ }
43
+ })
44
+ );
frontend/src/i18n.tsx ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createI18nApi, declareComponentKeys } from "i18nifty";
2
+
3
+ //List the languages you with to support
4
+ export const languages = ["en", "fr"] as const;
5
+
6
+ //If the user's browser language doesn't match any
7
+ //of the languages above specify the language to fallback to:
8
+ export const fallbackLanguage = "en";
9
+
10
+ export type Language = typeof languages[number];
11
+
12
+ export type LocalizedString = Parameters<typeof resolveLocalizedString>[0];
13
+
14
+ export const {
15
+ useTranslation,
16
+ resolveLocalizedString,
17
+ useLang,
18
+ $lang,
19
+ useResolveLocalizedString,
20
+ /** For use outside of React */
21
+ getTranslation,
22
+ } = createI18nApi<
23
+ | import ("pages/LeaderboardPage/LeaderboardPage").I18n
24
+ >()(
25
+ {
26
+ languages,
27
+ fallbackLanguage
28
+ },
29
+ {
30
+ "en": {
31
+ "LeaderboardPage": {
32
+ "greating": ({ who })=> `Hello ${who}`,
33
+ "how are you": "How are you feeling today?",
34
+ "learn more": ({ href }) => (
35
+ <>
36
+ Learn more about
37
+ <a href={href}>this website</a>.
38
+ </>
39
+ )
40
+ },
41
+ },
42
+ /* spell-checker: disable */
43
+ "fr": {
44
+ "LeaderboardPage": {
45
+ "greating": ({ who })=> `Bonjour ${who}`,
46
+ "how are you": "Comment vous sentez vous au jour d'hui?",
47
+ "learn more": ({ href }) => (
48
+ <>
49
+ En savoir plus à propos de
50
+ <a href={href}>ce site web</a>.
51
+ </>
52
+ )
53
+ },
54
+ }
55
+ /* spell-checker: enable */
56
+ }
57
+ );
frontend/src/main.jsx CHANGED
@@ -2,8 +2,16 @@ import { StrictMode } from 'react'
2
  import { createRoot } from 'react-dom/client'
3
  import './index.css'
4
  import App from './App.jsx'
 
5
  import { startReactDsfr } from "@codegouvfr/react-dsfr/spa";
6
- startReactDsfr({ defaultColorScheme: "system" });
 
 
 
 
 
 
 
7
 
8
  createRoot(document.getElementById('root')).render(
9
  <StrictMode>
 
2
  import { createRoot } from 'react-dom/client'
3
  import './index.css'
4
  import App from './App.jsx'
5
+ import { useLang } from "i18n";
6
  import { startReactDsfr } from "@codegouvfr/react-dsfr/spa";
7
+
8
+ startReactDsfr({
9
+ defaultColorScheme: "system",
10
+ useLang: function useLangDsfr() {
11
+ const { lang } = useLang();
12
+ return lang;
13
+ }
14
+ });
15
 
16
  createRoot(document.getElementById('root')).render(
17
  <StrictMode>
frontend/src/pages/LeaderboardPage/{LeaderboardPage.jsx → LeaderboardPage.tsx} RENAMED
@@ -2,9 +2,9 @@ import { useEffect } from "react";
2
  import Leaderboard from "./components/Leaderboard/Leaderboard";
3
  import { Box } from "@mui/material";
4
  import PageHeader from "../../components/shared/PageHeader";
5
- import Logo from "../../components/Logo/Logo";
6
  import { useLeaderboardData } from "./components/Leaderboard/hooks/useLeaderboardData";
7
  import { useLeaderboard } from "./components/Leaderboard/context/LeaderboardContext";
 
8
 
9
  function LeaderboardPage() {
10
  const { data, isLoading, error } = useLeaderboardData();
@@ -34,6 +34,7 @@ function LeaderboardPage() {
34
  </Box> */}
35
  <PageHeader
36
  title="Leaderboard"
 
37
  // subtitle={
38
  // <>
39
  // Comparer les grands modèles de langages {" "}
@@ -47,4 +48,11 @@ function LeaderboardPage() {
47
  );
48
  }
49
 
 
 
 
 
 
 
 
50
  export default LeaderboardPage;
 
2
  import Leaderboard from "./components/Leaderboard/Leaderboard";
3
  import { Box } from "@mui/material";
4
  import PageHeader from "../../components/shared/PageHeader";
 
5
  import { useLeaderboardData } from "./components/Leaderboard/hooks/useLeaderboardData";
6
  import { useLeaderboard } from "./components/Leaderboard/context/LeaderboardContext";
7
+ import { declareComponentKeys } from "i18nifty";
8
 
9
  function LeaderboardPage() {
10
  const { data, isLoading, error } = useLeaderboardData();
 
34
  </Box> */}
35
  <PageHeader
36
  title="Leaderboard"
37
+ subtitle={""}
38
  // subtitle={
39
  // <>
40
  // Comparer les grands modèles de langages {" "}
 
48
  );
49
  }
50
 
51
+ const { i18n } = declareComponentKeys<
52
+ | { K: "greating"; P: { who: string; } }
53
+ | "how are you"
54
+ | { K: "learn more"; P: { href: string; }; R: JSX.Element }
55
+ >()({ LeaderboardPage });
56
+ export type I18n = typeof i18n;
57
+
58
  export default LeaderboardPage;
frontend/src/pages/LeaderboardPage/components/Leaderboard/components/Filters/SearchBar.jsx CHANGED
@@ -185,7 +185,7 @@ const SearchBar = ({
185
  <InputBase
186
  value={localValue}
187
  onChange={handleLocalChange}
188
- placeholder='Search by model nametry "meta @architecture:llama @license:mit"'
189
  sx={{
190
  flex: 1,
191
  fontSize: "1rem",
@@ -286,7 +286,7 @@ const SearchBar = ({
286
  display: { xs: "none", md: "block" },
287
  }}
288
  >
289
- Advanced Filters
290
  </Typography>
291
  </Box>
292
  <InfoIconWithTooltip
@@ -316,8 +316,7 @@ const SearchBar = ({
316
  lineHeight: 1.5,
317
  }}
318
  >
319
- Supports strict search and regexUse semicolons for multiple
320
- terms
321
  </Typography>
322
  </Box>
323
  )}
 
185
  <InputBase
186
  value={localValue}
187
  onChange={handleLocalChange}
188
+ placeholder='Recherce par nom de modèle essayez "meta @architecture:llama @license:mit"'
189
  sx={{
190
  flex: 1,
191
  fontSize: "1rem",
 
286
  display: { xs: "none", md: "block" },
287
  }}
288
  >
289
+ Filtres avancés
290
  </Typography>
291
  </Box>
292
  <InfoIconWithTooltip
 
316
  lineHeight: 1.5,
317
  }}
318
  >
319
+ Supporte la recherche stricte et les expressions régulières Utilisez des points virgules pour plusieurs termes
 
320
  </Typography>
321
  </Box>
322
  )}
frontend/tsconfig.app.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2020",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "skipLibCheck": true,
9
+ "baseUrl": "src",
10
+
11
+ /* Bundler mode */
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "isolatedModules": true,
15
+ "moduleDetection": "force",
16
+ "noEmit": true,
17
+ "jsx": "react-jsx",
18
+ "allowJs": true,
19
+
20
+ /* Linting */
21
+ "strict": true,
22
+ "noUnusedLocals": true,
23
+ "noUnusedParameters": true,
24
+ "noFallthroughCasesInSwitch": true,
25
+ "noUncheckedSideEffectImports": true
26
+ },
27
+ "include": ["src"]
28
+ }
29
+
frontend/tsconfig.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
8
+
frontend/tsconfig.node.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2022",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "isolatedModules": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+
16
+ /* Linting */
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedSideEffectImports": true
22
+ },
23
+ "include": ["vite.config.ts"]
24
+ }
25
+
frontend/vite.config.js CHANGED
@@ -1,9 +1,13 @@
1
  import { defineConfig } from 'vite'
 
2
  import react from '@vitejs/plugin-react'
3
 
4
  // https://vite.dev/config/
5
  export default defineConfig({
6
- plugins: [react()],
 
 
 
7
  server: {
8
  proxy: {
9
  '/api': {
 
1
  import { defineConfig } from 'vite'
2
+ import tsconfigPaths from "vite-tsconfig-paths";
3
  import react from '@vitejs/plugin-react'
4
 
5
  // https://vite.dev/config/
6
  export default defineConfig({
7
+ plugins: [
8
+ react(),
9
+ tsconfigPaths()
10
+ ],
11
  server: {
12
  proxy: {
13
  '/api': {