Skip to content

Commit 9cb656c

Browse files
committed
feat(interface): add lang selector in settings page
1 parent bfd5649 commit 9cb656c

File tree

9 files changed

+71
-12
lines changed

9 files changed

+71
-12
lines changed

i18n/english.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ const ui = {
103103
vulnerabilities: "vulnerabilities (CVE)",
104104
licenses: "licenses conformance (SPDX)",
105105
dark: "dark",
106-
light: "light"
106+
light: "light",
107+
fr: "french",
108+
en: "english"
107109
},
108110
title: {
109111
maintainers: "maintainers",
@@ -184,6 +186,7 @@ const ui = {
184186
save: "save",
185187
defaultPannel: "Default Package Menu",
186188
themePannel: "Interface theme",
189+
langPannel: "Interface language",
187190
warnings: "SAST Warnings to ignore",
188191
flags: "Flags (emojis) to ignore",
189192
network: "Network",

i18n/french.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ const cli = {
8181
startHttp: {
8282
invalidScannerVersion: tS`le fichier d'analyse correspond à la version '${0}' du scanner et ne satisfait pas la range '${1}' attendu par la CLI`,
8383
regenerate: "veuillez re-générer un nouveau fichier d'analyse JSON en utilisant votre CLI"
84-
},
84+
}
8585
};
8686

8787
const ui = {
@@ -103,7 +103,9 @@ const ui = {
103103
vulnerabilities: "vulnérabilités",
104104
licenses: "conformité des licences (SPDX)",
105105
dark: "sombre",
106-
light: "clair"
106+
light: "clair",
107+
fr: "français",
108+
en: "anglais"
107109
},
108110
title: {
109111
maintainers: "mainteneurs",
@@ -184,6 +186,7 @@ const ui = {
184186
save: "sauvegarder",
185187
defaultPannel: "Panneau par défaut",
186188
themePannel: "Thème de l'interface",
189+
langPannel: "Langue de l'interface",
187190
warnings: "Avertissements à ignorer",
188191
flags: "Drapeau (emojis) à ignorer",
189192
network: "Réseau",

public/components/views/settings/settings.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export class Settings {
3939
/** @type {HTMLInputElement} */
4040
showFriendlyDependenciesCheckbox: document.querySelector("#show-friendly"),
4141
themeSelector: document.querySelector("#theme_selector"),
42+
langSelector: document.querySelector("#lang_selector"),
4243
disableExternalRequestsCheckbox: document.querySelector("#disable-external")
4344
};
4445

@@ -52,6 +53,7 @@ export class Settings {
5253
...this.dom.flagsCheckbox,
5354
this.dom.showFriendlyDependenciesCheckbox,
5455
this.dom.themeSelector,
56+
this.dom.langSelector,
5557
this.dom.disableExternalRequestsCheckbox
5658
];
5759
for (const formField of formFields) {
@@ -203,7 +205,8 @@ export class Settings {
203205
ignore: { flags: new Set(), warnings: new Set() },
204206
showFriendlyDependencies: this.dom.showFriendlyDependenciesCheckbox.checked,
205207
theme: this.dom.themeSelector.value,
206-
disableExternalRequests: this.dom.disableExternalRequestsCheckbox.checked
208+
disableExternalRequests: this.dom.disableExternalRequestsCheckbox.checked,
209+
lang: this.dom.langSelector.value
207210
};
208211

209212
for (const checkbox of this.dom.warningsCheckbox) {
@@ -228,15 +231,21 @@ export class Settings {
228231
"content-type": "application/json"
229232
}
230233
});
231-
this.config = newConfig;
234+
this.config = { ...newConfig, lang: this.config.lang };
232235
this.saveButton.classList.add("disabled");
233236

234-
window.dispatchEvent(new CustomEvent("settings-saved", { detail: this.config }));
237+
window.dispatchEvent(new CustomEvent("settings-saved", {
238+
detail: {
239+
...this.config,
240+
lang: newConfig.lang
241+
}
242+
}));
235243
}
236244

237245
updateSettings() {
238246
this.dom.defaultPackageMenu.value = this.config.defaultPackageMenu;
239247
this.dom.themeSelector.value = this.config.theme;
248+
this.dom.langSelector.value = this.config.lang;
240249

241250
const warnings = new Set(this.config.ignore.warnings);
242251
const flags = new Set(this.config.ignore.flags);

public/main.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,21 @@ async function updateShowInfoMenu(params) {
209209
function onSettingsSaved(defaultConfig = null) {
210210
async function updateSettings(config) {
211211
console.log("[INFO] Settings saved:", config);
212+
if (window.settings.config.lang !== config.lang) {
213+
window.location.reload();
214+
}
215+
212216
const warningsToIgnore = new Set(config.ignore.warnings);
213217
const flagsToIgnore = new Set(config.ignore.flags);
214218
const theme = config.theme;
219+
const lang = config.lang;
215220
secureDataSet.warningsToIgnore = warningsToIgnore;
216221
secureDataSet.flagsToIgnore = flagsToIgnore;
217222
secureDataSet.theme = theme;
218223
window.settings.config.ignore.warnings = warningsToIgnore;
219224
window.settings.config.ignore.flags = flagsToIgnore;
220225
window.settings.config.theme = theme;
226+
window.settings.config.lang = lang;
221227
window.settings.config.disableExternalRequests = config.disableExternalRequests;
222228

223229
if (theme === "dark") {

src/commands/lang.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Import Third-party Dependencies
22
import * as i18n from "@nodesecure/i18n";
3+
import { appCache } from "@nodesecure/cache";
34
import { select } from "@topcli/prompts";
45
import kleur from "kleur";
56

@@ -15,6 +16,20 @@ export async function set() {
1516
await i18n.setLocalLang(selectedLang);
1617
await i18n.getLocalLang();
1718

19+
try {
20+
const config = await appCache.getConfig();
21+
22+
if (config) {
23+
await appCache.updateConfig({
24+
...config,
25+
lang: selectedLang
26+
});
27+
}
28+
}
29+
catch {
30+
// Config does not exist, do nothing
31+
}
32+
1833
console.log(
1934
kleur.white().bold(`\n ${i18n.getTokenSync("cli.commands.lang.new_selection", kleur.yellow().bold(selectedLang))}`)
2035
);

views/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ <h1><i class="icon-cog"></i>[[=z.token('settings.general.title')]]</h1>
121121
<option value="dark">[[=z.token('package_info.navigation.dark')]]</option>
122122
<option value="light">[[=z.token('package_info.navigation.light')]]</option>
123123
</select>
124+
<label for="lang_selector" class="mt-10">[[=z.token('settings.general.langPannel')]]:</label>
125+
<select name="langSelector" id="lang_selector">
126+
<option value="french">[[=z.token('package_info.navigation.fr')]]</option>
127+
<option value="english">[[=z.token('package_info.navigation.en')]]</option>
128+
</select>
124129
<p class="settings-line-title">[[=z.token('settings.general.network')]]:</p>
125130
<div>
126131
<input type="checkbox" id="show-friendly" name="show-friendly" />

workspaces/server/src/config.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Import Third-party Dependencies
22
import { warnings } from "@nodesecure/js-x-ray";
33
import { appCache } from "@nodesecure/cache";
4+
import * as i18n from "@nodesecure/i18n";
45

56
// Import Internal Dependencies
67
import { logger } from "./logger.js";
@@ -16,6 +17,7 @@ const kDefaultConfig = {
1617
};
1718

1819
export async function get() {
20+
const lang = await i18n.getLocalLang();
1921
try {
2022
const config = await appCache.getConfig();
2123

@@ -26,7 +28,8 @@ export async function get() {
2628
warnings
2729
} = {},
2830
theme,
29-
disableExternalRequests = false
31+
disableExternalRequests = false,
32+
lang
3033
} = config;
3134
logger.info(
3235
// eslint-disable-next-line @stylistic/max-len
@@ -40,7 +43,8 @@ export async function get() {
4043
warnings
4144
},
4245
theme,
43-
disableExternalRequests
46+
disableExternalRequests,
47+
lang
4448
};
4549
}
4650
catch (err) {
@@ -50,7 +54,7 @@ export async function get() {
5054

5155
logger.info(`[config|get](fallback to default: ${JSON.stringify(kDefaultConfig)})`);
5256

53-
return kDefaultConfig;
57+
return { ...kDefaultConfig, lang };
5458
}
5559
}
5660

@@ -66,4 +70,11 @@ export async function set(newValue) {
6670

6771
throw err;
6872
}
73+
74+
const i18nLocalLang = await i18n.getLocalLang();
75+
if (i18nLocalLang !== newValue.lang) {
76+
logger.info(`[config|set](updating i18n lang to: ${newValue.lang})`);
77+
await i18n.setLocalLang(newValue.lang);
78+
await i18n.getLanguages();
79+
}
6980
}

workspaces/server/test/config.test.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import assert from "node:assert";
66
import cacache from "cacache";
77
import { warnings } from "@nodesecure/js-x-ray";
88
import { CACHE_PATH } from "@nodesecure/cache";
9+
import * as i18n from "@nodesecure/i18n";
910

1011
// Import Internal Dependencies
1112
import { get, set } from "../src/config.js";
@@ -17,6 +18,7 @@ describe("config", () => {
1718
let actualConfig;
1819

1920
before(async() => {
21+
await i18n.getLanguages();
2022
actualConfig = await get();
2123
});
2224

@@ -33,7 +35,8 @@ describe("config", () => {
3335
ignore: { flags: [], warnings: Object.entries(warnings)
3436
.filter(([_, { experimental }]) => experimental)
3537
.map(([warning]) => warning) },
36-
disableExternalRequests: false
38+
disableExternalRequests: false,
39+
lang: await i18n.getLocalLang()
3740
});
3841
});
3942

@@ -44,6 +47,7 @@ describe("config", () => {
4447
flags: ["foo"],
4548
warnings: ["bar"]
4649
},
50+
lang: "english",
4751
theme: "galaxy",
4852
disableExternalRequests: true
4953
};
@@ -60,6 +64,7 @@ describe("config", () => {
6064
flags: ["foz"],
6165
warnings: ["baz"]
6266
},
67+
lang: "english",
6368
theme: "galactic",
6469
disableExternalRequests: true
6570
};

workspaces/server/test/httpServer.test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ describe("httpServer", { concurrency: 1 }, () => {
223223
flags: ["foo"],
224224
warnings: ["bar"]
225225
},
226+
lang: "english",
226227
theme: "galaxy",
227228
disableExternalRequests: true
228229
};
@@ -240,19 +241,20 @@ describe("httpServer", { concurrency: 1 }, () => {
240241
});
241242

242243
test("PUT '/config' should update the config", async() => {
244+
const lang = await i18n.getLocalLang();
243245
const { data: actualConfig } = await get(new URL("/config", kHttpURL));
244246
// FIXME: use @mynusift/httpie instead of fetch. Atm it throws with put().
245247
// https://github.com/nodejs/undici/issues/583
246248
const { status } = await fetch(new URL("/config", kHttpURL), {
247249
method: "PUT",
248-
body: JSON.stringify({ fooz: "baz" }),
250+
body: JSON.stringify({ fooz: "baz", lang }),
249251
headers: { "Content-Type": "application/json" }
250252
});
251253

252254
assert.equal(status, 204);
253255

254256
const inCache = await cacache.get(CACHE_PATH, kConfigKey);
255-
assert.deepEqual(JSON.parse(inCache.data.toString()), { fooz: "baz" });
257+
assert.deepEqual(JSON.parse(inCache.data.toString()), { fooz: "baz", lang });
256258

257259
await fetch(new URL("/config", kHttpURL), {
258260
method: "PUT",

0 commit comments

Comments
 (0)