augmenttoolkit / client /src /i18n /TransHtml.tsx
Leon4gr45's picture
Upload folder using huggingface_hub (part 2)
cd99321 verified
Raw
History Blame Contribute Delete
2.15 kB
import type { JSX } from 'react'
import { useTranslation } from './TranslationContext'
interface TransHtmlProps<T extends keyof JSX.IntrinsicElements = 'span'> {
/**
* Translation key whose template legitimately contains markup (e.g.
* `'Turn <strong>{title}</strong> into a Journey'`).
*/
html: string
/**
* Values to interpolate into `{paramName}` placeholders. Every value is
* HTML-escaped before substitution, so passing user-controlled data is safe.
*/
params?: Record<string, string | number>
/**
* Element to render. Defaults to `<span>`. Use the tag that fits the
* surrounding flow — block, inline, list item, etc.
*/
as?: T
className?: string
/**
* `id` is forwarded so the component can be the target of `aria-labelledby`
* or `htmlFor`. Other ARIA attributes can be added if needed; we intentionally
* keep the surface small to discourage overloading this with arbitrary props.
*/
id?: string
}
/**
* Renders a translation that contains markup (e.g. `<strong>`) safely.
*
* Replaces the pattern that bit us in the Journey suggestion banner:
* <span dangerouslySetInnerHTML={{ __html: t('...', { user_input }) }} />
*
* That pattern interpolates `user_input` into the template *before* React
* ever sees it, so a trip title like `<script>alert(1)</script>` would inject
* a script tag. `TransHtml` runs `tHtml()` which:
*
* 1. HTML-escapes every interpolated value, neutralising it.
* 2. Sanitises the resulting string against an inline tag allow-list.
*
* Use this for any user-controlled value that lands in a markup template.
* Plain text-only templates can continue to use `<>{t('key', params)}</>`.
*/
export function TransHtml<T extends keyof JSX.IntrinsicElements = 'span'>({
html,
params,
as,
className,
id,
}: TransHtmlProps<T>) {
const { tHtml } = useTranslation()
const Tag = (as ?? 'span') as keyof JSX.IntrinsicElements
return (
// eslint-disable-next-line react/no-danger -- sanitised by tHtml (defence in depth)
<Tag className={className} id={id} dangerouslySetInnerHTML={{ __html: tHtml(html, params) }} />
)
}