XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

SpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 1 Star 0
返回提交历史

XFEstudio/SpaceNinjaServer

feat(webui): language selector (#593)

0a31ff7b
Sainan <sainan@calamity.inc>
提交于

代码差异

4 个文件 +150 -60
Modified src/controllers/custom/getItemListsController.ts +10 -9
@@ -1,5 +1,5 @@
1 1 import { RequestHandler } from "express";
2 import { getEnglishString } from "@/src/services/itemDataService";
2 import { getDict, getString } from "@/src/services/itemDataService";
3 3 import {
4 4 ExportArcanes,
5 5 ExportGear,
@@ -16,7 +16,8 @@ interface ListedItem {
16 16 fusionLimit?: number;
17 17 }
18 18
19 const getItemListsController: RequestHandler = (_req, res) => {
19 const getItemListsController: RequestHandler = (req, res) => {
20 const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
20 21 const weapons = [];
21 22 const miscitems = [];
22 23 for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
@@ -24,12 +25,12 @@ const getItemListsController: RequestHandler = (_req, res) => {
24 25 if (item.totalDamage !== 0) {
25 26 weapons.push({
26 27 uniqueName,
27 name: getEnglishString(item.name)
28 name: getString(item.name, lang)
28 29 });
29 30 } else if (!item.excludeFromCodex) {
30 31 miscitems.push({
31 32 uniqueName: "MiscItems:" + uniqueName,
32 name: getEnglishString(item.name)
33 name: getString(item.name, lang)
33 34 });
34 35 }
35 36 }
@@ -37,13 +38,13 @@ const getItemListsController: RequestHandler = (_req, res) => {
37 38 for (const [uniqueName, item] of Object.entries(ExportResources)) {
38 39 miscitems.push({
39 40 uniqueName: item.productCategory + ":" + uniqueName,
40 name: getEnglishString(item.name)
41 name: getString(item.name, lang)
41 42 });
42 43 }
43 44 for (const [uniqueName, item] of Object.entries(ExportGear)) {
44 45 miscitems.push({
45 46 uniqueName: "Consumables:" + uniqueName,
46 name: getEnglishString(item.name)
47 name: getString(item.name, lang)
47 48 });
48 49 }
49 50
@@ -52,7 +53,7 @@ const getItemListsController: RequestHandler = (_req, res) => {
52 53 for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) {
53 54 mods.push({
54 55 uniqueName,
55 name: getEnglishString(upgrade.name),
56 name: getString(upgrade.name, lang),
56 57 fusionLimit: upgrade.fusionLimit
57 58 });
58 59 if (upgrade.isStarter || upgrade.isFrivolous || upgrade.upgradeEntries) {
@@ -62,7 +63,7 @@ const getItemListsController: RequestHandler = (_req, res) => {
62 63 for (const [uniqueName, arcane] of Object.entries(ExportArcanes)) {
63 64 mods.push({
64 65 uniqueName,
65 name: getEnglishString(arcane.name)
66 name: getString(arcane.name, lang)
66 67 });
67 68 if (arcane.isFrivolous) {
68 69 badItems[uniqueName] = true;
@@ -75,7 +76,7 @@ const getItemListsController: RequestHandler = (_req, res) => {
75 76 .map(([uniqueName, warframe]) => {
76 77 return {
77 78 uniqueName,
78 name: getEnglishString(warframe.name)
79 name: getString(warframe.name, lang)
79 80 };
80 81 }),
81 82 weapons,
Modified src/services/itemDataService.ts +50 -2
@@ -1,7 +1,21 @@
1 1 import { getIndexAfter } from "@/src/helpers/stringHelpers";
2 2 import { logger } from "@/src/utils/logger";
3 3 import {
4 dict_de,
4 5 dict_en,
6 dict_es,
7 dict_fr,
8 dict_it,
9 dict_ja,
10 dict_ko,
11 dict_pl,
12 dict_pt,
13 dict_ru,
14 dict_tc,
15 dict_th,
16 dict_tr,
17 dict_uk,
18 dict_zh,
5 19 ExportRecipes,
6 20 ExportWarframes,
7 21 ExportWeapons,
@@ -70,6 +84,40 @@ export const getSuitByUniqueName = (uniqueName: string): IPowersuit | undefined
70 84 return ExportWarframes[uniqueName];
71 85 };
72 86
73 export const getEnglishString = (key: string): string => {
74 return dict_en[key] ?? key;
87 export const getDict = (lang: string): Record<string, string> => {
88 switch (lang) {
89 case "de":
90 return dict_de;
91 case "es":
92 return dict_es;
93 case "fr":
94 return dict_fr;
95 case "it":
96 return dict_it;
97 case "ja":
98 return dict_ja;
99 case "ko":
100 return dict_ko;
101 case "pl":
102 return dict_pl;
103 case "pt":
104 return dict_pt;
105 case "ru":
106 return dict_ru;
107 case "tc":
108 return dict_tc;
109 case "th":
110 return dict_th;
111 case "tr":
112 return dict_tr;
113 case "uk":
114 return dict_uk;
115 case "zh":
116 return dict_zh;
117 }
118 return dict_en;
119 };
120
121 export const getString = (key: string, dict: Record<string, string>): string => {
122 return dict[key] ?? key;
75 123 };
Modified static/webui/index.html +29 -7
@@ -7,18 +7,40 @@
7 7 <link rel="stylesheet" href="/webui/style.css" />
8 8 </head>
9 9 <body>
10 <nav class="navbar sticky-top bg-body-tertiary">
10 <nav class="navbar navbar-expand sticky-top bg-body-tertiary">
11 11 <div class="container">
12 12 <button class="navbar-toggler d-lg-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebar" aria-controls="sidebar" aria-label="Toggle sidebar">
13 13 <span class="navbar-toggler-icon"></span>
14 14 </button>
15 15 <a class="navbar-brand">OpenWF WebUI</a>
16 <div class="nav-item dropdown">
17 <button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button>
18 <ul class="dropdown-menu dropdown-menu-end">
19 <li><a class="dropdown-item" href="/webui/" onclick="logout();">Logout</a></li>
20 </ul>
21 </div>
16 <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
17 <li class="nav-item dropdown">
18 <button id="active-lang-name" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false"></button>
19 <ul class="dropdown-menu dropdown-menu-end">
20 <li><a class="dropdown-item active" href="#" data-lang="en" onclick="event.preventDefault();setLanguage('en');">English</a></li>
21 <li><a class="dropdown-item" href="#" data-lang="de" onclick="event.preventDefault();setLanguage('de');">Deutsch</a></li>
22 <li><a class="dropdown-item" href="#" data-lang="es" onclick="event.preventDefault();setLanguage('es');">Español</a></li>
23 <li><a class="dropdown-item" href="#" data-lang="fr" onclick="event.preventDefault();setLanguage('fr');">Français</a></li>
24 <li><a class="dropdown-item" href="#" data-lang="it" onclick="event.preventDefault();setLanguage('it');">Italiano</a></li>
25 <li><a class="dropdown-item" href="#" data-lang="ja" onclick="event.preventDefault();setLanguage('ja');">日本語</a></li>
26 <li><a class="dropdown-item" href="#" data-lang="ko" onclick="event.preventDefault();setLanguage('ko');">한국어</a></li>
27 <li><a class="dropdown-item" href="#" data-lang="pl" onclick="event.preventDefault();setLanguage('pl');">Polski</a></li>
28 <li><a class="dropdown-item" href="#" data-lang="pt" onclick="event.preventDefault();setLanguage('pt');">Português</a></li>
29 <li><a class="dropdown-item" href="#" data-lang="ru" onclick="event.preventDefault();setLanguage('ru');">Русский</a></li>
30 <li><a class="dropdown-item" href="#" data-lang="tr" onclick="event.preventDefault();setLanguage('tr');">Türkçe</a></li>
31 <li><a class="dropdown-item" href="#" data-lang="uk" onclick="event.preventDefault();setLanguage('uk');">Українська</a></li>
32 <li><a class="dropdown-item" href="#" data-lang="zh" onclick="event.preventDefault();setLanguage('zh');">简体中文</a></li>
33 <li><a class="dropdown-item" href="#" data-lang="tc" onclick="event.preventDefault();setLanguage('tc');">繁體中文</a></li>
34 <li><a class="dropdown-item" href="#" data-lang="th" onclick="event.preventDefault();setLanguage('th');">แบบไทย</a></li>
35 </ul>
36 </li>
37 <li class="nav-item dropdown">
38 <button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button>
39 <ul class="dropdown-menu dropdown-menu-end">
40 <li><a class="dropdown-item" href="/webui/" onclick="logout();">Logout</a></li>
41 </ul>
42 </li>
43 </ul>
22 44 </div>
23 45 </nav>
24 46 <div class="container pt-3 pb-3" id="main-view">
Modified static/webui/script.js +61 -42
@@ -84,52 +84,71 @@ single.on("route_load", function (event) {
84 84 }
85 85 });
86 86
87 window.itemListPromise = new Promise(resolve => {
88 const req = $.get("/custom/getItemLists");
89 req.done(data => {
90 window.archonCrystalUpgrades = data.archonCrystalUpgrades;
91
92 const itemMap = {
93 // Generics for rivens
94 "/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { name: "Archgun" },
95 "/Lotus/Weapons/Tenno/Melee/PlayerMeleeWeapon": { name: "Melee" },
96 "/Lotus/Weapons/Tenno/Pistol/LotusPistol": { name: "Pistol" },
97 "/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" },
98 "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" },
99 // Modular weapons
100 "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": { name: "Kitgun" },
101 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": { name: "Kitgun" },
102 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: "Kitgun" },
103 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: "Kitgun" },
104 "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" },
105 // Missing in data sources
106 "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" }
107 };
108 for (const [type, items] of Object.entries(data)) {
109 if (type == "archonCrystalUpgrades") {
110 Object.entries(items).forEach(([uniqueName, name]) => {
111 const option = document.createElement("option");
112 option.setAttribute("data-key", uniqueName);
113 option.value = name;
114 document.getElementById("datalist-" + type).appendChild(option);
115 });
116 } else if (type != "badItems") {
117 items.forEach(item => {
118 if (item.uniqueName in data.badItems) {
119 item.name += " (Imposter)";
120 } else if (item.uniqueName.substr(0, 18) != "/Lotus/Types/Game/") {
87 function setActiveLanguage(lang) {
88 window.lang = lang;
89 const lang_name = document.querySelector("[data-lang=" + lang + "]").textContent;
90 document.getElementById("active-lang-name").textContent = lang_name;
91 document.querySelector("[data-lang].active").classList.remove("active");
92 document.querySelector("[data-lang=" + lang + "]").classList.add("active");
93 }
94 setActiveLanguage(localStorage.getItem("lang") ?? "en");
95
96 function setLanguage(lang) {
97 setActiveLanguage(lang);
98 localStorage.setItem("lang", lang);
99 fetchItemList();
100 updateInventory();
101 }
102
103 function fetchItemList() {
104 window.itemListPromise = new Promise(resolve => {
105 const req = $.get("/custom/getItemLists?lang=" + window.lang);
106 req.done(data => {
107 window.archonCrystalUpgrades = data.archonCrystalUpgrades;
108
109 const itemMap = {
110 // Generics for rivens
111 "/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { name: "Archgun" },
112 "/Lotus/Weapons/Tenno/Melee/PlayerMeleeWeapon": { name: "Melee" },
113 "/Lotus/Weapons/Tenno/Pistol/LotusPistol": { name: "Pistol" },
114 "/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" },
115 "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" },
116 // Modular weapons
117 "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": { name: "Kitgun" },
118 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": { name: "Kitgun" },
119 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: "Kitgun" },
120 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: "Kitgun" },
121 "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" },
122 // Missing in data sources
123 "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" }
124 };
125 for (const [type, items] of Object.entries(data)) {
126 if (type == "archonCrystalUpgrades") {
127 Object.entries(items).forEach(([uniqueName, name]) => {
121 128 const option = document.createElement("option");
122 option.setAttribute("data-key", item.uniqueName);
123 option.value = item.name;
129 option.setAttribute("data-key", uniqueName);
130 option.value = name;
124 131 document.getElementById("datalist-" + type).appendChild(option);
125 }
126 itemMap[item.uniqueName] = { ...item, type };
127 });
132 });
133 } else if (type != "badItems") {
134 items.forEach(item => {
135 if (item.uniqueName in data.badItems) {
136 item.name += " (Imposter)";
137 } else if (item.uniqueName.substr(0, 18) != "/Lotus/Types/Game/") {
138 const option = document.createElement("option");
139 option.setAttribute("data-key", item.uniqueName);
140 option.value = item.name;
141 document.getElementById("datalist-" + type).appendChild(option);
142 }
143 itemMap[item.uniqueName] = { ...item, type };
144 });
145 }
128 146 }
129 }
130 resolve(itemMap);
147 resolve(itemMap);
148 });
131 149 });
132 });
150 }
151 fetchItemList();
133 152
134 153 function updateInventory() {
135 154 const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");