import { readFileSync } from 'node:fs' import { join } from 'node:path' import { describe, expect, it } from 'vitest' import { COPY } from '@/lib/directoryCopy' import { messages } from './messages' const SOURCES = [ 'components/AuthorAvatar.vue', 'components/AuthorFormPanel.vue', 'components/AuthorViewPanel.vue', 'components/DashboardShell.vue', 'components/BuilderMoreMenu.vue', 'components/DeleteTypeDialog.vue', 'components/DirectoryBadge.vue', 'components/DirectoryDialog.vue', 'components/DirectoryField.vue', 'components/DirectoryList.vue', 'components/DirectoryPageNotice.vue', 'components/DirectoryPanel.vue', 'components/DirectoryRowMenu.vue', 'components/LinkUserDialog.vue', 'components/MediaField.vue', 'components/PublishingLinksCard.vue', 'components/ReassignDialog.vue', 'components/SiteFormPanel.vue', 'components/SitesForbidden.vue', 'components/SlugField.vue', 'components/SocialLinksRepeater.vue', 'components/UserSelect.vue', 'composables/useDirectoryToasts.ts', 'composables/useSchemaDelete.ts', 'composables/useDirectoryView.ts', 'lib/directory.ts', 'lib/directoryCopy.ts', 'lib/directoryRules.ts', 'lib/schemaEntries.ts', 'lib/siteTitleTemplate.ts', 'views/AuthorsView.vue', 'views/SitesView.vue', ] const SAME_IN_FRENCH = new Set([ 'Articles', 'Auto', 'Cache', 'Avatar', 'Bio', 'Noindex', 'Site', 'Sites', '1 site', '{0} sites', 'Guest', 'Linked', 'Description', 'Image', 'Robots', ]) const LITERAL = /\bt\(\s*(?:'((?:\\.|[^'\\])*)'|"((?:\\.|[^"\\])*)")/g function keysIn(source: string) { return [...source.matchAll(LITERAL)].map((match) => (match[1] ?? match[2] ?? '').replace(/\\(['"\\])/g, '$1'), ) } describe('Sites and authors messages', () => { const keys = SOURCES.flatMap((file) => keysIn(readFileSync(join(import.meta.dirname, '..', file), 'utf8')).map((key) => ({ file, key, })), ) it('finds the strings of both screens', () => { expect(keys.length).toBeGreaterThan(100) }) it.each(keys.map(({ file, key }) => [file, key]))( '%s: "%s" has its own French entry', (_file, key) => { const french = (messages as Record)[key as string] expect(french).toMatch(/\S/) if (!SAME_IN_FRENCH.has(key as string)) { expect(french).not.toBe(key) } }, ) it.each(Object.values(COPY))('shared copy "%s" has its own French entry', (key) => { const french = (messages as Record)[key] expect(french).toBeTruthy() expect(french).not.toBe(key) }) })