Fix PR review comments on cookie banner i18n

Address locale normalization for region-tagged values, guard
language detection for non-DOM runtimes, validate DefaultLanguage
on update, pass translated texts through the deactivation flow,
handle slug collisions in migration, add organizations FK, fix
consent migration from name-keyed to slug-keyed data, render all
template placeholders in previews, and wrap helper text for i18n.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-23 23:41:37 +04:00
parent fa7a9c96c1
commit 2b6f131f43
17 changed files with 89 additions and 55 deletions

View File

@@ -59,7 +59,10 @@ export function EditCategoryForm({
/>
<div className="flex items-center gap-2">
<Button
onClick={() => onSave(editName, editSlug, editDescription)}
onClick={() => {
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(editSlug)) return;
onSave(editName, editSlug, editDescription);
}}
disabled={isUpdating}
>
{isUpdating ? __("Saving...") : __("Save")}

View File

@@ -68,11 +68,6 @@ export default function CookieBannerTranslationsPage({
const banner = data.node;
const existingLanguages = useMemo(
() => banner.translations.map(t => t.language),
[banner.translations],
);
const [selectedLanguage, setSelectedLanguage] = useState(
() => banner.defaultLanguage,
);
@@ -128,11 +123,7 @@ export default function CookieBannerTranslationsPage({
value={selectedLanguage}
onValueChange={setSelectedLanguage}
>
{SUPPORTED_LANGUAGES.filter(
l =>
existingLanguages.includes(l.code)
|| l.code === selectedLanguage,
).map(l => (
{SUPPORTED_LANGUAGES.map(l => (
<Option key={l.code} value={l.code}>
{l.label}
{l.code === banner.defaultLanguage ? ` (${__("default")})` : ""}

View File

@@ -28,7 +28,7 @@ function interpolateDescription(
description: string,
linkText: string,
): string {
return description.replace(
return description.replaceAll(
"{{privacy_policy_link}}",
linkText,
);
@@ -79,22 +79,23 @@ export function BannerPreview({
}}
>
{hasPlaceholder
? (
<>
{descriptionParts[0]}
<a
href="#"
onClick={e => e.preventDefault()}
style={{
color: "var(--probo-accent, #1a1a1a)",
textDecoration: "underline",
}}
>
{privacyPolicyLinkText}
</a>
{descriptionParts[1]}
</>
)
? descriptionParts.map((part, i) => (
<span key={i}>
{part}
{i < descriptionParts.length - 1 && (
<a
href="#"
onClick={e => e.preventDefault()}
style={{
color: "var(--probo-accent, #1a1a1a)",
textDecoration: "underline",
}}
>
{privacyPolicyLinkText}
</a>
)}
</span>
))
: (
interpolateDescription(bannerDescription, privacyPolicyLinkText)
)}

View File

@@ -44,6 +44,12 @@ export function PanelTranslationSection({
const visibleCategories = categories.filter(c => c.kind !== "UNCATEGORISED");
const translatedNecessaryName = (() => {
const necessaryCat = categories.find(c => c.kind === "NECESSARY");
if (!necessaryCat) return necessaryCategoryName;
return categoryTranslations?.[necessaryCat.id]?.name || necessaryCategoryName;
})();
const previewCategories = categories.map((c) => {
const translated = categoryTranslations?.[c.id];
return {
@@ -75,7 +81,7 @@ export function PanelTranslationSection({
<Field
label={__(TRANSLATION_LABELS.panel_description)}
>
<p className="text-xs text-txt-secondary mb-2">{"Use {{necessary_category}} to refer to the required cookies category name."}</p>
<p className="text-xs text-txt-secondary mb-2">{__("Use {{necessary_category}} to refer to the required cookies category name.")}</p>
<Textarea {...field} rows={3} />
</Field>
)}
@@ -146,7 +152,7 @@ export function PanelTranslationSection({
buttonRejectAll={buttonRejectAll}
buttonSave={buttonSave}
categories={previewCategories}
necessaryCategoryName={necessaryCategoryName}
necessaryCategoryName={translatedNecessaryName}
/>
</div>
</div>

View File

@@ -73,14 +73,13 @@ export function PlaceholderPreview({
</span>
<p style={{ margin: 0 }}>
{hasPlaceholder
? (
<>
{parts[0]}
<strong>{categoryName}</strong>
{parts[1]}
</>
)
: placeholderText.replace("{{category}}", categoryName)}
? parts.map((part, i) => (
<span key={i}>
{part}
{i < parts.length - 1 && <strong>{categoryName}</strong>}
</span>
))
: placeholderText}
</p>
<button
type="button"

View File

@@ -51,7 +51,7 @@ export function PlaceholderTranslationSection({
<Field
label={__(TRANSLATION_LABELS.placeholder_text)}
>
<p className="text-xs text-txt-secondary mb-2">{"Use {{category}} to refer to the content category."}</p>
<p className="text-xs text-txt-secondary mb-2">{__("Use {{category}} to refer to the content category.")}</p>
<Input {...field} />
</Field>
)}