Studio lot 6 : mention « image générée par IA »

Miroir du Flutter, qui fait référence : la mention est posée dans ResourceViewer, partagé
par Article, Slider, Quiz et Parcours. La langue vient du contexte visiteur, le libellé du
dictionnaire d'interface existant.

Le type ResourceDTO déclare enfin `origin` : le backend l'envoyait déjà, le front ne le
lisait pas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thomas Fransolet 2026-09-15 17:34:05 +02:00
parent 7ff72345b3
commit 82cff83bb6
3 changed files with 50 additions and 8 deletions

View File

@ -2,7 +2,9 @@
import dynamic from 'next/dynamic' import dynamic from 'next/dynamic'
import Image from 'next/image' import Image from 'next/image'
import type { ResourceDTO } from '@/lib/api/types' import { RESOURCE_ORIGIN_GENERATED, type ResourceDTO } from '@/lib/api/types'
import { ui } from '@/lib/i18n'
import { useVisitor } from '@/context/VisitorContext'
// Même stratégie que PdfSection : react-pdf a besoin de `window`. // Même stratégie que PdfSection : react-pdf a besoin de `window`.
const PdfViewer = dynamic(() => import('@/components/sections/pdf/PdfViewer'), { ssr: false }) const PdfViewer = dynamic(() => import('@/components/sections/pdf/PdfViewer'), { ssr: false })
@ -44,6 +46,8 @@ interface Props {
* Parent must have: position relative, explicit width & height (or fill via flex). * Parent must have: position relative, explicit width & height (or fill via flex).
*/ */
export default function ResourceViewer({ resource, alt = '', objectFit = 'contain' }: Props) { export default function ResourceViewer({ resource, alt = '', objectFit = 'contain' }: Props) {
// La mention IA suit la langue de visite, comme le reste des libellés d'interface.
const { language } = useVisitor()
const { url, type } = resource const { url, type } = resource
if (!url) return null if (!url) return null
@ -98,6 +102,7 @@ export default function ResourceViewer({ resource, alt = '', objectFit = 'contai
// Image, ImageUrl, or unknown → render as image (safe default) // Image, ImageUrl, or unknown → render as image (safe default)
return ( return (
<>
<Image <Image
src={url} src={url}
alt={alt} alt={alt}
@ -105,5 +110,24 @@ export default function ResourceViewer({ resource, alt = '', objectFit = 'contai
className={`object-${objectFit}`} className={`object-${objectFit}`}
sizes="100vw" sizes="100vw"
/> />
{resource.origin === RESOURCE_ORIGIN_GENERATED && (
<span
style={{
position: 'absolute',
left: 6,
bottom: 6,
padding: '3px 6px',
borderRadius: 4,
background: 'rgba(0,0,0,.72)',
color: '#fff',
fontSize: 9,
letterSpacing: '.02em',
pointerEvents: 'none',
}}
>
{ui('aiGeneratedImage', language)}
</span>
)}
</>
) )
} }

View File

@ -7,8 +7,13 @@ export interface ResourceDTO {
id?: string id?: string
url?: string url?: string
type?: string type?: string
/** ResourceOrigin cote backend : 0 televersee, 1 generee par le Studio, 2 derivee. */
origin?: number
} }
/** Une image generee porte la mention IA ; une image televersee, jamais. */
export const RESOURCE_ORIGIN_GENERATED = 1
export interface ContentDTO { export interface ContentDTO {
order: number order: number
title?: TranslationDTO[] title?: TranslationDTO[]

View File

@ -11,6 +11,19 @@ export function t(translations: AnyTranslation[] | undefined, lang: string): str
} }
const UI_STRINGS: Record<string, Record<string, string>> = { const UI_STRINGS: Record<string, Record<string, string>> = {
aiGeneratedImage: {
FR: 'Image générée par IA',
NL: 'Beeld gegenereerd door AI',
EN: 'AI-generated image',
DE: 'KI-generiertes Bild',
ES: 'Imagen generada por IA',
IT: 'Immagine generata dallIA',
PL: 'Obraz wygenerowany przez SI',
CN: '人工智能生成的图像',
AR: 'صورة من إنتاج الذكاء الاصطناعي',
UK: 'Зображення, створене ШІ',
LB: 'Bild generéiert vun der KI',
},
noEventsThisMonth: { noEventsThisMonth: {
FR: 'Aucun événement ce mois-ci', FR: 'Aucun événement ce mois-ci',
NL: 'Geen evenementen deze maand', NL: 'Geen evenementen deze maand',