Update deployment scripts, add leaderboard feature, and bump version to 2.0.0
This commit is contained in:
266
public/src/css/leaderboard.css
Normal file
266
public/src/css/leaderboard.css
Normal file
@@ -0,0 +1,266 @@
|
||||
/* Leaderboard Overlay */
|
||||
#leaderboard-overlay,
|
||||
#leaderboard-submit-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Leaderboard Modal */
|
||||
#leaderboard-modal {
|
||||
background: linear-gradient(180deg, #ff9f18 0%, #ff6b00 100%);
|
||||
border: 4px solid #000;
|
||||
border-radius: 15px;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#leaderboard-header {
|
||||
background: linear-gradient(180deg, #ff4500 0%, #d63000 100%);
|
||||
border-radius: 11px 11px 0 0;
|
||||
padding: 15px 20px;
|
||||
text-align: center;
|
||||
border-bottom: 3px solid #000;
|
||||
}
|
||||
|
||||
#leaderboard-title {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 2px 2px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;
|
||||
}
|
||||
|
||||
#leaderboard-song-title {
|
||||
font-size: 1.1em;
|
||||
color: #fff;
|
||||
margin-top: 5px;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
}
|
||||
|
||||
#leaderboard-difficulty {
|
||||
font-size: 0.9em;
|
||||
color: #ffe100;
|
||||
margin-top: 3px;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
}
|
||||
|
||||
#leaderboard-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
background: #fff8e8;
|
||||
}
|
||||
|
||||
#leaderboard-loading,
|
||||
#leaderboard-empty,
|
||||
#leaderboard-error {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
font-size: 1.2em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#leaderboard-error {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
/* Leaderboard Table */
|
||||
#leaderboard-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.leaderboard-row {
|
||||
display: flex;
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.leaderboard-header-row {
|
||||
background: #ff9f18;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.leaderboard-row:not(.leaderboard-header-row):hover {
|
||||
background: #ffe8c8;
|
||||
}
|
||||
|
||||
.leaderboard-rank {
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.leaderboard-name {
|
||||
flex: 1;
|
||||
padding: 0 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.leaderboard-score {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Top 3 ranks styling */
|
||||
.leaderboard-rank-1 {
|
||||
background: linear-gradient(90deg, #ffd700 0%, #fff8e8 30%);
|
||||
}
|
||||
.leaderboard-rank-1 .leaderboard-rank {
|
||||
color: #d4a500;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.leaderboard-rank-2 {
|
||||
background: linear-gradient(90deg, #c0c0c0 0%, #fff8e8 30%);
|
||||
}
|
||||
.leaderboard-rank-2 .leaderboard-rank {
|
||||
color: #888;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.leaderboard-rank-3 {
|
||||
background: linear-gradient(90deg, #cd7f32 0%, #fff8e8 30%);
|
||||
}
|
||||
.leaderboard-rank-3 .leaderboard-rank {
|
||||
color: #8b4513;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
#leaderboard-footer {
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
border-top: 2px solid #000;
|
||||
}
|
||||
|
||||
#leaderboard-close-btn {
|
||||
background: linear-gradient(180deg, #666 0%, #444 100%);
|
||||
color: #fff;
|
||||
border: 2px solid #000;
|
||||
border-radius: 8px;
|
||||
padding: 10px 30px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
#leaderboard-close-btn:hover {
|
||||
transform: scale(1.05);
|
||||
background: linear-gradient(180deg, #888 0%, #555 100%);
|
||||
}
|
||||
|
||||
/* Submit Modal */
|
||||
#leaderboard-submit-modal {
|
||||
background: linear-gradient(180deg, #4CAF50 0%, #388E3C 100%);
|
||||
border: 4px solid #000;
|
||||
border-radius: 15px;
|
||||
padding: 25px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#leaderboard-submit-title {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 2px 2px 0 #000;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#leaderboard-submit-score {
|
||||
font-size: 1.8em;
|
||||
font-weight: bold;
|
||||
color: #ffe100;
|
||||
text-shadow: 2px 2px 0 #000;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#leaderboard-submit-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#leaderboard-submit-form label {
|
||||
display: block;
|
||||
color: #fff;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
}
|
||||
|
||||
#leaderboard-name-input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
font-size: 1.2em;
|
||||
border: 3px solid #000;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#leaderboard-name-input.error {
|
||||
border-color: #f00;
|
||||
background: #ffe8e8;
|
||||
}
|
||||
|
||||
#leaderboard-submit-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#leaderboard-submit-btn,
|
||||
#leaderboard-cancel-btn {
|
||||
padding: 12px 25px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
border: 2px solid #000;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
#leaderboard-submit-btn {
|
||||
background: linear-gradient(180deg, #ff9f18 0%, #ff6b00 100%);
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
}
|
||||
|
||||
#leaderboard-submit-btn:hover:not(:disabled) {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
#leaderboard-submit-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
#leaderboard-cancel-btn {
|
||||
background: linear-gradient(180deg, #666 0%, #444 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#leaderboard-cancel-btn:hover {
|
||||
transform: scale(1.05);
|
||||
background: linear-gradient(180deg, #888 0%, #555 100%);
|
||||
}
|
||||
@@ -39,7 +39,8 @@ var assets = {
|
||||
"abstractfile.js",
|
||||
"idb.js",
|
||||
"plugins.js",
|
||||
"search.js"
|
||||
"search.js",
|
||||
"leaderboard.js"
|
||||
],
|
||||
"css": [
|
||||
"main.css",
|
||||
@@ -49,7 +50,8 @@ var assets = {
|
||||
"debug.css",
|
||||
"songbg.css",
|
||||
"view.css",
|
||||
"search.css"
|
||||
"search.css",
|
||||
"leaderboard.css"
|
||||
],
|
||||
"img": [
|
||||
"notes.png",
|
||||
@@ -98,7 +100,7 @@ var assets = {
|
||||
"audioSfx": [
|
||||
"se_pause.ogg",
|
||||
"se_calibration.ogg",
|
||||
|
||||
|
||||
"v_results.ogg",
|
||||
"v_sanka.ogg",
|
||||
"v_songsel.ogg",
|
||||
@@ -112,14 +114,14 @@ var assets = {
|
||||
"se_don.ogg",
|
||||
"se_ka.ogg",
|
||||
"se_jump.ogg",
|
||||
|
||||
|
||||
"se_balloon.ogg",
|
||||
"se_gameclear.ogg",
|
||||
"se_gamefail.ogg",
|
||||
"se_gamefullcombo.ogg",
|
||||
"se_results_countup.ogg",
|
||||
"se_results_crown.ogg",
|
||||
|
||||
|
||||
"v_fullcombo.ogg",
|
||||
"v_renda.ogg",
|
||||
"v_results_fullcombo.ogg",
|
||||
@@ -153,7 +155,7 @@ var assets = {
|
||||
"customsongs.html",
|
||||
"search.html"
|
||||
],
|
||||
|
||||
|
||||
"songs": [],
|
||||
"sounds": {},
|
||||
"image": {},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
275
public/src/js/leaderboard.js
Normal file
275
public/src/js/leaderboard.js
Normal file
@@ -0,0 +1,275 @@
|
||||
class Leaderboard {
|
||||
constructor(songId, difficulty, songTitle, onClose) {
|
||||
this.songId = songId
|
||||
this.difficulty = difficulty
|
||||
this.songTitle = songTitle
|
||||
this.onClose = onClose
|
||||
this.entries = []
|
||||
this.loading = true
|
||||
this.error = null
|
||||
this.font = strings.font
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.createModal()
|
||||
this.fetchLeaderboard()
|
||||
this.addEventListeners()
|
||||
}
|
||||
|
||||
createModal() {
|
||||
// Create overlay
|
||||
this.overlay = document.createElement("div")
|
||||
this.overlay.id = "leaderboard-overlay"
|
||||
this.overlay.innerHTML = `
|
||||
<div id="leaderboard-modal">
|
||||
<div id="leaderboard-header">
|
||||
<div id="leaderboard-title">${strings.leaderboardTitle}</div>
|
||||
<div id="leaderboard-song-title">${this.songTitle}</div>
|
||||
<div id="leaderboard-difficulty">${this.getDifficultyName()}</div>
|
||||
</div>
|
||||
<div id="leaderboard-content">
|
||||
<div id="leaderboard-loading">${strings.loading}</div>
|
||||
</div>
|
||||
<div id="leaderboard-footer">
|
||||
<button id="leaderboard-close-btn">${strings.close}</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
document.body.appendChild(this.overlay)
|
||||
}
|
||||
|
||||
getDifficultyName() {
|
||||
const diffNames = {
|
||||
easy: strings.easy,
|
||||
normal: strings.normal,
|
||||
hard: strings.hard,
|
||||
oni: strings.oni,
|
||||
ura: strings.oni
|
||||
}
|
||||
return diffNames[this.difficulty] || this.difficulty
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
this.overlay.querySelector("#leaderboard-close-btn").addEventListener("click", () => this.close())
|
||||
this.overlay.addEventListener("click", (e) => {
|
||||
if (e.target === this.overlay) {
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
|
||||
// Keyboard support
|
||||
this.keyHandler = (e) => {
|
||||
if (e.key === "Escape") {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this.keyHandler)
|
||||
}
|
||||
|
||||
fetchLeaderboard() {
|
||||
const url = `api/leaderboard?song_id=${encodeURIComponent(this.songId)}&difficulty=${encodeURIComponent(this.difficulty)}`
|
||||
|
||||
loader.ajax(url).then(response => {
|
||||
const data = JSON.parse(response)
|
||||
if (data.status === "ok") {
|
||||
this.entries = data.entries
|
||||
this.monthKey = data.month_key
|
||||
this.loading = false
|
||||
this.render()
|
||||
} else {
|
||||
throw new Error("Failed to load leaderboard")
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("Leaderboard fetch error:", error)
|
||||
this.error = error
|
||||
this.loading = false
|
||||
this.render()
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const content = this.overlay.querySelector("#leaderboard-content")
|
||||
|
||||
if (this.error) {
|
||||
content.innerHTML = `<div id="leaderboard-error">${strings.errorOccured}</div>`
|
||||
return
|
||||
}
|
||||
|
||||
if (this.entries.length === 0) {
|
||||
content.innerHTML = `<div id="leaderboard-empty">${strings.noEntries}</div>`
|
||||
return
|
||||
}
|
||||
|
||||
// Build entries table
|
||||
let html = `
|
||||
<div id="leaderboard-table">
|
||||
<div class="leaderboard-row leaderboard-header-row">
|
||||
<div class="leaderboard-rank">${strings.leaderboardRank}</div>
|
||||
<div class="leaderboard-name">${strings.leaderboardPlayer}</div>
|
||||
<div class="leaderboard-score">${strings.leaderboardScore}</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
for (const entry of this.entries) {
|
||||
const rankClass = entry.rank <= 3 ? `leaderboard-rank-${entry.rank}` : ""
|
||||
html += `
|
||||
<div class="leaderboard-row ${rankClass}">
|
||||
<div class="leaderboard-rank">${entry.rank}</div>
|
||||
<div class="leaderboard-name">${this.escapeHtml(entry.username)}</div>
|
||||
<div class="leaderboard-score">${entry.score_value.toLocaleString()}</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
html += `</div>`
|
||||
content.innerHTML = html
|
||||
}
|
||||
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement("div")
|
||||
div.textContent = text
|
||||
return div.innerHTML
|
||||
}
|
||||
|
||||
close() {
|
||||
document.removeEventListener("keydown", this.keyHandler)
|
||||
this.overlay.remove()
|
||||
if (this.onClose) {
|
||||
this.onClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Score submission modal for when players qualify for leaderboard
|
||||
class LeaderboardSubmit {
|
||||
constructor(songId, difficulty, score, rank, onSubmit, onClose) {
|
||||
this.songId = songId
|
||||
this.difficulty = difficulty
|
||||
this.score = score
|
||||
this.rank = rank
|
||||
this.onSubmit = onSubmit
|
||||
this.onClose = onClose
|
||||
this.submitting = false
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.createModal()
|
||||
this.addEventListeners()
|
||||
}
|
||||
|
||||
createModal() {
|
||||
this.overlay = document.createElement("div")
|
||||
this.overlay.id = "leaderboard-submit-overlay"
|
||||
|
||||
const rankText = strings.newRecord.replace("%s", this.rank)
|
||||
|
||||
this.overlay.innerHTML = `
|
||||
<div id="leaderboard-submit-modal">
|
||||
<div id="leaderboard-submit-title">${rankText}</div>
|
||||
<div id="leaderboard-submit-score">${this.score.toLocaleString()} ${strings.points}</div>
|
||||
<div id="leaderboard-submit-form">
|
||||
<label for="leaderboard-name-input">${strings.enterName}</label>
|
||||
<input type="text" id="leaderboard-name-input" maxlength="10" autocomplete="off">
|
||||
</div>
|
||||
<div id="leaderboard-submit-buttons">
|
||||
<button id="leaderboard-submit-btn">${strings.submit}</button>
|
||||
<button id="leaderboard-cancel-btn">${strings.cancel}</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
document.body.appendChild(this.overlay)
|
||||
|
||||
// Focus input
|
||||
setTimeout(() => {
|
||||
this.overlay.querySelector("#leaderboard-name-input").focus()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
const submitBtn = this.overlay.querySelector("#leaderboard-submit-btn")
|
||||
const cancelBtn = this.overlay.querySelector("#leaderboard-cancel-btn")
|
||||
const input = this.overlay.querySelector("#leaderboard-name-input")
|
||||
|
||||
submitBtn.addEventListener("click", () => this.submit())
|
||||
cancelBtn.addEventListener("click", () => this.close())
|
||||
|
||||
input.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
this.submit()
|
||||
} else if (e.key === "Escape") {
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
submit() {
|
||||
if (this.submitting) return
|
||||
|
||||
const input = this.overlay.querySelector("#leaderboard-name-input")
|
||||
const username = input.value.trim()
|
||||
|
||||
if (username.length < 1 || username.length > 10) {
|
||||
input.classList.add("error")
|
||||
return
|
||||
}
|
||||
|
||||
this.submitting = true
|
||||
const submitBtn = this.overlay.querySelector("#leaderboard-submit-btn")
|
||||
submitBtn.disabled = true
|
||||
submitBtn.textContent = strings.loading
|
||||
|
||||
const data = {
|
||||
song_id: String(this.songId),
|
||||
difficulty: this.difficulty,
|
||||
username: username,
|
||||
score: this.score
|
||||
}
|
||||
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", "api/score/leaderboard/save")
|
||||
xhr.setRequestHeader("Content-Type", "application/json")
|
||||
xhr.onload = () => {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
const result = JSON.parse(xhr.responseText)
|
||||
if (result.status === "ok") {
|
||||
this.close()
|
||||
if (this.onSubmit) {
|
||||
this.onSubmit(username)
|
||||
}
|
||||
} else {
|
||||
handleError(result.message || "Submit failed")
|
||||
}
|
||||
} catch (e) {
|
||||
handleError("Invalid response")
|
||||
}
|
||||
} else {
|
||||
handleError("Server error " + xhr.status)
|
||||
}
|
||||
}
|
||||
xhr.onerror = () => {
|
||||
handleError("Network error")
|
||||
}
|
||||
|
||||
const handleError = (msg) => {
|
||||
console.error("Leaderboard submit error:", msg)
|
||||
this.submitting = false
|
||||
submitBtn.disabled = false
|
||||
submitBtn.textContent = strings.submit
|
||||
input.classList.add("error")
|
||||
}
|
||||
|
||||
xhr.send(JSON.stringify(data))
|
||||
}
|
||||
|
||||
close() {
|
||||
this.overlay.remove()
|
||||
if (this.onClose) {
|
||||
this.onClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,7 @@ var translations = {
|
||||
tw: "zh-Hant",
|
||||
ko: "ko"
|
||||
},
|
||||
|
||||
|
||||
taikoWeb: {
|
||||
ja: "たいこウェブ",
|
||||
en: "Taiko Web",
|
||||
@@ -438,7 +438,79 @@ var translations = {
|
||||
tw: "連打數",
|
||||
ko: "연타 횟수"
|
||||
},
|
||||
|
||||
|
||||
// Leaderboard strings
|
||||
leaderboard: {
|
||||
ja: "ランキング",
|
||||
en: "Leaderboard",
|
||||
cn: "排行榜",
|
||||
tw: "排行榜",
|
||||
ko: "순위"
|
||||
},
|
||||
leaderboardTitle: {
|
||||
ja: "月間ランキング",
|
||||
en: "Monthly Leaderboard",
|
||||
cn: "月度排行榜",
|
||||
tw: "月度排行榜",
|
||||
ko: "월간 순위"
|
||||
},
|
||||
leaderboardRank: {
|
||||
ja: "順位",
|
||||
en: "Rank",
|
||||
cn: "排名",
|
||||
tw: "排名",
|
||||
ko: "순위"
|
||||
},
|
||||
leaderboardScore: {
|
||||
ja: "スコア",
|
||||
en: "Score",
|
||||
cn: "分数",
|
||||
tw: "分數",
|
||||
ko: "점수"
|
||||
},
|
||||
leaderboardPlayer: {
|
||||
ja: "プレイヤー",
|
||||
en: "Player",
|
||||
cn: "玩家",
|
||||
tw: "玩家",
|
||||
ko: "플레이어"
|
||||
},
|
||||
newRecord: {
|
||||
ja: "新記録!ランキング %s 位!",
|
||||
en: "New Record! Rank %s!",
|
||||
cn: "新纪录!排名第 %s 名!",
|
||||
tw: "新紀錄!排名第 %s 名!",
|
||||
ko: "신기록! %s 위!"
|
||||
},
|
||||
enterName: {
|
||||
ja: "名前を入力してください(1〜10文字)",
|
||||
en: "Enter your name (1-10 characters)",
|
||||
cn: "请输入你的名字(1-10个字符)",
|
||||
tw: "請輸入你的名字(1-10個字符)",
|
||||
ko: "이름을 입력하세요 (1-10자)"
|
||||
},
|
||||
submit: {
|
||||
ja: "登録",
|
||||
en: "Submit",
|
||||
cn: "提交",
|
||||
tw: "提交",
|
||||
ko: "제출"
|
||||
},
|
||||
close: {
|
||||
ja: "閉じる",
|
||||
en: "Close",
|
||||
cn: "关闭",
|
||||
tw: "關閉",
|
||||
ko: "닫기"
|
||||
},
|
||||
noEntries: {
|
||||
ja: "まだ記録がありません",
|
||||
en: "No entries yet",
|
||||
cn: "暂无记录",
|
||||
tw: "暫無記錄",
|
||||
ko: "아직 기록이 없습니다"
|
||||
},
|
||||
|
||||
errorOccured: {
|
||||
ja: "エラーが発生しました。再読み込みしてください。",
|
||||
en: "An error occurred, please refresh",
|
||||
@@ -879,7 +951,7 @@ var translations = {
|
||||
en: "Audio Latency Calibration",
|
||||
tw: "聲音延遲校正",
|
||||
ko: "오디오 레이턴시 조절"
|
||||
|
||||
|
||||
},
|
||||
content: {
|
||||
ja: "背景で鳴っている音を聴いてみましょう。\n\n音が聞こえたら、太鼓の面(%sまたは%s)をたたこう!",
|
||||
@@ -1472,26 +1544,26 @@ var translations = {
|
||||
}
|
||||
}
|
||||
var allStrings = {}
|
||||
function separateStrings(){
|
||||
for(var j in languageList){
|
||||
function separateStrings() {
|
||||
for (var j in languageList) {
|
||||
var lang = languageList[j]
|
||||
allStrings[lang] = {
|
||||
id: lang
|
||||
}
|
||||
var str = allStrings[lang]
|
||||
var translateObj = function(obj, name, str){
|
||||
if("en" in obj){
|
||||
for(var i in obj){
|
||||
var translateObj = function (obj, name, str) {
|
||||
if ("en" in obj) {
|
||||
for (var i in obj) {
|
||||
str[name] = obj[lang] || obj.en
|
||||
}
|
||||
}else if(obj){
|
||||
} else if (obj) {
|
||||
str[name] = {}
|
||||
for(var i in obj){
|
||||
for (var i in obj) {
|
||||
translateObj(obj[i], i, str[name])
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var i in translations){
|
||||
for (var i in translations) {
|
||||
translateObj(translations[i], i, str)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user