feat: SEO SPA editor integration

This commit is contained in:
Florian Sylvain committed 2026-10-16 03:06:57 +02:00
1 parent eb9876fde3
commit 470584400f
62 files changed
+7471 -58

No files matched your search

+85
View File
@@ -0,0 +1,85 @@
import { t } from '@/i18n'
import type { DescriptionSource } from '@/lib/seoPreview'
import type { SeoSuggestion, SuggestionCode, SuggestionField } from '@/lib/seoTypes'
function descriptionLength(params: Record<string, number>) {
return t('The description is {0} characters. About 70 to 160 reads best in results.', [
params.length,
])
}
const MESSAGES: Record<SuggestionCode, (params: Record<string, number>) => string> = {
title_long: (params) =>
t('The meta title is {0} characters. Search engines may cut it off after about {1}.', [
params.length,
params.max,
]),
title_empty: () => t('The meta title is empty.'),
desc_short: descriptionLength,
desc_long: descriptionLength,
cover_alt: () => t('The cover image has no alt text. Add a short description of it.'),
cover_none: () => t('This article has no cover image.'),
slug_long: (params) =>
t('The URL slug is {0} characters. Shorter addresses are easier to share.', [
params.length,
]),
no_au: () => t('This article has no author.'),
no_h: () => t('The body has no headings. H2 headings help readers scan the article.'),
body_alt: (params) =>
params.count === 1
? t('1 image in the body has no alt text.')
: t('{0} images in the body have no alt text.', [params.count]),
short: (params) =>
t('The body is {0} characters. Longer articles tend to do better in search.', [
params.length,
]),
}
const LINK_LABELS: Record<SuggestionField, () => string> = {
seo_title: () => t('Go to meta title'),
seo_description: () => t('Go to meta description'),
cover_image_alt: () => t('Go to cover image'),
cover_image_id: () => t('Go to cover image'),
slug: () => t('Go to URL slug'),
author_ids: () => t('Go to authors'),
body: () => t('Go to body'),
}
export function suggestionMessage(suggestion: SeoSuggestion) {
const message = MESSAGES[suggestion.code]
return message ? message(suggestion.params ?? {}) : suggestion.code
}
export function suggestionLinkLabel(field: SuggestionField) {
const label = LINK_LABELS[field]
return label ? label() : ''
}
const CHIPS = {
article_title: () => t('Using article title'),
body: () => t('Using first sentence of the body'),
excerpt: () => t('Using the excerpt'),
site_description: () => t('Using the site default description'),
cover_image: () => t('Using cover image'),
site_image: () => t('Using site social image'),
no_image: () => t('No image'),
canonical: () => t('Using site address and slug'),
meta_title: () => t('Using meta title'),
meta_description: () => t('Using meta description'),
}
export type ChipKey = keyof typeof CHIPS
export function chipLabel(key: ChipKey) {
return CHIPS[key]()
}
export function descriptionChip(source: DescriptionSource | string) {
const keys: Record<string, ChipKey> = {
excerpt: 'excerpt',
body: 'body',
site: 'site_description',
}
const key = keys[source]
return key ? chipLabel(key) : null
}