Add lang html attribute on everything

This commit is contained in:
DrMint 2024-06-03 23:19:02 +02:00
parent 85223ad63f
commit 4931664489
48 changed files with 445 additions and 302 deletions

View File

@ -4,9 +4,10 @@ import type { RichTextContent } from "src/shared/payload/payload-sdk";
interface Props { interface Props {
description: RichTextContent | string; description: RichTextContent | string;
lang?: string | undefined;
} }
const { description } = Astro.props; const { description, lang } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
@ -16,7 +17,7 @@ const { description } = Astro.props;
typeof description === "string" ? ( typeof description === "string" ? (
<p class="prose" set:html={description} /> <p class="prose" set:html={description} />
) : ( ) : (
<RichText content={description} /> <RichText content={description} context={{ lang }} />
) )
} }
<slot /> <slot />

View File

@ -3,14 +3,15 @@ interface Props {
pretitle?: string | undefined; pretitle?: string | undefined;
title: string; title: string;
subtitle?: string | undefined; subtitle?: string | undefined;
lang?: string | undefined;
} }
const { title, subtitle, pretitle } = Astro.props; const { title, subtitle, pretitle, lang } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<h1 class="high-contrast-text"> <h1 class="high-contrast-text" lang={lang}>
{ {
pretitle && ( pretitle && (
<span id="pretitle" class="font-2xl"> <span id="pretitle" class="font-2xl">

View File

@ -52,6 +52,7 @@ const { currentTheme } = Astro.locals;
"dark-theme": currentTheme === "dark", "dark-theme": currentTheme === "dark",
"texture-dots": !isIOS, "texture-dots": !isIOS,
"font-m": true, "font-m": true,
"debug-lang": false,
}}> }}>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@ -150,6 +151,11 @@ const { currentTheme } = Astro.locals;
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
<style is:global> <style is:global>
/* DEBUG */
.debug-lang [lang] {
outline: 5px solid red !important;
}
/* RESET */ /* RESET */
h1, h1,

View File

@ -16,6 +16,7 @@ const {
label, label,
target = undefined, target = undefined,
rel = undefined, rel = undefined,
lang,
} = formatEndpointSource(parentPage); } = formatEndpointSource(parentPage);
--- ---
@ -23,10 +24,8 @@ const {
<a class="pressable-label" href={href} target={target} rel={rel}> <a class="pressable-label" href={href} target={target} rel={rel}>
<Icon name="material-symbols:keyboard-return" /> <Icon name="material-symbols:keyboard-return" />
<p> <div class="font-xs">{typeLabel}</div>
<span class="font-xs">{typeLabel}</span> <p lang={lang}>{label}</p>
{label}
</p>
</a> </a>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
@ -35,16 +34,16 @@ const {
a { a {
height: 16px; height: 16px;
& > div {
border: 1px solid var(--color-base-1000);
border-radius: 9999px;
padding: 0.3em 0.5em;
}
& > p { & > p {
display: flex; display: flex;
place-items: center; place-items: center;
gap: 0.5em; gap: 0.5em;
span {
border: 1px solid var(--color-base-1000);
border-radius: 9999px;
padding: 0.3em 0.5em;
}
} }
} }
</style> </style>

View File

@ -32,6 +32,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<Metadata <Metadata
icon={icon} icon={icon}
title={translation.name} title={translation.name}
lang={translation.language}
values={[{ name: formatNumber(value) }]} values={[{ name: formatNumber(value) }]}
withBorder={false} withBorder={false}
/> />
@ -42,6 +43,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<Metadata <Metadata
icon={icon} icon={icon}
title={translation.name} title={translation.name}
lang={translation.language}
values={[{ name: value }]} values={[{ name: value }]}
withBorder={false} withBorder={false}
/> />
@ -52,10 +54,15 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<Metadata <Metadata
icon={icon} icon={icon}
title={translation.name} title={translation.name}
values={value.map(({ translations, page }) => ({ lang={translation.language}
name: getLocalizedMatch(translations).name, values={value.map(({ translations, page }) => {
...(page ? { href: getLocalizedUrl(`/pages/${page.slug}`) } : {}), const { name, language } = getLocalizedMatch(translations);
}))} return {
name,
lang: language,
...(page ? { href: getLocalizedUrl(`/pages/${page.slug}`) } : {}),
};
})}
/> />
); );

View File

