feat: add language selection to system settings

Add a language selector to the System Settings page supporting English,
Japanese, and Chinese. The i18n dictionary already contained zh
translations but lacked the UI to switch languages.

Changes:
- html/i18n.js: add sys_language key to en/ja/zh
- html/system.html: add language selector dropdown
- html/system.js: add changeLang() and selector initialization
- tools/httpd_sim.c: fix cookie parsing for multi-cookie headers;
  fix Set-Cookie response (missing Path=/, missing \r\n\r\n);
  add SO_REUSEADDR for faster port reuse
This commit is contained in:
Your Name
2026-07-15 01:35:10 +09:00
parent e96780fadb
commit e765d17c2b
4 changed files with 32 additions and 3 deletions
+3
View File
@@ -140,6 +140,7 @@ var LANG = {
sys_ip: 'IP address:', sys_ip: 'IP address:',
sys_netmask: 'Netmask:', sys_netmask: 'Netmask:',
sys_gateway: 'Gateway:', sys_gateway: 'Gateway:',
sys_language: 'Language:',
sys_ip_note: 'When updating the above settings, remember to point your browser to the new IP afterwards:', sys_ip_note: 'When updating the above settings, remember to point your browser to the new IP afterwards:',
sys_update: 'Update Settings', sys_update: 'Update Settings',
sys_save_label: 'Save all current settings to Flash:', sys_save_label: 'Save all current settings to Flash:',
@@ -318,6 +319,7 @@ var LANG = {
sys_ip: 'IP アドレス:', sys_ip: 'IP アドレス:',
sys_netmask: 'ネットマスク:', sys_netmask: 'ネットマスク:',
sys_gateway: 'ゲートウェイ:', sys_gateway: 'ゲートウェイ:',
sys_language: '言語:',
sys_ip_note: '上記設定を変更した場合は、ブラウザで新しい IP にアクセスしてください:', sys_ip_note: '上記設定を変更した場合は、ブラウザで新しい IP にアクセスしてください:',
sys_update: '設定更新', sys_update: '設定更新',
sys_save_label: '現在の設定をフラッシュに保存:', sys_save_label: '現在の設定をフラッシュに保存:',
@@ -496,6 +498,7 @@ var LANG = {
sys_ip: 'IP 地址:', sys_ip: 'IP 地址:',
sys_netmask: '子网掩码:', sys_netmask: '子网掩码:',
sys_gateway: '网关:', sys_gateway: '网关:',
sys_language: '语言:',
sys_ip_note: '更新上述设置后,请使用新的 IP 地址重新访问管理界面:', sys_ip_note: '更新上述设置后,请使用新的 IP 地址重新访问管理界面:',
sys_update: '更新设置', sys_update: '更新设置',
sys_save_label: '将当前全部设置保存到 Flash:', sys_save_label: '将当前全部设置保存到 Flash:',
+10
View File
@@ -37,6 +37,16 @@
<div class="lcol"> <label for="gw" data-i18n="sys_gateway">Gateway:</label></div> <div class="lcol"> <label for="gw" data-i18n="sys_gateway">Gateway:</label></div>
<div class="rcol"><input id="gw" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div> <div class="rcol"><input id="gw" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
</div> </div>
<div class="row">
<div class="lcol"> <label data-i18n="sys_language">Language:</label></div>
<div class="rcol">
<select id="lang-select" onchange="changeLang()">
<option value="en">English</option>
<option value="ja">日本語</option>
<option value="zh">中文</option>
</select>
</div>
</div>
<br/> <br/>
<span data-i18n="sys_ip_note">When updating the above settings, remember to point your browser to the new IP afterwards:</span><br/> <span data-i18n="sys_ip_note">When updating the above settings, remember to point your browser to the new IP afterwards:</span><br/>
<input style="width:40%;" class="action" id="ip_sub" onclick="ipSub();" type="button" data-i18n="sys_update" value="Update Settings"><br/> <input style="width:40%;" class="action" id="ip_sub" onclick="ipSub();" type="button" data-i18n="sys_update" value="Update Settings"><br/>
+7
View File
@@ -2,6 +2,11 @@ var systemInterval = Number();
var isSaving = false; var isSaving = false;
const ips = ["ip", "netmask", "gw"]; const ips = ["ip", "netmask", "gw"];
function changeLang() {
var lang = document.getElementById('lang-select').value;
setLang(lang);
}
function checkIp(ip) { function checkIp(ip) {
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/; const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
if (!ipv4.test(ip)) {alert(t('sys_invalid_ip') + ip); return false }; if (!ipv4.test(ip)) {alert(t('sys_invalid_ip') + ip); return false };
@@ -146,5 +151,7 @@ function resetSwitch() {
} }
window.addEventListener("load", function() { window.addEventListener("load", function() {
var langSel = document.getElementById('lang-select');
if (langSel) langSel.value = rtlLang;
systemInterval = setInterval(fetchIP, 1000); systemInterval = setInterval(fetchIP, 1000);
}); });
+12 -3
View File
@@ -500,6 +500,10 @@ struct Server serverConstructor(int port, void (*launch)(struct Server *server))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
int reuse = 1;
if (setsockopt(server.socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
perror("setsockopt(SO_REUSEADDR) failed");
if (bind(server.socket, (struct sockaddr*)&server.address, sizeof(server.address)) < 0) { if (bind(server.socket, (struct sockaddr*)&server.address, sizeof(server.address)) < 0) {
perror("Failed to bind socket...\n"); perror("Failed to bind socket...\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -554,8 +558,12 @@ char *scan_header(char *p)
break; break;
if (is_word(p, "\nContent-Type:")) if (is_word(p, "\nContent-Type:"))
content_type = p + 15; content_type = p + 15;
else if (is_word(p, "\nCookie:")) else if (is_word(p, "\nCookie:")) {
session = p + 17; session = p + 9;
while (*session == ' ') session++;
char *s = strstr(session, "session=");
if (s) session = s + 8;
}
} }
if (content_type && is_word(content_type, "multipart/form-data; boundary")) { if (content_type && is_word(content_type, "multipart/form-data; boundary")) {
printf("Found multiplart\n"); printf("Found multiplart\n");
@@ -799,7 +807,8 @@ void launch(struct Server *server)
printf("Password accepted!\n"); printf("Password accepted!\n");
response = "HTTP/1.1 302 Found\r\n" response = "HTTP/1.1 302 Found\r\n"
"Location: index.html\r\n" "Location: index.html\r\n"
"Set-Cookie: session=" SESSION_ID "; SameSite=Strict\r\n"; "Set-Cookie: session=" SESSION_ID "; Path=/; SameSite=Strict\r\n"
"\r\n";
} else { } else {
response = "HTTP/1.1 302 Found\r\n" response = "HTTP/1.1 302 Found\r\n"
"Location: login.html\r\n\r\n"; "Location: login.html\r\n\r\n";