Skip to content

Commit 0fb9264

Browse files
authored
Merge pull request #188 from aruznieto/i18n
feat: i18n - dynamic import on build time
2 parents 6f7bc6c + d826cda commit 0fb9264

File tree

9 files changed

+89
-14
lines changed

9 files changed

+89
-14
lines changed

frontend/public/locales/en/common.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@
6363
"optional": "Optional",
6464
"destination": "Destination",
6565
"username": "Username",
66-
"password": "Password"
66+
"password": "Password",
67+
"languageName": "English"
6768
}

frontend/public/locales/es-ES/common.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@
6363
"optional": "Opcional",
6464
"destination": "Destino",
6565
"username": "Nombre de usuario",
66-
"password": "Contraseña"
66+
"password": "Contraseña",
67+
"languageName": "Español"
6768
}

frontend/src/components/Settings/Settings.jsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,14 @@ import {
22
Accordion,
33
AccordionSummary,
44
AccordionDetails,
5-
Checkbox,
6-
Divider,
75
Grid,
86
Typography,
9-
TextField,
107
Select,
118
} from "@material-ui/core";
129
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
1310

14-
import API from "utils/API";
15-
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper";
16-
1711
import { useTranslation } from "react-i18next";
12+
import localesList from "generated/localesList.json";
1813

1914
function Settings() {
2015
const { t, i18n } = useTranslation();
@@ -31,8 +26,11 @@ function Settings() {
3126
<AccordionDetails>
3227
<Grid item>
3328
<Select native value={i18n.language} onChange={handleChange()}>
34-
<option value={"en"}>English</option>
35-
<option value={"es-ES"}>Español</option>
29+
{localesList.map((locale) => (
30+
<option key={locale.code} value={locale.code}>
31+
{locale.name}
32+
</option>
33+
))}
3634
</Select>
3735
</Grid>
3836
</AccordionDetails>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
"code": "en",
4+
"name": "English"
5+
},
6+
{
7+
"code": "es-ES",
8+
"name": "Español"
9+
}
10+
]

frontend/src/i18n.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import languageDetector from "i18next-browser-languagedetector";
33
import { initReactI18next } from "react-i18next";
44
import Backend from "i18next-http-backend";
55

6-
const userLanguage = window.navigator.language;
6+
import localesList from "./utils/localesList.json";
7+
const supportedLngs = localesList.map((locale) => locale.code);
78

89
i18n
910
.use(languageDetector)
@@ -23,7 +24,7 @@ i18n
2324
react: {
2425
useSuspense: true,
2526
},
26-
supportedLngs: ["en", "es-ES"],
27+
supportedLngs,
2728
backend: {
2829
loadPath: "/locales/{{lng}}/{{ns}}.json",
2930
},

frontend/src/utils/localesList.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
"code": "en",
4+
"name": "English"
5+
},
6+
{
7+
"code": "es-ES",
8+
"name": "Español"
9+
}
10+
]

frontend/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"baseUrl": "src",
66
"module": "NodeNext",
77
"moduleResolution": "NodeNext",
8-
"jsx": "preserve"
8+
"jsx": "preserve",
9+
"resolveJsonModule": true
910
},
1011
"include": ["src"]
1112
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import fs from "fs";
2+
import path from "path";
3+
import * as url from "url";
4+
5+
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
6+
7+
export default function GenerateLocalesPlugin() {
8+
return {
9+
name: "generate-locales",
10+
buildStart() {
11+
const localesDir = path.resolve(__dirname, "public", "locales");
12+
13+
if (fs.existsSync(localesDir)) {
14+
const localesList = fs
15+
.readdirSync(localesDir)
16+
.filter((file) => {
17+
return fs.statSync(path.join(localesDir, file)).isDirectory();
18+
})
19+
.map((locale) => {
20+
const commonFilePath = path.join(localesDir, locale, "common.json");
21+
if (fs.existsSync(commonFilePath)) {
22+
const commonFile = JSON.parse(
23+
fs.readFileSync(commonFilePath, "utf-8")
24+
);
25+
return {
26+
code: locale,
27+
name: commonFile.languageName || locale,
28+
};
29+
}
30+
return {
31+
code: locale,
32+
name: locale,
33+
};
34+
});
35+
36+
// Save the array to a JSON file
37+
const outputPath = path.resolve(
38+
__dirname,
39+
"src",
40+
"generated",
41+
"localesList.json"
42+
);
43+
fs.writeFileSync(outputPath, JSON.stringify(localesList, null, 2));
44+
45+
console.log(`Locales list saved to ${outputPath}`);
46+
} else {
47+
console.error("Locales directory not found.");
48+
}
49+
},
50+
};
51+
}

frontend/vite.config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import process from "node:process";
22
import { defineConfig, searchForWorkspaceRoot } from "vite";
33
import react from "@vitejs/plugin-react";
4+
import GenerateLocalesPlugin from "./vite-plugin-generate-locales.js";
45

56
export default defineConfig({
67
base: "/app",
@@ -21,11 +22,12 @@ export default defineConfig({
2122
components: "/src/components",
2223
utils: "/src/utils",
2324
external: "/src/external",
25+
generated: "/src/generated",
2426
},
2527
},
2628
build: {
2729
outDir: "build",
2830
chunkSizeWarningLimit: 1000,
2931
},
30-
plugins: [react()],
32+
plugins: [react(), GenerateLocalesPlugin()],
3133
});

0 commit comments

Comments
 (0)