import { describe, expect, test } from "bun:test"; import { createI18n } from "vue-i18n"; import { PEER_NAME_MAX_LENGTH, PEER_NAME_MIN_LENGTH, PEER_NAME_PUNCTUATION, } from "../src/constants/peer"; import en from "../src/lang/package/en"; import ru from "../src/lang/package/ru"; type Dictionary = Record; function leafMessages( value: unknown, prefix = "" ): Array<[key: string, message: string]> { if (typeof value === "string") { return [[prefix, value]]; } if (!value || typeof value !== "object") { return []; } return Object.entries(value as Dictionary).flatMap(([key, child]) => leafMessages(child, prefix ? `${prefix}.${key}` : key) ); } function interpolationValues(message: string): Record { const values: Record = { count: 2, n: 2, min: PEER_NAME_MIN_LENGTH, max: PEER_NAME_MAX_LENGTH, punctuation: PEER_NAME_PUNCTUATION, }; for (const match of message.matchAll(/\{([A-Za-z_][A-Za-z0-9_$-]*)\}/g)) { values[match[1]] ??= match[1]; } return values; } for (const [locale, dictionary] of Object.entries({ ru, en }) as Array< ["ru" | "en", Dictionary] >) { describe(`runtime-компиляция локализации ${locale}`, () => { test("каждое строковое сообщение компилируется без diagnostics", () => { const compilerDiagnostics: string[] = []; const thrown: string[] = []; const originalConsoleError = console.error; console.error = (...args: unknown[]) => { compilerDiagnostics.push(args.map(String).join(" ")); }; try { const i18n = createI18n({ legacy: false, locale, fallbackLocale: false, missingWarn: false, fallbackWarn: false, messages: { [locale]: dictionary }, }); for (const [key, message] of leafMessages(dictionary)) { try { const translated = i18n.global.t(key, interpolationValues(message)); if (typeof translated !== "string") { thrown.push(`${key}: результат не является строкой`); } } catch (error) { thrown.push( `${key}: ${error instanceof Error ? error.message : String(error)}` ); } } } finally { console.error = originalConsoleError; } expect(thrown).toEqual([]); expect(compilerDiagnostics).toEqual([]); }); test("набор знаков имени пира проходит интерполяцию как данные", () => { const i18n = createI18n({ legacy: false, locale, fallbackLocale: false, messages: { [locale]: dictionary }, }); const template = (dictionary as any).peer.form.nameHint as string; expect( i18n.global.t("peer.form.nameHint", { min: PEER_NAME_MIN_LENGTH, max: PEER_NAME_MAX_LENGTH, punctuation: PEER_NAME_PUNCTUATION, }) ).toBe( template .replace("{min}", String(PEER_NAME_MIN_LENGTH)) .replace("{max}", String(PEER_NAME_MAX_LENGTH)) .replace("{punctuation}", PEER_NAME_PUNCTUATION) ); }); }); }