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:
2026-05-13 03:43:04 +09:00
parent 401d72622e
commit c2fcc2fbbf
15 changed files with 490 additions and 172 deletions

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>관리자 대시보드</title>
<title><%= t('dashboard.browserTitle') %></title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body class="siteBody">
@@ -11,36 +11,36 @@
<main class="pageWrap">
<section class="dashboardHeader">
<h1>음악퀴즈 목록</h1>
<h1><%= t('dashboard.title') %></h1>
<div class="dashboardActions">
<a class="secondaryButton" href="/op/list">음악목록 수정</a>
<a class="secondaryButton" href="/op/datapack">데이터팩 수정</a>
<a class="secondaryButton" href="/op/list"><%= t('dashboard.editList') %></a>
<a class="secondaryButton" href="/op/datapack"><%= t('dashboard.editDatapack') %></a>
<form method="post" action="/op/dashboard/create" class="inlineForm">
<button type="submit" class="primaryButton">음악퀴즈 추가</button>
<button type="submit" class="primaryButton"><%= t('dashboard.addPack') %></button>
</form>
<button type="button" class="secondaryButton" id="deleteToggle">음악퀴즈 삭제</button>
<button type="button" class="secondaryButton" id="deleteToggle"><%= t('dashboard.deletePack') %></button>
</div>
</section>
<form method="post" action="/op/dashboard/delete" id="deleteForm" class="dashboardListForm">
<section class="cardRow horizontalScroll">
<% if (items.length === 0) { %>
<p class="muted">등록된 음악퀴즈가 없습니다. "음악퀴즈 추가" 버튼으로 새로 만들어 보세요.</p>
<p class="muted"><%= t('dashboard.emptyHint') %></p>
<% } %>
<% items.forEach(function (item) { %>
<article class="packCard editableCard" data-key="<%= item.key %>">
<label class="cardCheckbox" hidden>
<input type="checkbox" name="targetKey" value="<%= item.key %>" />
<span>선택</span>
<span><%= t('dashboard.select') %></span>
</label>
<a class="cardLink" href="/op/dashboard/<%= item.key %>">
<h2><%= item.definition ? item.definition.name : item.key %></h2>
<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>모드 폴더 <%= item.definition.modsFolder || '없음' %></li>
<li><%= t('dashboard.mcShort') %> <%= item.definition.mcVersion %></li>
<li><%= t('site.platform') %> <%= item.definition.platform.type %></li>
<li><%= t('site.modsFolder') %> <%= item.definition.modsFolder || t('site.noneFallback') %></li>
</ul>
<% } %>
</a>
@@ -48,8 +48,8 @@
<% }) %>
</section>
<div class="deleteConfirmRow" id="deleteConfirm" hidden>
<button type="button" class="secondaryButton" id="deleteCancel">취소</button>
<button type="submit" class="dangerButton">삭제 확인</button>
<button type="button" class="secondaryButton" id="deleteCancel"><%= t('common.cancel') %></button>
<button type="submit" class="dangerButton"><%= t('dashboard.confirmDelete') %></button>
</div>
</form>
</main>