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:
@@ -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")}
|
||||
|
||||
@@ -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")})` : ""}
|
||||
|
||||
@@ -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)
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user