import assert from 'node:assert/strict'; import { fileURLToPath } from 'node:url'; import vue from '@vitejs/plugin-vue'; import { createServer } from 'vite'; import { computed, createSSRApp, h } from 'vue'; import { renderToString } from 'vue/server-renderer'; const server = await createServer({ configFile: false, plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('../../resources/js', import.meta.url)), }, }, server: { middlewareMode: true, hmr: false, watch: null }, appType: 'custom', }); try { const { createLocalization, validateCatalog } = await server.ssrLoadModule( '/resources/js/lib/localization.ts', ); const { useLocalization } = await server.ssrLoadModule( '/resources/js/composables/useLocalization.ts', ); const { en } = await server.ssrLoadModule('/resources/js/locales/en.ts'); const { default: AlertError } = await server.ssrLoadModule( '/resources/js/components/AlertError.vue', ); const english = createLocalization(); assert.equal(english.t('common.save'), 'Save'); assert.equal( english.t('pagination.page', { page: 0, total: 2000 }), 'Page 0 of 2,000', ); for (const [count, expected] of [ [0, '0 tickets'], [1, '1 ticket'], [2, '2 tickets'], [1.5, '1.5 tickets'], ]) { assert.equal(english.t('tickets.count', { count }), expected); } assert.equal( english.number(1234.5, { minimumFractionDigits: 2 }), '1,234.50', ); assert.equal( english.number(12, { style: 'currency', currency: 'USD' }), '$12.00', ); assert.equal(english.date(new Date('2026-09-09T23:30:00Z')), '9/9/2026'); assert.equal( createLocalization({ timeZone: 'Europe/Berlin' }).date( new Date('2026-09-09T23:30:00Z'), ), '9/10/2026', ); assert.throws(() => english.t('missing.key'), /Missing translation/); assert.throws(() => english.t('toString'), /Missing translation/); assert.throws( () => english.t('pagination.page', { page: 1 }), /Missing translation parameter/, ); for (const count of [undefined, '1', Infinity, NaN]) { assert.throws( () => english.t('tickets.count', { count }), /finite count/, ); } const incomplete = { ...en }; delete incomplete['common.save']; assert.throws( () => validateCatalog(incomplete), /Missing or invalid translation/, ); assert.throws( () => validateCatalog({ ...en, 'tickets.count': { one: '{count} ticket' }, }), /plural fallback/, ); assert.throws( () => validateCatalog({ ...en, 'pagination.page': 'Page {unknown}' }), /placeholders/, ); assert.throws( () => validateCatalog({ ...en, 'common.save': '' }), /placeholders/, ); assert.throws( () => createLocalization({}, { de: incomplete }), /Missing or invalid translation/, ); for (const locale of ['de-DE', 'en_US', 'not a locale', '']) { assert.equal(createLocalization({ locale }).locale.value, 'en'); } const de = { ...en, 'common.error': 'Etwas ist schiefgelaufen.', 'common.save': 'Speichern', }; const translated = createLocalization({ locale: 'de_DE' }, { de }); assert.equal(translated.locale.value, 'de'); assert.equal(translated.number(1234.5), '1.234,5'); const label = computed(() => translated.t('common.save')); assert.equal(label.value, 'Speichern'); translated.setLocale('en'); assert.equal(label.value, 'Save'); translated.setLocale('de'); assert.equal(label.value, 'Speichern'); assert.equal(english.locale.value, 'en'); const renderAlert = (localization, props = {}) => renderToString( createSSRApp({ render: () => h(AlertError, { errors: ['Invalid input', 'Invalid input'], ...props, }), }).use(localization), ); const [englishHtml, germanHtml] = await Promise.all([ renderAlert(english), renderAlert(translated), ]); assert.match(englishHtml, /Something went wrong\./); assert.match(germanHtml, /Etwas ist schiefgelaufen\./); assert.equal((englishHtml.match(/Invalid input/g) ?? []).length, 1); assert.match( await renderAlert(translated, { title: 'Custom title' }), /Custom title/, ); assert.doesNotMatch( await renderAlert(translated, { title: '' }), /Etwas ist schiefgelaufen/, ); const InterpolatedText = { setup() { const { t } = useLocalization(); return () => h( 'p', t('pagination.page', { page: '', total: 1, }), ); }, }; const escaped = await renderToString( createSSRApp(InterpolatedText).use(createLocalization()), ); assert.match(escaped, /<script>/); assert.doesNotMatch(escaped, /