diff --git a/server/src/main.rs b/server/src/main.rs index 072bbe3..ff13132 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -31,11 +31,29 @@ async fn service_http(mut request:hyper::Request, args:Ht // Serve cached files and upgrade websocket connections. // { - use hyper::{Response, body::Bytes, header::{CONTENT_TYPE, CACHE_CONTROL}}; + use hyper::{Response, body::Bytes, header::{CONTENT_TYPE, CACHE_CONTROL, ACCEPT_LANGUAGE}}; use http_body_util::Full; println!("Serving: {}", request.uri().path()); + let mut language_code = 0; + if let Some(languages) = request.headers().get(ACCEPT_LANGUAGE) { + if let Ok(languages) = languages.to_str() { + for decl in languages.split(",") { + let decl = decl.trim().to_lowercase(); + if decl.len() >= 2 { + let code = &decl[..2]; + + language_code = match code { + "en" => 0, + "ja" => 1, + _ => { continue; } + }; break; + } + } + } + } + if hyper_tungstenite::is_upgrade_request(&request) { if let Ok((response, websocket)) = hyper_tungstenite::upgrade(&mut request, None) { tokio::task::spawn(async move { @@ -54,10 +72,23 @@ async fn service_http(mut request:hyper::Request, args:Ht } } else { match args.cache.fetch(request.uri().path()) { - Some(data) => Ok(Response::builder() - .header(CONTENT_TYPE, &data.mime) - .header(CACHE_CONTROL, "no-cache") - .body(Full::new(Bytes::from(data.data))).unwrap()), + Some(data) => { + let mut output = data.data; + match request.uri().path() { + "/.js" => { + output = [ + format!("let CONFIG_LANGUAGE = {};", language_code).as_bytes().to_vec(), + output, + ].concat(); + } + _ => { } + } + + Ok(Response::builder() + .header(CONTENT_TYPE, &data.mime) + .header(CACHE_CONTROL, "no-cache") + .body(Full::new(Bytes::from(output))).unwrap()) + } None => Ok(Response::builder() .header(CONTENT_TYPE, "text/html") .header(CACHE_CONTROL, "no-cache") diff --git a/www/js/interface.js b/www/js/interface.js index 4a8f6a3..1565d09 100644 --- a/www/js/interface.js +++ b/www/js/interface.js @@ -559,7 +559,7 @@ const INTERFACE = { let piece_id = GAME_DATA.board.tiles[INTERFACE_DATA.hover.tile].piece; if(piece_id !== null) { let piece = GAME_DATA.board.pieces[piece_id]; - text += " " + GAME.Const.Piece[piece.piece].name; + text += " " + LANG(GAME.Const.Piece[piece.piece].name); if(piece.promoted) { text += "+"; } } } else { @@ -591,13 +591,13 @@ const INTERFACE = { if(GAME_DATA.state.code == GAME.Const.State.Resign) { ctx.fillStyle = INTERFACE.Color.HintCheck; - message = "Resign"; + message = LANG("resign"); } else if(GAME_DATA.state.check != 0) { ctx.fillStyle = INTERFACE.Color.HintCheck; if(GAME_DATA.state.checkmate) { - message = "Checkmate"; + message = LANG("checkmate"); } else { - message = "Check"; + message = LANG("check"); } } diff --git a/www/js/language.js b/www/js/language.js index 5a17cbf..1e4fffc 100644 --- a/www/js/language.js +++ b/www/js/language.js @@ -1,5 +1,8 @@ const LANGUAGE = { }; +const LANG_ENGLISH = 0; +const LANG_JAPANESE = 1; + LANGUAGE.Term = class { constructor(en, jp) { this.data = [ en, jp ]; @@ -7,15 +10,74 @@ LANGUAGE.Term = class { }; LANGUAGE.Terms = { - Dawn: new LANGUAGE.Term( "Dawn", "暁" ), - Dusk: new LANGUAGE.Term( "Dusk", "黄昏" ), + dawn: new LANGUAGE.Term( "Dawn", "暁" ), + dusk: new LANGUAGE.Term( "Dusk", "黄昏" ), - Handle: new LANGUAGE.Term( "Handle", "" ), - Secret: new LANGUAGE.Term( "Secret", "" ), + handle: new LANGUAGE.Term( "Handle", "ハンドル" ), + secret: new LANGUAGE.Term( "Secret", "秘文" ), + invitation: new LANGUAGE.Term( "Invitation", "招待" ), - Browse: new LANGUAGE.Term( "Browse", "" ), - Resume: new LANGUAGE.Term( "Resume", "" ), + reconnect: new LANGUAGE.Term( "Reconnect", "再接続" ), + register: new LANGUAGE.Term( "Register", "登録" ), + login: new LANGUAGE.Term( "Log In", "ログイン" ), - Check: new LANGUAGE.Term( "Check", "王手" ), - Checkmate: new LANGUAGE.Term( "Checkmate", "詰み" ), + challenge: new LANGUAGE.Term( "Challenge", "挑戦" ), + + browse: new LANGUAGE.Term( "Browse", "対局列挙" ), + resume: new LANGUAGE.Term( "Resume", "続く" ), + live: new LANGUAGE.Term( "Live", "今頃" ), + history: new LANGUAGE.Term( "History", "再生" ), + practice: new LANGUAGE.Term( "Practice", "練習" ), + guide: new LANGUAGE.Term( "Guide", "ガイド" ), + about: new LANGUAGE.Term( "About", "概要" ), + + turn: new LANGUAGE.Term( "Turn", "手番数" ), + viewers: new LANGUAGE.Term( "Viewers", "観戦者" ), + + review: new LANGUAGE.Term( "Review", "再生" ), + view: new LANGUAGE.Term( "View", "観戦" ), + + refresh: new LANGUAGE.Term( "Refresh", "改まる" ), + + rotate: new LANGUAGE.Term( "Rotate", "回る" ), + mirror: new LANGUAGE.Term( "Mirror", "鏡像" ), + back: new LANGUAGE.Term( "Back", "戻る" ), + resign: new LANGUAGE.Term( "Resign", "投了" ), + confirm: new LANGUAGE.Term( "Confirm", "確認" ), + reset: new LANGUAGE.Term( "Reset", "リセット" ), + auto: new LANGUAGE.Term( "Auto", "自動" ), + + users: new LANGUAGE.Term( "Users", "ユーザー" ), + requests: new LANGUAGE.Term( "Requests", "挑戦状" ), + + accept: new LANGUAGE.Term( "Accept", "受け入れ" ), + decline: new LANGUAGE.Term( "Decline", "断る" ), + + check: new LANGUAGE.Term( "Check", "王手" ), + checkmate: new LANGUAGE.Term( "Checkmate", "詰み" ), + + Militia: new LANGUAGE.Term( "Militia", "士" ), + Lance: new LANGUAGE.Term( "Lance", "槍" ), + Knight: new LANGUAGE.Term( "Knight", "騎" ), + Tower: new LANGUAGE.Term( "Tower", "塔" ), + Castle: new LANGUAGE.Term( "Castle", "城" ), + Dragon: new LANGUAGE.Term( "Dragon", "竜" ), + Behemoth: new LANGUAGE.Term( "Behemoth", "獣" ), + Omen: new LANGUAGE.Term( "Omen", "兆" ), + + + //: new LANGUAGE.Term( "", "" ), }; + +function LANG(term) +{ + return LANGUAGE.Terms[term].data[CONFIG_LANGUAGE]; +} + +function LANG_PAGEOF(min, max, total) +{ + switch(CONFIG_LANGUAGE) { + case LANG_ENGLISH: return min + " - " + max + " of " + total; + case LANG_JAPANESE: return min + " - " + max + " 件中 " + total + " 件"; + } +} diff --git a/www/js/scene.js b/www/js/scene.js index 19c289e..07c41c9 100644 --- a/www/js/scene.js +++ b/www/js/scene.js @@ -10,7 +10,7 @@ const SCENES = { Offline:{ load() { UI.nav([ - UI.button("Reconnect", () => { RECONNECT(); }), + UI.button(LANG("reconnect"), () => { RECONNECT(); }), ], []); return true; }, @@ -28,12 +28,12 @@ const SCENES = { let container = document.createElement("section"); let form = document.createElement("div"); form.appendChild(UI.table(null, [ - [ UI.label("Handle", "handle"), UI.textbox("handle", "") ], - [ UI.label("Secret", "secret"), UI.password("secret") ], - [ UI.label("Invite Code", "code"), UI.password("code") ], + [ UI.label(LANG("handle"), "handle"), UI.textbox("handle", "") ], + [ UI.label(LANG("secret"), "secret"), UI.password("secret") ], + [ UI.label(LANG("invitation"), "code"), UI.password("code") ], ])); - let button = UI.button("Register", (event) => { + let button = UI.button(LANG("register"), (event) => { let handle = document.getElementById("handle"); let secret = document.getElementById("secret"); let code = document.getElementById("code"); @@ -123,11 +123,11 @@ const SCENES = { let container = document.createElement("section"); let form = document.createElement("div"); form.appendChild(UI.table(null, [ - [ UI.label("Handle", "handle"), UI.textbox("handle", "") ], - [ UI.label("Secret", "secret"), UI.password("secret") ], + [ UI.label(LANG("handle"), "handle"), UI.textbox("handle", "") ], + [ UI.label(LANG("secret"), "secret"), UI.password("secret") ], ])); - let button = UI.button("Login", (event) => { + let button = UI.button(LANG("login"), (event) => { let handle = document.getElementById("handle"); let secret = document.getElementById("secret"); @@ -198,10 +198,10 @@ const SCENES = { UI.mainnav( [ ], [ - UI.div([UI.text("0 - 0 of 0")]), + UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]), UI.button("◀", null), UI.button("▶", null), - UI.button("Refresh", null), + UI.button(LANG("refresh"), null), ], { auth:true, session:true } ); @@ -249,10 +249,10 @@ const SCENES = { UI.mainnav( [ ], [ - UI.div([UI.text("0 - 0 of 0")]), + UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]), UI.button("◀", null), UI.button("▶", null), - UI.button("Refresh", null), + UI.button(LANG("refresh"), null), ], { auth:true, session:true } ); @@ -354,10 +354,10 @@ const SCENES = { UI.mainnav( [ ], [ - UI.div([UI.text("0 - 0 of 0")]), + UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]), UI.button("◀", null), UI.button("▶", null), - UI.button("Refresh", null), + UI.button(LANG("refresh"), null), ], { auth:true, session:true } ); @@ -403,10 +403,10 @@ const SCENES = { UI.mainnav( [ ], [ - UI.div([UI.text("0 - 0 of 0")]), + UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]), UI.button("◀", null), UI.button("▶", null), - UI.button("Refresh", null), + UI.button(LANG("refresh"), null), ], { auth:true } ); @@ -503,15 +503,15 @@ const SCENES = { load(data) { let buttons_bottom = [ ]; if(data.mode != INTERFACE.Mode.Review) { - let button_resign = UI.button("Resign", () => { INTERFACE.resign(); }); + let button_resign = UI.button(LANG("resign"), () => { INTERFACE.resign(); }); button_resign.setAttribute("id", "button-resign"); buttons_bottom.push(button_resign); } - buttons_bottom.push(UI.button("Back", () => { LOAD(SCENES.Browse) })); + buttons_bottom.push(UI.button(LANG("back"), () => { LOAD(SCENES.Browse) })); UI.nav([ - UI.button("Rotate", () => { INTERFACE.rotate(); }), - UI.button("Mirror", () => { INTERFACE.mirror(); }), + UI.button(LANG("rotate"), () => { INTERFACE.rotate(); }), + UI.button(LANG("mirror"), () => { INTERFACE.mirror(); }), ], buttons_bottom); if(data.mode == INTERFACE.Mode.Review) { @@ -566,13 +566,13 @@ const SCENES = { GamePractice:{ load(data) { let buttons_bottom = [ ]; - buttons_bottom.push(UI.button("Reset", () => { INTERFACE.reset(); })); - buttons_bottom.push(UI.button("Back", () => { LOAD(SCENES.Browse) })); + buttons_bottom.push(UI.button(LANG("reset"), () => { INTERFACE.reset(); })); + buttons_bottom.push(UI.button(LANG("back"), () => { LOAD(SCENES.Browse) })); UI.nav([ - UI.button("Rotate", () => { INTERFACE.rotate(); }), - UI.button("Mirror", () => { INTERFACE.mirror(); }), - UI.button("Auto", () => { INTERFACE.auto(); }), + UI.button(LANG("rotate"), () => { INTERFACE.rotate(); }), + UI.button(LANG("mirror"), () => { INTERFACE.mirror(); }), + UI.button(LANG("auto"), () => { INTERFACE.auto(); }), ], buttons_bottom); let canvas = document.createElement("canvas"); @@ -589,7 +589,7 @@ const SCENES = { }, }, - Await:{ + /*Await:{ load() { if(sessionStorage.getItem("auth") === null) return false; UI.mainmenu("await"); @@ -629,7 +629,7 @@ const SCENES = { disconnect() { LOAD(SCENES.Offline); }, - }, + },*/ Challenge:{ load() { @@ -643,14 +643,14 @@ const SCENES = { UI.mainmenu("browse"); UI.mainnav( [ - UI.button("Users", () => { LOAD(SCENES.Challenge); }), - UI.button("Requests", () => { LOAD(SCENES.ChallengeList); }), + UI.button(LANG("users"), () => { LOAD(SCENES.Challenge); }), + UI.button(LANG("requests"), () => { LOAD(SCENES.ChallengeList); }), ], [ - UI.div([UI.text("0 - 0 of 0")]), + UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]), UI.button("◀", null), UI.button("▶", null), - UI.button("Refresh", null), + UI.button(LANG("refresh"), null), ] ); @@ -685,7 +685,7 @@ const SCENES = { MESSAGE_CHALLENGE(this.handle); }; callback = callback.bind({handle: data.users[r].handle}); - buttons.push(UI.button("Challenge", callback)); + buttons.push(UI.button(LANG("challenge"), callback)); rows.push([ UI.text(data.users[r].handle), @@ -694,7 +694,7 @@ const SCENES = { } let tbody = UI.table_content( - [ "User", "" ], + null, rows, ); @@ -720,14 +720,14 @@ const SCENES = { UI.mainmenu("browse"); UI.mainnav( [ - UI.button("Users", () => { LOAD(SCENES.Challenge); }), - UI.button("Requests", () => { LOAD(SCENES.ChallengeList); }), + UI.button(LANG("users"), () => { LOAD(SCENES.Challenge); }), + UI.button(LANG("requests"), () => { LOAD(SCENES.ChallengeList); }), ], [ - UI.div([UI.text("0 - 0 of 0")]), + UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]), UI.button("◀", null), UI.button("▶", null), - UI.button("Refresh", null), + UI.button(LANG("refresh"), null), ] ); @@ -762,13 +762,13 @@ const SCENES = { MESSAGE_CHALLENGE_ANSWER(this.handle, true); }; callback_accept = callback_accept.bind({handle: data.challenges[r].handle}); - buttons.push(UI.button("Accept", callback_accept)); + buttons.push(UI.button(LANG("accept"), callback_accept)); - let callback_reject = function() { + let callback_decline = function() { MESSAGE_CHALLENGE_ANSWER(this.handle, false); }; - callback_reject = callback_reject.bind({handle: data.challenges[r].handle}); - buttons.push(UI.button("Reject", callback_reject)); + callback_decline = callback_decline.bind({handle: data.challenges[r].handle}); + buttons.push(UI.button(LANG("decline"), callback_decline)); rows.push([ UI.text(data.challenges[r].handle), @@ -777,7 +777,7 @@ const SCENES = { } let tbody = UI.table_content( - [ "User", "" ], + null, rows, ); diff --git a/www/js/ui.js b/www/js/ui.js index 34bf748..eb200b9 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -105,13 +105,13 @@ const UI = { if(sessionStorage.getItem("auth") === null) { if(features.auth === true) { - left.appendChild(UI.button("Register", () => { LOAD(SCENES.Register); })); - left.appendChild(UI.button("Log In", () => { LOAD(SCENES.Authenticate); })); + left.appendChild(UI.button(LANG("register"), () => { LOAD(SCENES.Register); })); + left.appendChild(UI.button(LANG("login"), () => { LOAD(SCENES.Authenticate); })); } } else { if(features.session === true) { //left.appendChild(UI.button("Await", () => { })); - left.appendChild(UI.button("Challenge", () => { LOAD(SCENES.Challenge); })); + left.appendChild(UI.button(LANG("challenge"), () => { LOAD(SCENES.Challenge); })); } } @@ -141,16 +141,16 @@ const UI = { let top = [ ]; let bottom = [ ]; - top.push(UI.button("Browse", () => { LOAD(SCENES.Browse); }, page == "browse")); + top.push(UI.button(LANG("browse"), () => { LOAD(SCENES.Browse); }, page == "browse")); if(sessionStorage.getItem("auth") !== null) { - top.push(UI.button("Continue", () => { LOAD(SCENES.Continue); }, page == "continue")); + top.push(UI.button(LANG("resume"), () => { LOAD(SCENES.Continue); }, page == "continue")); //top.push(UI.button("Join", () => { LOAD(SCENES.Join); }, page == "join")); } - top.push(UI.button("Live", () => { LOAD(SCENES.Live); }, page == "live")); - top.push(UI.button("History", () => { LOAD(SCENES.History); }, page == "history")); - top.push(UI.button("Practice", () => { LOAD(SCENES.GamePractice); }, page == "practice")); - top.push(UI.button("Guide", () => { LOAD(SCENES.Guide); }, page == "guide")); - top.push(UI.button("About", () => { LOAD(SCENES.About); }, page == "about")); + top.push(UI.button(LANG("live"), () => { LOAD(SCENES.Live); }, page == "live")); + top.push(UI.button(LANG("history"), () => { LOAD(SCENES.History); }, page == "history")); + top.push(UI.button(LANG("practice"), () => { LOAD(SCENES.GamePractice); }, page == "practice")); + top.push(UI.button(LANG("guide"), () => { LOAD(SCENES.Guide); }, page == "guide")); + top.push(UI.button(LANG("about"), () => { LOAD(SCENES.About); }, page == "about")); if(sessionStorage.getItem("auth") !== null) { bottom.push(UI.button("Logout", () => { @@ -190,26 +190,19 @@ const UI = { spectate_callback = spectate_callback.bind({token: records[r].token}); if(records[r].is_player) { - let button_resume = UI.button("Resume", join_callback); + let button_resume = UI.button(LANG("resume"), join_callback); if(records[r].is_turn) { - console.log("HERE"); button_resume.setAttribute("class", "highlight"); } buttons.push(button_resume); - buttons.push(UI.button("Review", spectate_callback)); + buttons.push(UI.button(LANG("review"), spectate_callback)); } else { - if(sessionStorage.getItem("auth") !== null && (records[r].dawn == "" || records[r].dusk == "")) { - buttons.push(UI.button("Join", join_callback)); - } - buttons.push(UI.button("View", spectate_callback)); + buttons.push(UI.button(LANG("view"), spectate_callback)); } let dawn = UI.text(records[r].dawn); - if(records[r].dawn == "") { dawn = UI.span([UI.text("Vacant")], "text-system"); } - let dusk = UI.text(records[r].dusk); - if(records[r].dusk == "") { dusk = UI.span([UI.text("Vacant")], "text-system"); } rows.push([ dawn, @@ -221,7 +214,7 @@ const UI = { } let tbody = UI.table_content( - [ "Dawn", "Dusk", "Turn", "Spectators", "" ], + [ LANG("dawn"), LANG("dusk"), LANG("turn"), LANG("viewers"), "" ], rows, ); @@ -279,13 +272,10 @@ const UI = { }; view_callback = view_callback.bind({token: records[r].token}); - buttons.push(UI.button("View", view_callback)); + buttons.push(UI.button(LANG("review"), view_callback)); let dawn = UI.text(records[r].dawn); - if(records[r].dawn == "") { dawn = UI.span([UI.text("Vacant")], "text-system"); } - let dusk = UI.text(records[r].dusk); - if(records[r].dusk == "") { dusk = UI.span([UI.text("Vacant")], "text-system"); } rows.push([ dawn, @@ -296,7 +286,7 @@ const UI = { } let tbody = UI.table_content( - [ "Dawn", "Dusk", "Turns", "" ], + [ LANG("dawn"), LANG("dusk"), LANG("turns"), "" ], rows, );