Compare commits

..

2 Commits

Author SHA1 Message Date
Dhruwang 02e1f51678 fix: i18n strings 2026-05-20 18:51:28 +05:30
Dhruwang 4c538d3fca fix: render scheduled-plan-change description placeholders correctly
The pending plan change Alert was rendering literal "{plan}" and "{date}"
in the UI because the .replace() calls in pricing-table.tsx looked for
double-braced "{{plan}}" / "{{date}}" while the locale strings used
single braces.

Switch to idiomatic i18next interpolation: pass the values into t() and
update all 15 locale strings to use the {{plan}} / {{date}} placeholders
i18next expects. No more manual string surgery.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 18:37:51 +05:30
4 changed files with 17 additions and 19 deletions
@@ -2,7 +2,7 @@
import { z } from "zod";
import { ZId } from "@formbricks/types/common";
import { InvalidInputError, OperationNotAllowedError, ResourceNotFoundError } from "@formbricks/types/errors";
import { OperationNotAllowedError, ResourceNotFoundError, UnknownError } from "@formbricks/types/errors";
import { getEmailTemplateHtml } from "@/app/(app)/workspaces/[workspaceId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate";
import { capturePostHogEvent } from "@/lib/posthog";
import { getSurvey, updateSurvey } from "@/lib/survey/service";
@@ -176,7 +176,7 @@ export const generatePersonalLinksAction = authenticatedActionClient
);
if (!contactsResult || contactsResult.length === 0) {
throw new InvalidInputError("No contacts found for the selected segment");
throw new UnknownError("No contacts found for the selected segment");
}
capturePostHogEvent(
+1 -1
View File
@@ -2429,7 +2429,7 @@
"most_popular": "Самый популярный",
"pending_change_removed": "Запланированное изменение тарифа отменено.",
"pending_plan_badge": "Запланирован",
"pending_plan_change_description": "Твой тариф сменится на {plan} {date}.",
"pending_plan_change_description": "Твой тариф сменится на {plan} на {date}.",
"pending_plan_change_title": "Запланированное изменение тарифа",
"pending_plan_cta": "Запланирован",
"per_month": "в месяц",
@@ -436,17 +436,15 @@ export const PricingTable = ({
<Alert variant="info" className="max-w-4xl">
<AlertTitle>{t("workspace.settings.billing.pending_plan_change_title")}</AlertTitle>
<AlertDescription>
{t("workspace.settings.billing.pending_plan_change_description")
.replace("{{plan}}", getCurrentCloudPlanLabel(pendingChange.targetPlan, t))
.replace(
"{{date}}",
formatDateForDisplay(new Date(pendingChange.effectiveAt), locale, {
year: "numeric",
month: "short",
day: "numeric",
timeZone: "UTC",
})
)}
{t("workspace.settings.billing.pending_plan_change_description", {
plan: getCurrentCloudPlanLabel(pendingChange.targetPlan, t),
date: formatDateForDisplay(new Date(pendingChange.effectiveAt), locale, {
year: "numeric",
month: "short",
day: "numeric",
timeZone: "UTC",
}),
})}
</AlertDescription>
{hasBillingRights && (
<AlertButton onClick={() => void undoPendingChange()} loading={isPlanActionPending === "undo"}>
@@ -2,7 +2,7 @@
import { z } from "zod";
import { ZId } from "@formbricks/types/common";
import { InvalidInputError, OperationNotAllowedError, ResourceNotFoundError } from "@formbricks/types/errors";
import { OperationNotAllowedError, ResourceNotFoundError } from "@formbricks/types/errors";
import { ZSegmentCreateInput, ZSegmentFilters, ZSegmentUpdateInput } from "@formbricks/types/segment";
import { getOrganization } from "@/lib/organization/service";
import { capturePostHogEvent } from "@/lib/posthog";
@@ -49,7 +49,7 @@ export const createSegmentAction = authenticatedActionClient.inputSchema(ZSegmen
const surveyWorkspaceId = await getWorkspaceIdFromSurveyId(parsedInput.surveyId);
if (surveyWorkspaceId !== parsedInput.workspaceId) {
throw new InvalidInputError("Survey and segment are not in the same workspace");
throw new Error("Survey and segment are not in the same workspace");
}
}
@@ -82,7 +82,7 @@ export const createSegmentAction = authenticatedActionClient.inputSchema(ZSegmen
if (!parsedFilters.success) {
const errMsg =
parsedFilters.error.issues.find((issue) => issue.code === "custom")?.message || "Invalid filters";
throw new InvalidInputError(errMsg);
throw new Error(errMsg);
}
const segment = await createSegment(parsedInput);
@@ -139,7 +139,7 @@ export const updateSegmentAction = authenticatedActionClient.inputSchema(ZUpdate
if (!parsedFilters.success) {
const errMsg =
parsedFilters.error.issues.find((issue) => issue.code === "custom")?.message || "Invalid filters";
throw new InvalidInputError(errMsg);
throw new Error(errMsg);
}
await checkForRecursiveSegmentFilter(parsedFilters.data, parsedInput.segmentId);
@@ -169,7 +169,7 @@ export const loadNewSegmentAction = authenticatedActionClient
const segmentWorkspaceId = await getWorkspaceIdFromSegmentId(parsedInput.segmentId);
if (surveyWorkspaceId !== segmentWorkspaceId) {
throw new InvalidInputError("Segment and survey are not in the same workspace");
throw new Error("Segment and survey are not in the same workspace");
}
const organizationId = await getOrganizationIdFromSurveyId(parsedInput.surveyId);