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

@@ -4,6 +4,7 @@ import path from 'node:path'
import fsp from 'node:fs/promises'
import { manifestRootPath, manifestDirPath, fileDirPath, viewsDirPath, publicDirPath } from '../shared/paths.js'
import { loadEnv } from '../shared/env.js'
import { t, localeDict } from './i18n.js'
import { indexRouter } from './routes/index.js'
import { opRouter } from './routes/op.js'
@@ -23,6 +24,14 @@ app.set('trust proxy', 1)
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
// 모든 EJS 뷰에서 t('key') 로 ko-kr.json 의 문구를 가져올 수 있도록 노출.
// localeDict 는 클라이언트 측 JS 로 사전을 통째로 전달할 때 사용(listEditor 등).
app.use((_req, res, next) => {
res.locals.t = t
res.locals.localeDict = localeDict
next()
})
app.use(session({
secret: process.env.SESSION_SECRET ?? 'music-quiz-installer-dev-secret',
resave: false,
@@ -104,8 +113,8 @@ app.use('/', opRouter)
app.use((err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error(err)
const message = err instanceof Error ? err.message : '알 수 없는 오류'
res.status(500).send(`서버 오류: ${message}`)
const message = err instanceof Error ? err.message : t('errors.unknown')
res.status(500).send(t('errors.serverError', { message }))
})
app.listen(PORT, HOST, () => {