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 {
description: RichTextContent | string;
lang?: string | undefined;
}
const { description } = Astro.props;
const { description, lang } = Astro.props;
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
@ -16,7 +17,7 @@ const { description } = Astro.props;
typeof description === "string" ? (
<p class="prose" set:html={description} />
) : (
<RichText content={description} />
<RichText content={description} context={{ lang }} />
)
}
<slot />

View File

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

View File

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

View File

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

View File

@ -32,6 +32,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<Metadata
icon={icon}
title={translation.name}
lang={translation.language}
values={[{ name: formatNumber(value) }]}
withBorder={false}
/>
@ -42,6 +43,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<Metadata
icon={icon}
title={translation.name}
lang={translation.language}
values={[{ name: value }]}
withBorder={false}
/>
@ -52,10 +54,15 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<Metadata
icon={icon}
title={translation.name}
values={value.map(({ translations, page }) => ({
name: getLocalizedMatch(translations).name,
...(page ? { href: getLocalizedUrl(`/pages/${page.slug}`) } : {}),
}))}
lang={translation.language}
values={value.map(({ translations, page }) => {
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 {
block: GenericBlock;
lang?: string | undefined;
}
const { block } = Astro.props;
const { block, lang } = Astro.props;
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
{
isBlockLineBlock(block) ? (
<LineBlock block={block} />
<LineBlock block={block} lang={lang} />
) : isBlockCueBlock(block) ? (
<CueBlock block={block} />
<CueBlock block={block} lang={lang} />
) : (
<ErrorMessage
title={`Unknown block type: ${block.blockType}`}

View File

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

View File

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

View File

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

View File

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

View File

@ -32,6 +32,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<InlineMetadata
icon={icon}
title={translation.name}
lang={translation.language}
values={[{ name: formatNumber(value) }]}
withBorder={false}
/>
@ -42,6 +43,7 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<InlineMetadata
icon={icon}
title={translation.name}
lang={translation.language}
values={[{ name: value }]}
withBorder={false}
/>
@ -52,10 +54,15 @@ const { getLocalizedMatch, getLocalizedUrl, formatNumber } = await getI18n(
<InlineMetadata
icon={icon}
title={translation.name}
values={value.map(({ translations, page }) => ({
name: getLocalizedMatch(translations).name,
...(page ? { href: getLocalizedUrl(`/pages/${page.slug}`) } : {}),
}))}
lang={translation.language}
values={value.map(({ translations, page }) => {
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 {}
const { icon, title, values } = Astro.props;
const { icon, title, values, lang: titleLang } = Astro.props;
if (values.length === 0) return;
---
@ -14,9 +14,11 @@ if (values.length === 0) return;
<div id="container">
<div class="title">
<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 class="font-xs">{values.map(({ name }) => name).join(", ")}</div>
</div>
{/* ------------------------------------------- CSS -------------------------------------------- */}
@ -34,5 +36,11 @@ if (values.length === 0) return;
color: var(--color-base-750);
flex-shrink: 0;
}
& > #values {
& > span:not(:last-child)::after {
content: ", ";
}
}
}
</style>

View File

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

View File

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

View File

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

View File

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

View File

@ -19,6 +19,7 @@ interface Props {
pretitle?: string | undefined;
title: string;
subtitle?: string | undefined;
lang?: string | undefined;
href?: string | undefined;
attributes?: ComponentProps<typeof InlineAttributes>["attributes"];
disableRoundedTop?: boolean;
@ -40,6 +41,7 @@ const {
disableRoundedTop = false,
icon = "material-symbols:unknown-document",
iconHoverLabel = t("global.previewTypes.unknown"),
lang,
} = Astro.props;
/* 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">
{
smallTitle ? (
<p class="font-l">{title}</p>
<p class="font-l" lang={lang}>
{title}
</p>
) : (
<p>
<p lang={lang}>
{pretitle && (
<span id="pretitle" class="font-s">
{pretitle}

View File

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

View File

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

View File

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

View File

@ -2,15 +2,16 @@
import type { RichTextContent } from "src/shared/payload/payload-sdk";
import RTNode from "./components/RTNode.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";
interface Props {
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 ------------------------------------------- */}

View File

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

View File

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

View File

@ -15,7 +15,7 @@ const { node, context } = Astro.props;
{
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) => (
<RTNode node={node} context={context} />
))}

View File

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

View File

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

View File

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

View File

@ -15,12 +15,15 @@ const {
label,
target = undefined,
rel = undefined,
lang,
} = formatEndpointSource(source);
---
{/* ------------------------------------------- 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 -------------------------------------------- */}

View File

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

View File

@ -4,14 +4,15 @@ import { Icon } from "astro-icon/components";
interface Props {
icon: string;
title: string;
lang?: string | undefined;
}
const { icon, title } = Astro.props;
const { icon, title, lang } = Astro.props;
---
<div>
<Icon name={icon} width={24} height={24} />
<p class="font-2xl">{title}</p>
<p class="font-2xl" lang={lang}>{title}</p>
</div>
{/* ------------------------------------------- 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) {
case "url":
case "url": {
return {
href: source.url,
typeLabel: t("global.sources.typeLabel.url"),
@ -246,8 +255,9 @@ export const getI18n = async (locale: string) => {
target: "_blank",
rel: "noopener noreferrer",
};
}
case "collectible":
case "collectible": {
const rangeLabel = (() => {
switch (source.range?.type) {
case "timestamp":
@ -271,46 +281,62 @@ export const getI18n = async (locale: string) => {
}
})();
const translation = getLocalizedMatch(source.collectible.translations);
return {
href: getLocalizedUrl(`/collectibles/${source.collectible.slug}`),
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 {
href: getLocalizedUrl(`/pages/${source.page.slug}`),
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 {
href: getLocalizedUrl(`/folders/${source.folder.slug}`),
typeLabel: t("global.sources.typeLabel.folder"),
label: getLocalizedMatch(source.folder.translations).name,
lang: translation.language,
};
}
case "scans":
case "scans": {
const translation = getLocalizedMatch(source.collectible.translations);
return {
href: getLocalizedUrl(`/collectibles/${source.collectible.slug}/scans`),
typeLabel: t("global.sources.typeLabel.scans"),
label: formatInlineTitle(getLocalizedMatch(source.collectible.translations)),
lang: translation.language,
};
}
case "gallery":
case "gallery": {
const translation = getLocalizedMatch(source.collectible.translations);
return {
href: getLocalizedUrl(`/collectibles/${source.collectible.slug}/gallery`),
typeLabel: t("global.sources.typeLabel.gallery"),
label: formatInlineTitle(getLocalizedMatch(source.collectible.translations)),
lang: translation.language,
};
}
default:
default: {
return {
href: "/404",
label: `Invalid type ${source["type"]}`,
typeLabel: "Error",
};
}
}
};

View File

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

View File

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

View File

@ -32,7 +32,7 @@ const {
thumbnail,
} = audio;
const { pretitle, title, subtitle, description } = getLocalizedMatch(translations);
const { pretitle, title, subtitle, description, language } = getLocalizedMatch(translations);
const metaAttributes = [
...(filename && title !== filename
@ -79,8 +79,8 @@ const metaAttributes = [
<AudioPlayer audio={audio} class="audio_id-audio-player" />
<div id="info">
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
{description && <RichText content={description} />}
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} lang={language} />
{description && <RichText content={description} context={{ lang: language }} />}
{attributes.length > 0 && <Attributes attributes={attributes} />}
{credits.length > 0 && <Credits credits={credits} />}
{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 { getI18n } from "src/i18n/i18n";
import { Collections, type EndpointCollectible } from "src/shared/payload/payload-sdk";
import { formatInlineTitle } from "src/utils/format";
import Card from "components/Card.astro";
import AudioPlayer from "components/AudioPlayer.astro";
import VideoPlayer from "components/VideoPlayer.astro";
import InlineAttributes from "components/InlineAttributes.astro";
import { formatInlineTitle } from "src/utils/format";
interface Props {
content: EndpointCollectible["contents"][number];
@ -33,6 +33,34 @@ const href = (() => {
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 ------------------------------------------- */}
@ -40,14 +68,8 @@ const href = (() => {
<Card href={href} class="content_row-card">
<div id="title">
{
content.relationTo === Collections.GenericContents ? (
<p>{getLocalizedMatch(content.value.translations).name}</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>
title ? (
<p lang={language}>{title}</p>
) : (
<ErrorMessage
title="Unknown content type"
@ -68,7 +90,9 @@ const href = (() => {
) : range.type === "timeRange" ? (
range.start
) : range.type === "other" ? (
<RichText content={getLocalizedMatch(range.translations).note} />
<RichText
content={getLocalizedMatch(range.translations).note}
/> /* TODO: Provide lang */
) : (
<ErrorMessage
title="Unknown range type"

View File

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

View File

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

View File

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

View File

@ -50,10 +50,15 @@ const hasOutsideObi = obi ? Object.keys(obi).some((value) => !value.includes("in
title={translation.title}
pretitle={translation.pretitle}
subtitle={translation.subtitle}
lang={translation.language}
/>
<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} />}
</div>

View File

@ -34,8 +34,8 @@ const meta = getLocalizedMatch(folder.translations);
description: meta.description && formatRichTextToString(meta.description),
}}
parentPages={folder.parentPages}>
<AppLayoutTitle title={meta.name} />
{meta.description && <AppLayoutDescription description={meta.description} />}
<AppLayoutTitle title={meta.name} lang={meta.language} />
{meta.description && <AppLayoutDescription description={meta.description} lang={meta.language} />}
<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.sections.length > 0 && (
<div id="sections">
{folder.sections.sections.map(({ subfolders, translations }) => (
<FoldersSection
folders={subfolders}
title={
getLocalizedMatch<{
language: string;
name: string | undefined;
}>(translations).name
}
/>
))}
{folder.sections.sections.map(({ subfolders, translations }) => {
const { language, name } = getLocalizedMatch(translations);
return <FoldersSection folders={subfolders} title={name} lang={language} />;
})}
</div>
)
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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