feat: SEO SPA editor integration
This commit is contained in:
1 parent
eb9876fde3
commit
470584400f
62 files changed
+7471
-58
No files matched your search
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user