Add translations for tags and improve feed
This commit is contained in:
parent
ecb8618a67
commit
1dda4c9af8
12 changed files with 75 additions and 24 deletions
src
|
@ -145,7 +145,7 @@ const copyrightedCharacters = z
|
|||
`"copyrightedCharacters" cannot mix empty catch-all key with other keys`,
|
||||
)
|
||||
.default({});
|
||||
/** A record of the format `{ en: string, tok?: string, ... }`. */
|
||||
/** A record with a mandatory `en` value and optional strings for the remaining languages. */
|
||||
const langRecord = z.object({ [DEFAULT_LANG]: z.string() }).and(z.record(lang, z.string()));
|
||||
/** Common attributes for published content (stories + games). */
|
||||
const publishedContent = z
|
||||
|
@ -241,7 +241,6 @@ const publishedContent = z
|
|||
)
|
||||
.refine(({ isDraft, pubDate }) => isDraft || pubDate, `Missing "pubDate" for published content`)
|
||||
.refine(({ isDraft, description }) => isDraft || description, `Missing "description" for published content`)
|
||||
.refine(({ isDraft, pubDate }) => isDraft || pubDate, `Missing "pubDate" for published content`)
|
||||
.refine(({ isDraft, tags }) => isDraft || tags.length, `Missing "tags" for published content`);
|
||||
|
||||
// Types
|
||||
|
@ -312,14 +311,16 @@ const blogCollection = defineCollection({
|
|||
schema: ({ image }) =>
|
||||
z
|
||||
.object({
|
||||
// Optional parameters
|
||||
// Required parameters, but optional for drafts (isDraft === true)
|
||||
thumbnail: image().optional(),
|
||||
// Optional parameters
|
||||
thumbnailWidth: z.number().int().optional(),
|
||||
thumbnailHeight: z.number().int().optional(),
|
||||
prev: reference("blog").nullish(),
|
||||
next: reference("blog").nullish(),
|
||||
})
|
||||
.and(publishedContent),
|
||||
.and(publishedContent)
|
||||
.refine(({ isDraft, thumbnail }) => isDraft || thumbnail, `Missing "thumbnail" for published blog post`),
|
||||
});
|
||||
|
||||
// Data collections
|
||||
|
@ -364,7 +365,7 @@ const tagCategoriesCollection = defineCollection({
|
|||
.array(
|
||||
z.object({
|
||||
name: langRecord.or(z.string()),
|
||||
description: z.string().trim().optional(),
|
||||
description: langRecord.or(z.string().trim()).optional(),
|
||||
related: z.array(z.string()).default([]),
|
||||
}),
|
||||
)
|
||||
|
|
|
@ -15,7 +15,7 @@ posts:
|
|||
furaffinity: https://www.furaffinity.net/view/58363412/
|
||||
inkbunny: https://inkbunny.net/s/3442516
|
||||
sofurry: https://www.sofurry.com/view/2185882
|
||||
# sofurrybeta: TODO
|
||||
sofurrybeta: https://sofurrybeta.com/s/znJELqqm
|
||||
weasyl: https://www.weasyl.com/~badmanners/submissions/2422380/good-pet
|
||||
mastodon: https://meow.social/@BadManners/113260697648221209
|
||||
tags:
|
||||
|
@ -73,7 +73,7 @@ Sitting on a mess of his anal juices over the carpet, the otter hummed as he adm
|
|||
|
||||
"Hmmmf..." Jake carefully got up to sit back on the couch. "You were a good pet, but you make for an even better toy..."
|
||||
|
||||
Brad spoke apprehensively. "M-Master, I–"
|
||||
Brad spoke in an apprehensive Manner. "M-Master, I–"
|
||||
|
||||
"Quiet. Keep licking."
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ name: Types of vore
|
|||
index: 1
|
||||
tags:
|
||||
- name: { en: oral vore, tok: moku musi kepeken uta }
|
||||
description: Scenarios where prey are consumed by the predator's mouth.
|
||||
description:
|
||||
en: Scenarios where prey are consumed by the predator's mouth.
|
||||
tok: jan li moku musi e jan ante kepeken uta.
|
||||
- name: anal vore
|
||||
description: Scenarios where prey are consumed by the predator's butt/anus.
|
||||
- name: cock vore
|
||||
|
|
|
@ -8,7 +8,9 @@ tags:
|
|||
- name: taur predator
|
||||
description: Scenarios where at least one of the predators is a multi-legged centaur-like creature, with an animal lower body and anthropomorphic upper body.
|
||||
- name: { en: ambiguous predator, tok: sijelo pi jan pi wawa mute li ale }
|
||||
description: Scenarios where the body type of at least one of the predators is left ambiguous.
|
||||
description:
|
||||
en: Scenarios where the body type of at least one of the predators is left ambiguous.
|
||||
tok: jan pi moku musi pi wawa mute la toki tan sijelo li lon ala.
|
||||
- name: human prey
|
||||
description: Scenarios where at least one of the prey is a human person.
|
||||
- name: anthro prey
|
||||
|
@ -16,4 +18,6 @@ tags:
|
|||
- name: feral prey
|
||||
description: Scenarios where at least one of the predators is an animal based on a real or mythological creature.
|
||||
- name: { en: ambiguous prey, tok: sijelo pi jan pi wawa lili li ale }
|
||||
description: Scenarios where the body type of at least one of the predators is left ambiguous.
|
||||
description:
|
||||
en: Scenarios where the body type of at least one of the predators is left ambiguous.
|
||||
tok: jan pi moku musi pi wawa lili la toki tan sijelo li lon ala.
|
||||
|
|
|
@ -14,7 +14,9 @@ tags:
|
|||
- name: non-binary predator
|
||||
description: Scenarios where at least one of the predators has a non-binary gender expression, be they genderless/agender, intersex, androgynous, gender-fluid, non-binary and/or non-binary-presenting, et cetera, regardless of undergoing or having undergone gender transition or not.
|
||||
- name: { en: ambiguous gender predator, tok: jan pi wawa mute li meli anu mije }
|
||||
description: Scenarios where the gender at least one of the predators is left ambiguous.
|
||||
description:
|
||||
en: Scenarios where the gender at least one of the predators is left ambiguous.
|
||||
tok: jan pi moku musi pi wawa mute la toki tan meli anu mije anu tonsi li lon ala.
|
||||
- name: male prey
|
||||
description: Scenarios where at least one of the prey is a man and/or male-presenting.
|
||||
- name: female prey
|
||||
|
@ -28,4 +30,6 @@ tags:
|
|||
- name: non-binary prey
|
||||
description: Scenarios where at least one of the predators has a non-binary gender expression, be they genderless/agender, intersex, androgynous, gender-fluid, non-binary and/or non-binary-presenting, et cetera, regardless of undergoing or having undergone gender transition or not.
|
||||
- name: { en: ambiguous gender prey, tok: jan pi wawa lili li meli anu mije }
|
||||
description: Scenarios where the gender at least one of the predators is left ambiguous.
|
||||
description:
|
||||
en: Scenarios where the gender at least one of the predators is left ambiguous.
|
||||
tok: jan pi moku musi pi wawa lili la toki tan meli anu mije anu tonsi li lon ala.
|
||||
|
|
|
@ -2,7 +2,9 @@ name: Willingness
|
|||
index: 5
|
||||
tags:
|
||||
- name: { en: willing predator, tok: jan pi wawa mute li wile e moku musi }
|
||||
description: Scenarios where at least one of the predators participates in vore willingly.
|
||||
description:
|
||||
en: Scenarios where at least one of the predators participates in vore willingly.
|
||||
tok: jan pi moku musi pi wawa mute la jan li wile e moku musi.
|
||||
- name: semi-willing predator
|
||||
description: Scenarios where the willingness of at least one of the predators might be partial, oscillating between willing and unwilling, somewhere in-between, or ambiguous.
|
||||
- name: unwilling predator
|
||||
|
@ -14,6 +16,8 @@ tags:
|
|||
- name: semi-willing prey
|
||||
description: Scenarios where the willingness of at least one of the prey might be partial, oscillating between willing and unwilling, somewhere in-between, or ambiguous.
|
||||
- name: { en: unwilling prey, tok: jan pi wawa lili li wile ala e moku musi }
|
||||
description: Scenarios where at least one of the prey participates in vore unwillingly.
|
||||
description:
|
||||
en: Scenarios where at least one of the prey participates in vore unwillingly.
|
||||
tok: jan pi moku musi pi wawa lili la jan li wile ala e moku musi.
|
||||
- name: asleep prey
|
||||
description: Scenarios where at least one of the predators participates in vore while asleep.
|
||||
|
|
|
@ -6,9 +6,13 @@ tags:
|
|||
- name: commission
|
||||
description: Stories made as part of a commission to someone else.
|
||||
- name: { en: flash fiction, tok: lipu lili }
|
||||
description: Short-format stories with no more than 2,500 words.
|
||||
description:
|
||||
en: Short-format stories with no more than 2,500 words.
|
||||
tok: lipu li jo e nanpa nimi lili.
|
||||
- name: toki pona
|
||||
description: Stories written in toki pona, the language of good.
|
||||
description:
|
||||
en: Stories written in toki pona, the language of good.
|
||||
tok: lipu li kepeken toki pona.
|
||||
- name: behind the scenes
|
||||
description: Content where I go over the process of making other content.
|
||||
- name: retrospective
|
||||
|
|
|
@ -126,7 +126,7 @@ const UI_STRINGS = {
|
|||
},
|
||||
"published_content/syndication_furaffinity": {
|
||||
en: "Fur Affinity",
|
||||
tok: "lipu Panapinisi",
|
||||
tok: "lipu Panwapinisi",
|
||||
},
|
||||
"published_content/syndication_inkbunny": {
|
||||
en: "Inkbunny",
|
||||
|
@ -134,7 +134,7 @@ const UI_STRINGS = {
|
|||
},
|
||||
"published_content/syndication_sofurry": {
|
||||
en: "SoFurry",
|
||||
tok: "lipu Sopanli",
|
||||
tok: "lipu Sopanwi",
|
||||
},
|
||||
"published_content/syndication_weasyl": {
|
||||
en: "Weasyl",
|
||||
|
|
|
@ -66,8 +66,22 @@ const categorizedTags: Record<string, { name: string | null; description?: strin
|
|||
(await getCollection("tag-categories")).flatMap((category) =>
|
||||
category.data.tags.map<[string, { name: string | null; description?: string }]>(({ name, description }) =>
|
||||
typeof name === "string"
|
||||
? [name, { name, description }]
|
||||
: [name[DEFAULT_LANG], { name: name[props.lang] ?? null, description }],
|
||||
? [
|
||||
name,
|
||||
{
|
||||
name,
|
||||
description:
|
||||
typeof description === "object" ? (description[props.lang] ?? description[DEFAULT_LANG]) : description,
|
||||
},
|
||||
]
|
||||
: [
|
||||
name[DEFAULT_LANG],
|
||||
{
|
||||
name: name[props.lang] ?? null,
|
||||
description:
|
||||
typeof description === "object" ? (description[props.lang] ?? description[DEFAULT_LANG]) : description,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -3,6 +3,7 @@ import { getCollection } from "astro:content";
|
|||
import { slug } from "github-slugger";
|
||||
import GalleryLayout from "@layouts/GalleryLayout.astro";
|
||||
import { markdownToPlaintext } from "@utils/markdown_to_plaintext";
|
||||
import { DEFAULT_LANG } from "@i18n";
|
||||
|
||||
interface Tag {
|
||||
id: string;
|
||||
|
@ -38,7 +39,12 @@ const categorizedTags = tagCategories
|
|||
})
|
||||
.map((category) => {
|
||||
const tagList = category.data.tags.map<Tag>(({ name, description }) => {
|
||||
description = description && markdownToPlaintext(description).replaceAll(/\n+/g, " ");
|
||||
description =
|
||||
description &&
|
||||
markdownToPlaintext(typeof description === "object" ? description[DEFAULT_LANG] : description).replaceAll(
|
||||
/\n+/g,
|
||||
" ",
|
||||
);
|
||||
const tag = typeof name === "string" ? name : name.en;
|
||||
return { id: slug(tag), name: tag, description };
|
||||
});
|
||||
|
|
|
@ -62,7 +62,10 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
|||
if (key in acc) {
|
||||
throw new Error(`Duplicated tag "${key}" found in multiple tag-categories lists`);
|
||||
}
|
||||
acc[key] = { description, related: related.length > 0 ? related : undefined };
|
||||
acc[key] = {
|
||||
description: typeof description === "object" ? description[DEFAULT_LANG] : description,
|
||||
related: related.length > 0 ? related : undefined,
|
||||
};
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
|
|
|
@ -10,6 +10,7 @@ import { t, type Lang } from "@i18n";
|
|||
import type { AstroComponentFactory } from "astro/runtime/server/index.js";
|
||||
import mdxRenderer from "astro/jsx/server.js";
|
||||
import { htmlToAbsoluteUrls } from "./html_to_absolute_urls";
|
||||
import { formatCopyrightedCharacters } from "./format_copyrighted_characters";
|
||||
|
||||
export type FeedItem = RSSFeedItem &
|
||||
Required<Pick<RSSFeedItem, "title" | "pubDate" | "link" | "description" | "categories" | "content">>;
|
||||
|
@ -39,6 +40,7 @@ export async function storyFeedItem(
|
|||
slug: CollectionEntry<"stories">["slug"],
|
||||
content: AstroComponentFactory,
|
||||
): Promise<FeedItem> {
|
||||
const copyrightedCharacters = await formatCopyrightedCharacters(data.copyrightedCharacters);
|
||||
return {
|
||||
title: `New story! "${data.title}"`,
|
||||
pubDate: toNoonUTCDate(data.pubDate),
|
||||
|
@ -74,7 +76,10 @@ export async function storyFeedItem(
|
|||
: "") +
|
||||
`<hr><p><em>${t(data.lang, "story/warnings", data.wordCount, data.contentWarning)}</em></p>` +
|
||||
`<hr>${await container.renderToString(content)}` +
|
||||
`<hr><h2>Description</h2>${await markdown(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))}`,
|
||||
`<hr><h2>Description</h2>${await markdown(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))}` +
|
||||
(copyrightedCharacters.length > 0
|
||||
? `<ul>${copyrightedCharacters.map(({ user, characters }) => "<li>" + t(data.lang, "characters/characters_are_copyrighted_by", getLinkForUser(user, data.lang), characters) + "</li>")}</ul>`
|
||||
: ""),
|
||||
site,
|
||||
),
|
||||
{
|
||||
|
@ -90,6 +95,7 @@ export async function gameFeedItem(
|
|||
slug: CollectionEntry<"games">["slug"],
|
||||
content: AstroComponentFactory,
|
||||
): Promise<FeedItem> {
|
||||
const copyrightedCharacters = await formatCopyrightedCharacters(data.copyrightedCharacters);
|
||||
return {
|
||||
title: `New game! "${data.title}"`,
|
||||
pubDate: toNoonUTCDate(data.pubDate),
|
||||
|
@ -112,7 +118,10 @@ export async function gameFeedItem(
|
|||
`<hr><p>${t(data.lang, "game/platforms", data.platforms)}</p>` +
|
||||
`<hr><p><em>${data.contentWarning}</em></p>` +
|
||||
`<hr>${await container.renderToString(content)}` +
|
||||
`<hr><h2>Description</h2>${await markdown(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))}`,
|
||||
`<hr><h2>Description</h2>${await markdown(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))}` +
|
||||
(copyrightedCharacters.length > 0
|
||||
? `<ul>${copyrightedCharacters.map(({ user, characters }) => "<li>" + t(data.lang, "characters/characters_are_copyrighted_by", getLinkForUser(user, data.lang), characters) + "</li>")}</ul>`
|
||||
: ""),
|
||||
site,
|
||||
),
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue