test(ci): add exporter heading levels and a11y typography smoke; update MAIN_PLAN
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
import 'server-only'
|
||||
import { locales, type AppLocale } from '@/lib/i18n/locales'
|
||||
|
||||
export async function getMessages(locale: AppLocale) {
|
||||
const loc = locales.includes(locale) ? locale : 'en'
|
||||
const messages = (await import(`@/messages/${loc}.json`)).default
|
||||
return messages as Record<string, string>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { createI18n } from '@/lib/i18n'
|
||||
|
||||
describe('i18n util', () => {
|
||||
const i18n = createI18n({
|
||||
defaultLocale: 'en',
|
||||
messages: {
|
||||
en: { hello: 'Hello {name}', onlyEn: 'Only EN' },
|
||||
ko: { hello: '안녕하세요 {name}' },
|
||||
},
|
||||
})
|
||||
|
||||
it('로케일별 번역과 변수 치환을 지원한다', () => {
|
||||
expect(i18n.t('en', 'hello', { name: 'Jay' })).toBe('Hello Jay')
|
||||
expect(i18n.t('ko', 'hello', { name: '제이' })).toBe('안녕하세요 제이')
|
||||
})
|
||||
|
||||
it('키가 없으면 기본 로케일로 폴백한다', () => {
|
||||
expect(i18n.t('ko', 'onlyEn')).toBe('Only EN')
|
||||
})
|
||||
|
||||
it('완전히 없는 키는 키 그대로 반환한다', () => {
|
||||
expect(i18n.t('en', 'missing.key')).toBe('missing.key')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,23 @@
|
||||
type Messages = Record<string, Record<string, string>>
|
||||
|
||||
export function createI18n({ defaultLocale, messages }: { defaultLocale: string; messages: Messages }) {
|
||||
function format(template: string, params?: Record<string, string | number>) {
|
||||
if (!params) return template
|
||||
return template.replace(/\{(\w+)\}/g, (_, k) => (params[k] !== undefined ? String(params[k]) : `{${k}}`))
|
||||
}
|
||||
|
||||
function getMessage(locale: string, key: string): string | undefined {
|
||||
const m = messages[locale] || {}
|
||||
if (m[key] !== undefined) return m[key]
|
||||
const base = messages[defaultLocale] || {}
|
||||
return base[key]
|
||||
}
|
||||
|
||||
function t(locale: string, key: string, params?: Record<string, string | number>): string {
|
||||
const msg = getMessage(locale, key)
|
||||
if (typeof msg === 'string') return format(msg, params)
|
||||
return key
|
||||
}
|
||||
|
||||
return { t }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export const locales = ['en', 'ko'] as const
|
||||
export type AppLocale = typeof locales[number]
|
||||
export const defaultLocale: AppLocale = 'en'
|
||||
Reference in New Issue
Block a user