@ -11,18 +11,19 @@ import ErrorMessage from "components/ErrorMessage.astro";
interface Props { interface Props {
block: GenericBlock; block: GenericBlock;
lang?: string | undefined;
} }
const { block } = Astro.props; const { block, lang } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
{ {
isBlockLineBlock(block) ? ( isBlockLineBlock(block) ? (
<LineBlock block={block} /> <LineBlock block={block} lang={lang} />
) : isBlockCueBlock(block) ? ( ) : isBlockCueBlock(block) ? (
<CueBlock block={block} /> <CueBlock block={block} lang={lang} />
) : ( ) : (
<ErrorMessage <ErrorMessage
title={`Unknown block type: ${block.blockType}`} title={`Unknown block type: ${block.blockType}`}

View File

@ -4,15 +4,16 @@ import type { CueBlock } from "src/shared/payload/payload-sdk";
interface Props { interface Props {
block: CueBlock; block: CueBlock;
lang?: string | undefined;
} }
const { block } = Astro.props; const { block, lang } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div> <div>
<RichText content={block.content} /> <RichText content={block.content} context={{ lang }} />
</div> </div>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}

View File

@ -4,17 +4,18 @@ import type { LineBlock } from "src/shared/payload/payload-sdk";
interface Props { interface Props {
block: LineBlock; block: LineBlock;
lang?: string | undefined;
} }
const { block } = Astro.props; const { block, lang } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div id="line"> <div id="line">
<p>{block.blockName}</p> <p lang={lang}>{block.blockName}</p>
<div> <div>
<RichText content={block.content} /> <RichText content={block.content} context={{ lang }} />
</div> </div>
</div> </div>

View File

@ -15,16 +15,20 @@ const { getLocalizedMatch, getLocalizedUrl } = await getI18n(Astro.locals.curren
<div> <div>
{ {
credits.map(({ recorders, role: { icon, translations } }) => ( credits.map(({ recorders, role: { icon, translations } }) => {
<Metadata const { language, name } = getLocalizedMatch(translations);
icon={icon} return (
title={getLocalizedMatch(translations).name} <Metadata
values={recorders.map(({ username, id }) => ({ icon={icon}
name: username, title={name}
href: getLocalizedUrl(`/recorders/${id}`), lang={language}
}))} values={recorders.map(({ username, id }) => ({
/> name: username,
)) href: getLocalizedUrl(`/recorders/${id}`),
}))}
/>
);
})
} }
</div> </div>

View File

@ -2,36 +2,37 @@
interface Props { interface Props {
id?: string; id?: string;
header: number; header: number;
lang?: string | undefined;
} }
const { header, id } = Astro.props; const { header, id, lang } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
{ {
header === 1 ? ( header === 1 ? (
<h1 id={id}> <h1 id={id} lang={lang}>
<slot /> <slot />
</h1> </h1>
) : header === 2 ? ( ) : header === 2 ? (
<h2 id={id}> <h2 id={id} lang={lang}>
<slot /> <slot />
</h2> </h2>
) : header === 3 ? ( ) : header === 3 ? (
<h3 id={id}> <h3 id={id} lang={lang}>
<slot /> <slot />
</h3> </h3>
) : header === 4 ? ( ) : header === 4 ? (
<h4 id={id}> <h4 id={id} lang={lang}>
<slot /> <slot />
</h4> </h4>
) : header === 5 ? ( ) : header === 5 ? (
<h5 id={id}> <h5 id={id} lang={lang}>
<slot /> <slot />
</h5> </h5>
) : ( ) : (
<h6 id={id}> <h6 id={id} lang={lang}>
<slot /> <slot />
</h6> </h6>
) )

View File

@ -32,6 +32,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<InlineMetadata <InlineMetadata
icon={icon} icon={icon}
title={translation.name} title={translation.name}
lang={translation.language}
values={[{ name: formatNumber(value) }]} values={[{ name: formatNumber(value) }]}
withBorder={false} withBorder={false}
/> />
@ -42,6 +43,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<InlineMetadata <InlineMetadata
icon={icon} icon={icon}
title={translation.name} title={translation.name}
lang={translation.language}
values={[{ name: value }]} values={[{ name: value }]}
withBorder={false} withBorder={false}
/> />
@ -52,10 +54,15 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<InlineMetadata <InlineMetadata
icon={icon} icon={icon}
title={translation.name} title={translation.name}
values={value.map(({ translations, page }) => ({ lang={translation.language}
name: getLocalizedMatch(translations).name, values={value.map(({ translations, page }) => {
...(page ? { href: getLocalizedUrl(`/pages/${page.slug}`) } : {}), const { name, language } = getLocalizedMatch(translations);
}))} return {
name,
lang: language,
...(page ? { href: getLocalizedUrl(`/pages/${page.slug}`) } : {}),
};
})}
/> />
); );

View File

@ -4,7 +4,7 @@ import type { Attribute } from "src/utils/attributes";
interface Props extends Attribute {} interface Props extends Attribute {}
const { icon, title, values } = Astro.props; const { icon, title, values, lang: titleLang } = Astro.props;
if (values.length === 0) return; if (values.length === 0) return;
--- ---
@ -14,9 +14,11 @@ if (values.length === 0) return;
<div id="container"> <div id="container">
<div class="title"> <div class="title">
<Icon name={icon} /> <Icon name={icon} />
<p class="font-xs">{title}</p> <p class="font-xs" lang={titleLang}>{title}</p>
</div>
<div id="values" class="font-xs">
{values.map(({ name, lang }) => <span lang={lang}>{name}</span>)}
</div> </div>
<div class="font-xs">{values.map(({ name }) => name).join(", ")}</div>
</div> </div>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
@ -34,5 +36,11 @@ if (values.length === 0) return;
color: var(--color-base-750); color: var(--color-base-750);
flex-shrink: 0; flex-shrink: 0;
} }
& > #values {
& > span:not(:last-child)::after {
content: ", ";
}
}
} }
</style> </style>

View File

@ -23,6 +23,7 @@ interface Props {
title: string; title: string;
subtitle?: string | undefined; subtitle?: string | undefined;
description?: RichTextContent | undefined; description?: RichTextContent | undefined;
lang?: string | undefined;
attributes?: ComponentProps<typeof Attributes>["attributes"] | undefined; attributes?: ComponentProps<typeof Attributes>["attributes"] | undefined;
metaAttributes?: ComponentProps<typeof Attributes>["attributes"] | undefined; metaAttributes?: ComponentProps<typeof Attributes>["attributes"] | undefined;
credits?: EndpointCredit[] | undefined; credits?: EndpointCredit[] | undefined;
@ -38,6 +39,7 @@ const {
metaAttributes = [], metaAttributes = [],
credits = [], credits = [],
description, description,
lang,
pretitle, pretitle,
title, title,
subtitle, subtitle,
@ -96,12 +98,14 @@ const hasNavigation = previousImageHref || nextImageHref;
<div> <div>
{ {
smallTitle ? ( smallTitle ? (
<h1 class="font-4xl">{title}</h1> <h1 class="font-4xl" lang={lang}>
{title}
</h1>
) : ( ) : (
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} /> <AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} lang={lang} />
) )
} }
{description && <AppLayoutDescription description={description} />} {description && <AppLayoutDescription description={description} lang={lang} />}
</div> </div>
{attributes.length > 0 && <Attributes attributes={attributes} />} {attributes.length > 0 && <Attributes attributes={attributes} />}
{credits.length > 0 && <Credits credits={credits} />} {credits.length > 0 && <Credits credits={credits} />}

View File

@ -4,7 +4,7 @@ import TitleIcon from "./TitleIcon.astro";
interface Props extends Attribute {} interface Props extends Attribute {}
const { icon, title, values, withBorder = true } = Astro.props; const { icon, title, values, withBorder = true, lang: titleLang } = Astro.props;
if (values.length === 0) return; if (values.length === 0) return;
--- ---
@ -12,16 +12,16 @@ if (values.length === 0) return;
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div id="container"> <div id="container">
<TitleIcon title={title} icon={icon} /> <TitleIcon title={title} icon={icon} lang={titleLang} />
<div id="values" class:list={{ "with-border": withBorder }}> <div id="values" class:list={{ "with-border": withBorder }}>
{ {
values.map(({ name, href }) => values.map(({ name, href, lang }) =>
href ? ( href ? (
<a class="pressable high-contrast-text" href={href}> <a class="pressable high-contrast-text" href={href} lang={lang}>
{name} {name}
</a> </a>
) : ( ) : (
<div>{name}</div> <div lang={lang}>{name}</div>
) )
) )
} }

View File

