i18n: 서버 측 모든 UI 문구를 locales/server/ko-kr.json 으로 분리
- src/shared/i18n.ts: 공용 i18n 로더 (dotted-key + {{placeholder}} 보간)
- locales/server/ko-kr.json: 사이트 + 라우터 + 데이터팩 출력 사전
- EJS 뷰는 res.locals.t 미들웨어로 일괄 적용
- listEditor.js 등 클라이언트 JS 는 사전을 inline <script> 로 주입받아 tt() 헬퍼 사용
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>데이터팩 수정</title>
|
||||
<title><%= t('datapack.browserTitle') %></title>
|
||||
<link rel="stylesheet" href="/static/styles.css" />
|
||||
</head>
|
||||
<body class="siteBody">
|
||||
@@ -12,21 +12,21 @@
|
||||
<main class="pageWrap">
|
||||
<section class="dashboardHeader">
|
||||
<div>
|
||||
<a class="ghostLink" href="/op/dashboard">← 돌아가기</a>
|
||||
<h1 style="margin-top:20px;">데이터팩 수정</h1>
|
||||
<a class="ghostLink" href="/op/dashboard"><%= t('common.back') %></a>
|
||||
<h1 style="margin-top:20px;"><%= t('datapack.title') %></h1>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="dpControls">
|
||||
<button type="button" class="primaryButton" id="pickPackBtn">음악퀴즈 선택</button>
|
||||
<span class="muted" id="pickedLabel">선택된 음악퀴즈 없음</span>
|
||||
<button type="button" class="primaryButton" id="pickPackBtn"><%= t('datapack.pickPack') %></button>
|
||||
<span class="muted" id="pickedLabel"><%= t('datapack.pickedNone') %></span>
|
||||
</section>
|
||||
|
||||
<p class="muted" id="countLabel"></p>
|
||||
|
||||
<section class="dpActions" hidden id="dpActions">
|
||||
<button type="button" class="secondaryButton" id="exportBtn">데이터팩 출력</button>
|
||||
<button type="button" class="secondaryButton" id="copyBtn">복사</button>
|
||||
<button type="button" class="secondaryButton" id="exportBtn"><%= t('datapack.export') %></button>
|
||||
<button type="button" class="secondaryButton" id="copyBtn"><%= t('datapack.copy') %></button>
|
||||
<span class="statusText" id="dp-status"></span>
|
||||
</section>
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
<!-- 음악퀴즈 선택 팝업 -->
|
||||
<div class="modalOverlay" id="pickModal" hidden>
|
||||
<div class="modalCard">
|
||||
<header><h3>음악퀴즈 선택</h3>
|
||||
<button class="modalClose" type="button" data-modal-close>×</button>
|
||||
<header><h3><%= t('datapack.modalPickTitle') %></h3>
|
||||
<button class="modalClose" type="button" data-modal-close><%= t('common.close') %></button>
|
||||
</header>
|
||||
<div class="modalBody">
|
||||
<div class="cardRow horizontalScroll" id="pickList">
|
||||
@@ -47,8 +47,8 @@
|
||||
<p class="muted"><%= item.key %>.json</p>
|
||||
<% if (item.definition) { %>
|
||||
<ul class="metaList">
|
||||
<li>MC <%= item.definition.mcVersion %></li>
|
||||
<li>플랫폼 <%= item.definition.platform.type %></li>
|
||||
<li><%= t('dashboard.mcShort') %> <%= item.definition.mcVersion %></li>
|
||||
<li><%= t('site.platform') %> <%= item.definition.platform.type %></li>
|
||||
</ul>
|
||||
<% } %>
|
||||
</article>
|
||||
@@ -58,6 +58,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var I18N = <%- JSON.stringify(localeDict.datapack) %>;
|
||||
// 데이터팩 출력 본문의 "총 N곡" 패턴은 datapackOutput.summary 와 동일.
|
||||
var SUMMARY_PATTERN = <%- JSON.stringify(localeDict.datapackOutput.summary) %>;
|
||||
</script>
|
||||
<script>
|
||||
(function () {
|
||||
var pickModal = document.getElementById('pickModal')
|
||||
@@ -75,12 +80,10 @@
|
||||
card.addEventListener('click', function () {
|
||||
pickedKey = card.getAttribute('data-key')
|
||||
var name = card.getAttribute('data-name')
|
||||
document.getElementById('pickedLabel').textContent = '선택: ' + name
|
||||
document.getElementById('pickedLabel').textContent = I18N.pickedLabel.replace('{{name}}', name)
|
||||
pickModal.hidden = true
|
||||
document.getElementById('dpActions').hidden = false
|
||||
// 곡 수 미리 가져오기
|
||||
fetch('/op/list/' + encodeURIComponent(pickedKey)).catch(function () {})
|
||||
// 더 직접적으로: generate 호출 시점에 카운트도 나옴. 일단 비워둠.
|
||||
document.getElementById('countLabel').textContent = ''
|
||||
document.getElementById('codeOut').hidden = true
|
||||
})
|
||||
@@ -88,30 +91,30 @@
|
||||
document.getElementById('exportBtn').addEventListener('click', function () {
|
||||
if (!pickedKey) return
|
||||
var s = document.getElementById('dp-status')
|
||||
s.textContent = '출력 중…'; s.classList.remove('error')
|
||||
s.textContent = I18N.exporting; s.classList.remove('error')
|
||||
fetch('/op/datapack/' + encodeURIComponent(pickedKey) + '/generate')
|
||||
.then(function (r) { return r.text().then(function (t) { return { ok: r.ok, text: t } }) })
|
||||
.then(function (res) {
|
||||
if (!res.ok) {
|
||||
s.textContent = '실패: ' + res.text; s.classList.add('error')
|
||||
s.textContent = I18N.failed.replace('{{message}}', res.text); s.classList.add('error')
|
||||
return
|
||||
}
|
||||
var out = document.getElementById('codeOut')
|
||||
out.textContent = res.text
|
||||
out.hidden = false
|
||||
// 첫줄/둘째줄에서 카운트 가져와 표기
|
||||
// 첫줄/둘째줄에서 곡 개수를 추출해 카운트 라벨에 표시.
|
||||
var m = res.text.match(/총\s+(\d+)곡/)
|
||||
if (m) document.getElementById('countLabel').textContent = '총 ' + m[1] + '개의 음악을 찾았습니다.'
|
||||
s.textContent = '출력 완료'
|
||||
if (m) document.getElementById('countLabel').textContent = I18N.totalCount.replace('{{count}}', m[1])
|
||||
s.textContent = I18N.exported
|
||||
})
|
||||
.catch(function (err) { s.textContent = '실패: ' + err.message; s.classList.add('error') })
|
||||
.catch(function (err) { s.textContent = I18N.failed.replace('{{message}}', err.message); s.classList.add('error') })
|
||||
})
|
||||
document.getElementById('copyBtn').addEventListener('click', function () {
|
||||
var out = document.getElementById('codeOut')
|
||||
if (out.hidden) return
|
||||
navigator.clipboard.writeText(out.textContent).then(function () {
|
||||
var s = document.getElementById('dp-status')
|
||||
s.textContent = '복사됨'
|
||||
s.textContent = I18N.copied
|
||||
s.classList.remove('error')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user