feat(SPA): media field type

This commit is contained in:
Florian Sylvain committed 2026-10-21 15:17:15 +02:00
1 parent a2d596f4ca
commit 3e7fc9a4a5
57 files changed
+6109 -75

No files matched your search

+2
View File
@@ -14,10 +14,12 @@ const SOURCES = [
'components/EntryForm.vue',
'components/EntryNotices.vue',
'components/MarkdownField.vue',
'components/MediaFieldControl.vue',
'composables/useEntryEditorPage.ts',
'composables/useEntryRemoval.ts',
'lib/entryForm.ts',
'lib/entryMessages.ts',
'lib/mediaField.ts',
'lib/entryEditors.ts',
'views/EntryEditorPage.vue',
]
+52
View File
@@ -0,0 +1,52 @@
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { messages } from './messages'
const SOURCES = [
'components/ImageUploader.vue',
'components/MediaFieldControl.vue',
'components/MediaPicker.vue',
'components/SchemaChangeRow.vue',
'components/SchemaReviewDialog.vue',
'components/FieldCell.vue',
'components/EntryNotices.vue',
'lib/fieldTypes.ts',
'lib/mediaField.ts',
'lib/schemaBuilderDiff.ts',
]
const SAME_IN_FRENCH = new Set<string>(['Date'])
const LITERAL = /\bt\(\s*(?:'((?:\\.|[^'\\])*)'|"((?:\\.|[^"\\])*)")/g
function keysIn(source: string) {
return [...source.matchAll(LITERAL)].map((match) =>
(match[1] ?? match[2] ?? '').replace(/\\u0022/g, '"').replace(/\\(['"\\])/g, '$1'),
)
}
describe('Media field messages', () => {
const keys = SOURCES.flatMap((file) =>
keysIn(readFileSync(join(import.meta.dirname, '..', file), 'utf8')).map((key) => ({
file,
key,
})),
)
it('finds the strings of the feature', () => {
expect(keys.length).toBeGreaterThan(60)
})
it.each(keys.map(({ file, key }) => [file, key]))(
'%s: "%s" has its own French entry',
(_file, key) => {
const french = (messages as Record<string, string>)[key as string]
expect(french).toMatch(/\S/)
if (!SAME_IN_FRENCH.has(key as string)) {
expect(french).not.toBe(key)
}
},
)
})
+39
View File
@@ -2609,6 +2609,45 @@ export const messages = {
Heading: 'Titre',
List: 'Liste',
id: 'identifiant',
'Image no longer in the library': 'Image absente de la bibliothèque',
'Use this image': 'Utiliser cette image',
'Choose another image': 'Choisir une autre image',
'Clear {0}': 'Effacer {0}',
'Image removed from {0}.': 'Image retirée de {0}.',
'Image selected: {0}': 'Image sélectionnée : {0}',
'No image selected.': 'Aucune image sélectionnée.',
'One image from your library.': 'Une image de votre bibliothèque.',
'Pick an image': 'Choisir une image',
'Pick an image for {0}': 'Choisir une image pour {0}',
'Remove image from {0}': 'Retirer l’image de {0}',
'This image is no longer in the library': 'Cette image n’est plus dans la bibliothèque',
'You can still save. The entry keeps the link until you change it.':
'Vous pouvez quand même enregistrer. L’entrée garde le lien tant que vous ne le changez pas.',
'{0} cleared.': '{0} effacé.',
'{0} is required. Choose another image to save.':
'{0} est obligatoire. Choisissez une autre image pour enregistrer.',
Current: 'Actuelle',
'Images you upload appear here, ready to use in any article or entry.':
'Les images que vous importez apparaissent ici, prêtes à servir dans n’importe quel article ou entrée.',
'Leave this field empty.': 'Laisser ce champ vide.',
"Can't be applied": 'Ne peut pas être appliqué',
'Back to editing keeps your other changes.':
'Revenir à la modification conserve vos autres changements.',
'Pick an image for {0}.': 'Choisissez une image pour {0}.',
"The {0} field was changed after you opened this form. Reload the form to continue. What you typed here can't be saved against the old fields.":
'Le champ {0} a été modifié après l’ouverture de ce formulaire. Rechargez le formulaire pour continuer. Ce que vous avez saisi ici ne peut pas être enregistré avec les anciens champs.',
'{0} must be an image from the library.': '{0} doit être une image de la bibliothèque.',
'Holds one image from the media library. Entries link to the image instead of keeping a copy, so they always show its current version.':
'Contient une image de la médiathèque. Les entrées pointent vers l’image au lieu d’en garder une copie, donc elles affichent toujours sa version actuelle.',
'One image from your media library': 'Une image de votre médiathèque',
'Change image, {0}, {1}, {2}': 'Changer d’image, {0}, {1}, {2}',
'{0} by {1}': '{0} par {1}',
'1 entry is unaffected.': '1 entrée n’est pas concernée.',
"A media field can't have a default, so the existing entry would have no image. Make {0} optional to add it.":
'Un champ média ne peut pas avoir de valeur par défaut, donc l’entrée existante n’aurait pas d’image. Rendez {0} facultatif pour l’ajouter.',
"A media field can't have a default, so the {0} existing entries would have no image. Make {1} optional to add it.":
'Un champ média ne peut pas avoir de valeur par défaut, donc les {0} entrées existantes n’auraient pas d’image. Rendez {1} facultatif pour l’ajouter.',
'{0} entries are unaffected.': '{0} entrées ne sont pas concernées.',
} as const
export type MessageKey = keyof typeof messages