@ -15,10 +15,10 @@ const {
audio: { id, translations, attributes, filename, thumbnail, duration }, audio: { id, translations, attributes, filename, thumbnail, duration },
} = Astro.props; } = Astro.props;
const { pretitle, title, subtitle } = const { pretitle, title, subtitle, language } =
translations.length > 0 translations.length > 0
? getLocalizedMatch(translations) ? getLocalizedMatch(translations)
: { pretitle: undefined, title: filename, subtitle: undefined }; : { pretitle: undefined, title: filename, subtitle: undefined, language: undefined };
const attributesWithMeta = [ const attributesWithMeta = [
...attributes, ...attributes,
@ -36,6 +36,7 @@ const attributesWithMeta = [
pretitle={pretitle} pretitle={pretitle}
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
lang={language}
thumbnail={thumbnail} thumbnail={thumbnail}
href={getLocalizedUrl(`/audios/${id}`)} href={getLocalizedUrl(`/audios/${id}`)}
attributes={attributesWithMeta} attributes={attributesWithMeta}

View File

@ -18,7 +18,7 @@ const {
collectible: { slug, translations, thumbnail, attributes, languages, price, releaseDate }, collectible: { slug, translations, thumbnail, attributes, languages, price, releaseDate },
} = Astro.props; } = Astro.props;
const { title, pretitle, subtitle } = getLocalizedMatch(translations); const { title, pretitle, subtitle, language } = getLocalizedMatch(translations);
const additionalAttributes: Attribute[] = []; const additionalAttributes: Attribute[] = [];
@ -65,6 +65,7 @@ if (price) {
title={title} title={title}
pretitle={pretitle} pretitle={pretitle}
subtitle={subtitle} subtitle={subtitle}
lang={language}
thumbnail={thumbnail} thumbnail={thumbnail}
href={getLocalizedUrl(`/collectibles/${slug}`)} href={getLocalizedUrl(`/collectibles/${slug}`)}
attributes={[...attributes, ...additionalAttributes]} attributes={[...attributes, ...additionalAttributes]}

View File

@ -19,6 +19,7 @@ interface Props {
pretitle?: string | undefined; pretitle?: string | undefined;
title: string; title: string;
subtitle?: string | undefined; subtitle?: string | undefined;
lang?: string | undefined;
href?: string | undefined; href?: string | undefined;
attributes?: ComponentProps<typeof InlineAttributes>["attributes"]; attributes?: ComponentProps<typeof InlineAttributes>["attributes"];
disableRoundedTop?: boolean; disableRoundedTop?: boolean;
@ -40,6 +41,7 @@ const {
disableRoundedTop = false, disableRoundedTop = false,
icon = "material-symbols:unknown-document", icon = "material-symbols:unknown-document",
iconHoverLabel = t("global.previewTypes.unknown"), iconHoverLabel = t("global.previewTypes.unknown"),
lang,
} = Astro.props; } = Astro.props;
/* Clip the number of attributes such that the card isn't ridiculously long */ /* Clip the number of attributes such that the card isn't ridiculously long */
@ -104,9 +106,11 @@ for (const attribute of attributes) {
<div id="footer"> <div id="footer">
{ {
smallTitle ? ( smallTitle ? (
<p class="font-l">{title}</p> <p class="font-l" lang={lang}>
{title}
</p>
) : ( ) : (
<p> <p lang={lang}>
{pretitle && ( {pretitle && (
<span id="pretitle" class="font-s"> <span id="pretitle" class="font-s">
{pretitle} {pretitle}

View File

@ -14,10 +14,10 @@ const {
image: { id, translations, attributes, filename }, image: { id, translations, attributes, filename },
} = Astro.props; } = Astro.props;
const { pretitle, title, subtitle } = const { pretitle, title, subtitle, language } =
translations.length > 0 translations.length > 0
? getLocalizedMatch(translations) ? getLocalizedMatch(translations)
: { pretitle: undefined, title: filename, subtitle: undefined }; : { pretitle: undefined, title: filename, subtitle: undefined, language: undefined };
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
@ -26,6 +26,7 @@ const { pretitle, title, subtitle } =
pretitle={pretitle} pretitle={pretitle}
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
lang={language}
thumbnail={thumbnail} thumbnail={thumbnail}
href={getLocalizedUrl(`/images/${id}`)} href={getLocalizedUrl(`/images/${id}`)}
attributes={attributes} attributes={attributes}

View File

@ -16,7 +16,7 @@ const {
page: { slug, translations, thumbnail, attributes, updatedAt }, page: { slug, translations, thumbnail, attributes, updatedAt },
} = Astro.props; } = Astro.props;
const { title, pretitle, subtitle } = getLocalizedMatch(translations); const { title, pretitle, subtitle, language } = getLocalizedMatch(translations);
const metaAttributes: Attribute[] = [ const metaAttributes: Attribute[] = [
{ {
@ -34,6 +34,7 @@ const metaAttributes: Attribute[] = [
title={title} title={title}
pretitle={pretitle} pretitle={pretitle}
subtitle={subtitle} subtitle={subtitle}
lang={language}
thumbnail={thumbnail} thumbnail={thumbnail}
href={getLocalizedUrl(`/pages/${slug}`)} href={getLocalizedUrl(`/pages/${slug}`)}
attributes={[...attributes, ...metaAttributes]} attributes={[...attributes, ...metaAttributes]}

View File

@ -15,10 +15,10 @@ const {
video: { id, translations, attributes, filename, thumbnail, duration }, video: { id, translations, attributes, filename, thumbnail, duration },
} = Astro.props; } = Astro.props;
const { pretitle, title, subtitle } = const { pretitle, title, subtitle, language } =
translations.length > 0 translations.length > 0
? getLocalizedMatch(translations) ? getLocalizedMatch(translations)
: { pretitle: undefined, title: filename, subtitle: undefined }; : { pretitle: undefined, title: filename, subtitle: undefined, language: undefined };
const attributesWithMeta = [ const attributesWithMeta = [
...attributes, ...attributes,
@ -36,6 +36,7 @@ const attributesWithMeta = [
pretitle={pretitle} pretitle={pretitle}
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
lang={language}
thumbnail={thumbnail} thumbnail={thumbnail}
href={getLocalizedUrl(`/videos/${id}`)} href={getLocalizedUrl(`/videos/${id}`)}
attributes={attributesWithMeta} attributes={attributesWithMeta}

View File

@ -2,15 +2,16 @@
import type { RichTextContent } from "src/shared/payload/payload-sdk"; import type { RichTextContent } from "src/shared/payload/payload-sdk";
import RTNode from "./components/RTNode.astro"; import RTNode from "./components/RTNode.astro";
import RTProse from "./components/RTProse.astro"; import RTProse from "./components/RTProse.astro";
import { type RichTextContext, defaultContext } from "src/utils/richText"; import { type RichTextContext } from "src/utils/richText";
import ConditionalWrapper from "components/ConditionalWrapper.astro"; import ConditionalWrapper from "components/ConditionalWrapper.astro";
interface Props { interface Props {
content: RichTextContent; content: RichTextContent;
context?: RichTextContext; context?: Partial<RichTextContext>;
} }
const { content, context = defaultContext } = Astro.props; const { content, context: partialContext } = Astro.props;
const context: RichTextContext = { depth: partialContext?.depth ?? 1, lang: partialContext?.lang };
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}

View File

@ -14,7 +14,7 @@ const { node, context } = Astro.props;
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<HeaderTitle id={node.anchorHash} header={context.depth + 1}> <HeaderTitle id={node.anchorHash} header={context.depth + 1} lang={context.lang}>
<span>{`${node.anchorHash} `}</span> <span>{`${node.anchorHash} `}</span>
{node.fields.blockName} {node.fields.blockName}
</HeaderTitle> </HeaderTitle>

View File

@ -8,13 +8,13 @@ interface Props {
context: RichTextContext; context: RichTextContext;
} }
const { node } = Astro.props; const { node, context } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div> <div>
{node.fields.lines.map((block) => <Block block={block} />)} {node.fields.lines.map((block) => <Block block={block} lang={context.lang} />)}
</div> </div>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}

View File

@ -15,7 +15,7 @@ const { node, context } = Astro.props;
{ {
node.children.length > 0 && ( node.children.length > 0 && (
<p style={node.format ? `text-align: ${node.format};` : undefined}> <p style={node.format ? `text-align: ${node.format};` : undefined} lang={context.lang}>
{node.children.map((node) => ( {node.children.map((node) => (
<RTNode node={node} context={context} /> <RTNode node={node} context={context} />
))} ))}

View File

@ -24,7 +24,7 @@ const { title } = getLocalizedMatch(value.translations);
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div> <div>
<HeaderTitle header={context.depth + 2}> <HeaderTitle header={context.depth + 2} lang={context.lang}>
<Icon name="material-symbols:music-note" /> <Icon name="material-symbols:music-note" />
{title} {title}
</HeaderTitle> </HeaderTitle>

View File

@ -31,7 +31,7 @@ const mediaPage = getLocalizedUrl(`/images/${id}`);
<div> <div>
{ {
title && ( title && (
<HeaderTitle header={context.depth + 2}> <HeaderTitle header={context.depth + 2} lang={context.lang}>
<Icon name="material-symbols:imagesmode" /> <Icon name="material-symbols:imagesmode" />
{title} {title}
</HeaderTitle> </HeaderTitle>

View File

@ -24,7 +24,7 @@ const { title } = getLocalizedMatch(value.translations);
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div> <div>
<HeaderTitle header={context.depth + 2}> <HeaderTitle header={context.depth + 2} lang={context.lang}>
<Icon name="material-symbols:smart-display" /> <Icon name="material-symbols:smart-display" />
{title} {title}
</HeaderTitle> </HeaderTitle>

View File

@ -15,12 +15,15 @@ const {
label, label,
target = undefined, target = undefined,
rel = undefined, rel = undefined,
lang,
} = formatEndpointSource(source); } = formatEndpointSource(source);
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<a href={href} target={target} rel={rel}><div class="font-xs">{typeLabel}</div><p>{label}</p></a> <a href={href} target={target} rel={rel}>
<div class="font-xs">{typeLabel}</div><p lang={lang}>{label}</p>
</a>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}

View File

@ -6,9 +6,10 @@ import TitleIcon from "components/TitleIcon.astro";
interface Props { interface Props {
toc: TableOfContentEntry[]; toc: TableOfContentEntry[];
lang?: string | undefined;
} }
const { toc } = Astro.props; const { toc, lang } = Astro.props;
const { t } = await getI18n(Astro.locals.currentLocale); const { t } = await getI18n(Astro.locals.currentLocale);
--- ---
@ -16,7 +17,7 @@ const { t } = await getI18n(Astro.locals.currentLocale);
<div> <div>
<TitleIcon title={t("pages.tableOfContent")} icon="material-symbols:list-alt" /> <TitleIcon title={t("pages.tableOfContent")} icon="material-symbols:list-alt" />
<ol> <ol lang={lang}>
{toc.map((entry) => <TableOfContentItem entry={entry} />)} {toc.map((entry) => <TableOfContentItem entry={entry} />)}
</ol> </ol>
</div> </div>

View File

@ -4,14 +4,15 @@ import { Icon } from "astro-icon/components";
interface Props { interface Props {
icon: string; icon: string;
title: string; title: string;
lang?: string | undefined;
} }
const { icon, title } = Astro.props; const { icon, title, lang } = Astro.props;
--- ---
<div> <div>
<Icon name={icon} width={24} height={24} /> <Icon name={icon} width={24} height={24} />
<p class="font-2xl">{title}</p> <p class="font-2xl" lang={lang}>{title}</p>
</div> </div>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}

View File

@ -236,9 +236,18 @@ export const getI18n = async (locale: string) => {
} }
}; };
const formatEndpointSource = (source: EndpointSource) => { const formatEndpointSource = (
source: EndpointSource
): {
href: string;
typeLabel: string;
label: string;
lang?: string;
target?: string;
rel?: string;
} => {
switch (source.type) { switch (source.type) {
case "url": case "url": {
return { return {
href: source.url, href: source.url,
typeLabel: t("global.sources.typeLabel.url"), typeLabel: t("global.sources.typeLabel.url"),
@ -246,8 +255,9 @@ export const getI18n = async (locale: string) => {
target: "_blank", target: "_blank",
rel: "noopener noreferrer", rel: "noopener noreferrer",
}; };
}
case "collectible": case "collectible": {
const rangeLabel = (() => { const rangeLabel = (() => {
switch (source.range?.type) { switch (source.range?.type) {
case "timestamp": case "timestamp":
@ -271,46 +281,62 @@ export const getI18n = async (locale: string) => {
} }
})(); })();
const translation = getLocalizedMatch(source.collectible.translations);
return { return {
href: getLocalizedUrl(`/collectibles/${source.collectible.slug}`), href: getLocalizedUrl(`/collectibles/${source.collectible.slug}`),
typeLabel: t("global.sources.typeLabel.collectible"), typeLabel: t("global.sources.typeLabel.collectible"),
label: formatInlineTitle(getLocalizedMatch(source.collectible.translations)) + rangeLabel, label: formatInlineTitle(translation) + rangeLabel,
lang: translation.language,
}; };
}
case "page": case "page": {
const translation = getLocalizedMatch(source.page.translations);
return { return {
href: getLocalizedUrl(`/pages/${source.page.slug}`), href: getLocalizedUrl(`/pages/${source.page.slug}`),
typeLabel: t("global.sources.typeLabel.page"), typeLabel: t("global.sources.typeLabel.page"),
label: formatInlineTitle(getLocalizedMatch(source.page.translations)), label: formatInlineTitle(translation),
lang: translation.language,
}; };
}
case "folder": case "folder": {
const translation = getLocalizedMatch(source.folder.translations);
return { return {
href: getLocalizedUrl(`/folders/${source.folder.slug}`), href: getLocalizedUrl(`/folders/${source.folder.slug}`),
typeLabel: t("global.sources.typeLabel.folder"), typeLabel: t("global.sources.typeLabel.folder"),
label: getLocalizedMatch(source.folder.translations).name, label: getLocalizedMatch(source.folder.translations).name,
lang: translation.language,
}; };
}
case "scans": case "scans": {
const translation = getLocalizedMatch(source.collectible.translations);
return { return {
href: getLocalizedUrl(`/collectibles/${source.collectible.slug}/scans`), href: getLocalizedUrl(`/collectibles/${source.collectible.slug}/scans`),
typeLabel: t("global.sources.typeLabel.scans"), typeLabel: t("global.sources.typeLabel.scans"),
label: formatInlineTitle(getLocalizedMatch(source.collectible.translations)), label: formatInlineTitle(getLocalizedMatch(source.collectible.translations)),
lang: translation.language,
}; };
}
case "gallery": case "gallery": {
const translation = getLocalizedMatch(source.collectible.translations);
return { return {
href: getLocalizedUrl(`/collectibles/${source.collectible.slug}/gallery`), href: getLocalizedUrl(`/collectibles/${source.collectible.slug}/gallery`),
typeLabel: t("global.sources.typeLabel.gallery"), typeLabel: t("global.sources.typeLabel.gallery"),
label: formatInlineTitle(getLocalizedMatch(source.collectible.translations)), label: formatInlineTitle(getLocalizedMatch(source.collectible.translations)),
lang: translation.language,
}; };
}
default: default: {
return { return {
href: "/404", href: "/404",
label: `Invalid type ${source["type"]}`, label: `Invalid type ${source["type"]}`,
typeLabel: "Error", typeLabel: "Error",
}; };
}
} }
}; };

View File

@ -65,7 +65,7 @@ if (updatedBy) {
<MasoTarget> <MasoTarget>
<AsideLayout> <AsideLayout>
<Fragment slot="header"> <Fragment slot="header">
<AppLayoutTitle title={title} pretitle={pretitle} subtitle={subtitle} /> <AppLayoutTitle title={title} pretitle={pretitle} subtitle={subtitle} lang={language} />
</Fragment> </Fragment>
<Fragment slot="header-aside"> <Fragment slot="header-aside">
@ -88,7 +88,7 @@ if (updatedBy) {
</Fragment> </Fragment>
<Fragment slot="meta"> <Fragment slot="meta">
{summary && <AppLayoutDescription description={summary} />} {summary && <AppLayoutDescription description={summary} lang={language} />}
{ {
attributes.length > 0 && ( attributes.length > 0 && (
<div id="tags"> <div id="tags">
@ -118,12 +118,12 @@ if (updatedBy) {
{metaAttributes.length > 0 && <Attributes attributes={metaAttributes} />} {metaAttributes.length > 0 && <Attributes attributes={metaAttributes} />}
{toc.length > 0 && <TableOfContent toc={toc} />} {toc.length > 0 && <TableOfContent toc={toc} lang={language} />}
</div> </div>
<hr /> <hr />
<div id="text"> <div id="text">
<RichText content={content} /> <RichText content={content} context={{ lang: language }} />
</div> </div>
</AsideLayout> </AsideLayout>
</MasoTarget> </MasoTarget>

View File

@ -27,7 +27,7 @@ const { sources, translations } = event.events[index]!;
const { getLocalizedUrl } = await getI18n(Astro.locals.currentLocale); const { getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
const { getLocalizedMatch } = await getI18n(lang); const { getLocalizedMatch } = await getI18n(lang);
const { title, description, notes, credits } = getLocalizedMatch(translations); const { title, description, notes, credits, language } = getLocalizedMatch(translations);
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
@ -36,8 +36,14 @@ const { title, description, notes, credits } = getLocalizedMatch(translations);
<MasoTarget> <MasoTarget>
<Card class="timeline_partial-card"> <Card class="timeline_partial-card">
<div id="content"> <div id="content">
{title && <h4 class="font-xl">{title}</h4>} {
{description && <RichText content={description} />} title && (
<h4 lang={language} class="font-xl">
{title}
</h4>
)
}
{description && <RichText content={description} context={{ lang: language }} />}
</div> </div>
<div id="bottom" class="when-js when-no-print"> <div id="bottom" class="when-js when-no-print">
<TimelineSourcesButton sources={sources} /> <TimelineSourcesButton sources={sources} />

View File

@ -32,7 +32,7 @@ const {
thumbnail, thumbnail,
} = audio; } = audio;
const { pretitle, title, subtitle, description } = getLocalizedMatch(translations); const { pretitle, title, subtitle, description, language } = getLocalizedMatch(translations);
const metaAttributes = [ const metaAttributes = [
...(filename && title !== filename ...(filename && title !== filename
@ -79,8 +79,8 @@ const metaAttributes = [
<AudioPlayer audio={audio} class="audio_id-audio-player" /> <AudioPlayer audio={audio} class="audio_id-audio-player" />
<div id="info"> <div id="info">
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} /> <AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} lang={language} />
{description && <RichText content={description} />} {description && <RichText content={description} context={{ lang: language }} />}
{attributes.length > 0 && <Attributes attributes={attributes} />} {attributes.length > 0 && <Attributes attributes={attributes} />}
{credits.length > 0 && <Credits credits={credits} />} {credits.length > 0 && <Credits credits={credits} />}
{metaAttributes.length > 0 && <Attributes attributes={metaAttributes} />} {metaAttributes.length > 0 && <Attributes attributes={metaAttributes} />}

View File

@ -3,11 +3,11 @@ import ErrorMessage from "components/ErrorMessage.astro";
import RichText from "components/RichText/RichText.astro"; import RichText from "components/RichText/RichText.astro";
import { getI18n } from "src/i18n/i18n"; import { getI18n } from "src/i18n/i18n";
import { Collections, type EndpointCollectible } from "src/shared/payload/payload-sdk"; import { Collections, type EndpointCollectible } from "src/shared/payload/payload-sdk";
import { formatInlineTitle } from "src/utils/format";
import Card from "components/Card.astro"; import Card from "components/Card.astro";
import AudioPlayer from "components/AudioPlayer.astro"; import AudioPlayer from "components/AudioPlayer.astro";
import VideoPlayer from "components/VideoPlayer.astro"; import VideoPlayer from "components/VideoPlayer.astro";
import InlineAttributes from "components/InlineAttributes.astro"; import InlineAttributes from "components/InlineAttributes.astro";
import { formatInlineTitle } from "src/utils/format";
interface Props { interface Props {
content: EndpointCollectible["contents"][number]; content: EndpointCollectible["contents"][number];
@ -33,6 +33,34 @@ const href = (() => {
return undefined; return undefined;
} }
})(); })();
const { title, language } = (() => {
switch (content.relationTo) {
case Collections.GenericContents: {
const { language, name } = getLocalizedMatch(content.value.translations);
return { title: name, language };
}
case Collections.Pages: {
const translation = getLocalizedMatch(content.value.translations);
return { title: formatInlineTitle(translation), language: translation.language };
}
case Collections.Audios: {
const translation = getLocalizedMatch(content.value.translations);
return { title: formatInlineTitle(translation), language: translation.language };
}
case Collections.Videos: {
const translation = getLocalizedMatch(content.value.translations);
return { title: formatInlineTitle(translation), language: translation.language };
}
default: {
return { title: undefined, language: undefined };
}
}
})();
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
@ -40,14 +68,8 @@ const href = (() => {
<Card href={href} class="content_row-card"> <Card href={href} class="content_row-card">
<div id="title"> <div id="title">
{ {
content.relationTo === Collections.GenericContents ? ( title ? (
<p>{getLocalizedMatch(content.value.translations).name}</p> <p lang={language}>{title}</p>
) : content.relationTo === Collections.Pages ? (
<p>{formatInlineTitle(getLocalizedMatch(content.value.translations))}</p>
) : content.relationTo === Collections.Audios ? (
<p>{formatInlineTitle(getLocalizedMatch(content.value.translations))}</p>
) : content.relationTo === Collections.Videos ? (
<p>{formatInlineTitle(getLocalizedMatch(content.value.translations))}</p>
) : ( ) : (
<ErrorMessage <ErrorMessage
title="Unknown content type" title="Unknown content type"
@ -68,7 +90,9 @@ const href = (() => {
) : range.type === "timeRange" ? ( ) : range.type === "timeRange" ? (
range.start range.start
) : range.type === "other" ? ( ) : range.type === "other" ? (
<RichText content={getLocalizedMatch(range.translations).note} /> <RichText
content={getLocalizedMatch(range.translations).note}
/> /* TODO: Provide lang */
) : ( ) : (
<ErrorMessage <ErrorMessage
title="Unknown range type" title="Unknown range type"

View File

@ -20,10 +20,16 @@ if (galleryImage instanceof Response) {
const { parentPages, previousIndex, nextIndex, image } = galleryImage; const { parentPages, previousIndex, nextIndex, image } = galleryImage;
const { filename, translations, createdAt, updatedAt, credits, attributes } = image; const { filename, translations, createdAt, updatedAt, credits, attributes } = image;
const { pretitle, title, subtitle, description } = const { pretitle, title, subtitle, description, language } =
translations.length > 0 translations.length > 0
? getLocalizedMatch(translations) ? getLocalizedMatch(translations)
: { pretitle: undefined, title: filename, subtitle: undefined, description: undefined }; : {
pretitle: undefined,
title: filename,
subtitle: undefined,
description: undefined,
language: undefined,
};
const metaAttributes = [ const metaAttributes = [
...(filename && title !== filename ...(filename && title !== filename
@ -65,13 +71,14 @@ const metaAttributes = [
pretitle={pretitle} pretitle={pretitle}
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
description={description}
lang={language}
previousImageHref={previousIndex previousImageHref={previousIndex
? getLocalizedUrl(`/collectibles/${slug}/gallery/${previousIndex}`) ? getLocalizedUrl(`/collectibles/${slug}/gallery/${previousIndex}`)
: undefined} : undefined}
nextImageHref={nextIndex nextImageHref={nextIndex
? getLocalizedUrl(`/collectibles/${slug}/gallery/${nextIndex}`) ? getLocalizedUrl(`/collectibles/${slug}/gallery/${nextIndex}`)
: undefined} : undefined}
description={description}
filename={filename} filename={filename}
attributes={attributes} attributes={attributes}
metaAttributes={metaAttributes} metaAttributes={metaAttributes}

View File

@ -33,9 +33,14 @@ const translation = getLocalizedMatch(translations);
title={translation.title} title={translation.title}
pretitle={translation.pretitle} pretitle={translation.pretitle}
subtitle={translation.subtitle} subtitle={translation.subtitle}
lang={translation.language}
/> />
{translation.description && <AppLayoutDescription description={translation.description} />} {
translation.description && (
<AppLayoutDescription description={translation.description} lang={translation.language} />
)
}
<div> <div>
{ {

View File

@ -54,7 +54,7 @@ const {
} = collectible; } = collectible;
const translation = getLocalizedMatch(translations); const translation = getLocalizedMatch(translations);
const { pretitle, title, subtitle, description } = translation; const { pretitle, title, subtitle, description, language } = translation;
const metaAttributes: Attribute[] = [ const metaAttributes: Attribute[] = [
{ {
@ -150,7 +150,7 @@ if (price) {
backgroundImage={backgroundImage ?? thumbnail}> backgroundImage={backgroundImage ?? thumbnail}>
<AsideLayout> <AsideLayout>
<Fragment slot="header"> <Fragment slot="header">
<AppLayoutTitle title={title} pretitle={pretitle} subtitle={subtitle} /> <AppLayoutTitle title={title} pretitle={pretitle} subtitle={subtitle} lang={language} />
</Fragment> </Fragment>
<Fragment slot="header-aside"> <Fragment slot="header-aside">
@ -213,7 +213,7 @@ if (price) {
</Fragment> </Fragment>
<Fragment slot="meta"> <Fragment slot="meta">
{description && <AppLayoutDescription description={description} />} {description && <AppLayoutDescription description={description} lang={language} />}
<div id="tags"> <div id="tags">
<Attributes attributes={[...attributes, ...additionalAttributes]}> <Attributes attributes={[...attributes, ...additionalAttributes]}>

View File

@ -50,10 +50,15 @@ const hasOutsideObi = obi ? Object.keys(obi).some((value) => !value.includes("in
title={translation.title} title={translation.title}
pretitle={translation.pretitle} pretitle={translation.pretitle}
subtitle={translation.subtitle} subtitle={translation.subtitle}
lang={translation.language}
/> />
<div id="meta" class:list={{ "with-description": translation.description }}> <div id="meta" class:list={{ "with-description": translation.description }}>
{translation.description && <AppLayoutDescription description={translation.description} />} {
translation.description && (
<AppLayoutDescription description={translation.description} lang={translation.language} />
)
}
{credits.length > 0 && <Credits credits={credits} />} {credits.length > 0 && <Credits credits={credits} />}
</div> </div>

View File

@ -34,8 +34,8 @@ const meta = getLocalizedMatch(folder.translations);
description: meta.description && formatRichTextToString(meta.description), description: meta.description && formatRichTextToString(meta.description),
}} }}
parentPages={folder.parentPages}> parentPages={folder.parentPages}>
<AppLayoutTitle title={meta.name} /> <AppLayoutTitle title={meta.name} lang={meta.language} />
{meta.description && <AppLayoutDescription description={meta.description} />} {meta.description && <AppLayoutDescription description={meta.description} lang={meta.language} />}
<div id="main" class:list={{ complex: folder.sections.type === "multiple" }}> <div id="main" class:list={{ complex: folder.sections.type === "multiple" }}>
{ {
@ -45,17 +45,10 @@ const meta = getLocalizedMatch(folder.translations);
folder.sections.type === "multiple" && folder.sections.type === "multiple" &&
folder.sections.sections.length > 0 && ( folder.sections.sections.length > 0 && (
<div id="sections"> <div id="sections">
{folder.sections.sections.map(({ subfolders, translations }) => ( {folder.sections.sections.map(({ subfolders, translations }) => {
<FoldersSection const { language, name } = getLocalizedMatch(translations);
folders={subfolders} return <FoldersSection folders={subfolders} title={name} lang={language} />;
title={ })}
getLocalizedMatch<{
language: string;
name: string | undefined;
}>(translations).name
}
/>
))}
</div> </div>
) )
) )

View File

@ -2,16 +2,17 @@
import { Icon } from "astro-icon/components"; import { Icon } from "astro-icon/components";
interface Props { interface Props {
title: string; title: string;
lang?: string | undefined;
icon?: string | undefined; icon?: string | undefined;
href: string; href: string;
} }
const { icon = "material-symbols:folder", title, href } = Astro.props; const { icon = "material-symbols:folder", title, href, lang } = Astro.props;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<a href={href} class="pressable"> <a href={href} class="pressable" lang={lang}>
<Icon name={icon} /> <Icon name={icon} />
<div id="right" class="font-l"> <div id="right" class="font-l">
{title} {title}

View File

@ -5,10 +5,11 @@ import { getI18n } from "src/i18n/i18n";
interface Props { interface Props {
title?: string | undefined; title?: string | undefined;
lang?: string | undefined;
folders: EndpointFolder[]; folders: EndpointFolder[];
} }
const { title, folders } = Astro.props; const { title, folders, lang } = Astro.props;
const { getLocalizedUrl, getLocalizedMatch } = await getI18n(Astro.locals.currentLocale); const { getLocalizedUrl, getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
--- ---
@ -16,16 +17,26 @@ const { getLocalizedUrl, getLocalizedMatch } = await getI18n(Astro.locals.curren
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div> <div>
{title && <h3 class="font-serif font-3xl">{title}</h3>} {
title && (
<h3 class="font-serif font-3xl" lang={lang}>
{title}
</h3>
)
}
<section> <section>
{ {
folders.map(({ slug, translations, icon }) => ( folders.map(({ slug, translations, icon }) => {
<FolderCard const { name, language } = getLocalizedMatch(translations);
title={getLocalizedMatch(translations).name} return (
icon={icon} <FolderCard
href={getLocalizedUrl(`/folders/${slug}`)} title={name}
/> lang={language}
)) icon={icon}
href={getLocalizedUrl(`/folders/${slug}`)}
/>
);
})
} }
</section> </section>
</div> </div>

View File

@ -15,10 +15,16 @@ if (image instanceof Response) {
const { getLocalizedMatch, formatDate, t } = await getI18n(Astro.locals.currentLocale); const { getLocalizedMatch, formatDate, t } = await getI18n(Astro.locals.currentLocale);
const { filename, translations, attributes, credits, createdAt, updatedAt } = image; const { filename, translations, attributes, credits, createdAt, updatedAt } = image;
const { pretitle, title, subtitle, description } = const { pretitle, title, subtitle, description, language } =
translations.length > 0 translations.length > 0
? getLocalizedMatch(translations) ? getLocalizedMatch(translations)
: { pretitle: undefined, title: filename, subtitle: undefined, description: undefined }; : {
pretitle: undefined,
title: filename,
subtitle: undefined,
description: undefined,
language: undefined,
};
const metaAttributes = [ const metaAttributes = [
...(filename && title !== filename ...(filename && title !== filename
@ -60,6 +66,7 @@ const metaAttributes = [
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
description={description} description={description}
lang={language}
filename={filename} filename={filename}
attributes={attributes} attributes={attributes}
metaAttributes={metaAttributes} metaAttributes={metaAttributes}

View File

@ -21,8 +21,10 @@ const { t, getLocalizedMatch, getLocalizedUrl } = await getI18n(Astro.locals.cur
const { username, languages, avatar, translations } = recorder; const { username, languages, avatar, translations } = recorder;
const { biography } = const { biography, language } =
translations.length > 0 ? getLocalizedMatch(translations) : { biography: undefined }; translations.length > 0
? getLocalizedMatch(translations)
: { biography: undefined, language: undefined };
const additionalAttributes: Attribute[] = []; const additionalAttributes: Attribute[] = [];
@ -76,7 +78,7 @@ if (languages.length > 0) {
<Attributes attributes={additionalAttributes} /> <Attributes attributes={additionalAttributes} />
) )
} }
{biography && <RichText content={biography} />} {biography && <RichText content={biography} context={{ lang: language }} />}
</div> </div>
</AsideLayout> </AsideLayout>
</AppLayout> </AppLayout>

View File

@ -32,7 +32,7 @@ const {
thumbnail, thumbnail,
} = video; } = video;
const { pretitle, title, subtitle, description } = getLocalizedMatch(translations); const { pretitle, title, subtitle, description, language } = getLocalizedMatch(translations);
const metaAttributes = [ const metaAttributes = [
...(filename && title !== filename ...(filename && title !== filename
@ -79,8 +79,8 @@ const metaAttributes = [
<VideoPlayer class="video_id-video-player" video={video} /> <VideoPlayer class="video_id-video-player" video={video} />
<div id="info"> <div id="info">
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} /> <AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} lang={language} />
{description && <RichText content={description} />} {description && <RichText content={description} context={{ lang: language }} />}
{attributes.length > 0 && <Attributes attributes={attributes} />} {attributes.length > 0 && <Attributes attributes={attributes} />}
{credits.length > 0 && <Credits credits={credits} />} {credits.length > 0 && <Credits credits={credits} />}
{metaAttributes.length > 0 && <Attributes attributes={metaAttributes} />} {metaAttributes.length > 0 && <Attributes attributes={metaAttributes} />}

View File

@ -1,177 +1,177 @@
{ {
"disclaimer": "Usage subject to terms: https://openexchangerates.org/terms", "disclaimer": "Usage subject to terms: https://openexchangerates.org/terms",
"license": "https://openexchangerates.org/license", "license": "https://openexchangerates.org/license",
"timestamp": 1717311606, "timestamp": 1717441200,
"base": "USD", "base": "USD",
"rates": { "rates": {
"AED": 3.673, "AED": 3.6729,
"AFN": 71.74764, "AFN": 71.000004,
"ALL": 92.774572, "ALL": 92.614283,
"AMD": 388.026453, "AMD": 387.131734,
"ANG": 1.800967, "ANG": 1.801096,
"AOA": 856.5, "AOA": 855.5,
"ARS": 895.75, "ARS": 896.5042,
"AUD": 1.501051, "AUD": 1.498084,
"AWG": 1.8, "AWG": 1.8025,
"AZN": 1.7, "AZN": 1.7,
"BAM": 1.801335, "BAM": 1.803561,
"BBD": 2, "BBD": 2,
"BDT": 117.319831, "BDT": 117.373138,
"BGN": 1.80376, "BGN": 1.79623,
"BHD": 0.376146, "BHD": 0.376811,
"BIF": 2869.825082, "BIF": 2877.75,
"BMD": 1, "BMD": 1,
"BND": 1.350863, "BND": 1.349791,
"BOB": 6.919641, "BOB": 6.905296,
"BRL": 5.2463, "BRL": 5.224,
"BSD": 1, "BSD": 1,
"BTC": 0.000014753422, "BTC": 0.000014435756,
"BTN": 83.412383, "BTN": 83.083402,
"BWP": 13.698365, "BWP": 13.689724,
"BYN": 3.270274, "BYN": 3.270594,
"BZD": 2.014276, "BZD": 2.014404,
"CAD": 1.364236, "CAD": 1.364612,
"CDF": 2795.476589, "CDF": 2837,
"CHF": 0.902724, "CHF": 0.895608,
"CLF": 0.033266, "CLF": 0.032781,
"CLP": 917.92, "CLP": 904.56,
"CNH": 7.2598, "CNH": 7.252605,
"CNY": 7.1059, "CNY": 7.2418,
"COP": 3857.42575, "COP": 3851.069651,
"CRC": 520.654, "CRC": 520.658934,
"CUC": 1, "CUC": 1,
"CUP": 25.75, "CUP": 25.75,
"CVE": 101.556543, "CVE": 102.05,
"CZK": 22.754, "CZK": 22.645533,
"DJF": 177.923131, "DJF": 177.735,
"DKK": 6.8745, "DKK": 6.844293,
"DOP": 59.129634, "DOP": 59.3,
"DZD": 134.3668, "DZD": 134.770808,
"EGP": 47.27, "EGP": 47.0981,
"ERN": 15, "ERN": 15,
"ETB": 57.429333, "ETB": 57.325,
"EUR": 0.921107, "EUR": 0.91766,
"FJD": 2.2605, "FJD": 2.26045,
"FKP": 0.785114, "FKP": 0.781407,
"GBP": 0.785114, "GBP": 0.781407,
"GEL": 2.79, "GEL": 2.78,
"GGP": 0.785114, "GGP": 0.781407,
"GHS": 14.73912, "GHS": 14.89875,
"GIP": 0.785114, "GIP": 0.781407,
"GMD": 67.775, "GMD": 67.75,
"GNF": 8595.915864, "GNF": 8600,
"GTQ": 7.763297, "GTQ": 7.763776,
"GYD": 209.182545, "GYD": 209.196966,
"HKD": 7.8194, "HKD": 7.81967,
"HNL": 24.690766, "HNL": 24.691996,
"HRK": 6.94517, "HRK": 6.914515,
"HTG": 132.700916, "HTG": 132.719171,
"HUF": 359.235427, "HUF": 358.221421,
"IDR": 16255.1, "IDR": 16208.885803,
"ILS": 3.71944, "ILS": 3.66821,
"IMP": 0.785114, "IMP": 0.781407,
"INR": 83.460852, "INR": 83.14386,
"IQD": 1309.048153, "IQD": 1310,
"IRR": 42225, "IRR": 42212.5,
"ISK": 137.45, "ISK": 137.19,
"JEP": 0.785114, "JEP": 0.781407,
"JMD": 155.514613, "JMD": 154.849165,
"JOD": 0.7089, "JOD": 0.7089,
"JPY": 157.25001195, "JPY": 156.2245,
"KES": 130.5, "KES": 130.5,
"KGS": 87.7, "KGS": 87.7,
"KHR": 4090.237951, "KHR": 4110,
"KMF": 454.250319, "KMF": 452.450238,
"KPW": 900, "KPW": 900,
"KRW": 1383.09, "KRW": 1372.792278,
"KWD": 0.306166, "KWD": 0.306594,
"KYD": 0.832788, "KYD": 0.832818,
"KZT": 446.714065, "KZT": 447.136257,
"LAK": 21470.433423, "LAK": 21525,
"LBP": 89485.773014, "LBP": 89550,
"LKR": 300.649394, "LKR": 301.873431,
"LRD": 193.899976, "LRD": 193.825,
"LSL": 18.731201, "LSL": 18.67,
"LYD": 4.848262, "LYD": 4.85,
"MAD": 9.928805, "MAD": 9.94875,
"MDL": 17.608826, "MDL": 17.599529,
"MGA": 4439.313241, "MGA": 4458.75,
"MKD": 56.69353, "MKD": 56.499588,
"MMK": 2098.457405, "MMK": 2098.684565,
"MNT": 3450, "MNT": 3450,
"MOP": 8.047893, "MOP": 8.050001,
"MRU": 39.491596, "MRU": 39.5,
"MUR": 46.029291, "MUR": 46.280916,
"MVR": 15.45, "MVR": 15.4,
"MWK": 1731.611843, "MWK": 1732.5,
"MXN": 16.99024, "MXN": 17.725172,
"MYR": 4.7075, "MYR": 4.707,
"MZN": 63.899991, "MZN": 63.87499,
"NAD": 18.731201, "NAD": 18.67,
"NGN": 1487, "NGN": 1487,
"NIO": 36.785631, "NIO": 36.785319,
"NOK": 10.5091, "NOK": 10.46963,
"NPR": 133.459805, "NPR": 132.933695,
"NZD": 1.625752, "NZD": 1.618351,
"OMR": 0.384904, "OMR": 0.384968,
"PAB": 1, "PAB": 1,
"PEN": 3.748377, "PEN": 3.74,
"PGK": 3.8945, "PGK": 3.888768,
"PHP": 58.516495, "PHP": 58.674997,
"PKR": 278.10123, "PKR": 278.55,
"PLN": 3.939029, "PLN": 3.925167,
"PYG": 7533.099348, "PYG": 7522.122768,
"QAR": 3.646144, "QAR": 3.64125,
"RON": 4.5904, "RON": 4.5652,
"RSD": 108.097, "RSD": 107.475,
"RUB": 90.415913, "RUB": 89.885006,
"RWF": 1297.53527, "RWF": 1300.25,
"SAR": 3.751069, "SAR": 3.750763,
"SBD": 8.489576, "SBD": 8.489576,
"SCR": 13.775731, "SCR": 13.853072,
"SDG": 601, "SDG": 586,
"SEK": 10.5301, "SEK": 10.410035,
"SGD": 1.3534, "SGD": 1.3466,
"SHP": 0.785114, "SHP": 0.781407,
"SLL": 20969.5, "SLL": 20969.5,
"SOS": 571.125529, "SOS": 571,
"SRD": 32.1415, "SRD": 32.2345,
"SSP": 130.26, "SSP": 130.26,
"STD": 22281.8, "STD": 22281.8,
"STN": 22.565047, "STN": 22.825,
"SVC": 8.744186, "SVC": 8.743966,
"SYP": 2512.53, "SYP": 2512.53,
"SZL": 18.741997, "SZL": 18.67,
"THB": 36.690767, "THB": 36.6065,
"TJS": 10.717385, "TJS": 10.733365,
"TMT": 3.5, "TMT": 3.51,
"TND": 3.11625, "TND": 3.1005,
"TOP": 2.36145, "TOP": 2.361474,
"TRY": 32.257201, "TRY": 32.198301,
"TTD": 6.781487, "TTD": 6.782615,
"TWD": 32.4795, "TWD": 32.315798,
"TZS": 2603.181128, "TZS": 2600,
"UAH": 40.526826, "UAH": 40.385557,
"UGX": 3805.645029, "UGX": 3814.103609,
"USD": 1, "USD": 1,
"UYU": 38.728685, "UYU": 38.803839,
"UZS": 12610.529246, "UZS": 12592.16117,
"VES": 36.491554, "VES": 36.488764,
"VND": 25448.534056, "VND": 25442.075892,
"VUV": 118.722, "VUV": 118.722,
"WST": 2.8, "WST": 2.8,
"XAF": 604.206708, "XAF": 601.945767,
"XAG": 0.03288554, "XAG": 0.03272466,
"XAU": 0.00042967, "XAU": 0.00042591,
"XCD": 2.70255, "XCD": 2.70255,
"XDR": 0.755146, "XDR": 0.755079,
"XOF": 604.206708, "XOF": 601.945767,
"XPD": 0.00109906, "XPD": 0.00109421,
"XPF": 109.917326, "XPF": 109.506015,
"XPT": 0.00096453, "XPT": 0.00098505,
"YER": 250.400036, "YER": 250.375049,
"ZAR": 18.79534, "ZAR": 18.53245,
"ZMW": 27.138531, "ZMW": 25.998949,
"ZWL": 322 "ZWL": 322
} }
} }

View File

@ -1,6 +1,7 @@
export type Attribute = { export type Attribute = {
icon: string; icon: string;
title: string; title: string;
values: { name: string; href?: string }[]; lang?: string | undefined;
values: { name: string; href?: string | undefined; lang?: string | undefined }[];
withBorder?: boolean | undefined; withBorder?: boolean | undefined;
}; };

View File

@ -1,5 +1,4 @@
export type RichTextContext = { export type RichTextContext = {
depth: number; depth: number;
lang?: string | undefined;
}; };
export const defaultContext: RichTextContext = { depth: 1 };