Skip to content

Commit b4d6a4b

Browse files
committed
Share localized metadata sentence joining
1 parent 3e0a647 commit b4d6a4b

3 files changed

Lines changed: 27 additions & 21 deletions

File tree

web/i18n/audited-seo.ts

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
openGraphImageAlt,
33
openGraphImageTagline,
44
detailedSeoDescriptionCandidate,
5+
joinMetadataSentences,
56
seoDescription,
67
seoTitle,
78
shortSeoDescriptionCandidate,
@@ -67,23 +68,16 @@ function selectDescription(
6768
const tagline = openGraphImageTagline(locale);
6869
const contextualCandidates = authoredCandidates.flatMap((candidate) => [
6970
candidate,
70-
joinSentences(candidate, short),
71-
joinSentences(candidate, detailed),
72-
joinSentences(joinSentences(candidate, short), tagline),
71+
joinMetadataSentences(candidate, short),
72+
joinMetadataSentences(candidate, detailed),
73+
joinMetadataSentences(joinMetadataSentences(candidate, short), tagline),
7374
]);
7475
return seoDescription(locale, original, {
7576
minLength: 110,
7677
fallbackCandidates: contextualCandidates,
7778
});
7879
}
7980

80-
function joinSentences(first: string, second: string) {
81-
const leading = first.trim();
82-
const trailing = second.trim();
83-
const separator = /[.!?؟]$/u.test(leading) ? " " : ". ";
84-
return `${leading}${separator}${trailing}`;
85-
}
86-
8781
export function homeSeoCopy(locale: string, meta: SeoMessageLookup) {
8882
const title = selectTitle(locale, meta("title"), meta, [
8983
openGraphImageAlt(locale),
@@ -106,7 +100,7 @@ export function assetsSeoCopy(
106100
title: selectTitle(locale, t("metaTitle"), siteMeta, [t("title")]),
107101
description: selectDescription(locale, t("metaDescription"), [
108102
t("title"),
109-
joinSentences(t("title"), t("metaDescription")),
103+
joinMetadataSentences(t("title"), t("metaDescription")),
110104
t("description"),
111105
]),
112106
};
@@ -121,7 +115,7 @@ export function blogIndexSeoCopy(
121115
title: selectTitle(locale, t("metaTitle"), siteMeta, [t("title")]),
122116
description: selectDescription(locale, t("metaDescription"), [
123117
t("title"),
124-
joinSentences(t("title"), t("metaDescription")),
118+
joinMetadataSentences(t("title"), t("metaDescription")),
125119
]),
126120
};
127121
}
@@ -138,7 +132,7 @@ export function communitySeoCopy(
138132
]),
139133
description: selectDescription(locale, t("metaDescription"), [
140134
t("title"),
141-
joinSentences(t("title"), t("metaDescription")),
135+
joinMetadataSentences(t("title"), t("metaDescription")),
142136
t("description"),
143137
]),
144138
};
@@ -154,7 +148,7 @@ export function bestTerminalSeoCopy(
154148
description: selectDescription(locale, t("metaDescription"), [
155149
t("title"),
156150
t("cmuxBuiltFor"),
157-
joinSentences(t("title"), t("cmuxBuiltFor")),
151+
joinMetadataSentences(t("title"), t("cmuxBuiltFor")),
158152
]),
159153
};
160154
}
@@ -194,7 +188,7 @@ export function compareIndexSeoCopy(
194188
title: selectTitle(locale, t("metaTitle"), siteMeta, [t("title")]),
195189
description: selectDescription(locale, t("metaDescription"), [
196190
t("title"),
197-
joinSentences(t("title"), openGraphImageAlt(locale)),
191+
joinMetadataSentences(t("title"), openGraphImageAlt(locale)),
198192
t("intro"),
199193
]),
200194
};
@@ -222,9 +216,9 @@ export function comparePageSeoCopy(
222216
title: selectTitle(locale, t("metaTitle"), siteMeta, titleCandidates),
223217
description: selectDescription(locale, t("metaDescription"), [
224218
...descriptionCandidates,
225-
joinSentences(t("faqQ1"), t("faqA1")),
226-
joinSentences(t("faqQ2"), t("faqA2")),
227-
joinSentences(t("faqQ3"), t("faqA3")),
219+
joinMetadataSentences(t("faqQ1"), t("faqA1")),
220+
joinMetadataSentences(t("faqQ2"), t("faqA2")),
221+
joinMetadataSentences(t("faqQ3"), t("faqA3")),
228222
]),
229223
};
230224
}

web/i18n/seo.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ export function shortSeoDescriptionCandidate(locale: string) {
145145
return shortDescriptionSuffixes[locale] ?? shortDescriptionSuffixes.en;
146146
}
147147

148+
export function joinMetadataSentences(first: string, second: string) {
149+
const leading = first.trim();
150+
const trailing = second.trim();
151+
const separator = /[.!?؟]$/u.test(leading) ? " " : ". ";
152+
return `${leading}${separator}${trailing}`;
153+
}
154+
148155
export function seoDescription(
149156
locale: string,
150157
description: string,
@@ -164,9 +171,7 @@ export function seoDescription(
164171
: shortDescriptionSuffixes;
165172
const suffix = suffixes[locale] ?? suffixes.en;
166173
if (!trimmed.includes(suffix)) {
167-
const separator =
168-
/[.!?]$/.test(trimmed) || trimmed.endsWith("؟") ? " " : ". ";
169-
candidates.push(`${trimmed}${separator}${suffix}`);
174+
candidates.push(joinMetadataSentences(trimmed, suffix));
170175
}
171176
}
172177

web/tests/seo.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
buildAlternates,
99
canonicalUrl,
1010
hasLocalizedSeoCopy,
11+
joinMetadataSentences,
1112
openGraphDefaults,
1213
openGraphImageTagline,
1314
seoDescription,
@@ -66,6 +67,12 @@ describe("SEO metadata helpers", () => {
6667
expect(seoDescription("en", original)).toBe(original);
6768
});
6869

70+
test("joins metadata sentences without duplicating localized punctuation", () => {
71+
expect(joinMetadataSentences("សាកល្បង។", "បន្ទាប់")).toBe(
72+
"សាកល្បង។ បន្ទាប់",
73+
);
74+
});
75+
6976
test("preserves overbound authored copy when no complete candidate fits", () => {
7077
const description = `${"A".repeat(157)}👩🏽‍💻${"B".repeat(20)}`;
7178
expect(seoDescription("en", description)).toBe(description);

0 commit comments

Comments
 (0)