Compare commits

..

21 Commits

Author SHA1 Message Date
Dhruwang 8374eea770 fix: i18n lock 2026-04-17 15:15:06 +05:30
Dhruwang d3ccc623e0 Merge branch 'main' of https://github.com/formbricks/formbricks into fix/issue-7543-trial-conversion-template 2026-04-17 15:13:03 +05:30
Dhruwang Jariwala 60bd5cbeff fix: prevent environment ID leak in API error responses (#7753)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 08:38:32 +00:00
Dhruwang Jariwala b6a3a15379 fix: make other option input field mandatory when sole selection (#7751)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 08:06:00 +00:00
Johannes c68f214eff fix: keep sidebar switcher icons round with long labels (#7756)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Johannes <jobenjada@users.noreply.github.com>
2026-04-17 08:04:10 +00:00
Harsh Bhat c90ee84483 chore: Add survey to formbricks docs (#7746) 2026-04-16 12:13:55 +00:00
Dhruwang Jariwala dc1ee72594 chore: translation management revamp (scope 1) (#7733)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Johannes <johannes@formbricks.com>
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
2026-04-16 11:18:48 +00:00
Dhruwang Jariwala 924132287e fix: connect rating/NPS scale labels to label styling settings (#7738)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 10:59:59 +00:00
Dhruwang Jariwala e6f347aa07 fix: remove dark: variant classes from survey-ui to prevent host page style leakage (#7747) 2026-04-16 05:50:46 +00:00
Dhruwang Jariwala 367bc23dd4 fix: prevent offline replay from dropping survey blocks after completion (#7743) 2026-04-15 19:59:15 +00:00
XHamzaX a1a11b2bb8 fix: prevent OIDC button text overlap with 'last used' indicator (#7731)
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
2026-04-15 09:42:20 +00:00
Marius 0653c6a59f fix: strip @layer properties block to prevent host page CSS pollution (#7685)
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 06:58:35 +00:00
Anshuman Pandey b6d793e109 fix: fixes unique constraint error with singleUseId and surveyId (#7737) 2026-04-15 06:50:20 +00:00
Dhruwang Jariwala 439dd0b44e fix: add loading skeleton for responses page (#7700)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Johannes <johannes@formbricks.com>
2026-04-13 16:56:20 +00:00
Anshuman Pandey 2556f5e15d fix: add missing PostHog events (#7722)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:57:12 +00:00
Johannes cc0eec3bf0 feat: add auto-progress mode for rating and NPS surveys (#7709)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Johannes <jobenjada@users.noreply.github.com>
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
2026-04-13 11:22:50 +00:00
Johannes 4b009a8eb4 revert: enhance welcome card to support video uploads (#7712)
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
2026-04-13 08:17:05 +00:00
Johannes 2aaddf7306 fix: prevent TTC overcount for multi-question blocks (#7713)
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
2026-04-13 07:56:40 +00:00
Dhruwang Jariwala fb5d6145d0 fix: only show beforeunload warning when offline support is active (#7715) 2026-04-13 07:19:57 +00:00
Dhruwang Jariwala 59310bac93 fix: validate "Other" option text on required questions and remove duplicate response entry (#7716) 2026-04-13 07:05:08 +00:00
Niels Kaspers 8619916682 fix: fix duplicate block and misleading subheader in trial conversion template
- Block 3 now uses its own question ("What did you expect to do?")
  instead of duplicating Block 2's question
- Updated Block 5 subheader from "Please select one of the following
  options" to "Please describe below" to match the open text input type
- Added translations for the new Block 3 question across all locales

Fixes #7543
2026-03-23 09:49:08 +02:00
179 changed files with 3894 additions and 2877 deletions
@@ -475,7 +475,7 @@ export const MainNavigation = ({
);
const switcherIconClasses =
"flex h-9 w-9 items-center justify-center rounded-full bg-slate-100 text-slate-600";
"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-600";
const isInitialProjectsLoading = isWorkspaceDropdownOpen && !hasInitializedProjects && !workspaceLoadError;
return (
@@ -6,9 +6,11 @@ import {
TUserUpdateInput,
ZUserPersonalInfoUpdateInput,
} from "@formbricks/types/user";
import { getIsEmailUnique } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/lib/user";
import {
getIsEmailUnique,
verifyUserPassword,
} from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/lib/user";
import { EMAIL_VERIFICATION_DISABLED, PASSWORD_RESET_DISABLED } from "@/lib/constants";
import { verifyUserPassword } from "@/lib/user/password";
import { getUser, updateUser } from "@/lib/user/service";
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { AuthenticatedActionClientCtx } from "@/lib/utils/action-client/types/context";
@@ -1,9 +1,8 @@
import { beforeEach, describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database";
import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { verifyUserPassword } from "@/lib/user/password";
import { verifyPassword as mockVerifyPasswordImported } from "@/modules/auth/lib/utils";
import { getIsEmailUnique } from "./user";
import { getIsEmailUnique, verifyUserPassword } from "./user";
vi.mock("@/modules/auth/lib/utils", () => ({
verifyPassword: vi.fn(),
@@ -1,5 +1,42 @@
import { User } from "@prisma/client";
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { verifyPassword } from "@/modules/auth/lib/utils";
export const getUserById = reactCache(
async (userId: string): Promise<Pick<User, "password" | "identityProvider">> => {
const user = await prisma.user.findUnique({
where: {
id: userId,
},
select: {
password: true,
identityProvider: true,
},
});
if (!user) {
throw new ResourceNotFoundError("user", userId);
}
return user;
}
);
export const verifyUserPassword = async (userId: string, password: string): Promise<boolean> => {
const user = await getUserById(userId);
if (user.identityProvider !== "email" || !user.password) {
throw new InvalidInputError("Password is not set for this user");
}
const isCorrectPassword = await verifyPassword(password, user.password);
if (!isCorrectPassword) {
return false;
}
return true;
};
export const getIsEmailUnique = reactCache(async (email: string): Promise<boolean> => {
const user = await prisma.user.findUnique({
@@ -0,0 +1,22 @@
"use client";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { SkeletonLoader } from "@/modules/ui/components/skeleton-loader";
const Loading = () => {
return (
<PageContentWrapper>
<PageHeader pageTitle="" />
<div className="flex h-9 animate-pulse gap-2">
<div className="h-9 w-36 rounded-full bg-slate-200" />
<div className="h-9 w-36 rounded-full bg-slate-200" />
<div className="h-9 w-36 rounded-full bg-slate-200" />
<div className="h-9 w-36 rounded-full bg-slate-200" />
</div>
<SkeletonLoader type="summary" />
</PageContentWrapper>
);
};
export default Loading;
@@ -0,0 +1,23 @@
"use client";
import { useTranslation } from "react-i18next";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { SkeletonLoader } from "@/modules/ui/components/skeleton-loader";
const Loading = () => {
const { t } = useTranslation();
return (
<PageContentWrapper>
<PageHeader pageTitle={t("common.responses")} />
<div className="flex h-9 animate-pulse gap-1.5">
<div className="h-9 w-36 rounded-full bg-slate-200" />
<div className="h-9 w-36 rounded-full bg-slate-200" />
</div>
<SkeletonLoader type="responseTable" />
</PageContentWrapper>
);
};
export default Loading;
@@ -191,6 +191,61 @@ describe("getSurveySummaryMeta", () => {
expect(meta.dropOffPercentage).toBe(0);
expect(meta.ttcAverage).toBe(0);
});
test("uses block-level TTC to avoid multiplying by number of elements", () => {
const surveyWithOneBlockThreeElements: TSurvey = {
...mockBaseSurvey,
blocks: [
{
id: "block1",
name: "Block 1",
elements: [
{
id: "q1",
type: TSurveyElementTypeEnum.OpenText,
headline: { default: "Q1" },
required: false,
inputType: "text",
charLimit: { enabled: false },
},
{
id: "q2",
type: TSurveyElementTypeEnum.OpenText,
headline: { default: "Q2" },
required: false,
inputType: "text",
charLimit: { enabled: false },
},
{
id: "q3",
type: TSurveyElementTypeEnum.OpenText,
headline: { default: "Q3" },
required: false,
inputType: "text",
charLimit: { enabled: false },
},
] as TSurveyElement[],
},
],
questions: [],
};
const responses = [
{
id: "r1",
data: { q1: "a", q2: "b", q3: "c" },
updatedAt: new Date(),
contact: null,
contactAttributes: {},
language: "en",
ttc: { q1: 5000, q2: 5000, q3: 4800, _total: 14800 },
finished: true,
},
] as any;
const meta = getSurveySummaryMeta(surveyWithOneBlockThreeElements, responses, 1, mockQuotas);
expect(meta.ttcAverage).toBe(5000);
});
});
describe("getSurveySummaryDropOff", () => {
@@ -1094,7 +1094,9 @@ export const getResponsesForSummary = reactCache(
const transformedResponses: TSurveySummaryResponse[] = await Promise.all(
responses.map((responsePrisma) => {
return {
...responsePrisma,
id: responsePrisma.id,
data: (responsePrisma.data ?? {}) as TResponseData,
updatedAt: responsePrisma.updatedAt,
contact: responsePrisma.contact
? {
id: responsePrisma.contact.id as string,
@@ -1103,6 +1105,10 @@ export const getResponsesForSummary = reactCache(
)?.value as string,
}
: null,
contactAttributes: (responsePrisma.contactAttributes ?? {}) as TResponseContactAttributes,
language: responsePrisma.language,
ttc: (responsePrisma.ttc ?? {}) as TResponseTtc,
finished: responsePrisma.finished,
};
})
);
@@ -18,7 +18,6 @@ interface AirtableWrapperProps {
isEnabled: boolean;
webAppUrl: string;
locale: TUserLocale;
showReconnectButton?: boolean;
}
export const AirtableWrapper = ({
@@ -29,7 +28,6 @@ export const AirtableWrapper = ({
isEnabled,
webAppUrl,
locale,
showReconnectButton = false,
}: AirtableWrapperProps) => {
const [isConnected, setIsConnected] = useState(
airtableIntegration ? airtableIntegration.config?.key : false
@@ -51,8 +49,6 @@ export const AirtableWrapper = ({
setIsConnected={setIsConnected}
surveys={surveys}
locale={locale}
showReconnectButton={showReconnectButton}
handleAirtableAuthorization={handleAirtableAuthorization}
/>
) : (
<ConnectIntegration
@@ -1,6 +1,6 @@
"use client";
import { RefreshCcwIcon, Trash2Icon } from "lucide-react";
import { Trash2Icon } from "lucide-react";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
@@ -12,11 +12,9 @@ import { deleteIntegrationAction } from "@/app/(app)/environments/[environmentId
import { AddIntegrationModal } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/AddIntegrationModal";
import { timeSince } from "@/lib/time";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { Alert, AlertButton, AlertDescription } from "@/modules/ui/components/alert";
import { Button } from "@/modules/ui/components/button";
import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
import { EmptyState } from "@/modules/ui/components/empty-state";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
import { IntegrationModalInputs } from "../lib/types";
interface ManageIntegrationProps {
@@ -26,20 +24,10 @@ interface ManageIntegrationProps {
surveys: TSurvey[];
airtableArray: TIntegrationItem[];
locale: TUserLocale;
showReconnectButton: boolean;
handleAirtableAuthorization: () => Promise<void>;
}
export const ManageIntegration = ({
airtableIntegration,
environmentId,
setIsConnected,
surveys,
airtableArray,
showReconnectButton,
handleAirtableAuthorization,
locale,
}: ManageIntegrationProps) => {
export const ManageIntegration = (props: ManageIntegrationProps) => {
const { airtableIntegration, environmentId, setIsConnected, surveys, airtableArray } = props;
const { t } = useTranslation();
const tableHeaders = [
@@ -85,34 +73,15 @@ export const ManageIntegration = ({
: { isEditMode: false as const };
return (
<div className="mt-6 flex w-full flex-col items-center justify-center p-6">
{showReconnectButton && (
<Alert variant="warning" size="small" className="mb-4 w-full">
<AlertDescription>{t("environments.integrations.reconnect_button_description")}</AlertDescription>
<AlertButton onClick={handleAirtableAuthorization}>
{t("environments.integrations.reconnect_button")}
</AlertButton>
</Alert>
)}
<div className="flex w-full justify-end space-x-2">
<div className="mr-6 flex items-center">
<div className="flex w-full justify-end gap-x-6">
<div className="flex items-center">
<span className="mr-4 h-4 w-4 rounded-full bg-green-600"></span>
<span className="text-slate-500">
<span className="cursor-pointer text-slate-500">
{t("environments.integrations.connected_with_email", {
email: airtableIntegration.config.email,
})}
</span>
</div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline" onClick={handleAirtableAuthorization}>
<RefreshCcwIcon className="mr-2 h-4 w-4" />
{t("environments.integrations.reconnect_button")}
</Button>
</TooltipTrigger>
<TooltipContent>{t("environments.integrations.reconnect_button_tooltip")}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button
onClick={() => {
setDefaultValues(null);
@@ -153,7 +122,9 @@ export const ManageIntegration = ({
<div className="col-span-2 text-center">{data.surveyName}</div>
<div className="col-span-2 text-center">{data.tableName}</div>
<div className="col-span-2 text-center">{data.elements}</div>
<div className="col-span-2 text-center">{timeSince(data.createdAt.toString(), locale)}</div>
<div className="col-span-2 text-center">
{timeSince(data.createdAt.toString(), props.locale)}
</div>
</button>
))}
</div>
@@ -1,5 +1,4 @@
import { redirect } from "next/navigation";
import { logger } from "@formbricks/logger";
import { TIntegrationItem } from "@formbricks/types/integration";
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
import { AirtableWrapper } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/AirtableWrapper";
@@ -32,14 +31,8 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
);
let airtableArray: TIntegrationItem[] = [];
let isTokenValid = true;
if (airtableIntegration?.config.key) {
try {
airtableArray = await getAirtableTables(params.environmentId);
} catch (error) {
logger.error(error, "Failed to load Airtable bases — token may be expired or revoked");
isTokenValid = false;
}
airtableArray = await getAirtableTables(params.environmentId);
}
if (isReadOnly) {
return redirect("./");
@@ -58,7 +51,6 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
surveys={surveys}
webAppUrl={WEBAPP_URL}
locale={locale ?? DEFAULT_LOCALE}
showReconnectButton={!isTokenValid}
/>
</div>
</PageContentWrapper>
@@ -0,0 +1,81 @@
import { afterEach, describe, expect, test, vi } from "vitest";
import { captureSurveyResponsePostHogEvent } from "./posthog";
vi.mock("@/lib/posthog", () => ({
capturePostHogEvent: vi.fn(),
}));
describe("captureSurveyResponsePostHogEvent", () => {
afterEach(() => {
vi.clearAllMocks();
});
const makeParams = (responseCount: number) => ({
organizationId: "org-1",
surveyId: "survey-1",
surveyType: "link",
environmentId: "env-1",
responseCount,
});
test("fires on 1st response with milestone 'first'", async () => {
const { capturePostHogEvent } = await import("@/lib/posthog");
captureSurveyResponsePostHogEvent(makeParams(1));
expect(capturePostHogEvent).toHaveBeenCalledWith("org-1", "survey_response_received", {
survey_id: "survey-1",
survey_type: "link",
organization_id: "org-1",
environment_id: "env-1",
response_count: 1,
is_first_response: true,
milestone: "first",
});
});
test("fires on every 100th response", async () => {
const { capturePostHogEvent } = await import("@/lib/posthog");
for (const count of [100, 200, 300, 500, 1000, 5000]) {
captureSurveyResponsePostHogEvent(makeParams(count));
}
expect(capturePostHogEvent).toHaveBeenCalledTimes(6);
});
test("does NOT fire for 2nd through 99th responses", async () => {
const { capturePostHogEvent } = await import("@/lib/posthog");
for (const count of [2, 5, 10, 50, 99]) {
captureSurveyResponsePostHogEvent(makeParams(count));
}
expect(capturePostHogEvent).not.toHaveBeenCalled();
});
test("does NOT fire for non-100th counts above 100", async () => {
const { capturePostHogEvent } = await import("@/lib/posthog");
for (const count of [101, 150, 250, 499, 501]) {
captureSurveyResponsePostHogEvent(makeParams(count));
}
expect(capturePostHogEvent).not.toHaveBeenCalled();
});
test("sets milestone to count string for non-first milestones", async () => {
const { capturePostHogEvent } = await import("@/lib/posthog");
captureSurveyResponsePostHogEvent(makeParams(200));
expect(capturePostHogEvent).toHaveBeenCalledWith(
"org-1",
"survey_response_received",
expect.objectContaining({
is_first_response: false,
milestone: "200",
})
);
});
});
@@ -8,7 +8,7 @@ import { sendTelemetryEvents } from "@/app/api/(internal)/pipeline/lib/telemetry
import { ZPipelineInput } from "@/app/api/(internal)/pipeline/types/pipelines";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { CRON_SECRET, DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS, POSTHOG_KEY } from "@/lib/constants";
import { CRON_SECRET, POSTHOG_KEY } from "@/lib/constants";
import { generateStandardWebhookSignature } from "@/lib/crypto";
import { getIntegrations } from "@/lib/integration/service";
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
@@ -91,15 +91,10 @@ export const POST = async (request: Request) => {
const webhooks: Webhook[] = await getWebhooksForPipeline(environmentId, event, surveyId);
// Prepare webhook and email promises
// Fetch with timeout of 5 seconds to prevent hanging.
// `redirect: "manual"` blocks SSRF via redirect — webhook URLs are validated against private/internal
// ranges before delivery, but redirect targets would otherwise bypass that check. Gated on the same
// env var as `validateWebhookUrl`: self-hosters who opted into trusting internal URLs also get the
// pre-patch redirect-follow behavior for consistency.
const redirectMode: RequestRedirect = DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS ? "follow" : "manual";
// Fetch with timeout of 5 seconds to prevent hanging
const fetchWithTimeout = (url: string, options: RequestInit, timeout: number = 5000): Promise<Response> => {
return Promise.race([
fetch(url, { ...options, redirect: redirectMode }),
fetch(url, options),
new Promise<never>((_, reject) => setTimeout(() => reject(new Error("Timeout")), timeout)),
]);
};
@@ -1,44 +0,0 @@
import { Prisma } from "@prisma/client";
import { prisma } from "@formbricks/database";
import { ZId } from "@formbricks/types/common";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { validateInputs } from "@/lib/utils/validate";
export const getResponseIdByDisplayId = async (
environmentId: string,
displayId: string
): Promise<{ responseId: string | null }> => {
validateInputs([environmentId, ZId], [displayId, ZId]);
try {
const display = await prisma.display.findFirst({
where: {
id: displayId,
survey: {
environmentId,
},
},
select: {
response: {
select: {
id: true,
},
},
},
});
if (!display) {
throw new ResourceNotFoundError("Display", displayId);
}
return {
responseId: display.response?.id ?? null,
};
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
}
throw error;
}
};
@@ -1,40 +0,0 @@
import { logger } from "@formbricks/logger";
import { ResourceNotFoundError } from "@formbricks/types/errors";
import { responses } from "@/app/lib/api/response";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { getResponseIdByDisplayId } from "./lib/response";
export const OPTIONS = async (): Promise<Response> => {
return responses.successResponse({}, true);
};
export const GET = withV1ApiWrapper({
handler: async ({
req,
props,
}: THandlerParams<{ params: Promise<{ environmentId: string; displayId: string }> }>) => {
const params = await props.params;
try {
const response = await getResponseIdByDisplayId(params.environmentId, params.displayId);
return {
response: responses.successResponse(response, true),
};
} catch (error) {
if (error instanceof ResourceNotFoundError) {
return {
response: responses.notFoundResponse("Display", params.displayId, true),
};
}
logger.error(
{ error, url: req.url, environmentId: params.environmentId, displayId: params.displayId },
"Error in GET /api/v1/client/[environmentId]/displays/[displayId]/response"
);
return {
response: responses.internalServerErrorResponse("Something went wrong. Please try again."),
};
}
},
});
@@ -70,6 +70,7 @@ const mockEnvironmentData = {
displayOption: "displayOnce",
hiddenFields: { enabled: false },
isBackButtonHidden: false,
isAutoProgressingEnabled: true,
triggers: [],
displayPercentage: null,
delay: 0,
@@ -122,6 +123,13 @@ describe("getEnvironmentStateData", () => {
surveys: expect.any(Object),
}),
});
const prismaCall = vi.mocked(prisma.environment.findUnique).mock.calls[0][0];
expect(prismaCall.select.surveys.select).toEqual(
expect.objectContaining({
isAutoProgressingEnabled: true,
})
);
});
test("should throw ResourceNotFoundError when environment is not found", async () => {
@@ -121,6 +121,7 @@ export const getEnvironmentStateData = async (environmentId: string): Promise<En
displayOption: true,
hiddenFields: true,
isBackButtonHidden: true,
isAutoProgressingEnabled: true,
triggers: {
select: {
actionClass: {
@@ -123,14 +123,7 @@ export const POST = withV1ApiWrapper({
}
if (survey.environmentId !== environmentId) {
return {
response: responses.badRequestResponse(
"Survey is part of another environment",
{
"survey.environmentId": survey.environmentId,
environmentId,
},
true
),
response: responses.badRequestResponse("Survey does not belong to this environment", undefined, true),
};
}
@@ -75,11 +75,7 @@ export const POST = withV1ApiWrapper({
if (survey.environmentId !== environmentId) {
return {
response: responses.badRequestResponse(
"Survey does not belong to the environment",
{ surveyId, environmentId },
true
),
response: responses.badRequestResponse("Survey does not belong to this environment", undefined, true),
};
}
@@ -5,7 +5,7 @@ import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { fetchAirtableAuthToken } from "@/lib/airtable/service";
import { AIRTABLE_CLIENT_ID, WEBAPP_URL } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
import { createOrUpdateIntegration } from "@/lib/integration/service";
import { capturePostHogEvent } from "@/lib/posthog";
import { getOrganizationIdFromEnvironmentId } from "@/lib/utils/helper";
@@ -78,16 +78,12 @@ export const GET = withV1ApiWrapper({
}
const email = await getEmail(key.access_token);
// Preserve existing integration data (survey-to-table mappings) when re-authorizing
const existingIntegration = await getIntegrationByType(environmentId, "airtable");
const existingData = existingIntegration?.config?.data ?? [];
const airtableIntegrationInput = {
type: "airtable" as "airtable",
environment: environmentId,
config: {
key,
data: existingData,
data: [],
email,
},
};
@@ -1,7 +1,8 @@
import * as z from "zod";
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
import { responses } from "@/app/lib/api/response";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { getAirtableToken, getTables } from "@/lib/airtable/service";
import { getTables } from "@/lib/airtable/service";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { getIntegrationByType } from "@/lib/integration/service";
@@ -35,7 +36,7 @@ export const GET = withV1ApiWrapper({
};
}
const integration = await getIntegrationByType(environmentId, "airtable");
const integration = (await getIntegrationByType(environmentId, "airtable")) as TIntegrationAirtable;
if (!integration) {
return {
@@ -43,12 +44,7 @@ export const GET = withV1ApiWrapper({
};
}
// Use getAirtableToken to ensure the access token is refreshed if expired
const freshAccessToken = await getAirtableToken(environmentId);
const tables = await getTables(
{ ...integration.config.key, access_token: freshAccessToken },
baseId.data
);
const tables = await getTables(integration.config.key, baseId.data);
return {
response: responses.successResponse(tables),
};
@@ -1,178 +0,0 @@
import { beforeEach, describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database";
import { publicUserSelect } from "@/lib/user/public-user";
import { GET } from "./route";
const mocks = vi.hoisted(() => ({
headers: vi.fn(),
getSessionUser: vi.fn(),
parseApiKeyV2: vi.fn(),
hashSha256: vi.fn(),
verifySecret: vi.fn(),
applyRateLimit: vi.fn(),
notAuthenticatedResponse: vi.fn(
() => new Response(JSON.stringify({ message: "Not authenticated" }), { status: 401 })
),
tooManyRequestsResponse: vi.fn(
(message: string) => new Response(JSON.stringify({ message }), { status: 429 })
),
badRequestResponse: vi.fn((message: string) => new Response(JSON.stringify({ message }), { status: 400 })),
}));
vi.mock("next/headers", () => ({
headers: mocks.headers,
}));
vi.mock("@formbricks/database", () => ({
prisma: {
user: {
findUnique: vi.fn(),
},
apiKey: {
findUnique: vi.fn(),
findFirst: vi.fn(),
update: vi.fn(),
},
},
}));
vi.mock("@/app/api/v1/management/me/lib/utils", () => ({
getSessionUser: mocks.getSessionUser,
}));
vi.mock("@/app/lib/api/response", () => ({
responses: {
notAuthenticatedResponse: mocks.notAuthenticatedResponse,
tooManyRequestsResponse: mocks.tooManyRequestsResponse,
badRequestResponse: mocks.badRequestResponse,
},
}));
vi.mock("@/lib/crypto", () => ({
hashSha256: mocks.hashSha256,
parseApiKeyV2: mocks.parseApiKeyV2,
verifySecret: mocks.verifySecret,
}));
vi.mock("@/modules/core/rate-limit/helpers", () => ({
applyRateLimit: mocks.applyRateLimit,
}));
vi.mock("@/modules/core/rate-limit/rate-limit-configs", () => ({
rateLimitConfigs: {
api: {
v1: { windowMs: 60_000, max: 1000 },
},
},
}));
const getMockHeaders = (apiKey: string | null) => ({
get: (headerName: string) => (headerName === "x-api-key" ? apiKey : null),
});
describe("v1 management me route", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.headers.mockResolvedValue(getMockHeaders(null));
mocks.getSessionUser.mockResolvedValue(undefined);
mocks.parseApiKeyV2.mockReturnValue(null);
mocks.hashSha256.mockReturnValue("hashed-api-key");
mocks.verifySecret.mockResolvedValue(false);
mocks.applyRateLimit.mockResolvedValue(undefined);
});
test("returns a sanitized authenticated user for session-based requests", async () => {
const publicUser = {
id: "user_123",
name: "Test User",
email: "test@example.com",
emailVerified: new Date("2025-04-17T20:11:54.947Z"),
createdAt: new Date("2025-04-17T20:09:14.021Z"),
updatedAt: new Date("2026-04-22T22:12:39.104Z"),
twoFactorEnabled: false,
identityProvider: "email" as const,
notificationSettings: {
alert: {},
unsubscribedOrganizationIds: [],
},
locale: "en-US" as const,
lastLoginAt: new Date("2026-04-22T22:12:39.104Z"),
isActive: true,
};
mocks.getSessionUser.mockResolvedValue({ id: publicUser.id });
vi.mocked(prisma.user.findUnique).mockResolvedValue(publicUser as never);
const response = await GET();
const responseBody = await response.json();
expect(response.status).toBe(200);
expect(responseBody).toStrictEqual(JSON.parse(JSON.stringify(publicUser)));
expect(responseBody).not.toHaveProperty("password");
expect(responseBody).not.toHaveProperty("twoFactorSecret");
expect(responseBody).not.toHaveProperty("backupCodes");
expect(responseBody).not.toHaveProperty("identityProviderAccountId");
expect(prisma.user.findUnique).toHaveBeenCalledWith({
where: { id: publicUser.id },
select: publicUserSelect,
});
expect(mocks.applyRateLimit).toHaveBeenCalledWith(expect.any(Object), publicUser.id);
});
test("returns the existing unauthenticated response when no session is present", async () => {
const response = await GET();
const responseBody = await response.json();
expect(response.status).toBe(401);
expect(responseBody).toEqual({ message: "Not authenticated" });
expect(mocks.notAuthenticatedResponse).toHaveBeenCalled();
expect(prisma.user.findUnique).not.toHaveBeenCalled();
});
test("preserves the API key response path", async () => {
const apiKeyData = {
id: "api_key_123",
organizationId: "org_123",
hashedKey: "stored-hash",
lastUsedAt: new Date(),
apiKeyEnvironments: [
{
permission: "manage",
environment: {
id: "env_123",
type: "development",
createdAt: new Date("2025-01-01T00:00:00.000Z"),
updatedAt: new Date("2025-01-02T00:00:00.000Z"),
projectId: "project_123",
appSetupCompleted: true,
project: {
id: "project_123",
name: "My Project",
},
},
},
],
};
mocks.headers.mockResolvedValue(getMockHeaders("api-key"));
vi.mocked(prisma.apiKey.findFirst).mockResolvedValue(apiKeyData as never);
const response = await GET();
const responseBody = await response.json();
expect(response.status).toBe(200);
expect(responseBody).toStrictEqual({
id: "env_123",
type: "development",
createdAt: "2025-01-01T00:00:00.000Z",
updatedAt: "2025-01-02T00:00:00.000Z",
appSetupCompleted: true,
project: {
id: "project_123",
name: "My Project",
},
});
expect(mocks.getSessionUser).not.toHaveBeenCalled();
expect(mocks.applyRateLimit).toHaveBeenCalledWith(expect.any(Object), apiKeyData.id);
});
});
@@ -4,7 +4,6 @@ import { getSessionUser } from "@/app/api/v1/management/me/lib/utils";
import { responses } from "@/app/lib/api/response";
import { CONTROL_HASH } from "@/lib/constants";
import { hashSha256, parseApiKeyV2, verifySecret } from "@/lib/crypto";
import { publicUserSelect } from "@/lib/user/public-user";
import { applyRateLimit } from "@/modules/core/rate-limit/helpers";
import { rateLimitConfigs } from "@/modules/core/rate-limit/rate-limit-configs";
@@ -177,7 +176,6 @@ const handleSessionAuthentication = async () => {
const user = await prisma.user.findUnique({
where: { id: sessionUser.id },
select: publicUserSelect,
});
return Response.json(user);
@@ -96,14 +96,7 @@ const validateSurvey = async (responseInput: TResponseInput, environmentId: stri
}
if (survey.environmentId !== environmentId) {
return {
error: responses.badRequestResponse(
"Survey is part of another environment",
{
"survey.environmentId": survey.environmentId,
environmentId,
},
true
),
error: responses.badRequestResponse("Survey does not belong to this environment", undefined, true),
};
}
return { survey };
@@ -2,7 +2,7 @@ import { Prisma } from "@prisma/client";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database";
import { TContactAttributes } from "@formbricks/types/contact-attribute";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { DatabaseError, ResourceNotFoundError, UniqueConstraintError } from "@formbricks/types/errors";
import { TResponseWithQuotaFull, TSurveyQuota } from "@formbricks/types/quota";
import { TResponse } from "@formbricks/types/responses";
import { TTag } from "@formbricks/types/tags";
@@ -175,10 +175,34 @@ describe("createResponse V2", () => {
).rejects.toThrow(ResourceNotFoundError);
});
test("should throw DatabaseError on Prisma known request error", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Test Prisma Error", {
test("should throw UniqueConstraintError on P2002 with singleUseId target", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Unique constraint failed", {
code: "P2002",
clientVersion: "test",
meta: { target: ["surveyId", "singleUseId"] },
});
vi.mocked(mockTx.response.create).mockRejectedValue(prismaError);
await expect(
createResponse(mockResponseInput, mockTx as unknown as Prisma.TransactionClient)
).rejects.toThrow(UniqueConstraintError);
});
test("should throw DatabaseError on P2002 without singleUseId target", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Unique constraint failed", {
code: "P2002",
clientVersion: "test",
meta: { target: ["displayId"] },
});
vi.mocked(mockTx.response.create).mockRejectedValue(prismaError);
await expect(
createResponse(mockResponseInput, mockTx as unknown as Prisma.TransactionClient)
).rejects.toThrow(DatabaseError);
});
test("should throw DatabaseError on non-P2002 Prisma known request error", async () => {
const prismaError = new Prisma.PrismaClientKnownRequestError("Test Prisma Error", {
code: "P2025",
clientVersion: "test",
});
vi.mocked(mockTx.response.create).mockRejectedValue(prismaError);
await expect(
@@ -2,7 +2,7 @@ import "server-only";
import { Prisma } from "@prisma/client";
import { prisma } from "@formbricks/database";
import { TContactAttributes } from "@formbricks/types/contact-attribute";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { DatabaseError, ResourceNotFoundError, UniqueConstraintError } from "@formbricks/types/errors";
import { TResponseWithQuotaFull } from "@formbricks/types/quota";
import { TResponse, ZResponseInput } from "@formbricks/types/responses";
import { TTag } from "@formbricks/types/tags";
@@ -129,6 +129,13 @@ export const createResponse = async (
return response;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === "P2002") {
const target = (error.meta?.target as string[]) ?? [];
if (target?.includes("singleUseId")) {
throw new UniqueConstraintError("Response already submitted for this single-use link");
}
}
throw new DatabaseError(error.message);
}
@@ -124,11 +124,8 @@ describe("checkSurveyValidity", () => {
expect(result).toBeInstanceOf(Response);
expect(result?.status).toBe(400);
expect(responses.badRequestResponse).toHaveBeenCalledWith(
"Survey is part of another environment",
{
"survey.environmentId": "env-2",
environmentId: "env-1",
},
"Survey does not belong to this environment",
undefined,
true
);
});
@@ -17,14 +17,7 @@ export const checkSurveyValidity = async (
responseInput: TResponseInputV2
): Promise<Response | null> => {
if (survey.environmentId !== environmentId) {
return responses.badRequestResponse(
"Survey is part of another environment",
{
"survey.environmentId": survey.environmentId,
environmentId,
},
true
);
return responses.badRequestResponse("Survey does not belong to this environment", undefined, true);
}
if (survey.type === "link" && survey.singleUse?.enabled) {
@@ -1,6 +1,6 @@
import { UAParser } from "ua-parser-js";
import { ZEnvironmentId } from "@formbricks/types/environment";
import { InvalidInputError } from "@formbricks/types/errors";
import { InvalidInputError, UniqueConstraintError } from "@formbricks/types/errors";
import { TResponseWithQuotaFull } from "@formbricks/types/quota";
import { checkSurveyValidity } from "@/app/api/v2/client/[environmentId]/responses/lib/utils";
import { reportApiError } from "@/app/lib/api/api-error-reporter";
@@ -177,6 +177,10 @@ const createResponseForRequest = async ({
return responses.badRequestResponse(error.message, undefined, true);
}
if (error instanceof UniqueConstraintError) {
return responses.conflictResponse(error.message, undefined, true);
}
const response = getUnexpectedPublicErrorResponse();
reportApiError({
request,
+50
View File
@@ -339,6 +339,56 @@ describe("API Response Utilities", () => {
});
});
describe("conflictResponse", () => {
test("should return a conflict response", () => {
const message = "Resource already exists";
const details = { field: "singleUseId" };
const response = responses.conflictResponse(message, details);
expect(response.status).toBe(409);
return response.json().then((body) => {
expect(body).toEqual({
code: "conflict",
message,
details,
});
});
});
test("should handle undefined details", () => {
const message = "Resource already exists";
const response = responses.conflictResponse(message);
expect(response.status).toBe(409);
return response.json().then((body) => {
expect(body).toEqual({
code: "conflict",
message,
details: {},
});
});
});
test("should include CORS headers when cors is true", () => {
const message = "Resource already exists";
const response = responses.conflictResponse(message, undefined, true);
expect(response.headers.get("Access-Control-Allow-Origin")).toBe("*");
expect(response.headers.get("Access-Control-Allow-Methods")).toBe("GET, POST, PUT, DELETE, OPTIONS");
expect(response.headers.get("Access-Control-Allow-Headers")).toBe("Content-Type, Authorization");
});
test("should use custom cache control header when provided", () => {
const message = "Resource already exists";
const customCache = "no-cache";
const response = responses.conflictResponse(message, undefined, false, customCache);
expect(response.headers.get("Cache-Control")).toBe(customCache);
});
});
describe("tooManyRequestsResponse", () => {
test("should return a too many requests response", () => {
const message = "Rate limit exceeded";
+27 -1
View File
@@ -16,7 +16,8 @@ interface ApiErrorResponse {
| "method_not_allowed"
| "not_authenticated"
| "forbidden"
| "too_many_requests";
| "too_many_requests"
| "conflict";
message: string;
details: {
[key: string]: string | string[] | number | number[] | boolean | boolean[];
@@ -236,6 +237,30 @@ const internalServerErrorResponse = (
);
};
const conflictResponse = (
message: string,
details?: { [key: string]: string },
cors: boolean = false,
cache: string = "private, no-store"
) => {
const headers = {
...(cors && corsHeaders),
"Cache-Control": cache,
};
return Response.json(
{
code: "conflict",
message,
details: details || {},
} as ApiErrorResponse,
{
status: 409,
headers,
}
);
};
const tooManyRequestsResponse = (
message: string,
cors: boolean = false,
@@ -270,4 +295,5 @@ export const responses = {
successResponse,
tooManyRequestsResponse,
forbiddenResponse,
conflictResponse,
};
+3 -2
View File
@@ -971,13 +971,13 @@ const improveTrialConversion = (t: TFunction): TTemplate => {
elements: [
buildOpenTextElement({
id: reusableElementIds[2],
headline: t("templates.improve_trial_conversion_question_2_headline"),
headline: t("templates.improve_trial_conversion_question_3_headline"),
required: true,
inputType: "text",
}),
],
logic: [createBlockJumpLogic(reusableElementIds[2], block6Id, "isSubmitted")],
buttonLabel: t("templates.improve_trial_conversion_question_2_button_label"),
buttonLabel: t("templates.improve_trial_conversion_question_3_button_label"),
t,
}),
buildBlock({
@@ -4926,6 +4926,7 @@ export const previewSurvey = (projectName: string, t: TFunction): TSurvey => {
showLanguageSwitch: false,
followUps: [],
isBackButtonHidden: false,
isAutoProgressingEnabled: true,
isCaptureIpEnabled: false,
metadata: {},
questions: [], // Required for build-time type checking (Zod defaults to [] at runtime)
+33 -19
View File
@@ -125,6 +125,7 @@ checksums:
common/centered_modal: 982ff411cb7e91e30300c2ed56b7e507
common/change_organization: 3b2c873962509445ff2cb8cde5ad913b
common/change_workspace: 489cbcf7eef9b9b960e426fbf4da318f
common/choice_n: ee41eb382bae7289a221d959f3046965
common/choices: 8a7a77a71ec6eebc363c5dc0f8490a4d
common/choose_environment: 5762cd499529815fc3e6a7feea39f90b
common/choose_organization: a8f5db68012323bfbb1a0ad0fb194603
@@ -138,6 +139,7 @@ checksums:
common/close: 2c2e22f8424a1031de89063bd0022e16
common/code: 343bc5386149b97cece2b093c39034b2
common/collapse_rows: 24988527f9180f37aa55d2aa183ccb21
common/column_n: 550955aee6a92d8ccc96989300add693
common/completed: 0e4bbce9985f25eb673d9a054c8d5334
common/configuration: 923ec0502721489202f6222dd4107163
common/confirm: 90930b51154032f119fa75c1bd422d8b
@@ -209,6 +211,7 @@ checksums:
common/failed_to_copy_to_clipboard: de836a7d628d36c832809252f188f784
common/failed_to_load_organizations: 512808a2b674c7c28bca73f8f91fd87e
common/failed_to_load_workspaces: 6ee3448097394517dc605074cd4e6ea4
common/field_placeholder: ec26d96643d86da164162204ec6c650f
common/filter: 626325a05e4c8800f7ede7012b0cadaf
common/finish: ffa7a10f71182b48fefed7135bee24fa
common/first_name: cf040a5d6a9fd696be400380cc99f54b
@@ -220,10 +223,12 @@ checksums:
common/generate: 0345bf322c191e70d01fd6607ec5c2f8
common/go_back: b917ea82facb90c88c523b255d29f84b
common/go_to_dashboard: a6efa97d25e36fedc0af794f6ba610f2
common/headline: 0023cbe059bbadcc77312825cbbce5ac
common/hidden: fa290c6ada5869d744ed35e9cca64699
common/hidden_field: 3ed5c58d0ed359e558cdf7bd33606d2d
common/hidden_fields: 3de6cfd308293a826cb8679fd1d49972
common/hide_column: 23ce94db148f2d8e4a0923defead6cf1
common/html: f750870203043349d570d8f5865ca0f8
common/id: c8886d38aeea2ed5f785aba4fc96784b
common/image: 048ba7a239de0fbd883ade8558415830
common/images: 9305827c28694866f49db42b4c51831f
@@ -271,7 +276,6 @@ checksums:
common/months: da74749fbe80394fa0f72973d7b0964a
common/move_down: 4f4de55743043355ad4a839aff2c48ff
common/move_up: 69f25b205c677abdb26cbb69d97cd10b
common/multiple_languages: 7d8ddd4b40d32fcd7bd6f7bac6485b1f
common/my_product: ad022177062f9ef6e9acf33b13e889aa
common/name: 9368b5a047572b6051f334af5aa76819
common/new: 126d036fae5fb6b629728ecb97e6195b
@@ -286,6 +290,7 @@ checksums:
common/no_result_found: fedddbc0149972ea072a9e063198a16d
common/no_results: 0e9b73265c6542240f5a3bf6b43e9280
common/no_surveys_found: 7b74706fe4f4aacd7d858e19e444fe85
common/no_text_found: 27350f35bdd57b3701c7ec578a1a0e11
common/none_of_the_above: e007f0b1e046d5ddbbcfbd87940456ee
common/not_authenticated: fed6c62208524ea6782b5f9c07a95a4f
common/not_authorized: 4be80383fe1a6f52c61138f1aa8d01d4
@@ -309,6 +314,7 @@ checksums:
common/organization_settings: 11528aa89ae9935e55dcb54478058775
common/other: 79acaa6cd481262bea4e743a422529d2
common/other_filters: 20b09213c131db47eb8b23e72d0c4bea
common/other_placeholder: f3a0fa2eaaf75aa92b290449c928c081
common/others: 39160224ce0e35eb4eb252c997edf4d8
common/overlay_color: 4b72073285d13fff93d094aabffe05ac
common/overview: 30c54e4dc4ce599b87d94be34a8617f5
@@ -354,6 +360,7 @@ checksums:
common/responses: 14bb6c69f906d7bbd1359f7ef1bb3c28
common/restart: bab6232e89f24e3129f8e48268739d5b
common/role: 53743bbb6ca938f5b893552e839d067f
common/row_n: eb5bb04b244fadd7a6962aa58bf6bd17
common/saas: f01686245bcfb35a3590ab56db677bdb
common/sales: 38758eb50094cd8190a71fe67be4d647
common/save: f7a2929f33bc420195e59ac5a8bcd454
@@ -392,6 +399,7 @@ checksums:
common/storage_not_configured: b0c3e339f6d71f23fdd189e7bcb076f6
common/string: 4ddccc1974775ed7357f9beaf9361cec
common/styling: 240fc91eb03c52d46b137f82e7aec2a1
common/subheader: 73a37d57cb9807e574a42bd0c7e334ed
common/submit: 7c91ef5f747eea9f77a9c4f23e19fb2e
common/summary: 13eb7b8a239fb4702dfdaee69100a220
common/survey: b659d270a53dada994d926e0cc6e9a54
@@ -784,9 +792,6 @@ checksums:
environments/integrations/notion/update_connection_tooltip: 2429919f575e47f5c76e54b4442ba706
environments/integrations/notion_integration_description: 31a73dbe88fe18a078d6dc15f0c303e2
environments/integrations/please_select_a_survey_error: 465aa7048773079c8ffdde8b333b78eb
environments/integrations/reconnect_button: 8992a0f250278c116cb26be448b68ba2
environments/integrations/reconnect_button_description: 01f79dc561ff87b5f2a80bf66e492844
environments/integrations/reconnect_button_tooltip: 5552effda9df8d6778dda1cf42e5d880
environments/integrations/select_at_least_one_question_error: a3513cb02ab0de2a1531893ac0c7e089
environments/integrations/slack/already_connected_another_survey: 4508f9e4a2915e3818ea5f9e2695e000
environments/integrations/slack/channel_name: 1afcd1d0401850ff353f5ae27502b04a
@@ -1241,8 +1246,7 @@ checksums:
environments/surveys/copy_survey_partially_success: a436a5fb7167b95c2308794d35aab070
environments/surveys/copy_survey_success: a829e645fe034b3e712d0b8572a5edc4
environments/surveys/delete_survey_and_responses_warning: 3320c91c1fd27378b7f3d6abc003f2ae
environments/surveys/edit/1_choose_the_default_language_for_this_survey: d22759857c1bb3d6b337e8e9d501dad7
environments/surveys/edit/2_activate_translation_for_specific_languages: 9f23cb81ad301073df45ae36f0d94f9e
environments/surveys/edit/activate_translations: af127c1bed2b47e2012e3a23e489ecb8
environments/surveys/edit/add: 5196f5cd4ba3a6ac8edef91345e17f66
environments/surveys/edit/add_a_delay_or_auto_close_the_survey: b5fa358bf3ff324014060eb0baf6dd2f
environments/surveys/edit/add_a_four_digit_pin: 953cb3673d2135923e3b4474d33ffb2c
@@ -1291,6 +1295,8 @@ checksums:
environments/surveys/edit/assign: e80715ab64bf7cf463abb3a9fd1ad516
environments/surveys/edit/audience: a4d9fab4214a641e2d358fbb28f010e0
environments/surveys/edit/auto_close_on_inactivity: 093db516799315ccd4242a3675693012
environments/surveys/edit/auto_progress_rating_and_nps: 76b98e95a5b850850baa0ccc3c7fbf7c
environments/surveys/edit/auto_progress_rating_and_nps_description: cbf676789b9f3f47e36bdf35fa58282b
environments/surveys/edit/auto_save_disabled: f7411fb0dcfb8f7b19b85f0be54f2231
environments/surveys/edit/auto_save_disabled_tooltip: 77322e1e866b7d29f7641a88bbd3b681
environments/surveys/edit/auto_save_on: 1524d466830b00c5d727c701db404963
@@ -1336,6 +1342,7 @@ checksums:
environments/surveys/edit/caution_text: 3291e962c0e4c4656832837ddc512918
environments/surveys/edit/change_anyway: 6377497d40373f6d0f082670194981ab
environments/surveys/edit/change_background: fa71a993869f7d3ac553c547c12c3e9b
environments/surveys/edit/change_default: 6236a6c8a28489ba7c4cad7426806859
environments/surveys/edit/change_question_type: 2d555ae48df8dbedfc6a4e1ad492f4aa
environments/surveys/edit/change_survey_type: c26322043a476da6d94adb8b4efe1e93
environments/surveys/edit/change_the_background_to_a_color_image_or_animation: f1b9c9eb61497dd91b2550dd50c77836
@@ -1348,6 +1355,7 @@ checksums:
environments/surveys/edit/choose_where_to_run_the_survey: ad87bcae97c445f1fd9ac110ea24f117
environments/surveys/edit/city: 1831f32e1babbb29af27fac3053504a2
environments/surveys/edit/close_survey_on_response_limit: 256d0bccdbcbb3d20e39aabc5b376e5e
environments/surveys/edit/code: 343bc5386149b97cece2b093c39034b2
environments/surveys/edit/color: 9d53d1d120e8b8954bcae9a322573748
environments/surveys/edit/column_used_in_logic_error: deffbd3e8f4bd71a5e522682e8ee60dd
environments/surveys/edit/columns: 14896556dc1535d70198854757f704ec
@@ -1372,6 +1380,7 @@ checksums:
environments/surveys/edit/customize_survey_logo: 7f7e26026c88a727228f2d7a00d914e2
environments/surveys/edit/darken_or_lighten_background_of_your_choice: 304a64a8050ebf501d195e948cd25b6f
environments/surveys/edit/days_before_showing_this_survey_again: 9ee757e5c3a07844b12ceb406dc65b04
environments/surveys/edit/default_language: 06d01d2598419e36ba97d2d8719f849b
environments/surveys/edit/delete_anyways: cc8683ab625280eefc9776bd381dc2e8
environments/surveys/edit/delete_block: c00617cb0724557e486304276063807a
environments/surveys/edit/delete_choice: fd750208d414b9ad8c980c161a0199e1
@@ -1391,7 +1400,6 @@ checksums:
environments/surveys/edit/duplicate_question: 910751de01fdd327165968214717711b
environments/surveys/edit/edit_link: 40ba9e15beac77a46c5baf30be84ac54
environments/surveys/edit/edit_recall: 38a4a7378d02453e35d06f2532eef318
environments/surveys/edit/edit_translations: 2b21bea4b53e88342559272701e9fbf3
environments/surveys/edit/element_not_found: 196777ff6811dd177971ffc8e27a72c1
environments/surveys/edit/enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey: c70466147d49dcbb3686452f35c46428
environments/surveys/edit/enable_recaptcha_to_protect_your_survey_from_spam: 4483a5763718d201ac97caa1e1216e13
@@ -1527,11 +1535,13 @@ checksums:
environments/surveys/edit/long_answer: 3a97f8d2e90aba6e679917a0c5670c53
environments/surveys/edit/long_answer_toggle_description: 86bcdfeb74d9825c2f2d5a215e92d111
environments/surveys/edit/lower_label: 45985bca022d4370bd6e013af75d5160
environments/surveys/edit/manage_languages: 9c56d5afee8a73dfc283a452470f3a10
environments/surveys/edit/manage_languages: fe82303bc27b55ccfc076b527b185e39
environments/surveys/edit/manage_translations: 09b01c5c251e6dbc3dc6cd8b33fb6301
environments/surveys/edit/matrix_all_fields: 187240509163b2f52a400a565e57c67f
environments/surveys/edit/matrix_rows: 8f41f34e6ca28221cf1ebd948af4c151
environments/surveys/edit/max_file_size: 3d35a22048f4d22e24da698fb5fb77d7
environments/surveys/edit/max_file_size_limit_is: 78998639cde3587cecb272ba47e05f9e
environments/surveys/edit/missing_first: a0c8802636ade7bac86a0dacba00b8d4
environments/surveys/edit/move_question_to_block: e8d7ef1e2f727921cb7f5788849492ad
environments/surveys/edit/multiply: 89a0bb629167f97750ae1645a46ced0d
environments/surveys/edit/needed_for_self_hosted_cal_com_instance: d241e72f0332177d32ce6c35070757dc
@@ -1539,7 +1549,7 @@ checksums:
environments/surveys/edit/next_button_label: 39f1e82ae1dea5e400e8ed7c98c6ad9c
environments/surveys/edit/no_hidden_fields_yet_add_first_one_below: 9cc6cab3a6a42dbf835215897b5b8516
environments/surveys/edit/no_images_found_for: 7dabcbcc7084f59c6ec0971895dfcd29
environments/surveys/edit/no_languages_found_add_first_one_to_get_started: 22d7782c8504daf693cab3cf7135d6e3
environments/surveys/edit/no_languages_found_add_first_one_to_get_started: 4e66397232da6a463708220dc020bf42
environments/surveys/edit/no_option_found: a1a3aa7e6c13b6bb8df20a1a104c7c04
environments/surveys/edit/no_recall_items_found: 729e2b02e412cdc79f5ad94b1918620c
environments/surveys/edit/no_variables_yet_add_first_one_below: c8704b9ebc9c26c0e9dd50c099ba88cd
@@ -1566,6 +1576,7 @@ checksums:
environments/surveys/edit/please_enter_a_valid_url: 25d43dfb802c31cb59dc88453ea72fc4
environments/surveys/edit/please_set_a_survey_trigger: 0358142df37dd1724f629008a1db453a
environments/surveys/edit/please_specify: e1faa6cd085144f7339c7e74dc6fb366
environments/surveys/edit/present_your_survey_in_multiple_languages: 37f28b0a092d68322fedbc2e0c221ef3
environments/surveys/edit/prevent_double_submission: afc502baa2da81d9c9618da1c3b5a57a
environments/surveys/edit/prevent_double_submission_description: ef7d2aa22d43bdc6ccebb076c6aa9ce5
environments/surveys/edit/progress_saved: d7bfc189571f08bbb4d0240cb9363ffa
@@ -1655,6 +1666,7 @@ checksums:
environments/surveys/edit/seven_points: 4ead50fdfda45e8710767e1b1a84bf42
environments/surveys/edit/show_block_settings: bad99d99c9908874e45f5c350a88cc79
environments/surveys/edit/show_button: 6b364aac9d7ac71f34a438607c9693bc
environments/surveys/edit/show_in_order: 15784a59572eb8a6dba6b918c31a9493
environments/surveys/edit/show_language_switch: b6915a7f26d7079f2d4d844d74440413
environments/surveys/edit/show_multiple_times: 05239c532c9c05ef5d2990ba6ce12f60
environments/surveys/edit/show_only_once: 31858baf60ebcf193c7e35d9084af0af
@@ -1686,7 +1698,6 @@ checksums:
environments/surveys/edit/survey_preview: 33644451073149383d3ace08be930739
environments/surveys/edit/survey_styling: 7f96d6563e934e65687b74374a33b1dc
environments/surveys/edit/survey_trigger: f0c7014a684ca566698b87074fad5579
environments/surveys/edit/switch_multi_language_on_to_get_started: cca0ef91ee49095da30cd1e3f26c406f
environments/surveys/edit/target_block_not_found: 0a0c401017ab32364fec2fcbf815d832
environments/surveys/edit/targeted: ca615f1fc3b490d5a2187b27fb4a2073
environments/surveys/edit/ten_points: a1317b82003859f77fb3138c55450d63
@@ -1694,9 +1705,11 @@ checksums:
environments/surveys/edit/the_survey_will_be_shown_once_even_if_person_doesnt_respond: e45beba7ae126775f4966776c982a3b4
environments/surveys/edit/then: 5e941fb7dd51a18651fcfb865edd5ba6
environments/surveys/edit/this_action_will_remove_all_the_translations_from_this_survey: 3340c89696f10bdc01b9a1047ff0b987
environments/surveys/edit/this_will_remove_the_language_and_all_its_translations: 6a71ae70abbd61f13f15323d825a47f6
environments/surveys/edit/three_points: d7f299aec752d7d690ef0ab6373327ae
environments/surveys/edit/times: 5ab156c13df6bfd75c0b17ad0a92c78a
environments/surveys/edit/to_keep_the_placement_over_all_surveys_consistent_you_can: 7a078e6a39d4c30b465137d2b6ef3e67
environments/surveys/edit/translated: 5b9d805410310b726f12bacb06da44e3
environments/surveys/edit/trigger_survey_when_one_of_the_actions_is_fired: 8570291668ec9879d204f10e861112db
environments/surveys/edit/try_lollipop_or_mountain: c550a0f07b3ae40a237e30a4314a249c
environments/surveys/edit/type_field_id: 714b845806236bb8a9d6a09933b836e9
@@ -1769,6 +1782,7 @@ checksums:
environments/surveys/edit/verify_email_before_submission_description: 434ab3ee6134367513b633a9d4f7d772
environments/surveys/edit/visibility_and_recontact: c27cb4ff3a4262266902a335c3ad5d84
environments/surveys/edit/visibility_and_recontact_description: 2969ab679e1f6111dd96e95cee26e219
environments/surveys/edit/visible: 54ea1310fe55664c24a712eb17070fbd
environments/surveys/edit/wait: 014d18ade977bf08d75b995076596708
environments/surveys/edit/wait_a_few_seconds_after_the_trigger_before_showing_the_survey: 13d5521cf73be5afeba71f5db5847919
environments/surveys/edit/waiting_time_across_surveys: 6873c18d51830e2cadef67cce6a2c95c
@@ -2113,7 +2127,6 @@ checksums:
environments/workspace/languages/duplicate_language_or_language_id: 0e17e3794b24e2428ca6ffadae0d08f3
environments/workspace/languages/edit_languages: c9d36f6b28557cc7d54e87c37dc18fdd
environments/workspace/languages/identifier: 7d8ade6b85e96216bcd73adeeeeecd8c
environments/workspace/languages/incomplete_translations: d82908b5725f18f5849c7876ad497ebc
environments/workspace/languages/language: 277fd1a41cc237a437cd1d5e4a80463b
environments/workspace/languages/language_deleted_successfully: 4a805d030491f3fe608d2371b0cfcd83
environments/workspace/languages/languages_updated_successfully: 60de474c99c5059c0458cddd0b016c15
@@ -2124,7 +2137,6 @@ checksums:
environments/workspace/languages/remove_language: 1a64563b0f37109f97b78eddd493e381
environments/workspace/languages/remove_language_from_surveys_to_remove_it_from_workspace: 61bc96f9db31a29a649cc9ecd684bc39
environments/workspace/languages/search_items: b54b751c8b075200be579d6c8e58096b
environments/workspace/languages/translate: 59f9803b27e2030ba7323ed239116cf7
environments/workspace/look/add_background_color: 9be512ee1246e32d3958c56097d202d9
environments/workspace/look/add_background_color_description: adb6fcb392862b3d0e9420d9b5405ddb
environments/workspace/look/advanced_styling_field_border_radius: 63b8f3541a9792d705e67d5aca7b6451
@@ -2182,12 +2194,12 @@ checksums:
environments/workspace/look/advanced_styling_field_track_bg_description: 8a56258273dfe49e83fe752ea9e8daed
environments/workspace/look/advanced_styling_field_track_height: 9ce57cb4583039c224a37e013efb6b8f
environments/workspace/look/advanced_styling_field_track_height_description: 90243a4374e15d9118ad0fd93d5f3614
environments/workspace/look/advanced_styling_field_upper_label_color: 65d75c60dfdba88e5fed38bcb24a0a5d
environments/workspace/look/advanced_styling_field_upper_label_color_description: ae2276506807c7ceeb7a8b87723a8dd4
environments/workspace/look/advanced_styling_field_upper_label_size: ea0ca9a3ffa1650f97a31df453b0afc7
environments/workspace/look/advanced_styling_field_upper_label_size_description: 061668625be0f7a68ebc2e2ebe49e5a9
environments/workspace/look/advanced_styling_field_upper_label_weight: 946c5836d2cfaaee21e494424d550887
environments/workspace/look/advanced_styling_field_upper_label_weight_description: 916b03c719a8dead351679336aabcf53
environments/workspace/look/advanced_styling_field_upper_label_color: 2767a5db32742073a01aac16488e93dc
environments/workspace/look/advanced_styling_field_upper_label_color_description: 58f43ce21b7f6539cc937aa80c7e8060
environments/workspace/look/advanced_styling_field_upper_label_size: 3342babd1df61a3bdf7a3284137f7c24
environments/workspace/look/advanced_styling_field_upper_label_size_description: 867a89a79ed7ac7f1c6b0f3481a67f26
environments/workspace/look/advanced_styling_field_upper_label_weight: a9a0de9e840518d282cfdbcb02d059b5
environments/workspace/look/advanced_styling_field_upper_label_weight_description: 3cee88e1c8e75548dcb6004f0e44f31c
environments/workspace/look/advanced_styling_section_buttons: 3b44d6e2800e7bf3f133f1bce435f4c2
environments/workspace/look/advanced_styling_section_headlines: 6def704c0ac2ecb5951400c806856a41
environments/workspace/look/advanced_styling_section_inputs: 76bbeb561122a72fd3ec8c49eff7c563
@@ -2794,12 +2806,14 @@ checksums:
templates/improve_trial_conversion_question_1_subheader: 67c7047ba2365d461df14dbed3f9506d
templates/improve_trial_conversion_question_2_button_label: 89ddbcf710eba274963494f312bdc8a9
templates/improve_trial_conversion_question_2_headline: 05dd4820f60b9d267a9affc7e662f029
templates/improve_trial_conversion_question_3_button_label: 89ddbcf710eba274963494f312bdc8a9
templates/improve_trial_conversion_question_3_headline: 3daeccf3dfc7bf8e9868c10fb3ea0b19
templates/improve_trial_conversion_question_4_button_label: d94a6a11cfdf4ebde4c5332e585e2e96
templates/improve_trial_conversion_question_4_headline: 9b07341f65574c4165086ec107cebb45
templates/improve_trial_conversion_question_4_html: 8ce95691eeeae7ad61c4d2f867b918ca
templates/improve_trial_conversion_question_5_button_label: 89ddbcf710eba274963494f312bdc8a9
templates/improve_trial_conversion_question_5_headline: dbd99e216fcbf8693b8e77fbd77e1c84
templates/improve_trial_conversion_question_5_subheader: b9b478e967930358b0c74324a7c18fc8
templates/improve_trial_conversion_question_5_subheader: 859876a442a633f4aa0d78fd0ee4ab4c
templates/improve_trial_conversion_question_6_headline: f15239ecc4f1a6bd8bea77a38b39c844
templates/improve_trial_conversion_question_6_subheader: e147ddbb609fff6e6fc78fb1f4add0ac
templates/integration_setup_survey_description: 696ccab07d7098cdb79c224fa1208889
+5 -11
View File
@@ -3,6 +3,7 @@ import { logger } from "@formbricks/logger";
import { DatabaseError } from "@formbricks/types/errors";
import { TIntegrationItem } from "@formbricks/types/integration";
import {
TIntegrationAirtable,
TIntegrationAirtableConfigData,
TIntegrationAirtableCredential,
ZIntegrationAirtableBases,
@@ -23,11 +24,6 @@ export const getBases = async (key: string) => {
},
});
if (!req.ok) {
const body = await req.text().catch(() => "");
throw new Error(`Airtable API error fetching bases: ${req.status} ${req.statusText} ${body}`);
}
const res = await req.json();
return ZIntegrationAirtableBases.parse(res);
};
@@ -39,11 +35,6 @@ const tableFetcher = async (key: TIntegrationAirtableCredential, baseId: string)
},
});
if (!req.ok) {
const body = await req.text().catch(() => "");
throw new Error(`Airtable API error fetching tables: ${req.status} ${req.statusText} ${body}`);
}
const res = await req.json();
return res;
@@ -87,7 +78,10 @@ export const fetchAirtableAuthToken = async (formData: Record<string, any>) => {
export const getAirtableToken = async (environmentId: string) => {
try {
const airtableIntegration = await getIntegrationByType(environmentId, "airtable");
const airtableIntegration = (await getIntegrationByType(
environmentId,
"airtable"
)) as TIntegrationAirtable;
const { access_token, expiry_date, refresh_token } = ZIntegrationAirtableCredential.parse(
airtableIntegration?.config.key
+3 -11
View File
@@ -5,12 +5,7 @@ import { prisma } from "@formbricks/database";
import { logger } from "@formbricks/logger";
import { ZId, ZOptionalNumber, ZString } from "@formbricks/types/common";
import { DatabaseError } from "@formbricks/types/errors";
import {
TIntegration,
TIntegrationByType,
TIntegrationInput,
ZIntegrationType,
} from "@formbricks/types/integration";
import { TIntegration, TIntegrationInput, ZIntegrationType } from "@formbricks/types/integration";
import { ITEMS_PER_PAGE } from "../constants";
import { validateInputs } from "../utils/validate";
@@ -99,10 +94,7 @@ export const getIntegration = reactCache(async (integrationId: string): Promise<
});
export const getIntegrationByType = reactCache(
async <T extends TIntegrationInput["type"]>(
environmentId: string,
type: T
): Promise<TIntegrationByType<T> | null> => {
async (environmentId: string, type: TIntegrationInput["type"]): Promise<TIntegration | null> => {
validateInputs([environmentId, ZId], [type, ZIntegrationType]);
try {
@@ -114,7 +106,7 @@ export const getIntegrationByType = reactCache(
},
},
});
return integration ? (transformIntegration(integration) as TIntegrationByType<T>) : null;
return integration ? transformIntegration(integration) : null;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
+6 -2
View File
@@ -1,4 +1,8 @@
import { TIntegrationNotionConfig, TIntegrationNotionDatabase } from "@formbricks/types/integration/notion";
import {
TIntegrationNotion,
TIntegrationNotionConfig,
TIntegrationNotionDatabase,
} from "@formbricks/types/integration/notion";
import { ENCRYPTION_KEY } from "@/lib/constants";
import { symmetricDecrypt } from "@/lib/crypto";
import { getIntegrationByType } from "../integration/service";
@@ -25,7 +29,7 @@ const fetchPages = async (config: TIntegrationNotionConfig) => {
export const getNotionDatabases = async (environmentId: string): Promise<TIntegrationNotionDatabase[]> => {
let results: TIntegrationNotionDatabase[] = [];
try {
const notionIntegration = await getIntegrationByType(environmentId, "notion");
const notionIntegration = (await getIntegrationByType(environmentId, "notion")) as TIntegrationNotion;
if (notionIntegration && notionIntegration.config?.key.bot_id) {
results = await fetchPages(notionIntegration.config);
}
+2 -2
View File
@@ -1,7 +1,7 @@
import { Prisma } from "@prisma/client";
import { DatabaseError, UnknownError } from "@formbricks/types/errors";
import { TIntegration, TIntegrationItem } from "@formbricks/types/integration";
import { TIntegrationSlackCredential } from "@formbricks/types/integration/slack";
import { TIntegrationSlack, TIntegrationSlackCredential } from "@formbricks/types/integration/slack";
import { SLACK_MESSAGE_LIMIT } from "../constants";
import { deleteIntegration, getIntegrationByType } from "../integration/service";
import { truncateText } from "../utils/strings";
@@ -58,7 +58,7 @@ export const fetchChannels = async (slackIntegration: TIntegration): Promise<TIn
export const getSlackChannels = async (environmentId: string): Promise<TIntegrationItem[]> => {
let channels: TIntegrationItem[] = [];
try {
const slackIntegration = await getIntegrationByType(environmentId, "slack");
const slackIntegration = (await getIntegrationByType(environmentId, "slack")) as TIntegrationSlack;
if (slackIntegration && slackIntegration.config?.key) {
channels = await fetchChannels(slackIntegration);
}
@@ -209,6 +209,7 @@ const baseSurveyProperties = {
},
],
isBackButtonHidden: false,
isAutoProgressingEnabled: false,
isCaptureIpEnabled: false,
endings: [
{
+1
View File
@@ -48,6 +48,7 @@ export const selectSurvey = {
isVerifyEmailEnabled: true,
isSingleResponsePerEmailEnabled: true,
isBackButtonHidden: true,
isAutoProgressingEnabled: true,
isCaptureIpEnabled: true,
redirectUrl: true,
projectOverwrites: true,
-36
View File
@@ -1,36 +0,0 @@
import "server-only";
import { User } from "@prisma/client";
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { verifyPassword } from "@/modules/auth/lib/utils";
const getUserAuthenticationData = reactCache(
async (userId: string): Promise<Pick<User, "password" | "identityProvider">> => {
const user = await prisma.user.findUnique({
where: {
id: userId,
},
select: {
password: true,
identityProvider: true,
},
});
if (!user) {
throw new ResourceNotFoundError("user", userId);
}
return user;
}
);
export const verifyUserPassword = async (userId: string, password: string): Promise<boolean> => {
const user = await getUserAuthenticationData(userId);
if (user.identityProvider !== "email" || !user.password) {
throw new InvalidInputError("Password is not set for this user");
}
return await verifyPassword(password, user.password);
};
-20
View File
@@ -1,20 +0,0 @@
import { Prisma } from "@prisma/client";
export const publicUserSelect = {
id: true,
name: true,
email: true,
emailVerified: true,
createdAt: true,
updatedAt: true,
twoFactorEnabled: true,
identityProvider: true,
notificationSettings: true,
locale: true,
lastLoginAt: true,
isActive: true,
} as const satisfies Prisma.UserSelect;
export type TPublicUser = Prisma.UserGetPayload<{
select: typeof publicUserSelect;
}>;
+10 -10
View File
@@ -6,7 +6,6 @@ import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TOrganization } from "@formbricks/types/organizations";
import { TUserLocale, TUserUpdateInput } from "@formbricks/types/user";
import { deleteOrganization, getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
import { publicUserSelect } from "./public-user";
import { deleteUser, getUser, getUserByEmail, getUsersWithOrganization, updateUser } from "./service";
vi.mock("@formbricks/database", () => ({
@@ -48,6 +47,11 @@ describe("User Service", () => {
locale: "en-US" as TUserLocale,
lastLoginAt: new Date(),
isActive: true,
twoFactorSecret: null,
backupCodes: null,
password: null,
identityProviderAccountId: null,
groupId: null,
};
const mockOrganizations: TOrganization[] = [
@@ -98,12 +102,8 @@ describe("User Service", () => {
expect(result).toEqual(mockPrismaUser);
expect(prisma.user.findUnique).toHaveBeenCalledWith({
where: { id: "user1" },
select: publicUserSelect,
select: expect.any(Object),
});
expect(result).not.toHaveProperty("password");
expect(result).not.toHaveProperty("twoFactorSecret");
expect(result).not.toHaveProperty("backupCodes");
expect(result).not.toHaveProperty("identityProviderAccountId");
});
test("should return null when user not found", async () => {
@@ -134,7 +134,7 @@ describe("User Service", () => {
expect(result).toEqual(mockPrismaUser);
expect(prisma.user.findFirst).toHaveBeenCalledWith({
where: { email: "test@example.com" },
select: publicUserSelect,
select: expect.any(Object),
});
});
@@ -176,7 +176,7 @@ describe("User Service", () => {
expect(prisma.user.update).toHaveBeenCalledWith({
where: { id: "user1" },
data: updateData,
select: publicUserSelect,
select: expect.any(Object),
});
});
@@ -204,7 +204,7 @@ describe("User Service", () => {
expect(deleteOrganization).toHaveBeenCalledWith("org1");
expect(prisma.user.delete).toHaveBeenCalledWith({
where: { id: "user1" },
select: publicUserSelect,
select: expect.any(Object),
});
});
@@ -236,7 +236,7 @@ describe("User Service", () => {
},
},
},
select: publicUserSelect,
select: expect.any(Object),
});
});
+21 -7
View File
@@ -10,7 +10,21 @@ import { TUser, TUserLocale, TUserUpdateInput, ZUserUpdateInput } from "@formbri
import { deleteOrganization, getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
import { deleteBrevoCustomerByEmail } from "@/modules/auth/lib/brevo";
import { validateInputs } from "../utils/validate";
import { publicUserSelect } from "./public-user";
const responseSelection = {
id: true,
name: true,
email: true,
emailVerified: true,
createdAt: true,
updatedAt: true,
twoFactorEnabled: true,
identityProvider: true,
notificationSettings: true,
locale: true,
lastLoginAt: true,
isActive: true,
};
// function to retrive basic information about a user's user
export const getUser = reactCache(async (id: string): Promise<TUser | null> => {
@@ -21,7 +35,7 @@ export const getUser = reactCache(async (id: string): Promise<TUser | null> => {
where: {
id,
},
select: publicUserSelect,
select: responseSelection,
});
if (!user) {
@@ -45,7 +59,7 @@ export const getUserByEmail = reactCache(async (email: string): Promise<TUser |
where: {
email,
},
select: publicUserSelect,
select: responseSelection,
});
return user;
@@ -68,7 +82,7 @@ export const updateUser = async (personId: string, data: TUserUpdateInput): Prom
id: personId,
},
data: data,
select: publicUserSelect,
select: responseSelection,
});
return updatedUser;
@@ -91,7 +105,7 @@ const deleteUserById = async (id: string): Promise<TUser> => {
where: {
id,
},
select: publicUserSelect,
select: responseSelection,
});
return user;
} catch (error) {
@@ -139,7 +153,7 @@ export const getUsersWithOrganization = async (organizationId: string): Promise<
},
},
},
select: publicUserSelect,
select: responseSelection,
});
return users;
@@ -160,7 +174,7 @@ export const getUserLocale = reactCache(async (id: string): Promise<TUserLocale
where: {
id,
},
select: publicUserSelect,
select: responseSelection,
});
if (!user) {
+1 -1
View File
@@ -54,7 +54,7 @@ export const findRecallInfoById = (text: string, id: string): string | null => {
return match ? match[0] : null;
};
const getRecallItemLabel = <T extends TSurvey>(
export const getRecallItemLabel = <T extends TSurvey>(
recallItemId: string,
survey: T,
languageCode: string
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "Zentriertes Modalfenster",
"change_organization": "Organisation wechseln",
"change_workspace": "Workspace wechseln",
"choice_n": "Auswahl {{n}}",
"choices": "Entscheidungen",
"choose_environment": "Umgebung auswählen",
"choose_organization": "Organisation auswählen",
@@ -165,6 +166,7 @@
"close": "Schließen",
"code": "Code",
"collapse_rows": "Zeilen einklappen",
"column_n": "Spalte {{n}}",
"completed": "Abgeschlossen",
"configuration": "Konfiguration",
"confirm": "Bestätigen",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Fehler beim Kopieren in die Zwischenablage",
"failed_to_load_organizations": "Fehler beim Laden der Organisationen",
"failed_to_load_workspaces": "Projekte konnten nicht geladen werden",
"field_placeholder": "{{field}}-Platzhalter",
"filter": "Filter",
"finish": "Fertigstellen",
"first_name": "Vorname",
@@ -247,10 +250,12 @@
"generate": "Generieren",
"go_back": "Geh zurück",
"go_to_dashboard": "Zum Dashboard gehen",
"headline": "Überschrift",
"hidden": "Versteckt",
"hidden_field": "Verstecktes Feld",
"hidden_fields": "Versteckte Felder",
"hide_column": "Spalte ausblenden",
"html": "HTML",
"id": "ID",
"image": "Bild",
"images": "Bilder",
@@ -298,7 +303,6 @@
"months": "Monate",
"move_down": "Nach unten bewegen",
"move_up": "Nach oben bewegen",
"multiple_languages": "Mehrsprachigkeit",
"my_product": "mein Produkt",
"name": "Name",
"new": "Neu",
@@ -313,6 +317,7 @@
"no_result_found": "Kein Ergebnis gefunden",
"no_results": "Keine Ergebnisse",
"no_surveys_found": "Keine Umfragen gefunden.",
"no_text_found": "Kein Text gefunden",
"none_of_the_above": "Keine der oben genannten Optionen",
"not_authenticated": "Du bist nicht authentifiziert, um diese Aktion durchzuführen.",
"not_authorized": "Nicht berechtigt",
@@ -336,6 +341,7 @@
"organization_settings": "Organisationseinstellungen",
"other": "Andere",
"other_filters": "Weitere Filter",
"other_placeholder": "Sonstiger Platzhalter",
"others": "Andere",
"overlay_color": "Overlay-Farbe",
"overview": "Überblick",
@@ -381,6 +387,7 @@
"responses": "Antworten",
"restart": "Neustart",
"role": "Rolle",
"row_n": "Zeile {{n}}",
"saas": "SaaS",
"sales": "Vertrieb",
"save": "Speichern",
@@ -419,6 +426,7 @@
"storage_not_configured": "Dateispeicher nicht eingerichtet, Uploads werden wahrscheinlich fehlschlagen",
"string": "Text",
"styling": "Styling",
"subheader": "Unterüberschrift",
"submit": "Abschicken",
"summary": "Zusammenfassung",
"survey": "Umfrage",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Sende Daten an deine Notion Datenbank",
"please_select_a_survey_error": "Bitte wähle eine Umfrage aus",
"reconnect_button": "Erneut verbinden",
"reconnect_button_description": "Deine Integrationsverbindung ist abgelaufen. Bitte verbinde dich erneut, um weiterhin Antworten zu synchronisieren. Deine bestehenden Links und Daten bleiben erhalten.",
"reconnect_button_tooltip": "Verbinde die Integration erneut, um deinen Zugriff zu aktualisieren. Deine bestehenden Links und Daten bleiben erhalten.",
"select_at_least_one_question_error": "Bitte wähle mindestens eine Frage aus",
"slack": {
"already_connected_another_survey": "Du hast bereits eine andere Umfrage mit diesem Kanal verbunden.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Zwei-Faktor-Authentifizierung mit einem höheren Plan freischalten",
"update_personal_info": "Persönliche Daten aktualisieren",
"warning_cannot_delete_account": "Du bist der einzige Besitzer dieser Organisation. Bitte übertrage das Eigentum zuerst an ein anderes Mitglied.",
"warning_cannot_undo": "Das kann nicht rückgängig gemacht werden",
"wrong_password": "Falsches Passwort"
"warning_cannot_undo": "Das kann nicht rückgängig gemacht werden"
},
"teams": {
"add_members_description": "Füge Mitglieder zum Team hinzu und bestimme ihre Rolle.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Umfrage erfolgreich kopiert!",
"delete_survey_and_responses_warning": "Bist Du sicher, dass Du diese Umfrage und alle ihre Antworten löschen möchtest?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Wähle die Standardsprache für diese Umfrage:",
"2_activate_translation_for_specific_languages": "2. Übersetzung für bestimmte Sprachen aktivieren:",
"activate_translations": "Übersetzungen aktivieren",
"add": "+ hinzufügen",
"add_a_delay_or_auto_close_the_survey": "Füge eine Verzögerung hinzu oder schließe die Umfrage automatisch.",
"add_a_four_digit_pin": "Füge eine vierstellige PIN hinzu",
@@ -1363,6 +1366,8 @@
"assign": "Zuweisen =",
"audience": "Publikum",
"auto_close_on_inactivity": "Automatisches Schließen bei Inaktivität",
"auto_progress_rating_and_nps": "Bewertungs- und NPS-Fragen automatisch fortsetzen",
"auto_progress_rating_and_nps_description": "Fahre automatisch fort, sobald Befragte eine Antwort bei Bewertungs- oder NPS-Fragen auswählen. Dies gilt nur für Blöcke mit einer einzelnen Frage. Bei Pflichtfragen wird die Weiter-Schaltfläche ausgeblendet; bei optionalen Fragen bleibt sie zum Überspringen sichtbar.",
"auto_save_disabled": "Automatisches Speichern deaktiviert",
"auto_save_disabled_tooltip": "Ihre Umfrage wird nur im Entwurfsmodus automatisch gespeichert. So wird sichergestellt, dass öffentliche Umfragen nicht unbeabsichtigt aktualisiert werden.",
"auto_save_on": "Automatisches Speichern an",
@@ -1408,6 +1413,7 @@
"caution_text": "Änderungen werden zu Inkonsistenzen führen",
"change_anyway": "Trotzdem ändern",
"change_background": "Hintergrund ändern",
"change_default": "Standard ändern",
"change_question_type": "Fragetyp ändern",
"change_survey_type": "Die Änderung des Umfragetypen kann vorhandenen Zugriff beeinträchtigen",
"change_the_background_to_a_color_image_or_animation": "Hintergrund zu einer Farbe, einem Bild oder einer Animation ändern.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Wähle, wo die Umfrage durchgeführt werden soll.",
"city": "Stadt",
"close_survey_on_response_limit": "Umfrage bei Erreichen des Antwortlimits schließen",
"code": "Code",
"color": "Farbe",
"column_used_in_logic_error": "Diese Spalte wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.",
"columns": "Spalten",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Umfragelogo anpassen",
"darken_or_lighten_background_of_your_choice": "Hintergrund deiner Wahl abdunkeln oder aufhellen.",
"days_before_showing_this_survey_again": "oder mehr Tage müssen zwischen der zuletzt angezeigten Umfrage und der Anzeige dieser Umfrage vergehen.",
"default_language": "Standardsprache",
"delete_anyways": "Trotzdem löschen",
"delete_block": "Block löschen",
"delete_choice": "Auswahl löschen",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Frage duplizieren",
"edit_link": "Bearbeitungslink",
"edit_recall": "Erinnerung bearbeiten",
"edit_translations": "{lang} -Übersetzungen bearbeiten",
"element_not_found": "Frage nicht gefunden",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Befragten erlauben, die Sprache jederzeit zu wechseln. Benötigt mind. 2 aktive Sprachen.",
"enable_recaptcha_to_protect_your_survey_from_spam": "Spamschutz verwendet reCAPTCHA v3, um Spam-Antworten herauszufiltern.",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "Ermöglichen Sie den Befragten, längere Antworten über mehrere Zeilen zu schreiben.",
"lower_label": "Unteres Label",
"manage_languages": "Sprachen verwalten",
"manage_translations": "Übersetzungen verwalten",
"matrix_all_fields": "Alle Felder",
"matrix_rows": "Zeilen",
"max_file_size": "Maximale Dateigröße",
"max_file_size_limit_is": "Die maximale Dateigrößenbeschränkung beträgt",
"missing_first": "Fehlende zuerst",
"move_question_to_block": "Frage in Block verschieben",
"multiply": "Multiplizieren *",
"needed_for_self_hosted_cal_com_instance": "Benötigt für eine selbstgehostete Cal.com-Instanz",
@@ -1611,7 +1620,7 @@
"next_button_label": "Beschriftung der Schaltfläche \"Weiter\"",
"no_hidden_fields_yet_add_first_one_below": "Noch keine versteckten Felder. Füge das erste unten hinzu.",
"no_images_found_for": "Keine Bilder gefunden für ''{query}\"",
"no_languages_found_add_first_one_to_get_started": "Keine Sprachen gefunden. Füge die erste hinzu, um loszulegen.",
"no_languages_found_add_first_one_to_get_started": "In diesem Workspace wurden keine Umfragesprachen gefunden. Füge bitte eine hinzu, um loszulegen.",
"no_option_found": "Keine Option gefunden",
"no_recall_items_found": "Keine Recall-Elemente gefunden",
"no_variables_yet_add_first_one_below": "Noch keine Variablen. Füge die erste hinzu.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Bitte geben Sie eine gültige URL ein (z. B. https://beispiel.de)",
"please_set_a_survey_trigger": "Bitte richte einen Umfrage-Trigger ein",
"please_specify": "Bitte angeben",
"present_your_survey_in_multiple_languages": "Präsentiere deine Umfrage in mehreren Sprachen",
"prevent_double_submission": "Doppeltes Anbschicken verhindern",
"prevent_double_submission_description": "Nur eine Antwort pro E-Mail-Adresse zulassen (beta)",
"progress_saved": "Fortschritt gespeichert",
@@ -1729,6 +1739,7 @@
"seven_points": "7 Punkte",
"show_block_settings": "Block-Einstellungen anzeigen",
"show_button": "Button anzeigen",
"show_in_order": "In Reihenfolge anzeigen",
"show_language_switch": "Sprachwechsel anzeigen",
"show_multiple_times": "Begrenzte Anzahl von Malen anzeigen",
"show_only_once": "Nur einmal anzeigen",
@@ -1760,7 +1771,6 @@
"survey_preview": "Umfragevorschau 👀",
"survey_styling": "Umfrage Styling",
"survey_trigger": "Auslöser der Umfrage",
"switch_multi_language_on_to_get_started": "Aktiviere Mehrsprachigkeit, um loszulegen 👉",
"target_block_not_found": "Zielblock nicht gefunden",
"targeted": "Gezielt",
"ten_points": "10 Punkte",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Einmal anzeigen, auch wenn sie nicht antworten.",
"then": "dann",
"this_action_will_remove_all_the_translations_from_this_survey": "Diese Aktion entfernt alle Übersetzungen aus dieser Umfrage.",
"this_will_remove_the_language_and_all_its_translations": "Dies entfernt diese Sprache und alle zugehörigen Übersetzungen aus dieser Umfrage. Diese Aktion kann nicht rückgängig gemacht werden.",
"three_points": "3 Punkte",
"times": "Zeiten",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Um die Platzierung über alle Umfragen hinweg konsistent zu halten, kannst du",
"translated": "Übersetzt",
"trigger_survey_when_one_of_the_actions_is_fired": "Umfrage auslösen, wenn eine der Aktionen ausgeführt wird...",
"try_lollipop_or_mountain": "Versuch 'Lolli' oder 'Berge'...",
"type_field_id": "Feld-ID eingeben",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Lass nur Leute mit einer echten E-Mail antworten.",
"visibility_and_recontact": "Sichtbarkeit & erneute Kontaktaufnahme",
"visibility_and_recontact_description": "Steuern Sie, wann diese Umfrage erscheinen kann und wie oft sie erneut erscheinen kann.",
"visible": "Sichtbar",
"wait": "Warte",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Warte ein paar Sekunden nach dem Auslöser, bevor Du die Umfrage anzeigst",
"waiting_time_across_surveys": "Abkühlphase (umfrageübergreifend)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Doppelte Sprache oder Sprach-ID",
"edit_languages": "Sprachen bearbeiten",
"identifier": "Kennung (ISO)",
"incomplete_translations": "Unvollständige Übersetzungen",
"language": "Sprache",
"language_deleted_successfully": "Sprache erfolgreich gelöscht",
"languages_updated_successfully": "Sprachen erfolgreich aktualisiert",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Bitte wähle eine Sprache aus",
"remove_language": "Sprache entfernen",
"remove_language_from_surveys_to_remove_it_from_workspace": "Bitte entferne die Sprache aus diesen Umfragen, um sie aus dem Workspace zu entfernen.",
"search_items": "Elemente suchen",
"translate": "Übersetzen"
"search_items": "Elemente suchen"
},
"look": {
"add_background_color": "Hintergrundfarbe hinzufügen",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Färbt den nicht ausgefüllten Teil des Balkens.",
"advanced_styling_field_track_height": "Track-Höhe",
"advanced_styling_field_track_height_description": "Steuert die Dicke des Fortschrittsbalkens.",
"advanced_styling_field_upper_label_color": "Farbe des oberen Labels",
"advanced_styling_field_upper_label_color_description": "Färbt die kleine Beschriftung über Eingabefeldern.",
"advanced_styling_field_upper_label_size": "Schriftgröße des oberen Labels",
"advanced_styling_field_upper_label_size_description": "Skaliert die kleine Beschriftung über Eingabefeldern.",
"advanced_styling_field_upper_label_weight": "Schriftstärke des oberen Labels",
"advanced_styling_field_upper_label_weight_description": "Macht die Beschriftung leichter oder fetter.",
"advanced_styling_field_upper_label_color": "Labelfarbe",
"advanced_styling_field_upper_label_color_description": "Färbt die kleinen Labels über Eingabefeldern und Skalenbeschriftungen ein.",
"advanced_styling_field_upper_label_size": "Label-Schriftgröße",
"advanced_styling_field_upper_label_size_description": "Skaliert die kleinen Labels über Eingabefeldern und Skalenbeschriftungen.",
"advanced_styling_field_upper_label_weight": "Label-Schriftstärke",
"advanced_styling_field_upper_label_weight_description": "Macht die Labels dünner oder fetter.",
"advanced_styling_section_buttons": "Buttons",
"advanced_styling_section_headlines": "Überschriften & Beschreibungen",
"advanced_styling_section_inputs": "Eingabefelder",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Hilf uns, Dich besser zu verstehen:",
"improve_trial_conversion_question_2_button_label": "Weiter",
"improve_trial_conversion_question_2_headline": "Das tut mir leid zu hören. Was war das größte Problem bei der Nutzung von $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Weiter",
"improve_trial_conversion_question_3_headline": "Was haben Sie von $[projectName] erwartet?",
"improve_trial_conversion_question_4_button_label": "Erhalte 20% Rabatt",
"improve_trial_conversion_question_4_headline": "Das tut mir leid zu hören! Erhalte 20% Rabatt im ersten Jahr.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Wir freuen uns, dir einen 20% Rabatt auf einen Jahresplan anzubieten.</span></p>",
"improve_trial_conversion_question_5_button_label": "Weiter",
"improve_trial_conversion_question_5_headline": "Was möchtest Du erreichen?",
"improve_trial_conversion_question_5_subheader": "Bitte wähle eine der folgenden Optionen aus:",
"improve_trial_conversion_question_5_subheader": "Bitte beschreibe unten:",
"improve_trial_conversion_question_6_headline": "Wie löst Du dein Problem heutzutage?",
"improve_trial_conversion_question_6_subheader": "Bitte nenne alternative Lösungen:",
"integration_setup_survey_description": "Bewerte, wie einfach Nutzer Integrationen zu deinem Produkt hinzufügen können.",
+35 -22
View File
@@ -152,6 +152,7 @@
"centered_modal": "Centered Modal",
"change_organization": "Change organization",
"change_workspace": "Change workspace",
"choice_n": "Choice {{n}}",
"choices": "Choices",
"choose_environment": "Choose environment",
"choose_organization": "Choose organization",
@@ -165,6 +166,7 @@
"close": "Close",
"code": "Code",
"collapse_rows": "Collapse rows",
"column_n": "Column {{n}}",
"completed": "Completed",
"configuration": "Configuration",
"confirm": "Confirm",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Failed to copy to clipboard",
"failed_to_load_organizations": "Failed to load organizations",
"failed_to_load_workspaces": "Failed to load workspaces",
"field_placeholder": "{{field}} Placeholder",
"filter": "Filter",
"finish": "Finish",
"first_name": "First Name",
@@ -247,10 +250,12 @@
"generate": "Generate",
"go_back": "Go Back",
"go_to_dashboard": "Go to Dashboard",
"headline": "Headline",
"hidden": "Hidden",
"hidden_field": "Hidden field",
"hidden_fields": "Hidden fields",
"hide_column": "Hide column",
"html": "HTML",
"id": "ID",
"image": "Image",
"images": "Images",
@@ -298,7 +303,6 @@
"months": "months",
"move_down": "Move down",
"move_up": "Move up",
"multiple_languages": "Multiple languages",
"my_product": "my Product",
"name": "Name",
"new": "New",
@@ -313,6 +317,7 @@
"no_result_found": "No result found",
"no_results": "No results",
"no_surveys_found": "No surveys found.",
"no_text_found": "No text found",
"none_of_the_above": "None of the above",
"not_authenticated": "You are not authenticated to perform this action.",
"not_authorized": "Not authorized",
@@ -336,6 +341,7 @@
"organization_settings": "Organization settings",
"other": "Other",
"other_filters": "Other Filters",
"other_placeholder": "Other Placeholder",
"others": "Others",
"overlay_color": "Overlay color",
"overview": "Overview",
@@ -381,6 +387,7 @@
"responses": "Responses",
"restart": "Restart",
"role": "Role",
"row_n": "Row {{n}}",
"saas": "SaaS",
"sales": "Sales",
"save": "Save",
@@ -419,6 +426,7 @@
"storage_not_configured": "File storage not set up, uploads will likely fail",
"string": "Text",
"styling": "Styling",
"subheader": "Subheader",
"submit": "Submit",
"summary": "Summary",
"survey": "Survey",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Send data to your Notion database",
"please_select_a_survey_error": "Please select a survey",
"reconnect_button": "Reconnect",
"reconnect_button_description": "Your integration connection has expired. Please reconnect to continue syncing responses. Your existing links and data will be preserved.",
"reconnect_button_tooltip": "Reconnect the integration to refresh your access. Your existing links and data will be preserved.",
"select_at_least_one_question_error": "Please select at least one question",
"slack": {
"already_connected_another_survey": "You have already connected another survey to this channel.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Unlock two-factor authentication with a higher plan",
"update_personal_info": "Update your personal information",
"warning_cannot_delete_account": "You are the only owner of this organization. Please transfer ownership to another member first.",
"warning_cannot_undo": "This cannot be undone",
"wrong_password": "Wrong password"
"warning_cannot_undo": "This cannot be undone"
},
"teams": {
"add_members_description": "Add members to the team and determine their role.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Survey copied successfully",
"delete_survey_and_responses_warning": "Are you sure you want to delete this survey and all of its responses?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Choose the default language for this survey:",
"2_activate_translation_for_specific_languages": "2. Activate translation for specific languages:",
"activate_translations": "Activate translations",
"add": "Add +",
"add_a_delay_or_auto_close_the_survey": "Add a delay or auto-close the survey",
"add_a_four_digit_pin": "Add a four digit PIN",
@@ -1363,6 +1366,8 @@
"assign": "Assign =",
"audience": "Audience",
"auto_close_on_inactivity": "Auto close on inactivity",
"auto_progress_rating_and_nps": "Auto-progress rating and NPS questions",
"auto_progress_rating_and_nps_description": "Automatically advance when respondents select an answer on rating or NPS questions. This only applies to single-question blocks. Required questions hide the Next button; optional questions still show it for skipping.",
"auto_save_disabled": "Auto-save disabled",
"auto_save_disabled_tooltip": "Your survey is only auto-saved when in draft. This assures public surveys are not unintentionally updated.",
"auto_save_on": "Auto-save on",
@@ -1408,6 +1413,7 @@
"caution_text": "Changes will lead to inconsistencies",
"change_anyway": "Change anyway",
"change_background": "Change background",
"change_default": "Change default",
"change_question_type": "Change question type",
"change_survey_type": "Switching survey type affects existing access",
"change_the_background_to_a_color_image_or_animation": "Change the background to a color, image or animation.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Choose where to run the survey.",
"city": "City",
"close_survey_on_response_limit": "Close survey on response limit",
"code": "Code",
"color": "Color",
"column_used_in_logic_error": "This column is used in logic of question {questionIndex}. Please remove it from logic first.",
"columns": "Columns",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Customize the survey logo",
"darken_or_lighten_background_of_your_choice": "Darken or lighten background of your choice.",
"days_before_showing_this_survey_again": "or more days to pass between the last shown survey and showing this survey.",
"default_language": "Default language",
"delete_anyways": "Delete anyways",
"delete_block": "Delete block",
"delete_choice": "Delete choice",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Duplicate question",
"edit_link": "Edit link",
"edit_recall": "Edit Recall",
"edit_translations": "Edit {lang} translations",
"element_not_found": "Question not found",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Allow respondents to switch language at any time. Needs min. 2 active languages.",
"enable_recaptcha_to_protect_your_survey_from_spam": "Spam protection uses reCAPTCHA v3 to filter out the spam responses.",
@@ -1599,11 +1606,13 @@
"long_answer": "Long answer",
"long_answer_toggle_description": "Allow respondents to write longer, multi-line answers.",
"lower_label": "Lower Label",
"manage_languages": "Manage Languages",
"manage_languages": "Manage languages",
"manage_translations": "Manage translations",
"matrix_all_fields": "All fields",
"matrix_rows": "Rows",
"max_file_size": "Max file size",
"max_file_size_limit_is": "Max file size limit is",
"missing_first": "Missing first",
"move_question_to_block": "Move question to block",
"multiply": "Multiply *",
"needed_for_self_hosted_cal_com_instance": "Needed for a self-hosted Cal.com instance",
@@ -1611,7 +1620,7 @@
"next_button_label": "“Next” button label",
"no_hidden_fields_yet_add_first_one_below": "No hidden fields yet. Add the first one below.",
"no_images_found_for": "No images found for “{query}”",
"no_languages_found_add_first_one_to_get_started": "No languages found. Add the first one to get started.",
"no_languages_found_add_first_one_to_get_started": "No survey languages found in this workspace. Please add one to get started.",
"no_option_found": "No option found",
"no_recall_items_found": "No recall items found",
"no_variables_yet_add_first_one_below": "No variables yet. Add the first one below.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Please enter a valid URL (e.g., https://example.com)",
"please_set_a_survey_trigger": "Please set a survey trigger",
"please_specify": "Please specify",
"present_your_survey_in_multiple_languages": "Present your survey in multiple languages",
"prevent_double_submission": "Prevent double submission",
"prevent_double_submission_description": "Only allow 1 response per email address",
"progress_saved": "Progress saved",
@@ -1729,6 +1739,7 @@
"seven_points": "7 points",
"show_block_settings": "Show Block settings",
"show_button": "Show Button",
"show_in_order": "Show in order",
"show_language_switch": "Show language switch",
"show_multiple_times": "Show a limited number of times",
"show_only_once": "Show only once",
@@ -1760,7 +1771,6 @@
"survey_preview": "Survey Preview 👀",
"survey_styling": "Survey styling",
"survey_trigger": "Survey Trigger",
"switch_multi_language_on_to_get_started": "Switch multi-language on to get started 👉",
"target_block_not_found": "Target block not found",
"targeted": "Targeted",
"ten_points": "10 points",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Show a single time, even if they do not respond.",
"then": "Then",
"this_action_will_remove_all_the_translations_from_this_survey": "This action will remove all the translations from this survey.",
"this_will_remove_the_language_and_all_its_translations": "This will remove this language and all its translations from this survey. This action cannot be undone.",
"three_points": "3 points",
"times": "times",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "To keep the placement over all surveys consistent, you can",
"translated": "Translated",
"trigger_survey_when_one_of_the_actions_is_fired": "Trigger survey when one of the actions is fired…",
"try_lollipop_or_mountain": "Try “lollipop” or “mountain”…",
"type_field_id": "Type field id",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Only let people with a real email respond.",
"visibility_and_recontact": "Visibility & Recontact",
"visibility_and_recontact_description": "Control when this survey can appear and how often it can reappear.",
"visible": "Visible",
"wait": "Wait",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Wait a few seconds after the trigger before showing the survey",
"waiting_time_across_surveys": "Cooldown Period (across surveys)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Duplicate language or language ID",
"edit_languages": "Edit languages",
"identifier": "Identifier (ISO)",
"incomplete_translations": "Incomplete translations",
"language": "Language",
"language_deleted_successfully": "Language deleted successfully",
"languages_updated_successfully": "Languages updated successfully",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Please select a language",
"remove_language": "Remove Language",
"remove_language_from_surveys_to_remove_it_from_workspace": "Please remove the language from these surveys in order to remove it from the workspace.",
"search_items": "Search items",
"translate": "Translate"
"search_items": "Search items"
},
"look": {
"add_background_color": "Add background color",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Colors the unfilled portion of the bar.",
"advanced_styling_field_track_height": "Track Height",
"advanced_styling_field_track_height_description": "Controls the progress bar thickness.",
"advanced_styling_field_upper_label_color": "Headline Label Color",
"advanced_styling_field_upper_label_color_description": "Colors the small label above inputs.",
"advanced_styling_field_upper_label_size": "Headline Label Font Size",
"advanced_styling_field_upper_label_size_description": "Scales the small label above inputs.",
"advanced_styling_field_upper_label_weight": "Headline Label Font Weight",
"advanced_styling_field_upper_label_weight_description": "Makes the label lighter or bolder.",
"advanced_styling_field_upper_label_color": "Label Color",
"advanced_styling_field_upper_label_color_description": "Colors the small labels above inputs and scale labels.",
"advanced_styling_field_upper_label_size": "Label Font Size",
"advanced_styling_field_upper_label_size_description": "Scales the small labels above inputs and scale labels.",
"advanced_styling_field_upper_label_weight": "Label Font Weight",
"advanced_styling_field_upper_label_weight_description": "Makes the labels lighter or bolder.",
"advanced_styling_section_buttons": "Buttons",
"advanced_styling_section_headlines": "Headlines & Descriptions",
"advanced_styling_section_inputs": "Inputs",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Help us understand you better:",
"improve_trial_conversion_question_2_button_label": "Next",
"improve_trial_conversion_question_2_headline": "Sorry to hear. What was the biggest problem using $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Next",
"improve_trial_conversion_question_3_headline": "What did you expect $[projectName] to do?",
"improve_trial_conversion_question_4_button_label": "Get 20% off",
"improve_trial_conversion_question_4_headline": "Sorry to hear! Get 20% off the first year.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>We are happy to offer you a 20% discount on a yearly plan.</span></p>",
"improve_trial_conversion_question_5_button_label": "Next",
"improve_trial_conversion_question_5_headline": "What would you like to achieve?",
"improve_trial_conversion_question_5_subheader": "Please select one of the following options:",
"improve_trial_conversion_question_5_subheader": "Please describe below:",
"improve_trial_conversion_question_6_headline": "How are you solving your problem now?",
"improve_trial_conversion_question_6_subheader": "Please name alternative solutions:",
"integration_setup_survey_description": "Evaluate how easily users can add integrations to your product. Find blind spots.",
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "Modal centrado",
"change_organization": "Cambiar organización",
"change_workspace": "Cambiar espacio de trabajo",
"choice_n": "Opción {{n}}",
"choices": "Opciones",
"choose_environment": "Elegir entorno",
"choose_organization": "Elegir organización",
@@ -165,6 +166,7 @@
"close": "Cerrar",
"code": "Código",
"collapse_rows": "Contraer filas",
"column_n": "Columna {{n}}",
"completed": "Completado",
"configuration": "Configuración",
"confirm": "Confirmar",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Error al copiar al portapapeles",
"failed_to_load_organizations": "Error al cargar organizaciones",
"failed_to_load_workspaces": "Error al cargar los proyectos",
"field_placeholder": "Marcador de posición de {{field}}",
"filter": "Filtro",
"finish": "Finalizar",
"first_name": "Nombre",
@@ -247,10 +250,12 @@
"generate": "Generar",
"go_back": "Volver",
"go_to_dashboard": "Ir al panel de control",
"headline": "Titular",
"hidden": "Oculto",
"hidden_field": "Campo oculto",
"hidden_fields": "Campos ocultos",
"hide_column": "Ocultar columna",
"html": "HTML",
"id": "ID",
"image": "Imagen",
"images": "Imágenes",
@@ -298,7 +303,6 @@
"months": "meses",
"move_down": "Mover hacia abajo",
"move_up": "Mover hacia arriba",
"multiple_languages": "Múltiples idiomas",
"my_product": "mi producto",
"name": "Nombre",
"new": "Nuevo",
@@ -313,6 +317,7 @@
"no_result_found": "No se encontró resultado",
"no_results": "Sin resultados",
"no_surveys_found": "No se encontraron encuestas.",
"no_text_found": "No se encontró texto",
"none_of_the_above": "Ninguna de las anteriores",
"not_authenticated": "No estás autenticado para realizar esta acción.",
"not_authorized": "No autorizado",
@@ -336,6 +341,7 @@
"organization_settings": "Ajustes de la organización",
"other": "Otro",
"other_filters": "Otros Filtros",
"other_placeholder": "Otro marcador de posición",
"others": "Otros",
"overlay_color": "Color de superposición",
"overview": "Resumen",
@@ -381,6 +387,7 @@
"responses": "Respuestas",
"restart": "Reiniciar",
"role": "Rol",
"row_n": "Fila {{n}}",
"saas": "SaaS",
"sales": "Ventas",
"save": "Guardar",
@@ -419,6 +426,7 @@
"storage_not_configured": "Almacenamiento de archivos no configurado, es probable que fallen las subidas",
"string": "Texto",
"styling": "Estilo",
"subheader": "Subtítulo",
"submit": "Enviar",
"summary": "Resumen",
"survey": "Encuesta",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Envía datos a tu base de datos de Notion",
"please_select_a_survey_error": "Por favor, selecciona una encuesta",
"reconnect_button": "Reconectar",
"reconnect_button_description": "Tu conexión de integración ha caducado. Por favor, reconecta para seguir sincronizando las respuestas. Tus enlaces y datos existentes se conservarán.",
"reconnect_button_tooltip": "Reconecta la integración para actualizar tu acceso. Tus enlaces y datos existentes se conservarán.",
"select_at_least_one_question_error": "Por favor, selecciona al menos una pregunta",
"slack": {
"already_connected_another_survey": "Ya has conectado otra encuesta a este canal.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Desbloquea la autenticación de dos factores con un plan superior",
"update_personal_info": "Actualiza tu información personal",
"warning_cannot_delete_account": "Eres el único propietario de esta organización. Por favor, transfiere la propiedad a otro miembro primero.",
"warning_cannot_undo": "Esto no se puede deshacer",
"wrong_password": "Contraseña incorrecta"
"warning_cannot_undo": "Esto no se puede deshacer"
},
"teams": {
"add_members_description": "Añade miembros al equipo y determina su rol.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "¡Encuesta copiada correctamente!",
"delete_survey_and_responses_warning": "¿Estás seguro de que quieres eliminar esta encuesta y todas sus respuestas?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Elige el idioma predeterminado para esta encuesta:",
"2_activate_translation_for_specific_languages": "2. Activa la traducción para idiomas específicos:",
"activate_translations": "Activar traducciones",
"add": "Añadir +",
"add_a_delay_or_auto_close_the_survey": "Añadir un retraso o cerrar automáticamente la encuesta",
"add_a_four_digit_pin": "Añadir un PIN de cuatro dígitos",
@@ -1363,6 +1366,8 @@
"assign": "Asignar =",
"audience": "Audiencia",
"auto_close_on_inactivity": "Cierre automático por inactividad",
"auto_progress_rating_and_nps": "Avanzar automáticamente en preguntas de valoración y NPS",
"auto_progress_rating_and_nps_description": "Avanza automáticamente cuando los encuestados seleccionen una respuesta en preguntas de valoración o NPS. Esto solo se aplica a bloques de una sola pregunta. Las preguntas obligatorias ocultan el botón Siguiente; las preguntas opcionales aún lo muestran para omitirlas.",
"auto_save_disabled": "Guardado automático desactivado",
"auto_save_disabled_tooltip": "Su encuesta solo se guarda automáticamente cuando está en borrador. Esto asegura que las encuestas públicas no se actualicen involuntariamente.",
"auto_save_on": "Guardado automático activado",
@@ -1408,6 +1413,7 @@
"caution_text": "Los cambios provocarán inconsistencias",
"change_anyway": "Cambiar de todos modos",
"change_background": "Cambiar fondo",
"change_default": "Cambiar predeterminado",
"change_question_type": "Cambiar tipo de pregunta",
"change_survey_type": "Cambiar el tipo de encuesta afecta al acceso existente",
"change_the_background_to_a_color_image_or_animation": "Cambiar el fondo a un color, imagen o animación.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Elige dónde ejecutar la encuesta.",
"city": "Ciudad",
"close_survey_on_response_limit": "Cerrar encuesta al alcanzar el límite de respuestas",
"code": "Código",
"color": "Color",
"column_used_in_logic_error": "Esta columna se usa en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.",
"columns": "Columnas",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Personalizar el logotipo de la encuesta",
"darken_or_lighten_background_of_your_choice": "Oscurece o aclara el fondo de tu elección.",
"days_before_showing_this_survey_again": "o más días deben transcurrir entre la última encuesta mostrada y la visualización de esta encuesta.",
"default_language": "Idioma predeterminado",
"delete_anyways": "Eliminar de todos modos",
"delete_block": "Eliminar bloque",
"delete_choice": "Eliminar opción",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Duplicar pregunta",
"edit_link": "Editar enlace",
"edit_recall": "Editar recuperación",
"edit_translations": "Editar traducciones de {lang}",
"element_not_found": "Pregunta no encontrada",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Permitir a los participantes cambiar el idioma de la encuesta en cualquier momento durante la encuesta.",
"enable_recaptcha_to_protect_your_survey_from_spam": "La protección contra spam utiliza reCAPTCHA v3 para filtrar las respuestas spam.",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "Permitir a los encuestados escribir respuestas más largas y de varias líneas.",
"lower_label": "Etiqueta inferior",
"manage_languages": "Gestionar idiomas",
"manage_translations": "Gestionar traducciones",
"matrix_all_fields": "Todos los campos",
"matrix_rows": "Filas",
"max_file_size": "Tamaño máximo de archivo",
"max_file_size_limit_is": "El límite de tamaño máximo de archivo es",
"missing_first": "Faltantes primero",
"move_question_to_block": "Mover pregunta al bloque",
"multiply": "Multiplicar *",
"needed_for_self_hosted_cal_com_instance": "Necesario para una instancia Cal.com autohospedada",
@@ -1611,7 +1620,7 @@
"next_button_label": "Etiqueta del botón \"Siguiente\"",
"no_hidden_fields_yet_add_first_one_below": "Aún no hay campos ocultos. Añade el primero a continuación.",
"no_images_found_for": "No se encontraron imágenes para ''{query}\"",
"no_languages_found_add_first_one_to_get_started": "No se encontraron idiomas. Añade el primero para comenzar.",
"no_languages_found_add_first_one_to_get_started": "No se encontraron idiomas de encuesta en este espacio de trabajo. Por favor, añade uno para comenzar.",
"no_option_found": "No se encontró ninguna opción",
"no_recall_items_found": "No se encontraron elementos de recuperación",
"no_variables_yet_add_first_one_below": "No hay variables todavía. Añade la primera a continuación.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Por favor, introduce una URL válida (p. ej., https://example.com)",
"please_set_a_survey_trigger": "Establece un disparador de encuesta",
"please_specify": "Por favor, especifica",
"present_your_survey_in_multiple_languages": "Presenta tu encuesta en varios idiomas",
"prevent_double_submission": "Evitar envío duplicado",
"prevent_double_submission_description": "Permitir solo 1 respuesta por dirección de correo electrónico",
"progress_saved": "Progreso guardado",
@@ -1729,6 +1739,7 @@
"seven_points": "7 puntos",
"show_block_settings": "Mostrar ajustes del bloque",
"show_button": "Mostrar botón",
"show_in_order": "Mostrar en orden",
"show_language_switch": "Mostrar cambio de idioma",
"show_multiple_times": "Mostrar un número limitado de veces",
"show_only_once": "Mostrar solo una vez",
@@ -1760,7 +1771,6 @@
"survey_preview": "Vista previa de la encuesta 👀",
"survey_styling": "Estilo del formulario",
"survey_trigger": "Activador de la encuesta",
"switch_multi_language_on_to_get_started": "Activa el modo multiidioma para comenzar 👉",
"target_block_not_found": "Bloque objetivo no encontrado",
"targeted": "Dirigido",
"ten_points": "10 puntos",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Mostrar una sola vez, incluso si no responden.",
"then": "Entonces",
"this_action_will_remove_all_the_translations_from_this_survey": "Esta acción eliminará todas las traducciones de esta encuesta.",
"this_will_remove_the_language_and_all_its_translations": "Esto eliminará este idioma y todas sus traducciones de esta encuesta. Esta acción no se puede deshacer.",
"three_points": "3 puntos",
"times": "veces",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Para mantener la ubicación coherente en todas las encuestas, puedes",
"translated": "Traducido",
"trigger_survey_when_one_of_the_actions_is_fired": "Activar encuesta cuando se dispare una de las acciones...",
"try_lollipop_or_mountain": "Prueba 'piruleta' o 'montaña'...",
"type_field_id": "Escribe el id del campo",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Solo permite responder a personas con un correo electrónico real.",
"visibility_and_recontact": "Visibilidad y recontacto",
"visibility_and_recontact_description": "Controla cuándo puede aparecer esta encuesta y con qué frecuencia puede volver a aparecer.",
"visible": "Visible",
"wait": "Esperar",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Esperar unos segundos después del disparador antes de mostrar la encuesta",
"waiting_time_across_surveys": "Periodo de espera (entre encuestas)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Idioma o ID de idioma duplicado",
"edit_languages": "Editar idiomas",
"identifier": "Identificador (ISO)",
"incomplete_translations": "Traducciones incompletas",
"language": "Idioma",
"language_deleted_successfully": "Idioma eliminado correctamente",
"languages_updated_successfully": "Idiomas actualizados correctamente",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Por favor, selecciona un idioma",
"remove_language": "Eliminar idioma",
"remove_language_from_surveys_to_remove_it_from_workspace": "Por favor, elimina el idioma de estas encuestas para poder eliminarlo del espacio de trabajo.",
"search_items": "Buscar elementos",
"translate": "Traducir"
"search_items": "Buscar elementos"
},
"look": {
"add_background_color": "Añadir color de fondo",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Colorea la parte no rellenada de la barra.",
"advanced_styling_field_track_height": "Altura de la pista",
"advanced_styling_field_track_height_description": "Controla el grosor de la barra de progreso.",
"advanced_styling_field_upper_label_color": "Color de la etiqueta del titular",
"advanced_styling_field_upper_label_color_description": "Colorea la etiqueta pequeña sobre los campos de entrada.",
"advanced_styling_field_upper_label_size": "Tamaño de fuente de la etiqueta del titular",
"advanced_styling_field_upper_label_size_description": "Escala la etiqueta pequeña sobre los campos de entrada.",
"advanced_styling_field_upper_label_weight": "Grosor de fuente de la etiqueta del titular",
"advanced_styling_field_upper_label_weight_description": "Hace que la etiqueta sea más ligera o más gruesa.",
"advanced_styling_field_upper_label_color": "Color de etiqueta",
"advanced_styling_field_upper_label_color_description": "Colorea las pequeñas etiquetas sobre los campos de entrada y las etiquetas de escala.",
"advanced_styling_field_upper_label_size": "Tamaño de fuente de etiqueta",
"advanced_styling_field_upper_label_size_description": "Escala las pequeñas etiquetas sobre los campos de entrada y las etiquetas de escala.",
"advanced_styling_field_upper_label_weight": "Grosor de fuente de etiqueta",
"advanced_styling_field_upper_label_weight_description": "Hace que las etiquetas sean más ligeras o más gruesas.",
"advanced_styling_section_buttons": "Botones",
"advanced_styling_section_headlines": "Títulos y descripciones",
"advanced_styling_section_inputs": "Campos de entrada",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Ayúdanos a entenderte mejor:",
"improve_trial_conversion_question_2_button_label": "Siguiente",
"improve_trial_conversion_question_2_headline": "Lamentamos oír eso. ¿Cuál fue el mayor problema al usar $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Siguiente",
"improve_trial_conversion_question_3_headline": "¿Qué esperabas que hiciera $[projectName]?",
"improve_trial_conversion_question_4_button_label": "Obtener 20 % de descuento",
"improve_trial_conversion_question_4_headline": "¡Sentimos oírlo! Obtén un 20 % de descuento en el primer año.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Nos complace ofrecerte un 20 % de descuento en un plan anual.</span></p>",
"improve_trial_conversion_question_5_button_label": "Siguiente",
"improve_trial_conversion_question_5_headline": "¿Qué te gustaría conseguir?",
"improve_trial_conversion_question_5_subheader": "Por favor, selecciona una de las siguientes opciones:",
"improve_trial_conversion_question_5_subheader": "Por favor, describe a continuación:",
"improve_trial_conversion_question_6_headline": "¿Cómo estás solucionando tu problema ahora?",
"improve_trial_conversion_question_6_subheader": "Por favor, nombra soluciones alternativas:",
"integration_setup_survey_description": "Evalúa con qué facilidad los usuarios pueden añadir integraciones a tu producto. Encuentra puntos ciegos.",
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "Au centre",
"change_organization": "Changer d'organisation",
"change_workspace": "Changer d'espace de travail",
"choice_n": "Choix {{n}}",
"choices": "Choix",
"choose_environment": "Choisir l'environnement",
"choose_organization": "Choisir l'organisation",
@@ -165,6 +166,7 @@
"close": "Fermer",
"code": "Code",
"collapse_rows": "Réduire les lignes",
"column_n": "Colonne {{n}}",
"completed": "Terminé",
"configuration": "Configuration",
"confirm": "Confirmer",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Échec de la copie dans le presse-papiers",
"failed_to_load_organizations": "Échec du chargement des organisations",
"failed_to_load_workspaces": "Échec du chargement des projets",
"field_placeholder": "Espace réservé {{field}}",
"filter": "Filtre",
"finish": "Terminer",
"first_name": "Prénom",
@@ -247,10 +250,12 @@
"generate": "Générer",
"go_back": "Retourner",
"go_to_dashboard": "Aller au tableau de bord",
"headline": "Titre principal",
"hidden": "Caché",
"hidden_field": "Champ caché",
"hidden_fields": "Champs cachés",
"hide_column": "Cacher la colonne",
"html": "HTML",
"id": "ID",
"image": "Image",
"images": "Images",
@@ -298,7 +303,6 @@
"months": "mois",
"move_down": "Déplacer vers le bas",
"move_up": "Déplacer vers le haut",
"multiple_languages": "Plusieurs langues",
"my_product": "mon produit",
"name": "Nom",
"new": "Nouveau",
@@ -313,6 +317,7 @@
"no_result_found": "Aucun résultat trouvé",
"no_results": "Aucun résultat",
"no_surveys_found": "Aucun sondage trouvé.",
"no_text_found": "Aucun texte trouvé",
"none_of_the_above": "Aucun des éléments ci-dessus",
"not_authenticated": "Vous n'êtes pas authentifié pour effectuer cette action.",
"not_authorized": "Non autorisé",
@@ -336,6 +341,7 @@
"organization_settings": "Paramètres de l'organisation",
"other": "Autre",
"other_filters": "Autres filtres",
"other_placeholder": "Autre espace réservé",
"others": "Autres",
"overlay_color": "Couleur de superposition",
"overview": "Aperçu",
@@ -381,6 +387,7 @@
"responses": "Réponses",
"restart": "Recommencer",
"role": "Rôle",
"row_n": "Ligne {{n}}",
"saas": "SaaS",
"sales": "Ventes",
"save": "Enregistrer",
@@ -419,6 +426,7 @@
"storage_not_configured": "Stockage de fichiers non configuré, les téléchargements risquent d'échouer",
"string": "Texte",
"styling": "Style",
"subheader": "Sous-titre",
"submit": "Soumettre",
"summary": "Résumé",
"survey": "Enquête",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Envoyez des données à votre base de données Notion.",
"please_select_a_survey_error": "Veuillez sélectionner une enquête.",
"reconnect_button": "Reconnecter",
"reconnect_button_description": "Ta connexion à l'intégration a expiré. Reconnecte-toi pour continuer à synchroniser les réponses. Tes liens et données existants seront conservés.",
"reconnect_button_tooltip": "Reconnecte l'intégration pour actualiser ton accès. Tes liens et données existants seront conservés.",
"select_at_least_one_question_error": "Veuillez sélectionner au moins une question.",
"slack": {
"already_connected_another_survey": "Vous avez déjà connecté une autre enquête à ce canal.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Débloquez l'authentification à deux facteurs avec une offre supérieure",
"update_personal_info": "Mettez à jour vos informations personnelles",
"warning_cannot_delete_account": "Tu es le seul propriétaire de cette organisation. Transfère la propriété à un autre membre d'abord.",
"warning_cannot_undo": "Cette opération est irréversible.",
"wrong_password": "Mot de passe incorrect"
"warning_cannot_undo": "Cette opération est irréversible."
},
"teams": {
"add_members_description": "Ajoutez des membres à l'équipe et déterminez leur rôle.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Enquête copiée avec succès !",
"delete_survey_and_responses_warning": "Êtes-vous sûr de vouloir supprimer cette enquête et toutes ses réponses?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Choisissez la langue par défaut pour ce sondage :",
"2_activate_translation_for_specific_languages": "2. Activer la traduction pour des langues spécifiques:",
"activate_translations": "Activer les traductions",
"add": "Ajouter +",
"add_a_delay_or_auto_close_the_survey": "Ajouter un délai ou fermer automatiquement l'enquête",
"add_a_four_digit_pin": "Ajoutez un code PIN à quatre chiffres.",
@@ -1363,6 +1366,8 @@
"assign": "Attribuer =",
"audience": "Public",
"auto_close_on_inactivity": "Fermeture automatique en cas d'inactivité",
"auto_progress_rating_and_nps": "Progression automatique pour les questions d'évaluation et NPS",
"auto_progress_rating_and_nps_description": "Passe automatiquement à la question suivante lorsque les répondants sélectionnent une réponse aux questions d'évaluation ou NPS. Cela s'applique uniquement aux blocs à question unique. Les questions obligatoires masquent le bouton Suivant ; les questions facultatives l'affichent toujours pour permettre de passer la question.",
"auto_save_disabled": "Sauvegarde automatique désactivée",
"auto_save_disabled_tooltip": "Votre sondage n'est sauvegardé automatiquement que lorsqu'il est en brouillon. Cela garantit que les sondages publics ne sont pas mis à jour involontairement.",
"auto_save_on": "Sauvegarde automatique activée",
@@ -1408,6 +1413,7 @@
"caution_text": "Les changements entraîneront des incohérences.",
"change_anyway": "Changer de toute façon",
"change_background": "Changer l'arrière-plan",
"change_default": "Modifier la langue par défaut",
"change_question_type": "Changer le type de question",
"change_survey_type": "Le changement de type de sondage affecte l'accès existant",
"change_the_background_to_a_color_image_or_animation": "Changez l'arrière-plan en une couleur, une image ou une animation.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Choisissez où réaliser l'enquête.",
"city": "Ville",
"close_survey_on_response_limit": "Fermer l'enquête sur la limite de réponse",
"code": "Code",
"color": "Couleur",
"column_used_in_logic_error": "Cette colonne est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.",
"columns": "Colonnes",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Personnaliser le logo de l'enquête",
"darken_or_lighten_background_of_your_choice": "Assombrir ou éclaircir l'arrière-plan de votre choix.",
"days_before_showing_this_survey_again": "ou plus de jours doivent s'écouler entre le dernier sondage affiché et l'affichage de ce sondage.",
"default_language": "Langue par défaut",
"delete_anyways": "Supprimer quand même",
"delete_block": "Supprimer le bloc",
"delete_choice": "Supprimer l'option",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Dupliquer la question",
"edit_link": "Modifier le lien",
"edit_recall": "Modifier le rappel",
"edit_translations": "Modifier les traductions {lang}",
"element_not_found": "Question non trouvée",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Permettre aux répondants de changer de langue à tout moment. Nécessite au moins 2 langues actives.",
"enable_recaptcha_to_protect_your_survey_from_spam": "La protection contre le spam utilise reCAPTCHA v3 pour filtrer les réponses indésirables.",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "Permettre aux répondants d'écrire des réponses plus longues et sur plusieurs lignes.",
"lower_label": "Étiquette inférieure",
"manage_languages": "Gérer les langues",
"manage_translations": "Gérer les traductions",
"matrix_all_fields": "Tous les champs",
"matrix_rows": "Lignes",
"max_file_size": "Taille maximale du fichier",
"max_file_size_limit_is": "La limite de taille maximale du fichier est",
"missing_first": "Manquantes en premier",
"move_question_to_block": "Déplacer la question vers le bloc",
"multiply": "Multiplier *",
"needed_for_self_hosted_cal_com_instance": "Nécessaire pour une instance Cal.com auto-hébergée",
@@ -1611,7 +1620,7 @@
"next_button_label": "Libellé du bouton « Suivant »",
"no_hidden_fields_yet_add_first_one_below": "Aucun champ caché pour le moment. Ajoutez le premier ci-dessous.",
"no_images_found_for": "Aucune image trouvée pour ''{query}\"",
"no_languages_found_add_first_one_to_get_started": "Aucune langue trouvée. Ajoutez la première pour commencer.",
"no_languages_found_add_first_one_to_get_started": "Aucune langue d'enquête trouvée dans cet espace de travail. Veuillez en ajouter une pour commencer.",
"no_option_found": "Aucune option trouvée",
"no_recall_items_found": "Aucun élément de rappel trouvé",
"no_variables_yet_add_first_one_below": "Aucune variable pour le moment. Ajoutez la première ci-dessous.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Veuillez entrer une URL valide (par exemple, https://example.com)",
"please_set_a_survey_trigger": "Veuillez définir un déclencheur d'enquête.",
"please_specify": "Veuillez préciser",
"present_your_survey_in_multiple_languages": "Présente ton questionnaire dans plusieurs langues",
"prevent_double_submission": "Empêcher la double soumission",
"prevent_double_submission_description": "Autoriser uniquement 1 réponse par adresse e-mail",
"progress_saved": "Progression enregistrée",
@@ -1729,6 +1739,7 @@
"seven_points": "7 points",
"show_block_settings": "Afficher les paramètres du bloc",
"show_button": "Afficher le bouton",
"show_in_order": "Afficher dans l'ordre",
"show_language_switch": "Afficher le changement de langue",
"show_multiple_times": "Afficher un nombre limité de fois",
"show_only_once": "Afficher une seule fois",
@@ -1760,7 +1771,6 @@
"survey_preview": "Aperçu du sondage 👀",
"survey_styling": "Style de formulaire",
"survey_trigger": "Déclencheur d'enquête",
"switch_multi_language_on_to_get_started": "Activez le mode multilingue pour commencer 👉",
"target_block_not_found": "Bloc cible non trouvé",
"targeted": "Ciblé",
"ten_points": "10 points",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Afficher une seule fois, même si la personne ne répond pas.",
"then": "Alors",
"this_action_will_remove_all_the_translations_from_this_survey": "Cette action supprimera toutes les traductions de cette enquête.",
"this_will_remove_the_language_and_all_its_translations": "Cela supprimera cette langue et toutes ses traductions de ce questionnaire. Cette action est irréversible.",
"three_points": "3 points",
"times": "fois",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Pour maintenir la cohérence du placement sur tous les sondages, vous pouvez",
"translated": "Traduit",
"trigger_survey_when_one_of_the_actions_is_fired": "Déclencher l'enquête lorsqu'une des actions est déclenchée...",
"try_lollipop_or_mountain": "Essayez 'sucette' ou 'montagne'...",
"type_field_id": "Identifiant de champ de type",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Ne laissez répondre que les personnes ayant une véritable adresse e-mail.",
"visibility_and_recontact": "Visibilité et recontact",
"visibility_and_recontact_description": "Contrôlez quand cette enquête peut apparaître et à quelle fréquence elle peut réapparaître.",
"visible": "Visible",
"wait": "Attendre",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Attendez quelques secondes après le déclencheur avant de montrer l'enquête.",
"waiting_time_across_surveys": "Période de refroidissement (entre les sondages)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Langue ou identifiant de langue en double",
"edit_languages": "Modifier les langues",
"identifier": "Identifiant (ISO)",
"incomplete_translations": "Traductions incomplètes",
"language": "Langue",
"language_deleted_successfully": "Langue supprimée avec succès",
"languages_updated_successfully": "Langues mises à jour avec succès",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Veuillez sélectionner une langue",
"remove_language": "Supprimer la langue",
"remove_language_from_surveys_to_remove_it_from_workspace": "Veuillez supprimer la langue de ces sondages afin de la retirer de l'espace de travail.",
"search_items": "Rechercher des éléments",
"translate": "Traduire"
"search_items": "Rechercher des éléments"
},
"look": {
"add_background_color": "Ajouter une couleur d'arrière-plan",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Colore la partie non remplie de la barre.",
"advanced_styling_field_track_height": "Hauteur de la piste",
"advanced_styling_field_track_height_description": "Contrôle l'épaisseur de la barre de progression.",
"advanced_styling_field_upper_label_color": "Couleur de l'étiquette du titre",
"advanced_styling_field_upper_label_color_description": "Colore le petit libellé au-dessus des champs de saisie.",
"advanced_styling_field_upper_label_size": "Taille de police de l'étiquette du titre",
"advanced_styling_field_upper_label_size_description": "Ajuste la taille du petit libellé au-dessus des champs de saisie.",
"advanced_styling_field_upper_label_weight": "Graisse de police de l'étiquette du titre",
"advanced_styling_field_upper_label_weight_description": "Rend le libellé plus léger ou plus gras.",
"advanced_styling_field_upper_label_color": "Couleur de l'étiquette",
"advanced_styling_field_upper_label_color_description": "Colore les petites étiquettes au-dessus des champs de saisie et des échelles.",
"advanced_styling_field_upper_label_size": "Taille de police de l'étiquette",
"advanced_styling_field_upper_label_size_description": "Ajuste la taille des petites étiquettes au-dessus des champs de saisie et des échelles.",
"advanced_styling_field_upper_label_weight": "Graisse de police de l'étiquette",
"advanced_styling_field_upper_label_weight_description": "Rend les étiquettes plus légères ou plus grasses.",
"advanced_styling_section_buttons": "Boutons",
"advanced_styling_section_headlines": "Titres et descriptions",
"advanced_styling_section_inputs": "Champs de saisie",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Aidez-nous à mieux vous comprendre :",
"improve_trial_conversion_question_2_button_label": "Suivant",
"improve_trial_conversion_question_2_headline": "Désolé d'apprendre cela. Quel était le plus gros problème rencontré avec $[projectName] ?",
"improve_trial_conversion_question_3_button_label": "Suivant",
"improve_trial_conversion_question_3_headline": "Qu'attendiez-vous de $[projectName] ?",
"improve_trial_conversion_question_4_button_label": "Obtenez 20 % de réduction",
"improve_trial_conversion_question_4_headline": "Désolé d'apprendre cela ! Bénéficiez de 20 % de réduction sur la première année.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Nous sommes heureux de vous offrir une remise de 20 % sur un plan annuel.</span></p>",
"improve_trial_conversion_question_5_button_label": "Suivant",
"improve_trial_conversion_question_5_headline": "Que souhaitez-vous accomplir ?",
"improve_trial_conversion_question_5_subheader": "Veuillez sélectionner l'une des options suivantes :",
"improve_trial_conversion_question_5_subheader": "Merci de décrire ci-dessous :",
"improve_trial_conversion_question_6_headline": "Comment résolvez-vous votre problème maintenant ?",
"improve_trial_conversion_question_6_subheader": "Veuillez nommer des solutions alternatives :",
"integration_setup_survey_description": "Évaluez la facilité avec laquelle les utilisateurs peuvent ajouter des intégrations à votre produit. Identifiez les points aveugles.",
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "Középre helyezett kizárólagos",
"change_organization": "Szervezet módosítása",
"change_workspace": "Munkaterület módosítása",
"choice_n": "{{n}}. választás",
"choices": "Választási lehetőségek",
"choose_environment": "Környezet kiválasztása",
"choose_organization": "Szervezet kiválasztása",
@@ -165,6 +166,7 @@
"close": "Bezárás",
"code": "Kód",
"collapse_rows": "Sorok összecsukása",
"column_n": "{{n}}. oszlop",
"completed": "Befejezve",
"configuration": "Beállítás",
"confirm": "Megerősítés",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Nem sikerült másolni a vágólapra",
"failed_to_load_organizations": "Nem sikerült betölteni a szervezeteket",
"failed_to_load_workspaces": "Nem sikerült a munkaterületek betöltése",
"field_placeholder": "{{field}} helyőrző",
"filter": "Szűrő",
"finish": "Befejezés",
"first_name": "Keresztnév",
@@ -247,10 +250,12 @@
"generate": "Előállítás",
"go_back": "Vissza",
"go_to_dashboard": "Ugrás a vezérlőpultra",
"headline": "Címsor",
"hidden": "Rejtett",
"hidden_field": "Rejtett mező",
"hidden_fields": "Rejtett mezők",
"hide_column": "Oszlop elrejtése",
"html": "HTML",
"id": "Azonosító",
"image": "Kép",
"images": "Képek",
@@ -298,7 +303,6 @@
"months": "hónap",
"move_down": "Mozgatás le",
"move_up": "Mozgatás fel",
"multiple_languages": "Több nyelv",
"my_product": "saját termék",
"name": "Név",
"new": "Új",
@@ -313,6 +317,7 @@
"no_result_found": "Nem található eredmény",
"no_results": "Nincs találat",
"no_surveys_found": "Nem találhatók kérdőívek.",
"no_text_found": "Nem található szöveg",
"none_of_the_above": "A fentiek közül egyik sem",
"not_authenticated": "Nincs jogosultsága ennek a műveletnek a végrehajtásához.",
"not_authorized": "Nincs felhatalmazva",
@@ -336,6 +341,7 @@
"organization_settings": "Szervezet beállításai",
"other": "Egyéb",
"other_filters": "Egyéb szűrők",
"other_placeholder": "Egyéb helyőrző",
"others": "Mások",
"overlay_color": "Rávetítés színe",
"overview": "Áttekintés",
@@ -381,6 +387,7 @@
"responses": "Válaszok",
"restart": "Újraindítás",
"role": "Szerep",
"row_n": "{{n}}. sor",
"saas": "SaaS",
"sales": "Értékesítés",
"save": "Mentés",
@@ -419,6 +426,7 @@
"storage_not_configured": "A fájltároló nincs beállítva, a feltöltések valószínűleg sikertelenek lesznek",
"string": "Szöveg",
"styling": "Stíluskészítés",
"subheader": "Alcím",
"submit": "Elküldés",
"summary": "Összegzés",
"survey": "Kérdőív",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Adatok küldése a Notion-adatbázisba",
"please_select_a_survey_error": "Válasszon kérdőívet",
"reconnect_button": "Újracsatlakozás",
"reconnect_button_description": "Az integráció kapcsolata lejárt. Kérjük, csatlakozzon újra a válaszok szinkronizálásának folytatásához. A meglévő hivatkozások és adatok megmaradnak.",
"reconnect_button_tooltip": "Csatlakoztassa újra az integrációt a hozzáférés frissítéséhez. A meglévő hivatkozások és adatok megmaradnak.",
"select_at_least_one_question_error": "Válasszon legalább egy kérdést",
"slack": {
"already_connected_another_survey": "Már hozzákapcsolt egy másik kérdőívet ehhez a csatornához.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "A kétfaktoros hitelesítés feloldása egy magasabb csomaggal",
"update_personal_info": "Személyes információk frissítése",
"warning_cannot_delete_account": "Ön az egyetlen tulajdonosa ennek a szervezetnek. Először adja át a tulajdonjogot egy másik tagnak.",
"warning_cannot_undo": "Ezt nem lehet visszavonni",
"wrong_password": "Hibás jelszó"
"warning_cannot_undo": "Ezt nem lehet visszavonni"
},
"teams": {
"add_members_description": "Tagok hozzáadása a csapathoz és a szerepük meghatározása.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "A kérdőív sikeresen másolva",
"delete_survey_and_responses_warning": "Biztosan törölni szeretné ezt a kérdőívet és az összes válaszát?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Válassza ki a kérdőív alapértelmezett nyelvét:",
"2_activate_translation_for_specific_languages": "2. Aktiválja a fordítást bizonyos nyelvekhez:",
"activate_translations": "Fordítások aktiválása",
"add": "Hozzáadás +",
"add_a_delay_or_auto_close_the_survey": "Késleltetés hozzáadása vagy a kérdőív automatikus lezárása",
"add_a_four_digit_pin": "Négy számjegyű PIN-kód hozzáadása",
@@ -1363,6 +1366,8 @@
"assign": "= hozzárendelése",
"audience": "Közönség",
"auto_close_on_inactivity": "Automatikus lezárás tétlenségnél",
"auto_progress_rating_and_nps": "Automatikus továbblépés értékelési és NPS kérdéseknél",
"auto_progress_rating_and_nps_description": "Automatikus továbblépés, amikor a válaszadók kiválasztanak egy választ az értékelési vagy NPS kérdéseknél. Ez csak az egykérdéses blokkokra vonatkozik. A kötelező kérdések elrejtik a Tovább gombot; az opcionális kérdések továbbra is megjelenítik azt a kihagyás lehetősége érdekében.",
"auto_save_disabled": "Az automatikus mentés letiltva",
"auto_save_disabled_tooltip": "A kérdőív csak akkor kerül automatikusan mentésre, ha piszkozatban van. Ez biztosítja, hogy a nyilvános kérdőívek ne legyenek véletlenül frissítve.",
"auto_save_on": "Automatikus mentés bekapcsolva",
@@ -1408,6 +1413,7 @@
"caution_text": "A változtatások következetlenségekhez vezetnek",
"change_anyway": "Változtatás mindenképp",
"change_background": "Háttér megváltoztatása",
"change_default": "Alapértelmezett módosítása",
"change_question_type": "Kérdés típusának megváltoztatása",
"change_survey_type": "A kérdőív típusának megváltoztatása befolyásolja a meglévő hozzáférést",
"change_the_background_to_a_color_image_or_animation": "A háttér megváltoztatása színre, képre vagy animációra.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Annak kiválasztása, hogy hol fusson a kérdőív.",
"city": "Város",
"close_survey_on_response_limit": "Kérdőív lezárása a válaszkorlátnál",
"code": "Kód",
"color": "Szín",
"column_used_in_logic_error": "Ez az oszlop használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.",
"columns": "Oszlopok",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "A kérdőív logójának személyre szabása",
"darken_or_lighten_background_of_your_choice": "A választási lehetőség hátterének sötétítése vagy világosítása.",
"days_before_showing_this_survey_again": "vagy több napnak kell eltelnie az utolsó megjelenített kérdőív és ezen kérdőív megjelenése között.",
"default_language": "Alapértelmezett nyelv",
"delete_anyways": "Törlés mindenképp",
"delete_block": "Blokk törlése",
"delete_choice": "Választási lehetőség törlése",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Kérdés kettőzése",
"edit_link": "Hivatkozás szerkesztése",
"edit_recall": "Visszahívás szerkesztése",
"edit_translations": "{lang} fordítások szerkesztése",
"element_not_found": "A kérdés nem található",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Lehetővé tétel a válaszadóknak, hogy bármikor nyelvet váltsanak. Legalább 2 aktív nyelvet igényel.",
"enable_recaptcha_to_protect_your_survey_from_spam": "A szemét elleni védekezés a reCAPTCHA v3-at használja a kéretlen válaszok kiszűréséhez.",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "Lehetővé tétel a válaszadóknak, hogy hosszabb, többsoros válaszokat írjanak.",
"lower_label": "Alsó címke",
"manage_languages": "Nyelvek kezelése",
"manage_translations": "Fordítások kezelése",
"matrix_all_fields": "Összes mező",
"matrix_rows": "Sorok",
"max_file_size": "Legnagyobb fájlméret",
"max_file_size_limit_is": "A legnagyobb fájlméretkorlát",
"missing_first": "Hiányzók először",
"move_question_to_block": "Kérdés áthelyezése egy blokkba",
"multiply": "Szorzás *",
"needed_for_self_hosted_cal_com_instance": "Saját üzemeltetésű Cal.com-példányhoz szükséges",
@@ -1611,7 +1620,7 @@
"next_button_label": "A „Következő” gomb címkéje",
"no_hidden_fields_yet_add_first_one_below": "Még nincsenek rejtett mezők. Adja hozzá az elsőt lent.",
"no_images_found_for": "Nem találhatók képek a(z) „{query}” lekérdezéshez",
"no_languages_found_add_first_one_to_get_started": "Nem találhatók nyelvek. Adja hozzá az elsőt a kezdéshez.",
"no_languages_found_add_first_one_to_get_started": "Nem található felmérési nyelv ebben a munkaterületen. Kérem, adjon hozzá egyet a kezdéshez.",
"no_option_found": "Nem található lehetőség",
"no_recall_items_found": "Nem találhatók visszahívási elemek",
"no_variables_yet_add_first_one_below": "Még nincsenek változók. Adja hozzá az elsőt lent.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Adjon meg egy érvényes URL-t (például https://example.com)",
"please_set_a_survey_trigger": "Állítson be kérdőív-aktiválót",
"please_specify": "Adja meg",
"present_your_survey_in_multiple_languages": "Mutassa be felmérését több nyelven",
"prevent_double_submission": "Kettős beküldés megakadályozása",
"prevent_double_submission_description": "E-mail-címenként csak 1 válasz engedélyezése",
"progress_saved": "Folyamat elmentve",
@@ -1729,6 +1739,7 @@
"seven_points": "7 pont",
"show_block_settings": "Blokkbeállítások megjelenítése",
"show_button": "Gomb megjelenítése",
"show_in_order": "Sorrendben megjelenítés",
"show_language_switch": "Nyelvválasztó megjelenítése",
"show_multiple_times": "Megjelenítés korlátozott számú alkalommal",
"show_only_once": "Megjelenítés csak egyszer",
@@ -1760,7 +1771,6 @@
"survey_preview": "Kérdőív előnézete 👀",
"survey_styling": "Kérdőív stílusának beállítása",
"survey_trigger": "Kérdőív aktiválója",
"switch_multi_language_on_to_get_started": "Kapcsolja be a többnyelvűséget a kezdéshez 👉",
"target_block_not_found": "A célblokk nem található",
"targeted": "Célzott",
"ten_points": "10 pont",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Megjelenítés egyetlen alkalommal, még akkor is, ha nem válaszolnak.",
"then": "Azután",
"this_action_will_remove_all_the_translations_from_this_survey": "Ez a művelet eltávolítja az összes fordítást ebből a kérdőívből.",
"this_will_remove_the_language_and_all_its_translations": "Ez eltávolítja ezt a nyelvet és az összes fordítását ebből a felmérésből. Ez a művelet nem vonható vissza.",
"three_points": "3 pont",
"times": "alkalom",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Ahhoz, hogy következetesen megtartsa az elhelyezést az összes kérdőívnél, az alábbiakat teheti:",
"translated": "Lefordítva",
"trigger_survey_when_one_of_the_actions_is_fired": "A kérdőív aktiválása, ha a műveletek egyikét elindítják…",
"try_lollipop_or_mountain": "A „nyalóka” vagy „hegy” kipróbálása…",
"type_field_id": "Mezőazonosító beírása",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Csak valódi e-mail-címmel rendelkező személyek válaszolhassanak.",
"visibility_and_recontact": "Láthatóság és újbóli kapcsolatfelvétel",
"visibility_and_recontact_description": "Annak vezérlése, hogy ez a kérdőív mikor jelenhet meg és milyen gyakran jelenhet meg újra.",
"visible": "Látható",
"wait": "Várakozás",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Várakozás néhány másodpercig az aktiválás után, mielőtt megjelenítené a kérdőívet",
"waiting_time_across_surveys": "Várakozási időszak (kérdőívek között)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Kettőzött nyelv vagy nyelvazonosító",
"edit_languages": "Nyelvek szerkesztése",
"identifier": "Azonosító (ISO)",
"incomplete_translations": "Befejezetlen fordítások",
"language": "Nyelv",
"language_deleted_successfully": "A nyelv sikeresen törölve",
"languages_updated_successfully": "A nyelvek sikeresen frissítve",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Válasszon egy nyelvet",
"remove_language": "Nyelv eltávolítása",
"remove_language_from_surveys_to_remove_it_from_workspace": "Távolítsa el a nyelvet ezekből a kérdőívekből, hogy eltávolítsa azt a munkaterületről.",
"search_items": "Elemek keresése",
"translate": "Fordítás"
"search_items": "Elemek keresése"
},
"look": {
"add_background_color": "Háttérszín hozzáadása",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Kiszínezi a sáv kitöltetlen részét.",
"advanced_styling_field_track_height": "Követés magassága",
"advanced_styling_field_track_height_description": "A folyamatjelző vastagságát vezérli.",
"advanced_styling_field_upper_label_color": "Címsor címkéjének színe",
"advanced_styling_field_upper_label_color_description": "Kiszínezi a beviteli mezők fölötti kis címkéket.",
"advanced_styling_field_upper_label_size": "Címsor címkéjének betűmérete",
"advanced_styling_field_upper_label_size_description": "Átméretezi a beviteli mezők fölötti kis címkéket.",
"advanced_styling_field_upper_label_weight": "Címsor címkéjének betűvastagsága",
"advanced_styling_field_upper_label_weight_description": "Vékonyabbá vagy vastagabbá teszi a címkét.",
"advanced_styling_field_upper_label_color": "Címke színe",
"advanced_styling_field_upper_label_color_description": "A beviteli mezők feletti kis címkék és a skálacímkék színét állítja be.",
"advanced_styling_field_upper_label_size": "Címke betűmérete",
"advanced_styling_field_upper_label_size_description": "A beviteli mezők feletti kis címkék és a skálacímkék betűméretét állítja be.",
"advanced_styling_field_upper_label_weight": "Címke betűvastagsága",
"advanced_styling_field_upper_label_weight_description": "A címkék vékonyabbá vagy vastagabbá tételét teszi lehetővé.",
"advanced_styling_section_buttons": "Gombok",
"advanced_styling_section_headlines": "Címsorok és leírások",
"advanced_styling_section_inputs": "Beviteli mezők",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Segítsen nekünk jobban megérteni Önt:",
"improve_trial_conversion_question_2_button_label": "Következő",
"improve_trial_conversion_question_2_headline": "Sajnálattal halljuk. Mi volt a legnagyobb probléma a(z) $[projectName] projekt használatával?",
"improve_trial_conversion_question_3_button_label": "Következő",
"improve_trial_conversion_question_3_headline": "Mit várt a(z) $[projectName] projekttől?",
"improve_trial_conversion_question_4_button_label": "20% kedvezmény",
"improve_trial_conversion_question_4_headline": "Sajnálattal halljuk! 20% kedvezményt kap az első évre.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Boldogan felajánlunk 20% kedvezményt az éves csomagra.</span></p>",
"improve_trial_conversion_question_5_button_label": "Következő",
"improve_trial_conversion_question_5_headline": "Mit szeretne elérni?",
"improve_trial_conversion_question_5_subheader": "Válassza ki a következő lehetőségek egyikét:",
"improve_trial_conversion_question_5_subheader": "Kérjük, ismertesse az alábbiakban:",
"improve_trial_conversion_question_6_headline": "Hogyan oldja meg a problémáját most?",
"improve_trial_conversion_question_6_subheader": "Nevezzen meg alternatív megoldásokat:",
"integration_setup_survey_description": "Annak kiértékelése, hogy a felhasználók mennyire könnyen tudnak integrációkat hozzáadni a termékéhez. A vakfoltok megtalálása.",
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "中央モーダル",
"change_organization": "組織を変更",
"change_workspace": "ワークスペースを変更",
"choice_n": "選択肢 {{n}}",
"choices": "選択肢",
"choose_environment": "環境を選択",
"choose_organization": "組織を選択",
@@ -165,6 +166,7 @@
"close": "閉じる",
"code": "コード",
"collapse_rows": "行を非表示",
"column_n": "列 {{n}}",
"completed": "完了",
"configuration": "設定",
"confirm": "確認",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "クリップボードへのコピーに失敗しました",
"failed_to_load_organizations": "組織の読み込みに失敗しました",
"failed_to_load_workspaces": "ワークスペースの読み込みに失敗しました",
"field_placeholder": "{{field}} プレースホルダー",
"filter": "フィルター",
"finish": "完了",
"first_name": "名",
@@ -247,10 +250,12 @@
"generate": "生成",
"go_back": "戻る",
"go_to_dashboard": "ダッシュボードへ移動",
"headline": "見出し",
"hidden": "非表示",
"hidden_field": "非表示フィールド",
"hidden_fields": "非表示フィールド",
"hide_column": "列を非表示",
"html": "HTML",
"id": "ID",
"image": "画像",
"images": "画像",
@@ -298,7 +303,6 @@
"months": "ヶ月",
"move_down": "下に移動",
"move_up": "上に移動",
"multiple_languages": "多言語",
"my_product": "マイプロダクト",
"name": "名前",
"new": "新規",
@@ -313,6 +317,7 @@
"no_result_found": "結果が見つかりません",
"no_results": "結果なし",
"no_surveys_found": "フォームが見つかりません。",
"no_text_found": "テキストが見つかりません",
"none_of_the_above": "いずれも該当しません",
"not_authenticated": "このアクションを実行するための認証がされていません。",
"not_authorized": "権限がありません",
@@ -336,6 +341,7 @@
"organization_settings": "組織設定",
"other": "その他",
"other_filters": "その他のフィルター",
"other_placeholder": "その他のプレースホルダー",
"others": "その他",
"overlay_color": "オーバーレイの色",
"overview": "概要",
@@ -381,6 +387,7 @@
"responses": "回答",
"restart": "再開",
"role": "役割",
"row_n": "行 {{n}}",
"saas": "SaaS",
"sales": "セールス",
"save": "保存",
@@ -419,6 +426,7 @@
"storage_not_configured": "ファイルストレージが設定されていないため、アップロードは失敗する可能性があります",
"string": "テキスト",
"styling": "スタイル",
"subheader": "小見出し",
"submit": "送信",
"summary": "概要",
"survey": "フォーム",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "回答を直接Notionに送信します",
"please_select_a_survey_error": "フォームを選択してください",
"reconnect_button": "再接続",
"reconnect_button_description": "統合の接続が期限切れになりました。回答の同期を続けるには再接続してください。既存のリンクとデータは保持されます。",
"reconnect_button_tooltip": "統合を再接続してアクセスを更新します。既存のリンクとデータは保持されます。",
"select_at_least_one_question_error": "少なくとも1つの質問を選択してください",
"slack": {
"already_connected_another_survey": "このチャンネルには別のフォームがすでに接続されています。",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "上位プランで二段階認証をアンロック",
"update_personal_info": "個人情報を更新",
"warning_cannot_delete_account": "あなたは、この組織の唯一のオーナーです。まず、別のメンバーにオーナーシップを譲渡してください。",
"warning_cannot_undo": "この操作は元に戻せません",
"wrong_password": "パスワードが間違っています"
"warning_cannot_undo": "この操作は元に戻せません"
},
"teams": {
"add_members_description": "チームにメンバーを追加し、役割を決定します。",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "フォームを正常にコピーしました!",
"delete_survey_and_responses_warning": "本当にこのフォームとすべての回答を削除しますか?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. このフォームのデフォルト言語を選択してください:",
"2_activate_translation_for_specific_languages": "2. 特定の言語の翻訳を有効にしてください:",
"activate_translations": "翻訳を有効化",
"add": "追加 +",
"add_a_delay_or_auto_close_the_survey": "遅延を追加するか、フォームを自動的に閉じる",
"add_a_four_digit_pin": "4桁のPINを追加",
@@ -1363,6 +1366,8 @@
"assign": "割り当て =",
"audience": "オーディエンス",
"auto_close_on_inactivity": "非アクティブ時に自動閉鎖",
"auto_progress_rating_and_nps": "評価とNPSの質問を自動進行",
"auto_progress_rating_and_nps_description": "評価またはNPSの質問で回答者が選択肢を選んだ際に自動的に次へ進みます。これは単一質問ブロックにのみ適用されます。必須の質問では「次へ」ボタンが非表示になり、任意の質問ではスキップ用に引き続き表示されます。",
"auto_save_disabled": "自動保存が無効",
"auto_save_disabled_tooltip": "アンケートは下書き状態の時のみ自動保存されます。これにより、公開中のアンケートが意図せず更新されることを防ぎます。",
"auto_save_on": "自動保存オン",
@@ -1408,6 +1413,7 @@
"caution_text": "変更は不整合を引き起こします",
"change_anyway": "とにかく変更",
"change_background": "背景を変更",
"change_default": "デフォルトを変更",
"change_question_type": "質問の種類を変更",
"change_survey_type": "フォームの種類を変更すると、既存のアクセスに影響します",
"change_the_background_to_a_color_image_or_animation": "背景を色、画像、またはアニメーションに変更します。",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "フォームを実行する場所を選択してください。",
"city": "市区町村",
"close_survey_on_response_limit": "回答数の上限でフォームを閉じる",
"code": "コード",
"color": "色",
"column_used_in_logic_error": "この列は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
"columns": "列",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "アンケートのロゴをカスタマイズする",
"darken_or_lighten_background_of_your_choice": "お好みの背景を暗くしたり明るくしたりします。",
"days_before_showing_this_survey_again": "最後に表示されたアンケートとこのアンケートを表示するまでに、この日数以上の期間を空ける必要があります。",
"default_language": "デフォルト言語",
"delete_anyways": "削除する",
"delete_block": "ブロックを削除",
"delete_choice": "選択肢を削除",
@@ -1463,7 +1471,6 @@
"duplicate_question": "質問を複製",
"edit_link": "編集 リンク",
"edit_recall": "リコールを編集",
"edit_translations": "{lang} 翻訳を編集",
"element_not_found": "質問が見つかりません",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "回答者がいつでも言語を切り替えられるようにします。最低2つのアクティブな言語が必要です。",
"enable_recaptcha_to_protect_your_survey_from_spam": "スパム対策はreCAPTCHA v3を使用してスパム回答をフィルタリングします。",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "回答者が長文の複数行の回答を書けるようにします。",
"lower_label": "下限ラベル",
"manage_languages": "言語を管理",
"manage_translations": "翻訳を管理",
"matrix_all_fields": "すべてのフィールド",
"matrix_rows": "行",
"max_file_size": "最大ファイルサイズ",
"max_file_size_limit_is": "最大ファイルサイズの上限は",
"missing_first": "未翻訳を優先",
"move_question_to_block": "質問をブロックに移動",
"multiply": "乗算 *",
"needed_for_self_hosted_cal_com_instance": "セルフホストのCal.comインスタンスに必要",
@@ -1611,7 +1620,7 @@
"next_button_label": "「次へ」ボタンのラベル",
"no_hidden_fields_yet_add_first_one_below": "まだ非表示フィールドがありません。以下で最初のものを追加してください。",
"no_images_found_for": "''{query}'' の画像が見つかりません",
"no_languages_found_add_first_one_to_get_started": "言語が見つかりません。始めるには、最初のものを追加してください。",
"no_languages_found_add_first_one_to_get_started": "このワークスペースにはアンケート言語が見つかりませんでした。開始するには言語を追加してください。",
"no_option_found": "オプションが見つかりません",
"no_recall_items_found": "リコール項目が見つかりません",
"no_variables_yet_add_first_one_below": "まだ変数がありません。以下で最初のものを追加してください。",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "有効な URL を入力してください (例:https://example.com)",
"please_set_a_survey_trigger": "フォームのトリガーを設定してください",
"please_specify": "具体的に指定してください",
"present_your_survey_in_multiple_languages": "アンケートを複数の言語で表示",
"prevent_double_submission": "二重送信を防ぐ",
"prevent_double_submission_description": "メールアドレスごとに1つの回答のみを許可する",
"progress_saved": "進捗を保存しました",
@@ -1729,6 +1739,7 @@
"seven_points": "7点",
"show_block_settings": "ブロック設定を表示",
"show_button": "ボタンを表示",
"show_in_order": "順番に表示",
"show_language_switch": "言語切り替えを表示",
"show_multiple_times": "限られた回数表示する",
"show_only_once": "一度だけ表示",
@@ -1760,7 +1771,6 @@
"survey_preview": "アンケートプレビュー 👀",
"survey_styling": "フォームのスタイル",
"survey_trigger": "フォームのトリガー",
"switch_multi_language_on_to_get_started": "多言語機能をオンにして開始 👉",
"target_block_not_found": "対象ブロックが見つかりません",
"targeted": "ターゲット",
"ten_points": "10点",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "回答がなくても1回だけ表示します。",
"then": "その後",
"this_action_will_remove_all_the_translations_from_this_survey": "このアクションは、このフォームからすべての翻訳を削除します。",
"this_will_remove_the_language_and_all_its_translations": "この言語とすべての翻訳がこのアンケートから削除されます。この操作は元に戻せません。",
"three_points": "3点",
"times": "回",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "すべてのフォームの配置を一貫させるために、",
"translated": "翻訳済み",
"trigger_survey_when_one_of_the_actions_is_fired": "以下のアクションのいずれかが発火したときにフォームをトリガーします...",
"try_lollipop_or_mountain": "「lollipop」や「mountain」を試してみてください...",
"type_field_id": "フィールドIDを入力",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "有効なメールアドレスを持つ人のみが回答できるようにする",
"visibility_and_recontact": "表示と再接触",
"visibility_and_recontact_description": "このフォームがいつ表示され、どのくらいの頻度で再表示できるかをコントロールします。",
"visible": "表示",
"wait": "待つ",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "トリガーから数秒待ってからフォームを表示します",
"waiting_time_across_surveys": "クールダウン期間(アンケート全体)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "重複する言語または言語ID",
"edit_languages": "言語を編集",
"identifier": "識別子(ISO",
"incomplete_translations": "未完了の翻訳",
"language": "言語",
"language_deleted_successfully": "言語を正常に削除しました",
"languages_updated_successfully": "言語を正常に更新しました",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "言語を選択してください",
"remove_language": "言語を削除",
"remove_language_from_surveys_to_remove_it_from_workspace": "ワークスペースから削除するには、これらのフォームから言語を削除してください。",
"search_items": "アイテムを検索",
"translate": "翻訳"
"search_items": "アイテムを検索"
},
"look": {
"add_background_color": "背景色を追加",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "バーの未入力部分の色を設定します。",
"advanced_styling_field_track_height": "トラックの高さ",
"advanced_styling_field_track_height_description": "プログレスバーの太さを調整します。",
"advanced_styling_field_upper_label_color": "見出しラベルの色",
"advanced_styling_field_upper_label_color_description": "入力フィールド上部の小さなラベルの色を設定します。",
"advanced_styling_field_upper_label_size": "見出しラベルのフォントサイズ",
"advanced_styling_field_upper_label_size_description": "入力フィールド上部の小さなラベルのサイズを調整します。",
"advanced_styling_field_upper_label_weight": "見出しラベルのフォント太さ",
"advanced_styling_field_upper_label_weight_description": "ラベルを細くまたは太くします。",
"advanced_styling_field_upper_label_color": "ラベルの色",
"advanced_styling_field_upper_label_color_description": "入力欄の上にある小さなラベルとスケールラベルの色を設定します。",
"advanced_styling_field_upper_label_size": "ラベルのフォントサイズ",
"advanced_styling_field_upper_label_size_description": "入力欄の上にある小さなラベルとスケールラベルのサイズを調整します。",
"advanced_styling_field_upper_label_weight": "ラベルのフォント太さ",
"advanced_styling_field_upper_label_weight_description": "ラベルの太さを細くしたり太くしたりします。",
"advanced_styling_section_buttons": "ボタン",
"advanced_styling_section_headlines": "見出しと説明",
"advanced_styling_section_inputs": "入力フィールド",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "私たちをよりよく理解するためにお手伝いください:",
"improve_trial_conversion_question_2_button_label": "次へ",
"improve_trial_conversion_question_2_headline": "残念です。$[projectName]を使う上で最も大きな問題は何でしたか?",
"improve_trial_conversion_question_3_button_label": "次へ",
"improve_trial_conversion_question_3_headline": "$[projectName]に何を期待していましたか?",
"improve_trial_conversion_question_4_button_label": "20%オフを取得",
"improve_trial_conversion_question_4_headline": "残念です!初年度20%オフをゲット。",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>年間プランで20%の割引を提供させていただきます。</span></p>",
"improve_trial_conversion_question_5_button_label": "次へ",
"improve_trial_conversion_question_5_headline": "何を達成したいですか?",
"improve_trial_conversion_question_5_subheader": "以下のオプションから一つ選択してください",
"improve_trial_conversion_question_5_subheader": "以下に詳しくご記入ください:",
"improve_trial_conversion_question_6_headline": "今、問題をどのように解決していますか?",
"improve_trial_conversion_question_6_subheader": "代替ソリューションを挙げてください:",
"integration_setup_survey_description": "ユーザーが製品に統合を追加するのがどれだけ簡単かを評価する。盲点を見つける。",
+35 -22
View File
@@ -152,6 +152,7 @@
"centered_modal": "Gecentreerd modaal",
"change_organization": "Organisatie wijzigen",
"change_workspace": "Werkruimte wijzigen",
"choice_n": "Keuze {{n}}",
"choices": "Keuzes",
"choose_environment": "Kies omgeving",
"choose_organization": "Kies organisatie",
@@ -165,6 +166,7 @@
"close": "Dichtbij",
"code": "Code",
"collapse_rows": "Rijen samenvouwen",
"column_n": "Kolom {{n}}",
"completed": "Voltooid",
"configuration": "Configuratie",
"confirm": "Bevestigen",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Kopiëren naar klembord mislukt",
"failed_to_load_organizations": "Laden van organisaties mislukt",
"failed_to_load_workspaces": "Laden van werkruimtes mislukt",
"field_placeholder": "Tijdelijke aanduiding voor {{field}}",
"filter": "Filter",
"finish": "Finish",
"first_name": "Voornaam",
@@ -247,10 +250,12 @@
"generate": "Genereren",
"go_back": "Ga terug",
"go_to_dashboard": "Ga naar Dashboard",
"headline": "Kop",
"hidden": "Verborgen",
"hidden_field": "Verborgen veld",
"hidden_fields": "Verborgen velden",
"hide_column": "Kolom verbergen",
"html": "HTML",
"id": "ID",
"image": "Afbeelding",
"images": "Afbeeldingen",
@@ -298,7 +303,6 @@
"months": "maanden",
"move_down": "Ga naar beneden",
"move_up": "Ga omhoog",
"multiple_languages": "Meerdere talen",
"my_product": "mijn product",
"name": "Naam",
"new": "Nieuw",
@@ -313,6 +317,7 @@
"no_result_found": "Geen resultaat gevonden",
"no_results": "Geen resultaten",
"no_surveys_found": "Geen enquêtes gevonden.",
"no_text_found": "Geen tekst gevonden",
"none_of_the_above": "Geen van bovenstaande",
"not_authenticated": "U bent niet geverifieerd om deze actie uit te voeren.",
"not_authorized": "Niet geautoriseerd",
@@ -336,6 +341,7 @@
"organization_settings": "Organisatie-instellingen",
"other": "Ander",
"other_filters": "Overige filters",
"other_placeholder": "Andere tijdelijke aanduiding",
"others": "Anderen",
"overlay_color": "Overlaykleur",
"overview": "Overzicht",
@@ -381,6 +387,7 @@
"responses": "Reacties",
"restart": "Opnieuw opstarten",
"role": "Rol",
"row_n": "Rij {{n}}",
"saas": "SaaS",
"sales": "Verkoop",
"save": "Redden",
@@ -419,6 +426,7 @@
"storage_not_configured": "Bestandsopslag is niet ingesteld, uploads zullen waarschijnlijk mislukken",
"string": "Tekst",
"styling": "Styling",
"subheader": "Subkop",
"submit": "Indienen",
"summary": "Samenvatting",
"survey": "Vragenlijst",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Verzend gegevens naar uw Notion-database",
"please_select_a_survey_error": "Selecteer een enquête",
"reconnect_button": "Opnieuw verbinden",
"reconnect_button_description": "Je integratieverbinding is verlopen. Maak opnieuw verbinding om door te gaan met het synchroniseren van reacties. Je bestaande links en gegevens blijven behouden.",
"reconnect_button_tooltip": "Verbind de integratie opnieuw om je toegang te vernieuwen. Je bestaande links en gegevens blijven behouden.",
"select_at_least_one_question_error": "Selecteer minimaal één vraag",
"slack": {
"already_connected_another_survey": "U heeft al een andere enquête aan dit kanaal gekoppeld.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Ontgrendel tweefactorauthenticatie met een hoger abonnement",
"update_personal_info": "Update uw persoonlijke gegevens",
"warning_cannot_delete_account": "U bent de enige eigenaar van deze organisatie. Draag het eigendom eerst over aan een ander lid.",
"warning_cannot_undo": "Dit kan niet ongedaan worden gemaakt",
"wrong_password": "Verkeerd wachtwoord"
"warning_cannot_undo": "Dit kan niet ongedaan worden gemaakt"
},
"teams": {
"add_members_description": "Voeg leden toe aan het team en bepaal hun rol.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Enquête succesvol gekopieerd!",
"delete_survey_and_responses_warning": "Weet u zeker dat u deze enquête en alle antwoorden erop wilt verwijderen?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Kies de standaardtaal voor deze enquête:",
"2_activate_translation_for_specific_languages": "2. Activeer vertaling voor specifieke talen:",
"activate_translations": "Vertalingen activeren",
"add": "Voeg + toe",
"add_a_delay_or_auto_close_the_survey": "Voeg een vertraging toe of sluit de enquête automatisch",
"add_a_four_digit_pin": "Voeg een viercijferige pincode toe",
@@ -1363,6 +1366,8 @@
"assign": "Toewijzen =",
"audience": "Publiek",
"auto_close_on_inactivity": "Automatisch sluiten bij inactiviteit",
"auto_progress_rating_and_nps": "Automatisch doorgaan bij beoordelings- en NPS-vragen",
"auto_progress_rating_and_nps_description": "Ga automatisch verder wanneer respondenten een antwoord selecteren bij beoordelings- of NPS-vragen. Dit geldt alleen voor blokken met één vraag. Bij verplichte vragen wordt de Volgende-knop verborgen; bij optionele vragen blijft deze zichtbaar om de vraag over te slaan.",
"auto_save_disabled": "Automatisch opslaan uitgeschakeld",
"auto_save_disabled_tooltip": "Uw enquête wordt alleen automatisch opgeslagen wanneer deze een concept is. Dit zorgt ervoor dat openbare enquêtes niet onbedoeld worden bijgewerkt.",
"auto_save_on": "Automatisch opslaan aan",
@@ -1408,6 +1413,7 @@
"caution_text": "Veranderingen zullen tot inconsistenties leiden",
"change_anyway": "Hoe dan ook veranderen",
"change_background": "Achtergrond wijzigen",
"change_default": "Standaard wijzigen",
"change_question_type": "Vraagtype wijzigen",
"change_survey_type": "Als u van enquêtetype verandert, heeft dit invloed op de bestaande toegang",
"change_the_background_to_a_color_image_or_animation": "Verander de achtergrond in een kleur, afbeelding of animatie.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Kies waar u de enquête wilt uitvoeren.",
"city": "Stad",
"close_survey_on_response_limit": "Sluit enquête over responslimiet",
"code": "Code",
"color": "Kleur",
"column_used_in_logic_error": "Deze kolom wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.",
"columns": "Kolommen",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Pas het enquêtelogo aan",
"darken_or_lighten_background_of_your_choice": "Maak de achtergrond naar keuze donkerder of lichter.",
"days_before_showing_this_survey_again": "of meer dagen moeten verstrijken tussen de laatst getoonde enquête en het tonen van deze enquête.",
"default_language": "Standaardtaal",
"delete_anyways": "Toch verwijderen",
"delete_block": "Blok verwijderen",
"delete_choice": "Keuze verwijderen",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Vraag dupliceren",
"edit_link": "Link bewerken",
"edit_recall": "Bewerken Terugroepen",
"edit_translations": "Bewerk {lang} vertalingen",
"element_not_found": "Vraag niet gevonden",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Sta respondenten toe om op elk moment van taal te wisselen. Vereist min. 2 actieve talen.",
"enable_recaptcha_to_protect_your_survey_from_spam": "Spambeveiliging maakt gebruik van reCAPTCHA v3 om de spamreacties eruit te filteren.",
@@ -1599,11 +1606,13 @@
"long_answer": "Lang antwoord",
"long_answer_toggle_description": "Sta respondenten toe om langere antwoorden met meerdere regels te schrijven.",
"lower_label": "Lager etiket",
"manage_languages": "Beheer talen",
"manage_languages": "Talen beheren",
"manage_translations": "Vertalingen beheren",
"matrix_all_fields": "Alle velden",
"matrix_rows": "Rijen",
"max_file_size": "Maximale bestandsgrootte",
"max_file_size_limit_is": "Maximale bestandsgroottelimiet is",
"missing_first": "Ontbrekende eerst",
"move_question_to_block": "Vraag naar blok verplaatsen",
"multiply": "Vermenigvuldig *",
"needed_for_self_hosted_cal_com_instance": "Nodig voor een zelf-gehoste Cal.com-instantie",
@@ -1611,7 +1620,7 @@
"next_button_label": "Knoplabel 'Volgende'",
"no_hidden_fields_yet_add_first_one_below": "Nog geen verborgen velden. Voeg de eerste hieronder toe.",
"no_images_found_for": "Geen afbeeldingen gevonden voor ''{query}'",
"no_languages_found_add_first_one_to_get_started": "Geen talen gevonden. Voeg de eerste toe om aan de slag te gaan.",
"no_languages_found_add_first_one_to_get_started": "Geen enquêtetalen gevonden in deze werkruimte. Voeg er een toe om te beginnen.",
"no_option_found": "Geen optie gevonden",
"no_recall_items_found": "Geen recall-items gevonden",
"no_variables_yet_add_first_one_below": "Nog geen variabelen. Voeg de eerste hieronder toe.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Voer een geldige URL in (bijvoorbeeld https://example.com)",
"please_set_a_survey_trigger": "Stel een enquêtetrigger in",
"please_specify": "Gelieve te specificeren",
"present_your_survey_in_multiple_languages": "Toon je enquête in meerdere talen",
"prevent_double_submission": "Voorkom dubbele indiening",
"prevent_double_submission_description": "Er is slechts 1 reactie per e-mailadres toegestaan",
"progress_saved": "Voortgang opgeslagen",
@@ -1729,6 +1739,7 @@
"seven_points": "7 punten",
"show_block_settings": "Blokinstellingen tonen",
"show_button": "Toon knop",
"show_in_order": "Toon op volgorde",
"show_language_switch": "Toon taalwissel",
"show_multiple_times": "Toon een beperkt aantal keren",
"show_only_once": "Slechts één keer weergeven",
@@ -1760,7 +1771,6 @@
"survey_preview": "Enquêtevoorbeeld 👀",
"survey_styling": "Vorm styling",
"survey_trigger": "Enquêtetrigger",
"switch_multi_language_on_to_get_started": "Schakel meertaligheid in om te beginnen 👉",
"target_block_not_found": "Doelblok niet gevonden",
"targeted": "Gericht",
"ten_points": "10 punten",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Toon één keer, zelfs als ze niet reageren.",
"then": "Dan",
"this_action_will_remove_all_the_translations_from_this_survey": "Met deze actie worden alle vertalingen uit deze enquête verwijderd.",
"this_will_remove_the_language_and_all_its_translations": "Dit verwijdert deze taal en alle vertalingen uit deze enquête. Deze actie kan niet ongedaan worden gemaakt.",
"three_points": "3 punten",
"times": "keer",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Om de plaatsing over alle enquêtes consistent te houden, kunt u dat doen",
"translated": "Vertaald",
"trigger_survey_when_one_of_the_actions_is_fired": "Enquête activeren wanneer een van de acties wordt afgevuurd...",
"try_lollipop_or_mountain": "Probeer 'lollipop' of 'berg'...",
"type_field_id": "Typ veld-ID",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Laat alleen mensen met een echte e-mail reageren.",
"visibility_and_recontact": "Zichtbaarheid & opnieuw contact",
"visibility_and_recontact_description": "Bepaal wanneer deze enquête kan verschijnen en hoe vaak deze opnieuw kan verschijnen.",
"visible": "Zichtbaar",
"wait": "Wachten",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Wacht een paar seconden na de trigger voordat u de enquête weergeeft",
"waiting_time_across_surveys": "Afkoelperiode (voor alle enquêtes)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Dubbele taal of taal-ID",
"edit_languages": "Talen bewerken",
"identifier": "Identifier (ISO)",
"incomplete_translations": "Onvolledige vertalingen",
"language": "Taal",
"language_deleted_successfully": "Taal succesvol verwijderd",
"languages_updated_successfully": "Talen succesvol bijgewerkt",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Selecteer een taal",
"remove_language": "Taal verwijderen",
"remove_language_from_surveys_to_remove_it_from_workspace": "Verwijder de taal uit deze enquêtes om deze uit de werkruimte te verwijderen.",
"search_items": "Items zoeken",
"translate": "Vertalen"
"search_items": "Items zoeken"
},
"look": {
"add_background_color": "Achtergrondkleur toevoegen",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Kleurt het ongevulde gedeelte van de balk.",
"advanced_styling_field_track_height": "Spoorhoogte",
"advanced_styling_field_track_height_description": "Regelt de dikte van de voortgangsbalk.",
"advanced_styling_field_upper_label_color": "Koplabelkleur",
"advanced_styling_field_upper_label_color_description": "Kleurt het kleine label boven invoervelden.",
"advanced_styling_field_upper_label_size": "Lettergrootte koplabel",
"advanced_styling_field_upper_label_size_description": "Schaalt het kleine label boven invoervelden.",
"advanced_styling_field_upper_label_weight": "Letterdikte koplabel",
"advanced_styling_field_upper_label_weight_description": "Maakt het label lichter of vetter.",
"advanced_styling_field_upper_label_color": "Labelkleur",
"advanced_styling_field_upper_label_color_description": "Kleurt de kleine labels boven invoervelden en schaallabels.",
"advanced_styling_field_upper_label_size": "Lettergrootte label",
"advanced_styling_field_upper_label_size_description": "Past de grootte aan van de kleine labels boven invoervelden en schaallabels.",
"advanced_styling_field_upper_label_weight": "Letterdikte label",
"advanced_styling_field_upper_label_weight_description": "Maakt de labels lichter of dikgedrukt.",
"advanced_styling_section_buttons": "Knoppen",
"advanced_styling_section_headlines": "Koppen & beschrijvingen",
"advanced_styling_section_inputs": "Invoervelden",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Help ons u beter te begrijpen:",
"improve_trial_conversion_question_2_button_label": "Volgende",
"improve_trial_conversion_question_2_headline": "Sorry om te horen. Wat was het grootste probleem bij het gebruik van $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Volgende",
"improve_trial_conversion_question_3_headline": "Wat had je verwacht dat $[projectName] zou doen?",
"improve_trial_conversion_question_4_button_label": "Krijg 20% korting",
"improve_trial_conversion_question_4_headline": "Sorry om te horen! Krijg het eerste jaar 20% korting.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>We bieden u graag 20% korting op een jaarabonnement.</span></p>",
"improve_trial_conversion_question_5_button_label": "Volgende",
"improve_trial_conversion_question_5_headline": "Wat zou je graag willen bereiken?",
"improve_trial_conversion_question_5_subheader": "Selecteer een van de volgende opties:",
"improve_trial_conversion_question_5_subheader": "Beschrijf hieronder:",
"improve_trial_conversion_question_6_headline": "Hoe los jij je probleem nu op?",
"improve_trial_conversion_question_6_subheader": "Noem alternatieve oplossingen:",
"integration_setup_survey_description": "Evalueer hoe gemakkelijk gebruikers integraties aan uw product kunnen toevoegen. Zoek blinde vlekken.",
+35 -22
View File
@@ -152,6 +152,7 @@
"centered_modal": "Modal Centralizado",
"change_organization": "Alterar organização",
"change_workspace": "Alterar espaço de trabalho",
"choice_n": "Escolha {{n}}",
"choices": "Escolhas",
"choose_environment": "Escolher ambiente",
"choose_organization": "Escolher organização",
@@ -165,6 +166,7 @@
"close": "Fechar",
"code": "Código",
"collapse_rows": "Recolher linhas",
"column_n": "Coluna {{n}}",
"completed": "Concluído",
"configuration": "Configuração",
"confirm": "Confirmar",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
"failed_to_load_organizations": "Falha ao carregar organizações",
"failed_to_load_workspaces": "Falha ao carregar projetos",
"field_placeholder": "Espaço reservado de {{field}}",
"filter": "Filtro",
"finish": "Terminar",
"first_name": "Primeiro nome",
@@ -247,10 +250,12 @@
"generate": "Gerar",
"go_back": "Voltar",
"go_to_dashboard": "Ir para o Painel",
"headline": "Título",
"hidden": "Escondido",
"hidden_field": "Campo oculto",
"hidden_fields": "Campos ocultos",
"hide_column": "Ocultar coluna",
"html": "HTML",
"id": "ID",
"image": "imagem",
"images": "Imagens",
@@ -298,7 +303,6 @@
"months": "meses",
"move_down": "Descer",
"move_up": "Subir",
"multiple_languages": "Vários idiomas",
"my_product": "meu produto",
"name": "Nome",
"new": "Novo",
@@ -313,6 +317,7 @@
"no_result_found": "Nenhum resultado encontrado",
"no_results": "Nenhum resultado",
"no_surveys_found": "Não foram encontradas pesquisas.",
"no_text_found": "Nenhum texto encontrado",
"none_of_the_above": "Nenhuma das opções acima",
"not_authenticated": "Você não está autenticado para realizar essa ação.",
"not_authorized": "Não autorizado",
@@ -336,6 +341,7 @@
"organization_settings": "Configurações da Organização",
"other": "outro",
"other_filters": "Outros Filtros",
"other_placeholder": "Outro espaço reservado",
"others": "Outros",
"overlay_color": "Cor da sobreposição",
"overview": "Visão Geral",
@@ -381,6 +387,7 @@
"responses": "Respostas",
"restart": "Reiniciar",
"role": "Rolê",
"row_n": "Linha {{n}}",
"saas": "SaaS",
"sales": "vendas",
"save": "Salvar",
@@ -419,6 +426,7 @@
"storage_not_configured": "Armazenamento de arquivos não configurado, uploads provavelmente falharão",
"string": "Texto",
"styling": "Estilização",
"subheader": "Subtítulo",
"submit": "Enviar",
"summary": "Resumo",
"survey": "Pesquisa",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Enviar dados para seu banco de dados do Notion",
"please_select_a_survey_error": "Por favor, escolha uma pesquisa",
"reconnect_button": "Reconectar",
"reconnect_button_description": "Sua conexão de integração expirou. Por favor, reconecte para continuar sincronizando respostas. Seus links e dados existentes serão preservados.",
"reconnect_button_tooltip": "Reconecte a integração para atualizar seu acesso. Seus links e dados existentes serão preservados.",
"select_at_least_one_question_error": "Por favor, selecione pelo menos uma pergunta",
"slack": {
"already_connected_another_survey": "Você já conectou outra pesquisa a este canal.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Desbloqueia a autenticação de dois fatores com um plano melhor",
"update_personal_info": "Atualize suas informações pessoais",
"warning_cannot_delete_account": "Você é o único dono desta organização. Transfere a propriedade para outra pessoa primeiro.",
"warning_cannot_undo": "Isso não pode ser desfeito",
"wrong_password": "Senha incorreta"
"warning_cannot_undo": "Isso não pode ser desfeito"
},
"teams": {
"add_members_description": "Adicione membros à equipe e determine sua função.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Pesquisa copiada com sucesso!",
"delete_survey_and_responses_warning": "Você tem certeza de que quer deletar essa pesquisa e todas as suas respostas?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Escolha o idioma padrão para essa pesquisa:",
"2_activate_translation_for_specific_languages": "2. Ativar tradução para idiomas específicos:",
"activate_translations": "Ativar traduções",
"add": "Adicionar +",
"add_a_delay_or_auto_close_the_survey": "Adicione um atraso ou feche a pesquisa automaticamente",
"add_a_four_digit_pin": "Adicione um PIN de quatro dígitos",
@@ -1363,6 +1366,8 @@
"assign": "atribuir =",
"audience": "Público",
"auto_close_on_inactivity": "Fechar automaticamente por inatividade",
"auto_progress_rating_and_nps": "Avançar automaticamente em perguntas de avaliação e NPS",
"auto_progress_rating_and_nps_description": "Avança automaticamente quando os respondentes selecionam uma resposta em perguntas de avaliação ou NPS. Isso se aplica apenas a blocos com uma única pergunta. Perguntas obrigatórias ocultam o botão Próximo; perguntas opcionais ainda o exibem para permitir pular.",
"auto_save_disabled": "Salvamento automático desativado",
"auto_save_disabled_tooltip": "Sua pesquisa só é salva automaticamente quando está em rascunho. Isso garante que pesquisas públicas não sejam atualizadas involuntariamente.",
"auto_save_on": "Salvamento automático ativado",
@@ -1408,6 +1413,7 @@
"caution_text": "Mudanças vão levar a inconsistências",
"change_anyway": "Mudar mesmo assim",
"change_background": "Mudar fundo",
"change_default": "Alterar padrão",
"change_question_type": "Mudar tipo de pergunta",
"change_survey_type": "Alterar o tipo de pesquisa afeta o acesso existente",
"change_the_background_to_a_color_image_or_animation": "Mude o fundo para uma cor, imagem ou animação.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Escolha onde realizar a pesquisa.",
"city": "cidade",
"close_survey_on_response_limit": "Fechar pesquisa ao atingir limite de respostas",
"code": "Código",
"color": "cor",
"column_used_in_logic_error": "Esta coluna é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
"columns": "colunas",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Personalizar o logo da pesquisa",
"darken_or_lighten_background_of_your_choice": "Escureça ou clareie o fundo da sua escolha.",
"days_before_showing_this_survey_again": "ou mais dias devem passar entre a última pesquisa exibida e a exibição desta pesquisa.",
"default_language": "Idioma padrão",
"delete_anyways": "Excluir mesmo assim",
"delete_block": "Excluir bloco",
"delete_choice": "Deletar opção",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Duplicar pergunta",
"edit_link": "Editar link",
"edit_recall": "Editar Lembrete",
"edit_translations": "Editar traduções de {lang}",
"element_not_found": "Pergunta não encontrada",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Permitir que os respondentes alterem o idioma a qualquer momento. Necessita de no mínimo 2 idiomas ativos.",
"enable_recaptcha_to_protect_your_survey_from_spam": "A proteção contra spam usa o reCAPTCHA v3 para filtrar as respostas de spam.",
@@ -1599,11 +1606,13 @@
"long_answer": "resposta longa",
"long_answer_toggle_description": "Permitir que os respondentes escrevam respostas mais longas e com várias linhas.",
"lower_label": "Etiqueta Inferior",
"manage_languages": "Gerenciar Idiomas",
"manage_languages": "Gerenciar idiomas",
"manage_translations": "Gerenciar traduções",
"matrix_all_fields": "Todos os campos",
"matrix_rows": "Linhas",
"max_file_size": "Tamanho máximo do arquivo",
"max_file_size_limit_is": "O limite de tamanho máximo do arquivo é",
"missing_first": "Faltantes primeiro",
"move_question_to_block": "Mover pergunta para o bloco",
"multiply": "Multiplicar *",
"needed_for_self_hosted_cal_com_instance": "Necessário para uma instância auto-hospedada do Cal.com",
@@ -1611,7 +1620,7 @@
"next_button_label": "Próximo",
"no_hidden_fields_yet_add_first_one_below": "Ainda não há campos ocultos. Adicione o primeiro abaixo.",
"no_images_found_for": "Nenhuma imagem encontrada para ''{query}\"",
"no_languages_found_add_first_one_to_get_started": "Nenhum idioma encontrado. Adicione o primeiro para começar.",
"no_languages_found_add_first_one_to_get_started": "Nenhum idioma de pesquisa encontrado neste espaço de trabalho. Por favor, adicione um para começar.",
"no_option_found": "Nenhuma opção encontrada",
"no_recall_items_found": "Nenhum item de recuperação encontrado",
"no_variables_yet_add_first_one_below": "Ainda não há variáveis. Adicione a primeira abaixo.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Por favor, insira uma URL válida (ex.: https://example.com)",
"please_set_a_survey_trigger": "Por favor, configure um gatilho para a pesquisa",
"please_specify": "Por favor, especifique",
"present_your_survey_in_multiple_languages": "Apresente sua pesquisa em vários idiomas",
"prevent_double_submission": "Evitar envio duplicado",
"prevent_double_submission_description": "Permitir apenas 1 resposta por endereço de email",
"progress_saved": "Progresso salvo",
@@ -1729,6 +1739,7 @@
"seven_points": "7 pontos",
"show_block_settings": "Mostrar configurações do bloco",
"show_button": "Mostrar Botão",
"show_in_order": "Mostrar em ordem",
"show_language_switch": "Mostrar troca de idioma",
"show_multiple_times": "Mostrar um número limitado de vezes",
"show_only_once": "Mostrar só uma vez",
@@ -1760,7 +1771,6 @@
"survey_preview": "Prévia da pesquisa 👀",
"survey_styling": "Estilização de Formulários",
"survey_trigger": "Gatilho de Pesquisa",
"switch_multi_language_on_to_get_started": "Ative o modo multilíngue para começar 👉",
"target_block_not_found": "Bloco de destino não encontrado",
"targeted": "direcionado",
"ten_points": "10 pontos",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Mostrar uma única vez, mesmo que não respondam.",
"then": "Então",
"this_action_will_remove_all_the_translations_from_this_survey": "Essa ação vai remover todas as traduções dessa pesquisa.",
"this_will_remove_the_language_and_all_its_translations": "Isso removerá este idioma e todas as suas traduções desta pesquisa. Esta ação não pode ser desfeita.",
"three_points": "3 pontos",
"times": "times",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Para manter a colocação consistente em todas as pesquisas, você pode",
"translated": "Traduzido",
"trigger_survey_when_one_of_the_actions_is_fired": "Disparar pesquisa quando uma das ações for executada...",
"try_lollipop_or_mountain": "Tenta 'pirulito' ou 'montanha'...",
"type_field_id": "Digite o id do campo",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Deixe só quem tem um email real responder.",
"visibility_and_recontact": "Visibilidade e recontato",
"visibility_and_recontact_description": "Controle quando esta pesquisa pode aparecer e com que frequência pode reaparecer.",
"visible": "Visível",
"wait": "Espera",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Espera alguns segundos depois do gatilho antes de mostrar a pesquisa",
"waiting_time_across_surveys": "Período de espera (entre pesquisas)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Idioma ou ID de idioma duplicado",
"edit_languages": "Editar idiomas",
"identifier": "Identificador (ISO)",
"incomplete_translations": "Traduções incompletas",
"language": "Idioma",
"language_deleted_successfully": "Idioma excluído com sucesso",
"languages_updated_successfully": "Idiomas atualizados com sucesso",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Por favor, selecione um idioma",
"remove_language": "Remover idioma",
"remove_language_from_surveys_to_remove_it_from_workspace": "Por favor, remova o idioma dessas pesquisas para removê-lo do workspace.",
"search_items": "Buscar itens",
"translate": "Traduzir"
"search_items": "Buscar itens"
},
"look": {
"add_background_color": "Adicionar cor de fundo",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Colore a porção não preenchida da barra.",
"advanced_styling_field_track_height": "Altura da trilha",
"advanced_styling_field_track_height_description": "Controla a espessura da barra de progresso.",
"advanced_styling_field_upper_label_color": "Cor do rótulo do título",
"advanced_styling_field_upper_label_color_description": "Colore o pequeno rótulo acima dos campos de entrada.",
"advanced_styling_field_upper_label_size": "Tamanho da fonte do rótulo do título",
"advanced_styling_field_upper_label_size_description": "Ajusta o tamanho do pequeno rótulo acima dos campos de entrada.",
"advanced_styling_field_upper_label_weight": "Peso da fonte do rótulo do título",
"advanced_styling_field_upper_label_weight_description": "Torna o rótulo mais leve ou mais negrito.",
"advanced_styling_field_upper_label_color": "Cor do tulo",
"advanced_styling_field_upper_label_color_description": "Colore os pequenos rótulos acima dos campos de entrada e rótulos de escala.",
"advanced_styling_field_upper_label_size": "Tamanho da Fonte do tulo",
"advanced_styling_field_upper_label_size_description": "Ajusta o tamanho dos pequenos rótulos acima dos campos de entrada e rótulos de escala.",
"advanced_styling_field_upper_label_weight": "Peso da Fonte do tulo",
"advanced_styling_field_upper_label_weight_description": "Torna os rótulos mais leves ou mais pesados.",
"advanced_styling_section_buttons": "Botões",
"advanced_styling_section_headlines": "Títulos e descrições",
"advanced_styling_section_inputs": "Campos de entrada",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Ajuda a gente a te entender melhor:",
"improve_trial_conversion_question_2_button_label": "Próximo",
"improve_trial_conversion_question_2_headline": "Que chato ouvir isso. Qual foi o maior problema ao usar $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Próximo",
"improve_trial_conversion_question_3_headline": "O que você esperava que $[projectName] fizesse?",
"improve_trial_conversion_question_4_button_label": "Ganhe 20% de desconto",
"improve_trial_conversion_question_4_headline": "Que pena ouvir isso! Ganhe 20% de desconto no primeiro ano.",
"improve_trial_conversion_question_4_html": "Estamos felizes em te oferecer um desconto de 20% no plano anual.",
"improve_trial_conversion_question_5_button_label": "Próximo",
"improve_trial_conversion_question_5_headline": "O que você gostaria de alcançar?",
"improve_trial_conversion_question_5_subheader": "Por favor, escolha uma das opções a seguir:",
"improve_trial_conversion_question_5_subheader": "Por favor, descreva abaixo:",
"improve_trial_conversion_question_6_headline": "Como você tá resolvendo seu problema agora?",
"improve_trial_conversion_question_6_subheader": "Por favor, nomeie soluções alternativas:",
"integration_setup_survey_description": "Avalie quão fácil é para os usuários adicionarem integrações ao seu produto. Encontre pontos cegos.",
+35 -22
View File
@@ -152,6 +152,7 @@
"centered_modal": "Modal Centralizado",
"change_organization": "Alterar organização",
"change_workspace": "Alterar espaço de trabalho",
"choice_n": "Escolha {{n}}",
"choices": "Escolhas",
"choose_environment": "Escolha o ambiente",
"choose_organization": "Escolher organização",
@@ -165,6 +166,7 @@
"close": "Fechar",
"code": "Código",
"collapse_rows": "Recolher linhas",
"column_n": "Coluna {{n}}",
"completed": "Concluído",
"configuration": "Configuração",
"confirm": "Confirmar",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
"failed_to_load_organizations": "Falha ao carregar organizações",
"failed_to_load_workspaces": "Falha ao carregar projetos",
"field_placeholder": "Espaço reservado de {{field}}",
"filter": "Filtro",
"finish": "Concluir",
"first_name": "Primeiro nome",
@@ -247,10 +250,12 @@
"generate": "Gerar",
"go_back": "Voltar",
"go_to_dashboard": "Ir para o Painel",
"headline": "Título",
"hidden": "Oculto",
"hidden_field": "Campo oculto",
"hidden_fields": "Campos ocultos",
"hide_column": "Ocultar coluna",
"html": "HTML",
"id": "ID",
"image": "Imagem",
"images": "Imagens",
@@ -298,7 +303,6 @@
"months": "meses",
"move_down": "Mover para baixo",
"move_up": "Mover para cima",
"multiple_languages": "Várias línguas",
"my_product": "o meu produto",
"name": "Nome",
"new": "Novo",
@@ -313,6 +317,7 @@
"no_result_found": "Nenhum resultado encontrado",
"no_results": "Nenhum resultado",
"no_surveys_found": "Nenhum inquérito encontrado.",
"no_text_found": "Nenhum texto encontrado",
"none_of_the_above": "Nenhuma das opções acima",
"not_authenticated": "Não está autenticado para realizar esta ação.",
"not_authorized": "Não autorizado",
@@ -336,6 +341,7 @@
"organization_settings": "Configurações da Organização",
"other": "Outro",
"other_filters": "Outros Filtros",
"other_placeholder": "Outro espaço reservado",
"others": "Outros",
"overlay_color": "Cor da sobreposição",
"overview": "Visão geral",
@@ -381,6 +387,7 @@
"responses": "Respostas",
"restart": "Reiniciar",
"role": "Função",
"row_n": "Linha {{n}}",
"saas": "SaaS",
"sales": "Vendas",
"save": "Guardar",
@@ -419,6 +426,7 @@
"storage_not_configured": "Armazenamento de ficheiros não configurado, uploads provavelmente falharão",
"string": "Texto",
"styling": "Estilo",
"subheader": "Subtítulo",
"submit": "Submeter",
"summary": "Resumo",
"survey": "Inquérito",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Enviar dados para a sua base de dados do Notion",
"please_select_a_survey_error": "Por favor, selecione um inquérito",
"reconnect_button": "Voltar a ligar",
"reconnect_button_description": "A ligação da tua integração expirou. Por favor, volta a ligar para continuar a sincronizar as respostas. As tuas ligações e dados existentes serão preservados.",
"reconnect_button_tooltip": "Volta a ligar a integração para atualizar o teu acesso. As tuas ligações e dados existentes serão preservados.",
"select_at_least_one_question_error": "Por favor, selecione pelo menos uma pergunta",
"slack": {
"already_connected_another_survey": "Já ligou outro inquérito a este canal.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Desbloqueie a autenticação de dois fatores com um plano superior",
"update_personal_info": "Atualize as suas informações pessoais",
"warning_cannot_delete_account": "É o único proprietário desta organização. Transfira a propriedade para outro membro primeiro.",
"warning_cannot_undo": "Isto não pode ser desfeito",
"wrong_password": "Palavra-passe incorreta"
"warning_cannot_undo": "Isto não pode ser desfeito"
},
"teams": {
"add_members_description": "Adicionar membros à equipa e determinar o seu papel.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Inquérito copiado com sucesso!",
"delete_survey_and_responses_warning": "Tem a certeza de que deseja eliminar este inquérito e todas as suas respostas?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Escolha o idioma padrão para este inquérito:",
"2_activate_translation_for_specific_languages": "2. Ativar tradução para idiomas específicos:",
"activate_translations": "Ativar traduções",
"add": "Adicionar +",
"add_a_delay_or_auto_close_the_survey": "Adicionar um atraso ou fechar automaticamente o inquérito",
"add_a_four_digit_pin": "Adicione um PIN de quatro dígitos",
@@ -1363,6 +1366,8 @@
"assign": "Atribuir =",
"audience": "Público",
"auto_close_on_inactivity": "Fechar automaticamente por inatividade",
"auto_progress_rating_and_nps": "Avançar automaticamente em perguntas de classificação e NPS",
"auto_progress_rating_and_nps_description": "Avança automaticamente quando os inquiridos selecionam uma resposta em perguntas de classificação ou NPS. Isto aplica-se apenas a blocos com uma única pergunta. Perguntas obrigatórias ocultam o botão Seguinte; perguntas opcionais continuam a mostrá-lo para permitir saltar.",
"auto_save_disabled": "Guardar automático desativado",
"auto_save_disabled_tooltip": "O seu inquérito só é guardado automaticamente quando está em rascunho. Isto garante que os inquéritos públicos não sejam atualizados involuntariamente.",
"auto_save_on": "Guardar automático ativado",
@@ -1408,6 +1413,7 @@
"caution_text": "As alterações levarão a inconsistências",
"change_anyway": "Alterar mesmo assim",
"change_background": "Alterar fundo",
"change_default": "Alterar predefinição",
"change_question_type": "Alterar tipo de pergunta",
"change_survey_type": "Alterar o tipo de inquérito afeta o acesso existente",
"change_the_background_to_a_color_image_or_animation": "Altere o fundo para uma cor, imagem ou animação",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Escolha onde realizar o inquérito.",
"city": "Cidade",
"close_survey_on_response_limit": "Fechar inquérito no limite de respostas",
"code": "Código",
"color": "Cor",
"column_used_in_logic_error": "Esta coluna é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
"columns": "Colunas",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Personalizar o logótipo do inquérito",
"darken_or_lighten_background_of_your_choice": "Escurecer ou clarear o fundo da sua escolha.",
"days_before_showing_this_survey_again": "ou mais dias a decorrer entre o último inquérito apresentado e a apresentação deste inquérito.",
"default_language": "Idioma predefinido",
"delete_anyways": "Eliminar mesmo assim",
"delete_block": "Eliminar bloco",
"delete_choice": "Eliminar escolha",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Duplicar pergunta",
"edit_link": "Editar link",
"edit_recall": "Editar Lembrete",
"edit_translations": "Editar traduções {lang}",
"element_not_found": "Pergunta não encontrada",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Permitir que os inquiridos mudem de idioma a qualquer momento. Necessita de pelo menos 2 idiomas ativos.",
"enable_recaptcha_to_protect_your_survey_from_spam": "A proteção contra spam usa o reCAPTCHA v3 para filtrar as respostas de spam.",
@@ -1599,11 +1606,13 @@
"long_answer": "Resposta longa",
"long_answer_toggle_description": "Permitir que os inquiridos escrevam respostas mais longas e com várias linhas.",
"lower_label": "Etiqueta Inferior",
"manage_languages": "Gerir Idiomas",
"manage_languages": "Gerir idiomas",
"manage_translations": "Gerir traduções",
"matrix_all_fields": "Todos os campos",
"matrix_rows": "Linhas",
"max_file_size": "Tamanho máximo de ficheiro",
"max_file_size_limit_is": "O limite de tamanho máximo de ficheiro é",
"missing_first": "Em falta primeiro",
"move_question_to_block": "Mover pergunta para o bloco",
"multiply": "Multiplicar *",
"needed_for_self_hosted_cal_com_instance": "Necessário para uma instância auto-hospedada do Cal.com",
@@ -1611,7 +1620,7 @@
"next_button_label": "Rótulo do botão \"Seguinte\"",
"no_hidden_fields_yet_add_first_one_below": "Ainda não há campos ocultos. Adicione o primeiro abaixo.",
"no_images_found_for": "Não foram encontradas imagens para ''{query}\"",
"no_languages_found_add_first_one_to_get_started": "Nenhuma língua encontrada. Adicione a primeira para começar.",
"no_languages_found_add_first_one_to_get_started": "Não foram encontrados idiomas de inquérito neste espaço de trabalho. Por favor, adiciona um para começar.",
"no_option_found": "Nenhuma opção encontrada",
"no_recall_items_found": "Nenhum item de recuperação encontrado",
"no_variables_yet_add_first_one_below": "Ainda não há variáveis. Adicione a primeira abaixo.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Por favor, insira um URL válido (por exemplo, https://example.com)",
"please_set_a_survey_trigger": "Por favor, defina um desencadeador de inquérito",
"please_specify": "Por favor, especifique",
"present_your_survey_in_multiple_languages": "Apresenta o teu inquérito em vários idiomas",
"prevent_double_submission": "Impedir submissão dupla",
"prevent_double_submission_description": "Permitir apenas 1 resposta por endereço de email",
"progress_saved": "Progresso guardado",
@@ -1729,6 +1739,7 @@
"seven_points": "7 pontos",
"show_block_settings": "Mostrar definições do bloco",
"show_button": "Mostrar Botão",
"show_in_order": "Mostrar por ordem",
"show_language_switch": "Mostrar alternador de idioma",
"show_multiple_times": "Mostrar um número limitado de vezes",
"show_only_once": "Mostrar apenas uma vez",
@@ -1760,7 +1771,6 @@
"survey_preview": "Pré-visualização do questionário 👀",
"survey_styling": "Estilo do formulário",
"survey_trigger": "Desencadeador de Inquérito",
"switch_multi_language_on_to_get_started": "Ative o modo multilingue para começar 👉",
"target_block_not_found": "Bloco de destino não encontrado",
"targeted": "Alvo",
"ten_points": "10 pontos",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Mostrar uma única vez, mesmo que não respondam.",
"then": "Então",
"this_action_will_remove_all_the_translations_from_this_survey": "Esta ação irá remover todas as traduções deste inquérito.",
"this_will_remove_the_language_and_all_its_translations": "Isto irá remover este idioma e todas as suas traduções deste inquérito. Esta ação não pode ser revertida.",
"three_points": "3 pontos",
"times": "tempos",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Para manter a colocação consistente em todos os questionários, pode",
"translated": "Traduzido",
"trigger_survey_when_one_of_the_actions_is_fired": "Desencadear inquérito quando uma das ações for disparada...",
"try_lollipop_or_mountain": "Experimente 'cão' ou 'planta'...",
"type_field_id": "Escreva o id do campo",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Permitir apenas que pessoas com um email real respondam.",
"visibility_and_recontact": "Visibilidade e Recontacto",
"visibility_and_recontact_description": "Controlar quando este inquérito pode aparecer e com que frequência pode reaparecer.",
"visible": "Visível",
"wait": "Aguardar",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Aguarde alguns segundos após o gatilho antes de mostrar o inquérito",
"waiting_time_across_surveys": "Período de espera (entre inquéritos)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Idioma ou ID de idioma duplicado",
"edit_languages": "Editar idiomas",
"identifier": "Identificador (ISO)",
"incomplete_translations": "Traduções incompletas",
"language": "Idioma",
"language_deleted_successfully": "Idioma eliminado com sucesso",
"languages_updated_successfully": "Idiomas atualizados com sucesso",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Por favor, selecione um idioma",
"remove_language": "Remover idioma",
"remove_language_from_surveys_to_remove_it_from_workspace": "Por favor, remova o idioma destes inquéritos para o poder remover do espaço de trabalho.",
"search_items": "Pesquisar itens",
"translate": "Traduzir"
"search_items": "Pesquisar itens"
},
"look": {
"add_background_color": "Adicionar cor de fundo",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Colore a porção não preenchida da barra.",
"advanced_styling_field_track_height": "Altura da faixa",
"advanced_styling_field_track_height_description": "Controla a espessura da barra de progresso.",
"advanced_styling_field_upper_label_color": "Cor da etiqueta do título",
"advanced_styling_field_upper_label_color_description": "Colore a pequena etiqueta acima dos campos de entrada.",
"advanced_styling_field_upper_label_size": "Tamanho da fonte da etiqueta do título",
"advanced_styling_field_upper_label_size_description": "Ajusta o tamanho da pequena etiqueta acima dos campos de entrada.",
"advanced_styling_field_upper_label_weight": "Peso da fonte da etiqueta do título",
"advanced_styling_field_upper_label_weight_description": "Torna a etiqueta mais leve ou mais negrito.",
"advanced_styling_field_upper_label_color": "Cor da Etiqueta",
"advanced_styling_field_upper_label_color_description": "Define a cor das pequenas etiquetas acima dos campos de entrada e das etiquetas de escala.",
"advanced_styling_field_upper_label_size": "Tamanho da Fonte da Etiqueta",
"advanced_styling_field_upper_label_size_description": "Ajusta o tamanho das pequenas etiquetas acima dos campos de entrada e das etiquetas de escala.",
"advanced_styling_field_upper_label_weight": "Espessura da Fonte da Etiqueta",
"advanced_styling_field_upper_label_weight_description": "Torna as etiquetas mais finas ou mais grossas.",
"advanced_styling_section_buttons": "Botões",
"advanced_styling_section_headlines": "Títulos e descrições",
"advanced_styling_section_inputs": "Campos de entrada",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Ajude-nos a compreendê-lo melhor:",
"improve_trial_conversion_question_2_button_label": "Seguinte",
"improve_trial_conversion_question_2_headline": "Lamentamos saber. Qual foi o maior problema ao usar $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Seguinte",
"improve_trial_conversion_question_3_headline": "O que esperava que $[projectName] fizesse?",
"improve_trial_conversion_question_4_button_label": "Obtenha 20% de desconto",
"improve_trial_conversion_question_4_headline": "Lamentamos saber! Obtenha 20% de desconto no primeiro ano.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Estamos felizes por lhe oferecer um desconto de 20% num plano anual.</span></p>",
"improve_trial_conversion_question_5_button_label": "Seguinte",
"improve_trial_conversion_question_5_headline": "O que gostaria de alcançar?",
"improve_trial_conversion_question_5_subheader": "Por favor, selecione uma das seguintes opções:",
"improve_trial_conversion_question_5_subheader": "Por favor, descreve abaixo:",
"improve_trial_conversion_question_6_headline": "Como está a resolver o seu problema agora?",
"improve_trial_conversion_question_6_subheader": "Por favor, nomeie soluções alternativas:",
"integration_setup_survey_description": "Avalie a facilidade com que os utilizadores podem adicionar integrações ao seu produto. Encontre pontos cegos.",
+35 -22
View File
@@ -152,6 +152,7 @@
"centered_modal": "Modală centralizată",
"change_organization": "Schimbă organizația",
"change_workspace": "Schimbă spațiul de lucru",
"choice_n": "Opțiunea {{n}}",
"choices": "Alegeri",
"choose_environment": "Alege mediul",
"choose_organization": "Alege organizația",
@@ -165,6 +166,7 @@
"close": "Închide",
"code": "Cod",
"collapse_rows": "Restrânge rânduri",
"column_n": "Coloana {{n}}",
"completed": "Completat",
"configuration": "Configurare",
"confirm": "Confirmare",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Nu s-a reușit copierea în clipboard",
"failed_to_load_organizations": "Nu s-a reușit încărcarea organizațiilor",
"failed_to_load_workspaces": "Nu s-au putut încărca workspaces",
"field_placeholder": "Substituent {{field}}",
"filter": "Filtru",
"finish": "Finalizează",
"first_name": "Prenume",
@@ -247,10 +250,12 @@
"generate": "Generează",
"go_back": "Înapoi",
"go_to_dashboard": "Mergi la Tablou de Bord",
"headline": "Titlu",
"hidden": "Ascuns",
"hidden_field": "Câmp ascuns",
"hidden_fields": "Câmpuri ascunse",
"hide_column": "Ascunde coloana",
"html": "HTML",
"id": "ID",
"image": "Imagine",
"images": "Imagini",
@@ -298,7 +303,6 @@
"months": "luni",
"move_down": "Mută în jos",
"move_up": "Mută sus",
"multiple_languages": "Mai multe limbi",
"my_product": "produsul meu",
"name": "Nume",
"new": "Nou",
@@ -313,6 +317,7 @@
"no_result_found": "Niciun rezultat găsit",
"no_results": "Nicio rezultat",
"no_surveys_found": "Nu au fost găsite sondaje.",
"no_text_found": "Niciun text găsit",
"none_of_the_above": "Niciuna dintre cele de mai sus",
"not_authenticated": "Nu sunteți autentificat pentru a efectua această acțiune.",
"not_authorized": "Neautorizat",
@@ -336,6 +341,7 @@
"organization_settings": "Setări Organizație",
"other": "Altele",
"other_filters": "Alte Filtre",
"other_placeholder": "Alt substituent",
"others": "Altele",
"overlay_color": "Culoare overlay",
"overview": "Prezentare generală",
@@ -381,6 +387,7 @@
"responses": "Răspunsuri",
"restart": "Repornește",
"role": "Rolul",
"row_n": "Rândul {{n}}",
"saas": "SaaS",
"sales": "Vânzări",
"save": "Salvează",
@@ -419,6 +426,7 @@
"storage_not_configured": "Stocarea fișierelor neconfigurată, upload-urile vor eșua probabil",
"string": "Text",
"styling": "Stilizare",
"subheader": "Subtitlu",
"submit": "Trimite",
"summary": "Sumar",
"survey": "Chestionar",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Trimiteți datele în baza de date Notion",
"please_select_a_survey_error": "Vă rugăm să selectați un sondaj",
"reconnect_button": "Reconectează",
"reconnect_button_description": "Conexiunea integrării tale a expirat. Te rugăm să te reconectezi pentru a continua sincronizarea răspunsurilor. Linkurile și datele tale existente vor fi păstrate.",
"reconnect_button_tooltip": "Reconectează integrarea pentru a reîmprospăta accesul. Linkurile și datele tale existente vor fi păstrate.",
"select_at_least_one_question_error": "Vă rugăm să selectați cel puțin o întrebare",
"slack": {
"already_connected_another_survey": "Ați conectat deja un alt chestionar la acest canal.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Deblocați autentificarea în doi pași cu un plan superior",
"update_personal_info": "Actualizează informațiile tale personale",
"warning_cannot_delete_account": "Ești singurul proprietar al acestei organizații. Te rugăm să transferi proprietatea către un alt membru mai întâi.",
"warning_cannot_undo": "Aceasta nu poate fi anulată",
"wrong_password": "Parolă greșită"
"warning_cannot_undo": "Aceasta nu poate fi anulată"
},
"teams": {
"add_members_description": "Adaugă membri în echipă și stabilește rolul lor.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "\"Sondaj copiat cu succes!\"",
"delete_survey_and_responses_warning": "Sigur doriți să ștergeți acest sondaj și toate răspunsurile sale?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Alege limba implicită pentru acest sondaj:",
"2_activate_translation_for_specific_languages": "2. Activați traducerea pentru anumite limbi:",
"activate_translations": "Activează traducerile",
"add": "Adaugă +",
"add_a_delay_or_auto_close_the_survey": "Adăugați o întârziere sau închideți automat sondajul",
"add_a_four_digit_pin": "Adăugați un cod PIN din patru cifre",
@@ -1363,6 +1366,8 @@
"assign": "Atribuire =",
"audience": "Public",
"auto_close_on_inactivity": "Închidere automată la inactivitate",
"auto_progress_rating_and_nps": "Avansare automată pentru întrebări de rating și NPS",
"auto_progress_rating_and_nps_description": "Avansează automat când respondenții selectează un răspuns la întrebările de rating sau NPS. Aceasta se aplică doar blocurilor cu o singură întrebare. Întrebările obligatorii ascund butonul Următorul; întrebările opționale îl afișează în continuare pentru a permite omiterea.",
"auto_save_disabled": "Salvare automată dezactivată",
"auto_save_disabled_tooltip": "Chestionarul dvs. este salvat automat doar când este în ciornă. Acest lucru asigură că sondajele publice nu sunt actualizate neintenționat.",
"auto_save_on": "Salvare automată activată",
@@ -1408,6 +1413,7 @@
"caution_text": "Schimbările vor duce la inconsecvențe",
"change_anyway": "Schimbă oricum",
"change_background": "Schimbați fundalul",
"change_default": "Schimbă implicit",
"change_question_type": "Schimbă tipul întrebării",
"change_survey_type": "Schimbarea tipului chestionarului afectează accesul existent",
"change_the_background_to_a_color_image_or_animation": "Schimbați fundalul cu o culoare, imagine sau animație.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Alegeți unde să rulați chestionarul.",
"city": "Oraș",
"close_survey_on_response_limit": "Închideți sondajul la limită de răspunsuri",
"code": "Cod",
"color": "Culoare",
"column_used_in_logic_error": "Această coloană este folosită în logica întrebării {questionIndex}. Vă rugăm să o eliminați din logică mai întâi.",
"columns": "Coloane",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Personalizează logo-ul chestionarului",
"darken_or_lighten_background_of_your_choice": "Întunecați sau luminați fundalul după preferințe.",
"days_before_showing_this_survey_again": "sau mai multe zile să treacă între ultima afișare a sondajului și afișarea acestui sondaj.",
"default_language": "Limba implicită",
"delete_anyways": "Șterge oricum",
"delete_block": "Șterge blocul",
"delete_choice": "Șterge alegerea",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Duplică întrebarea",
"edit_link": "Editare legătură",
"edit_recall": "Editează Referințele",
"edit_translations": "Editează traducerile {lang}",
"element_not_found": "Întrebarea nu a fost găsită",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Permite respondenților să schimbe limba în orice moment. Necesită minimum 2 limbi active.",
"enable_recaptcha_to_protect_your_survey_from_spam": "Protecția împotriva spamului folosește reCAPTCHA v3 pentru a filtra răspunsurile de spam.",
@@ -1599,11 +1606,13 @@
"long_answer": "Răspuns lung",
"long_answer_toggle_description": "Permite respondenților să scrie răspunsuri mai lungi, pe mai multe rânduri.",
"lower_label": "Etichetă inferioară",
"manage_languages": "Gestionați limbile",
"manage_languages": "Gestionează limbile",
"manage_translations": "Gestionează traducerile",
"matrix_all_fields": "Toate câmpurile",
"matrix_rows": "Rânduri",
"max_file_size": "Dimensiune maximă fișier",
"max_file_size_limit_is": "Limita maximă pentru dimensiunea fișierului este",
"missing_first": "Lipsă întâi",
"move_question_to_block": "Mută întrebarea în bloc",
"multiply": "Multiplicare",
"needed_for_self_hosted_cal_com_instance": "Necesar pentru un exemplu autogăzduit Cal.com",
@@ -1611,7 +1620,7 @@
"next_button_label": "Etichetă buton \"Următorul\"",
"no_hidden_fields_yet_add_first_one_below": "Nu există încă câmpuri ascunse. Adăugați primul mai jos.",
"no_images_found_for": "Nicio imagine găsită pentru ''{query}\"",
"no_languages_found_add_first_one_to_get_started": "Nu s-au găsit limbi. Adaugă prima pentru a începe.",
"no_languages_found_add_first_one_to_get_started": "Nu s-au găsit limbi de chestionar în acest spațiu de lucru. Te rugăm să adaugi una pentru a începe.",
"no_option_found": "Nicio opțiune găsită",
"no_recall_items_found": "Nu au fost găsite elemente de reamintire",
"no_variables_yet_add_first_one_below": "Nu există variabile încă. Adăugați prima mai jos.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Vă rugăm să introduceți un URL valid (de exemplu, https://example.com)",
"please_set_a_survey_trigger": "Vă rugăm să setați un declanșator sondaj",
"please_specify": "Vă rugăm să specificați",
"present_your_survey_in_multiple_languages": "Prezintă chestionarul tău în mai multe limbi",
"prevent_double_submission": "Prevenire trimitere dublă",
"prevent_double_submission_description": "Permite doar 1 răspuns per adresă de email.",
"progress_saved": "Progres salvat",
@@ -1729,6 +1739,7 @@
"seven_points": "7 puncte",
"show_block_settings": "Afișează setările blocului",
"show_button": "Afișează butonul",
"show_in_order": "Afișează în ordine",
"show_language_switch": "Afișează comutatorul de limbă",
"show_multiple_times": "Afișează de mai multe ori",
"show_only_once": "Afișează doar o dată",
@@ -1760,7 +1771,6 @@
"survey_preview": "Previzualizare chestionar 👀",
"survey_styling": "Stilizare formular",
"survey_trigger": "Declanșator sondaj",
"switch_multi_language_on_to_get_started": "Activați opțiunea multi-limbă pentru a începe 👉",
"target_block_not_found": "Blocul țintă nu a fost găsit",
"targeted": "Ţintite",
"ten_points": "10 puncte",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Afișează o singură dată, chiar dacă persoana nu răspunde.",
"then": "Apoi",
"this_action_will_remove_all_the_translations_from_this_survey": "Această acțiune va elimina toate traducerile din acest sondaj.",
"this_will_remove_the_language_and_all_its_translations": "Aceasta va elimina această limbă și toate traducerile ei din acest chestionar. Această acțiune nu poate fi anulată.",
"three_points": "3 puncte",
"times": "ori",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Pentru a menține amplasarea consecventă pentru toate sondajele, puteți",
"translated": "Tradus",
"trigger_survey_when_one_of_the_actions_is_fired": "Declanșați sondajul atunci când una dintre acțiuni este realizată...",
"try_lollipop_or_mountain": "Încercați „lollipop” sau „mountain”...",
"type_field_id": "ID câmp tip",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Permite doar persoanelor cu un email real să răspundă.",
"visibility_and_recontact": "Vizibilitate și recontactare",
"visibility_and_recontact_description": "Controlează când poate apărea acest sondaj și cât de des poate reapărea.",
"visible": "Vizibil",
"wait": "Așteptați",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Așteptați câteva secunde după declanșare înainte de a afișa sondajul",
"waiting_time_across_surveys": "Perioadă de răcire (între sondaje)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Limbă sau ID de limbă duplicat",
"edit_languages": "Editați limbile",
"identifier": "Identificator (ISO)",
"incomplete_translations": "Traduceri incomplete",
"language": "Limba",
"language_deleted_successfully": "Limba a fost ștearsă cu succes",
"languages_updated_successfully": "Limbile au fost actualizate cu succes",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Vă rugăm să selectați o limbă",
"remove_language": "Eliminați limba",
"remove_language_from_surveys_to_remove_it_from_workspace": "Vă rugăm să eliminați limba din aceste sondaje pentru a o elimina din spațiul de lucru.",
"search_items": "Căutați elemente",
"translate": "Traduceți"
"search_items": "Căutați elemente"
},
"look": {
"add_background_color": "Adăugați culoare de fundal",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Colorează partea necompletată a barei.",
"advanced_styling_field_track_height": "Înălțime track",
"advanced_styling_field_track_height_description": "Controlează grosimea barei de progres.",
"advanced_styling_field_upper_label_color": "Culoare etichetă titlu",
"advanced_styling_field_upper_label_color_description": "Colorează eticheta mică de deasupra câmpurilor.",
"advanced_styling_field_upper_label_size": "Mărime font etichetă titlu",
"advanced_styling_field_upper_label_size_description": "Redimensionea eticheta mică de deasupra câmpurilor.",
"advanced_styling_field_upper_label_weight": "Grosime font etichetă titlu",
"advanced_styling_field_upper_label_weight_description": "Face eticheta mai subțire sau mai îngroșată.",
"advanced_styling_field_upper_label_color": "Culoare etichetă",
"advanced_styling_field_upper_label_color_description": "Colorează etichetele mici de deasupra câmpurilor de introducere și etichetele de scală.",
"advanced_styling_field_upper_label_size": "Dimensiune font etichetă",
"advanced_styling_field_upper_label_size_description": "Ajustează dimensiunea etichetelor mici de deasupra câmpurilor de introducere și a etichetelor de scală.",
"advanced_styling_field_upper_label_weight": "Grosime font etichetă",
"advanced_styling_field_upper_label_weight_description": "Face etichetele mai subțiri sau mai îngroșate.",
"advanced_styling_section_buttons": "Butoane",
"advanced_styling_section_headlines": "Titluri și descrieri",
"advanced_styling_section_inputs": "Inputuri",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Ajută-ne să te înțelegem mai bine:",
"improve_trial_conversion_question_2_button_label": "Următorul",
"improve_trial_conversion_question_2_headline": "Ne pare rău să auzim asta. Care a fost cea mai mare problemă folosind $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Următorul",
"improve_trial_conversion_question_3_headline": "Ce ați fi așteptat de la $[projectName]?",
"improve_trial_conversion_question_4_button_label": "Obțineți 20% reducere",
"improve_trial_conversion_question_4_headline": "Ne pare rău să auzim asta! Obțineți 20% reducere în primul an.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Suntem bucuroși să vă oferim o reducere de 20% la un plan anual.</span></p>",
"improve_trial_conversion_question_5_button_label": "Următorul",
"improve_trial_conversion_question_5_headline": "Ce ați dori să obțineți?",
"improve_trial_conversion_question_5_subheader": " rugăm să selectați una dintre următoarele opțiuni:",
"improve_trial_conversion_question_5_subheader": "Te rugăm să descrii mai jos:",
"improve_trial_conversion_question_6_headline": "Cum rezolvați acum problema dumneavoastră?",
"improve_trial_conversion_question_6_subheader": "Vă rugăm să numiți soluțiile alternative:",
"integration_setup_survey_description": "Evaluați cât de ușor pot utilizatorii să adauge integrări la produsul dvs. Identificați punctele oarbe.",
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "Центрированное модальное окно",
"change_organization": "Сменить организацию",
"change_workspace": "Сменить рабочее пространство",
"choice_n": "Вариант {{n}}",
"choices": "Варианты",
"choose_environment": "Выберите среду",
"choose_organization": "Выберите организацию",
@@ -165,6 +166,7 @@
"close": "Закрыть",
"code": "Код",
"collapse_rows": "Свернуть строки",
"column_n": "Колонка {{n}}",
"completed": "Завершено",
"configuration": "Конфигурация",
"confirm": "Подтвердить",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Не удалось скопировать в буфер обмена",
"failed_to_load_organizations": "Не удалось загрузить организации",
"failed_to_load_workspaces": "Не удалось загрузить рабочие пространства",
"field_placeholder": "Заполнитель {{field}}",
"filter": "Фильтр",
"finish": "Завершить",
"first_name": "Имя",
@@ -247,10 +250,12 @@
"generate": "Сгенерировать",
"go_back": "Назад",
"go_to_dashboard": "Перейти к панели управления",
"headline": "Заголовок",
"hidden": "Скрыто",
"hidden_field": "Скрытое поле",
"hidden_fields": "Скрытые поля",
"hide_column": "Скрыть столбец",
"html": "HTML",
"id": "ID",
"image": "Изображение",
"images": "Изображения",
@@ -298,7 +303,6 @@
"months": "месяцы",
"move_down": "Переместить вниз",
"move_up": "Переместить вверх",
"multiple_languages": "Несколько языков",
"my_product": "мой продукт",
"name": "Имя",
"new": "Новый",
@@ -313,6 +317,7 @@
"no_result_found": "Результат не найден",
"no_results": "Нет результатов",
"no_surveys_found": "Опросы не найдены.",
"no_text_found": "Текст не найден",
"none_of_the_above": "Ничего из вышеперечисленного",
"not_authenticated": "У вас нет прав для выполнения этого действия.",
"not_authorized": "Нет доступа",
@@ -336,6 +341,7 @@
"organization_settings": "Настройки организации",
"other": "Другое",
"other_filters": "Другие фильтры",
"other_placeholder": "Другой заполнитель",
"others": "Другие",
"overlay_color": "Цвет наложения",
"overview": "Обзор",
@@ -381,6 +387,7 @@
"responses": "Ответы",
"restart": "Перезапустить",
"role": "Роль",
"row_n": "Строка {{n}}",
"saas": "SaaS",
"sales": "Продажи",
"save": "Сохранить",
@@ -419,6 +426,7 @@
"storage_not_configured": "Хранилище файлов не настроено, загрузка, скорее всего, не удастся",
"string": "Текст",
"styling": "Стилизация",
"subheader": "Подзаголовок",
"submit": "Отправить",
"summary": "Сводка",
"survey": "Опрос",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Отправляйте данные в вашу базу данных Notion",
"please_select_a_survey_error": "Пожалуйста, выберите опрос",
"reconnect_button": "Переподключить",
"reconnect_button_description": "Срок действия подключения интеграции истёк. Пожалуйста, переподключитесь, чтобы продолжить синхронизацию ответов. Ваши существующие ссылки и данные будут сохранены.",
"reconnect_button_tooltip": "Переподключите интеграцию, чтобы обновить доступ. Ваши существующие ссылки и данные будут сохранены.",
"select_at_least_one_question_error": "Пожалуйста, выберите хотя бы один вопрос",
"slack": {
"already_connected_another_survey": "Вы уже подключили другой опрос к этому каналу.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Откройте двухфакторную аутентификацию с более высоким тарифом",
"update_personal_info": "Обновить личную информацию",
"warning_cannot_delete_account": "Вы являетесь единственным владельцем этой организации. Пожалуйста, сначала передайте права другому участнику.",
"warning_cannot_undo": "Это действие необратимо",
"wrong_password": "Неверный пароль"
"warning_cannot_undo": "Это действие необратимо"
},
"teams": {
"add_members_description": "Добавьте участников в команду и определите их роль.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Опрос успешно скопирован!",
"delete_survey_and_responses_warning": "Вы уверены, что хотите удалить этот опрос и все его ответы?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Выберите язык по умолчанию для этого опроса:",
"2_activate_translation_for_specific_languages": "2. Активируйте перевод для выбранных языков:",
"activate_translations": "Активировать переводы",
"add": "Добавить +",
"add_a_delay_or_auto_close_the_survey": "Добавить задержку или автоматически закрыть опрос",
"add_a_four_digit_pin": "Добавить четырёхзначный PIN-код",
@@ -1363,6 +1366,8 @@
"assign": "Назначить =",
"audience": "Аудитория",
"auto_close_on_inactivity": "Автоматически закрывать при бездействии",
"auto_progress_rating_and_nps": "Автоматический переход для вопросов с оценкой и NPS",
"auto_progress_rating_and_nps_description": "Автоматически переходить к следующему шагу, когда респонденты выбирают ответ в вопросах с оценкой или NPS. Это применяется только к блокам с одним вопросом. В обязательных вопросах кнопка «Далее» скрыта; в необязательных вопросах она остается видимой для пропуска.",
"auto_save_disabled": "Автосохранение отключено",
"auto_save_disabled_tooltip": "Ваш опрос автоматически сохраняется только в режиме черновика. Это гарантирует, что публичные опросы не будут случайно обновлены.",
"auto_save_on": "Автосохранение включено",
@@ -1408,6 +1413,7 @@
"caution_text": "Изменения приведут к несоответствиям",
"change_anyway": "Всё равно изменить",
"change_background": "Изменить фон",
"change_default": "Изменить по умолчанию",
"change_question_type": "Изменить тип вопроса",
"change_survey_type": "Смена типа опроса влияет на существующий доступ",
"change_the_background_to_a_color_image_or_animation": "Изменить фон на цвет, изображение или анимацию.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Выберите, где запускать опрос.",
"city": "Город",
"close_survey_on_response_limit": "Закрыть опрос при достижении лимита ответов",
"code": "Код",
"color": "Цвет",
"column_used_in_logic_error": "Этот столбец используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите его из логики.",
"columns": "Столбцы",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Настроить логотип опроса",
"darken_or_lighten_background_of_your_choice": "Затемните или осветлите выбранный фон.",
"days_before_showing_this_survey_again": "или больше дней должно пройти между последним показом опроса и показом этого опроса.",
"default_language": "Язык по умолчанию",
"delete_anyways": "Удалить в любом случае",
"delete_block": "Удалить блок",
"delete_choice": "Удалить вариант",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Дублировать вопрос",
"edit_link": "Редактировать ссылку",
"edit_recall": "Редактировать напоминание",
"edit_translations": "Редактировать переводы на {lang}",
"element_not_found": "Вопрос не найден",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Разрешить респондентам менять язык опроса в любое время. Требуется минимум 2 активных языка.",
"enable_recaptcha_to_protect_your_survey_from_spam": "Для защиты от спама используется reCAPTCHA v3, чтобы отфильтровывать спам-ответы.",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "Позволить респондентам писать более длинные, многострочные ответы.",
"lower_label": "Нижняя метка",
"manage_languages": "Управление языками",
"manage_translations": "Управление переводами",
"matrix_all_fields": "Все поля",
"matrix_rows": "Строки",
"max_file_size": "Максимальный размер файла",
"max_file_size_limit_is": "Ограничение максимального размера файла",
"missing_first": "Сначала отсутствующие",
"move_question_to_block": "Переместить вопрос в блок",
"multiply": "Умножить *",
"needed_for_self_hosted_cal_com_instance": "Требуется для самостоятельного размещения Cal.com",
@@ -1611,7 +1620,7 @@
"next_button_label": "Метка кнопки «Далее»",
"no_hidden_fields_yet_add_first_one_below": "Скрытых полей пока нет. Добавьте первое ниже.",
"no_images_found_for": "Изображения не найдены для «{query}»",
"no_languages_found_add_first_one_to_get_started": "Языки не найдены. Добавьте первый, чтобы начать.",
"no_languages_found_add_first_one_to_get_started": "В этом рабочем пространстве не найдено языков опроса. Пожалуйста, добавьте язык, чтобы начать работу.",
"no_option_found": "Вариант не найден",
"no_recall_items_found": "Не найдено ни одного элемента для напоминания",
"no_variables_yet_add_first_one_below": "Пока нет переменных. Добавьте первую ниже.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Пожалуйста, введите корректный URL (например, https://example.com)",
"please_set_a_survey_trigger": "Пожалуйста, установите триггер опроса",
"please_specify": "Пожалуйста, уточните",
"present_your_survey_in_multiple_languages": "Представьте свой опрос на нескольких языках",
"prevent_double_submission": "Предотвратить повторную отправку",
"prevent_double_submission_description": "Разрешить только 1 ответ на один адрес электронной почты",
"progress_saved": "Прогресс сохранён",
@@ -1729,6 +1739,7 @@
"seven_points": "7 баллов",
"show_block_settings": "Показать настройки блока",
"show_button": "Показать кнопку",
"show_in_order": "Показать по порядку",
"show_language_switch": "Показать переключатель языка",
"show_multiple_times": "Показать ограниченное количество раз",
"show_only_once": "Показать только один раз",
@@ -1760,7 +1771,6 @@
"survey_preview": "Предпросмотр опроса 👀",
"survey_styling": "Оформление формы",
"survey_trigger": "Триггер опроса",
"switch_multi_language_on_to_get_started": "Включите многоязычный режим, чтобы начать 👉",
"target_block_not_found": "Целевой блок не найден",
"targeted": "Нацелен",
"ten_points": "10 баллов",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Показать один раз, даже если не будет ответа.",
"then": "Затем",
"this_action_will_remove_all_the_translations_from_this_survey": "Это действие удалит все переводы из этого опроса.",
"this_will_remove_the_language_and_all_its_translations": "Это удалит данный язык и все его переводы из этого опроса. Это действие нельзя отменить.",
"three_points": "3 балла",
"times": "раз",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "Чтобы сохранить единое расположение во всех опросах, вы можете",
"translated": "Переведено",
"trigger_survey_when_one_of_the_actions_is_fired": "Запустить опрос при выполнении одного из действий...",
"try_lollipop_or_mountain": "Попробуйте «lollipop» или «mountain»...",
"type_field_id": "Введите id поля",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Разрешить отвечать только пользователям с реальным email.",
"visibility_and_recontact": "Видимость и повторный контакт",
"visibility_and_recontact_description": "Управляйте, когда этот опрос может появляться и как часто он может повторяться.",
"visible": "Видимый",
"wait": "Ожидание",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Подождите несколько секунд после срабатывания триггера перед показом опроса",
"waiting_time_across_surveys": "Период ожидания (между опросами)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Дублирующийся язык или идентификатор языка",
"edit_languages": "Редактировать языки",
"identifier": "Идентификатор (ISO)",
"incomplete_translations": "Неполные переводы",
"language": "Язык",
"language_deleted_successfully": "Язык успешно удалён",
"languages_updated_successfully": "Языки успешно обновлены",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Пожалуйста, выберите язык",
"remove_language": "Удалить язык",
"remove_language_from_surveys_to_remove_it_from_workspace": "Пожалуйста, удалите язык из этих опросов, чтобы удалить его из рабочей области.",
"search_items": "Поиск элементов",
"translate": "Перевести"
"search_items": "Поиск элементов"
},
"look": {
"add_background_color": "Добавить цвет фона",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Задаёт цвет незаполненной части полосы.",
"advanced_styling_field_track_height": "Высота трека",
"advanced_styling_field_track_height_description": "Управляет толщиной индикатора прогресса.",
"advanced_styling_field_upper_label_color": "Цвет метки заголовка",
"advanced_styling_field_upper_label_color_description": "Задаёт цвет маленькой метки над полями ввода.",
"advanced_styling_field_upper_label_size": "Размер шрифта метки заголовка",
"advanced_styling_field_upper_label_size_description": "Изменяет размер маленькой метки над полями ввода.",
"advanced_styling_field_upper_label_weight": "Толщина шрифта метки заголовка",
"advanced_styling_field_upper_label_weight_description": "Делает метку тоньше или жирнее.",
"advanced_styling_field_upper_label_color": "Цвет подписи",
"advanced_styling_field_upper_label_color_description": "Задаёт цвет маленьких подписей над полями ввода и подписей шкалы.",
"advanced_styling_field_upper_label_size": "Размер шрифта подписи",
"advanced_styling_field_upper_label_size_description": "Изменяет размер маленьких подписей над полями ввода и подписей шкалы.",
"advanced_styling_field_upper_label_weight": "Насыщенность шрифта подписи",
"advanced_styling_field_upper_label_weight_description": "Делает подписи светлее или жирнее.",
"advanced_styling_section_buttons": "Кнопки",
"advanced_styling_section_headlines": "Заголовки и описания",
"advanced_styling_section_inputs": "Поля ввода",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Помогите нам лучше вас понять:",
"improve_trial_conversion_question_2_button_label": "Далее",
"improve_trial_conversion_question_2_headline": "Жаль это слышать. Какая была самая большая проблема при использовании $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Далее",
"improve_trial_conversion_question_3_headline": "Что вы ожидали от $[projectName]?",
"improve_trial_conversion_question_4_button_label": "Получить скидку 20%",
"improve_trial_conversion_question_4_headline": "Жаль это слышать! Получите 20% скидку на первый год.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Мы рады предложить вам скидку 20% на годовой тариф.</span></p>",
"improve_trial_conversion_question_5_button_label": "Далее",
"improve_trial_conversion_question_5_headline": "Чего бы вы хотели достичь?",
"improve_trial_conversion_question_5_subheader": "Пожалуйста, выберите один из следующих вариантов:",
"improve_trial_conversion_question_5_subheader": "Пожалуйста, опишите ниже:",
"improve_trial_conversion_question_6_headline": "Как вы сейчас решаете свою проблему?",
"improve_trial_conversion_question_6_subheader": "Пожалуйста, укажите альтернативные решения:",
"integration_setup_survey_description": "Оцените, насколько легко пользователи могут добавлять интеграции в ваш продукт. Найдите слабые места.",
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "Centrerad modal",
"change_organization": "Byt organisation",
"change_workspace": "Byt arbetsyta",
"choice_n": "Val {{n}}",
"choices": "Val",
"choose_environment": "Välj miljö",
"choose_organization": "Välj organisation",
@@ -165,6 +166,7 @@
"close": "Stäng",
"code": "Kod",
"collapse_rows": "Dölj rader",
"column_n": "Kolumn {{n}}",
"completed": "Slutförd",
"configuration": "Konfiguration",
"confirm": "Bekräfta",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "Misslyckades att kopiera till urklipp",
"failed_to_load_organizations": "Misslyckades att ladda organisationer",
"failed_to_load_workspaces": "Det gick inte att ladda arbetsytor",
"field_placeholder": "Platshållare för {{field}}",
"filter": "Filter",
"finish": "Slutför",
"first_name": "Förnamn",
@@ -247,10 +250,12 @@
"generate": "Generera",
"go_back": "Gå tillbaka",
"go_to_dashboard": "Gå till instrumentpanelen",
"headline": "Rubrik",
"hidden": "Dold",
"hidden_field": "Dolt fält",
"hidden_fields": "Dolda fält",
"hide_column": "Dölj kolumn",
"html": "HTML",
"id": "ID",
"image": "Bild",
"images": "Bilder",
@@ -298,7 +303,6 @@
"months": "månader",
"move_down": "Flytta ner",
"move_up": "Flytta upp",
"multiple_languages": "Flera språk",
"my_product": "min produkt",
"name": "Namn",
"new": "Ny",
@@ -313,6 +317,7 @@
"no_result_found": "Inget resultat hittades",
"no_results": "Inga resultat",
"no_surveys_found": "Inga enkäter hittades.",
"no_text_found": "Ingen text hittades",
"none_of_the_above": "Inget av ovanstående",
"not_authenticated": "Du är inte autentiserad för att utföra denna åtgärd.",
"not_authorized": "Ej behörig",
@@ -336,6 +341,7 @@
"organization_settings": "Organisationsinställningar",
"other": "Annat",
"other_filters": "Andra filter",
"other_placeholder": "Annan platshållare",
"others": "Andra",
"overlay_color": "Overlay-färg",
"overview": "Översikt",
@@ -381,6 +387,7 @@
"responses": "Svar",
"restart": "Starta om",
"role": "Roll",
"row_n": "Rad {{n}}",
"saas": "SaaS",
"sales": "Försäljning",
"save": "Spara",
@@ -419,6 +426,7 @@
"storage_not_configured": "Fillagring är inte konfigurerad, uppladdningar kommer sannolikt att misslyckas",
"string": "Text",
"styling": "Styling",
"subheader": "Underrubrik",
"submit": "Skicka",
"summary": "Sammanfattning",
"survey": "Enkät",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "Skicka data till din Notion-databas",
"please_select_a_survey_error": "Vänligen välj en enkät",
"reconnect_button": "Återanslut",
"reconnect_button_description": "Din integrationsanslutning har gått ut. Vänligen återanslut för att fortsätta synkronisera svar. Dina befintliga länkar och data kommer att bevaras.",
"reconnect_button_tooltip": "Återanslut integrationen för att uppdatera din åtkomst. Dina befintliga länkar och data kommer att bevaras.",
"select_at_least_one_question_error": "Vänligen välj minst en fråga",
"slack": {
"already_connected_another_survey": "Du har redan anslutit en annan enkät till denna kanal.",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "Lås upp tvåfaktorsautentisering med en högre plan",
"update_personal_info": "Uppdatera din personliga information",
"warning_cannot_delete_account": "Du är den enda ägaren av denna organisation. Vänligen överför ägarskapet till en annan medlem först.",
"warning_cannot_undo": "Detta kan inte ångras",
"wrong_password": "Fel lösenord"
"warning_cannot_undo": "Detta kan inte ångras"
},
"teams": {
"add_members_description": "Lägg till medlemmar i teamet och bestäm deras roll.",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "Enkät kopierad!",
"delete_survey_and_responses_warning": "Är du säker på att du vill ta bort denna enkät och alla dess svar?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Välj standardspråk för denna enkät:",
"2_activate_translation_for_specific_languages": "2. Aktivera översättning för specifika språk:",
"activate_translations": "Aktivera översättningar",
"add": "Lägg till +",
"add_a_delay_or_auto_close_the_survey": "Lägg till fördröjning eller stäng enkäten automatiskt",
"add_a_four_digit_pin": "Lägg till en fyrsiffrig PIN",
@@ -1363,6 +1366,8 @@
"assign": "Tilldela =",
"audience": "Målgrupp",
"auto_close_on_inactivity": "Stäng automatiskt vid inaktivitet",
"auto_progress_rating_and_nps": "Gå vidare automatiskt vid betygs- och NPS-frågor",
"auto_progress_rating_and_nps_description": "Gå automatiskt vidare när respondenter väljer ett svar på betygs- eller NPS-frågor. Detta gäller endast block med en enda fråga. Obligatoriska frågor döljer Nästa-knappen; valfria frågor visar den fortfarande för att kunna hoppas över.",
"auto_save_disabled": "Automatisk sparning inaktiverad",
"auto_save_disabled_tooltip": "Din enkät sparas endast automatiskt när den är ett utkast. Detta säkerställer att publika enkäter inte uppdateras oavsiktligt.",
"auto_save_on": "Automatisk sparning på",
@@ -1408,6 +1413,7 @@
"caution_text": "Ändringar kommer att leda till inkonsekvenser",
"change_anyway": "Ändra ändå",
"change_background": "Ändra bakgrund",
"change_default": "Ändra standard",
"change_question_type": "Ändra frågetyp",
"change_survey_type": "Byte av enkättyp påverkar befintlig åtkomst",
"change_the_background_to_a_color_image_or_animation": "Ändra bakgrunden till en färg, bild eller animering.",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "Välj var enkäten ska köras.",
"city": "Stad",
"close_survey_on_response_limit": "Stäng enkät vid svarsgräns",
"code": "Kod",
"color": "Färg",
"column_used_in_logic_error": "Denna kolumn används i logiken för fråga {questionIndex}. Vänligen ta bort den från logiken först.",
"columns": "Kolumner",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "Anpassa undersökningens logotyp",
"darken_or_lighten_background_of_your_choice": "Gör bakgrunden mörkare eller ljusare efter eget val.",
"days_before_showing_this_survey_again": "eller fler dagar måste gå mellan den senaste visade enkäten och att visa denna enkät.",
"default_language": "Standardspråk",
"delete_anyways": "Ta bort ändå",
"delete_block": "Ta bort block",
"delete_choice": "Ta bort val",
@@ -1463,7 +1471,6 @@
"duplicate_question": "Duplicera fråga",
"edit_link": "Redigera länk",
"edit_recall": "Redigera återkallning",
"edit_translations": "Redigera {lang} översättningar",
"element_not_found": "Fråga hittades inte",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "Tillåt respondenter att byta språk när som helst. Kräver minst 2 aktiva språk.",
"enable_recaptcha_to_protect_your_survey_from_spam": "Spamskydd använder reCAPTCHA v3 för att filtrera bort spam-svar.",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "Tillåt respondenter att skriva längre svar på flera rader.",
"lower_label": "Lägre etikett",
"manage_languages": "Hantera språk",
"manage_translations": "Hantera översättningar",
"matrix_all_fields": "Alla fält",
"matrix_rows": "Rader",
"max_file_size": "Max filstorlek",
"max_file_size_limit_is": "Maximal filstorleksgräns är",
"missing_first": "Saknade först",
"move_question_to_block": "Flytta fråga till block",
"multiply": "Multiplicera *",
"needed_for_self_hosted_cal_com_instance": "Behövs för en självhostad Cal.com-instans",
@@ -1611,7 +1620,7 @@
"next_button_label": "\"Nästa\"-knappetikett",
"no_hidden_fields_yet_add_first_one_below": "Inga dolda fält ännu. Lägg till det första nedan.",
"no_images_found_for": "Inga bilder hittades för ''{query}\"",
"no_languages_found_add_first_one_to_get_started": "Inga språk hittades. Lägg till det första för att komma igång.",
"no_languages_found_add_first_one_to_get_started": "Inga undersökningsspråk hittades i denna arbetsyta. Lägg till ett för att komma igång.",
"no_option_found": "Inget alternativ hittat",
"no_recall_items_found": "Inga återkallningsobjekt hittades",
"no_variables_yet_add_first_one_below": "Inga variabler ännu. Lägg till den första nedan.",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "Vänligen ange en giltig URL (t.ex. https://example.com)",
"please_set_a_survey_trigger": "Vänligen ställ in en enkätutlösare",
"please_specify": "Vänligen specificera",
"present_your_survey_in_multiple_languages": "Presentera din enkät på flera språk",
"prevent_double_submission": "Förhindra dubbelinskickning",
"prevent_double_submission_description": "Tillåt endast 1 svar per e-postadress",
"progress_saved": "Framsteg sparade",
@@ -1729,6 +1739,7 @@
"seven_points": "7 poäng",
"show_block_settings": "Visa blockinställningar",
"show_button": "Visa knapp",
"show_in_order": "Visa i ordning",
"show_language_switch": "Visa språkväxlare",
"show_multiple_times": "Visa ett begränsat antal gånger",
"show_only_once": "Visa endast en gång",
@@ -1760,7 +1771,6 @@
"survey_preview": "Enkätförhandsgranskning 👀",
"survey_styling": "Formulärstil",
"survey_trigger": "Enkätutlösare",
"switch_multi_language_on_to_get_started": "Slå på flerspråkighet för att komma igång 👉",
"target_block_not_found": "Målblock hittades inte",
"targeted": "Riktad",
"ten_points": "10 poäng",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "Visa en enda gång, även om de inte svarar.",
"then": "Sedan",
"this_action_will_remove_all_the_translations_from_this_survey": "Denna åtgärd kommer att ta bort alla översättningar från denna enkät.",
"this_will_remove_the_language_and_all_its_translations": "Detta tar bort språket och alla dess översättningar från denna enkät. Denna åtgärd kan inte ångras.",
"three_points": "3 poäng",
"times": "gånger",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "För att hålla placeringen konsekvent över alla enkäter kan du",
"translated": "Översatt",
"trigger_survey_when_one_of_the_actions_is_fired": "Utlös enkät när en av åtgärderna aktiveras...",
"try_lollipop_or_mountain": "Prova 'lollipop' eller 'mountain'...",
"type_field_id": "Skriv fält-ID",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "Låt endast personer med en riktig e-post svara.",
"visibility_and_recontact": "Synlighet och återkontakt",
"visibility_and_recontact_description": "Kontrollera när denna enkät kan visas och hur ofta den kan visas igen.",
"visible": "Synlig",
"wait": "Vänta",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "Vänta några sekunder efter utlösningen innan enkäten visas",
"waiting_time_across_surveys": "Väntetid (mellan enkäter)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "Duplicerat språk eller språk-ID",
"edit_languages": "Redigera språk",
"identifier": "Identifierare (ISO)",
"incomplete_translations": "Ofullständiga översättningar",
"language": "Språk",
"language_deleted_successfully": "Språket har tagits bort",
"languages_updated_successfully": "Språken har uppdaterats",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "Vänligen välj ett språk",
"remove_language": "Ta bort språk",
"remove_language_from_surveys_to_remove_it_from_workspace": "Ta bort språket från dessa enkäter för att kunna ta bort det från arbetsytan.",
"search_items": "Sök objekt",
"translate": "Översätt"
"search_items": "Sök objekt"
},
"look": {
"add_background_color": "Lägg till bakgrundsfärg",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "Färgar den ofyllda delen av stapeln.",
"advanced_styling_field_track_height": "Spårets höjd",
"advanced_styling_field_track_height_description": "Styr tjockleken på förloppsstapeln.",
"advanced_styling_field_upper_label_color": "Rubriketikettens färg",
"advanced_styling_field_upper_label_color_description": "Färgar den lilla etiketten ovanför fälten.",
"advanced_styling_field_upper_label_size": "Rubriketikettens teckenstorlek",
"advanced_styling_field_upper_label_size_description": "Skalar storleken på den lilla etiketten ovanför fälten.",
"advanced_styling_field_upper_label_weight": "Rubriketikettens teckentjocklek",
"advanced_styling_field_upper_label_weight_description": "Gör etiketten tunnare eller fetare.",
"advanced_styling_field_upper_label_color": "Etiketfärg",
"advanced_styling_field_upper_label_color_description": "Färglägger de små etiketterna ovanför inmatningsfält och skalans etiketter.",
"advanced_styling_field_upper_label_size": "Etiketttextstorlek",
"advanced_styling_field_upper_label_size_description": "Skalar storleken på de små etiketterna ovanför inmatningsfält och skalans etiketter.",
"advanced_styling_field_upper_label_weight": "Etiketttextvikt",
"advanced_styling_field_upper_label_weight_description": "Gör etiketterna ljusare eller fetare.",
"advanced_styling_section_buttons": "Knappar",
"advanced_styling_section_headlines": "Rubriker & beskrivningar",
"advanced_styling_section_inputs": "Inmatningar",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "Hjälp oss förstå dig bättre:",
"improve_trial_conversion_question_2_button_label": "Nästa",
"improve_trial_conversion_question_2_headline": "Tråkigt att höra. Vad var det största problemet med att använda $[projectName]?",
"improve_trial_conversion_question_3_button_label": "Nästa",
"improve_trial_conversion_question_3_headline": "Vad förväntade du dig att $[projectName] skulle göra?",
"improve_trial_conversion_question_4_button_label": "Få 20% rabatt",
"improve_trial_conversion_question_4_headline": "Tråkigt att höra! Få 20% rabatt första året.",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>Vi erbjuder dig gärna 20% rabatt på en årsplan.</span></p>",
"improve_trial_conversion_question_5_button_label": "Nästa",
"improve_trial_conversion_question_5_headline": "Vad vill du uppnå?",
"improve_trial_conversion_question_5_subheader": "Vänligen välj ett av följande alternativ:",
"improve_trial_conversion_question_5_subheader": "Beskriv gärna nedan:",
"improve_trial_conversion_question_6_headline": "Hur löser du ditt problem nu?",
"improve_trial_conversion_question_6_subheader": "Vänligen nämn alternativa lösningar:",
"integration_setup_survey_description": "Utvärdera hur enkelt användare kan lägga till integrationer i din produkt. Hitta blinda fläckar.",
+35 -22
View File
@@ -152,6 +152,7 @@
"centered_modal": "居中 模态",
"change_organization": "切换组织",
"change_workspace": "切换工作区",
"choice_n": "选项 {{n}}",
"choices": "选项",
"choose_environment": "选择 环境",
"choose_organization": "选择 组织",
@@ -165,6 +166,7 @@
"close": "关闭",
"code": "代码",
"collapse_rows": "折叠 行",
"column_n": "列 {{n}}",
"completed": "完成",
"configuration": "配置",
"confirm": "确认",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "复制到剪贴板失败",
"failed_to_load_organizations": "加载组织失败",
"failed_to_load_workspaces": "加载工作区失败",
"field_placeholder": "{{field}} 占位符",
"filter": "筛选",
"finish": "完成",
"first_name": "名字",
@@ -247,10 +250,12 @@
"generate": "生成",
"go_back": "返回 ",
"go_to_dashboard": "转到 Dashboard",
"headline": "标题",
"hidden": "隐藏",
"hidden_field": "隐藏 字段",
"hidden_fields": "隐藏 字段",
"hide_column": "隐藏 列",
"html": "HTML",
"id": "ID",
"image": "图片",
"images": "图片",
@@ -298,7 +303,6 @@
"months": "月",
"move_down": "下移",
"move_up": "上移",
"multiple_languages": "多种 语言",
"my_product": "我的产品",
"name": "名称",
"new": "新建",
@@ -313,6 +317,7 @@
"no_result_found": "没有 结果",
"no_results": "没有 结果",
"no_surveys_found": "未找到 调查",
"no_text_found": "未找到文本",
"none_of_the_above": "以上 都 不 是",
"not_authenticated": "您 未 认证 以 执行 该 操作。",
"not_authorized": "未授权",
@@ -336,6 +341,7 @@
"organization_settings": "组织 设置",
"other": "其他",
"other_filters": "其他筛选条件",
"other_placeholder": "其他占位符",
"others": "其他",
"overlay_color": "覆盖层颜色",
"overview": "概览",
@@ -381,6 +387,7 @@
"responses": "反馈",
"restart": "重新启动",
"role": "角色",
"row_n": "行 {{n}}",
"saas": "SaaS",
"sales": "销售",
"save": "保存",
@@ -419,6 +426,7 @@
"storage_not_configured": "文件存储 未设置,上传 可能 失败",
"string": "文本",
"styling": "样式",
"subheader": "副标题",
"submit": "提交",
"summary": "概要",
"survey": "调查",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "将 数据 发送到 您的 Notion 数据库",
"please_select_a_survey_error": "请选择 一个 调查",
"reconnect_button": "重新连接",
"reconnect_button_description": "你的集成连接已过期。请重新连接以继续同步响应。你现有的链接和数据将被保留。",
"reconnect_button_tooltip": "重新连接集成以刷新你的访问权限。你现有的链接和数据将被保留。",
"select_at_least_one_question_error": "请选择至少 一个问题",
"slack": {
"already_connected_another_survey": "您 已 经 将 另 一 个 调 查 连 接 到 此 频 道 。",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "使用 更高 级 方案 解锁 双 重 因素 验证",
"update_personal_info": "更新你的个人信息",
"warning_cannot_delete_account": "您 是 该 组织 的 唯一 拥有者 。 请 先 将 所有权 转移 给 其他 成员 。",
"warning_cannot_undo": "此 无法 撤销。",
"wrong_password": "密码错误"
"warning_cannot_undo": "此 无法 撤销。"
},
"teams": {
"add_members_description": "将 成员 添加到 团队 ,并 确定 他们 的 角色",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "调查成功复制!",
"delete_survey_and_responses_warning": "您 确定 要 删除 此 调查 及 所有 回复 吗?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. 选择 此 调查 的 默认 语言:",
"2_activate_translation_for_specific_languages": "2. 激活 特定 语言 的 翻译:",
"activate_translations": "激活翻译",
"add": "添加 +",
"add_a_delay_or_auto_close_the_survey": "添加 延迟 或 自动 关闭 调查",
"add_a_four_digit_pin": "添加 一个 四 位 数 PIN",
@@ -1363,6 +1366,8 @@
"assign": "指派 =",
"audience": "受众",
"auto_close_on_inactivity": "自动关闭 在 无活动时",
"auto_progress_rating_and_nps": "自动推进评分和 NPS 问题",
"auto_progress_rating_and_nps_description": "当受访者在评分或 NPS 问题上选择答案时自动前进。这仅适用于单问题区块。必填问题会隐藏\"下一步\"按钮;可选问题仍会显示该按钮以便跳过。",
"auto_save_disabled": "自动保存已禁用",
"auto_save_disabled_tooltip": "您的调查仅在草稿状态时自动保存。这确保公开的调查不会被意外更新。",
"auto_save_on": "自动保存已启用",
@@ -1408,6 +1413,7 @@
"caution_text": "更改 会导致 不一致",
"change_anyway": "还是更改",
"change_background": "更改 背景",
"change_default": "更改默认语言",
"change_question_type": "更改 问题类型",
"change_survey_type": "更改 调查 类型 会影 响 现有 访问",
"change_the_background_to_a_color_image_or_animation": "将 背景 更改为 颜色 、 图像 或 动画。",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "选择 调查 运行 的 位置 。",
"city": "城市",
"close_survey_on_response_limit": "在响应限制时关闭 调查",
"code": "代码",
"color": "颜色",
"column_used_in_logic_error": "\"这个 列 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"",
"columns": "列",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "自定义调查 logo",
"darken_or_lighten_background_of_your_choice": "根据 您 的 选择 暗化 或 亮化 背景。",
"days_before_showing_this_survey_again": "距离上次显示问卷后需间隔不少于指定天数,才能再次显示此问卷。",
"default_language": "默认语言",
"delete_anyways": "仍然删除",
"delete_block": "删除区块",
"delete_choice": "删除 选择",
@@ -1463,7 +1471,6 @@
"duplicate_question": "复制问题",
"edit_link": "编辑 链接",
"edit_recall": "编辑 调用",
"edit_translations": "编辑 {lang} 翻译",
"element_not_found": "未找到问题",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "允许受访者在调查过程中随时切换语言。需要至少启用两种语言。",
"enable_recaptcha_to_protect_your_survey_from_spam": "垃圾 邮件 保护 使用 reCAPTCHA v3 来 过滤 掉 垃圾 响应 。",
@@ -1599,11 +1606,13 @@
"long_answer": "长答案",
"long_answer_toggle_description": "允许受访者填写较长的多行答案。",
"lower_label": "下限标签",
"manage_languages": "管理 语言",
"manage_languages": "管理语言",
"manage_translations": "管理翻译",
"matrix_all_fields": "所有字段",
"matrix_rows": "行",
"max_file_size": "最大文件大小",
"max_file_size_limit_is": "最大文件大小限制为",
"missing_first": "缺失优先",
"move_question_to_block": "将问题移动到区块",
"multiply": "乘 *",
"needed_for_self_hosted_cal_com_instance": "需要用于 自建 Cal.com 实例",
@@ -1611,7 +1620,7 @@
"next_button_label": "\"下一步\" 按钮标签",
"no_hidden_fields_yet_add_first_one_below": "还没有隐藏字段。 在下面添加第一个。",
"no_images_found_for": "未找到与 \"{query}\" 相关的图片",
"no_languages_found_add_first_one_to_get_started": "没有找到语言。添加一个以开始。",
"no_languages_found_add_first_one_to_get_started": "此工作区中未找到调查问卷语言。添加一个以开始使用。",
"no_option_found": "找不到选择",
"no_recall_items_found": "未找到召回项",
"no_variables_yet_add_first_one_below": "还没有变量。 在下面添加第一个。",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "请输入有效的 URL(例如, https://example.com ",
"please_set_a_survey_trigger": "请 设置 一个 调查 触发",
"please_specify": "请 指定",
"present_your_survey_in_multiple_languages": "以多种语言展示您的调查问卷",
"prevent_double_submission": "防止 重复 提交",
"prevent_double_submission_description": "只允许每个 email 地址提供 1 个回复",
"progress_saved": "进度已保存",
@@ -1729,6 +1739,7 @@
"seven_points": "7 分",
"show_block_settings": "显示区块设置",
"show_button": "显示 按钮",
"show_in_order": "按顺序显示",
"show_language_switch": "显示 语言 切换",
"show_multiple_times": "显示有限次数",
"show_only_once": "仅 显示 一次",
@@ -1760,7 +1771,6 @@
"survey_preview": "问卷预览 👀",
"survey_styling": "表单 样式",
"survey_trigger": "调查 触发",
"switch_multi_language_on_to_get_started": "开启多语言以开始使用 👉",
"target_block_not_found": "未找到目标区块",
"targeted": "定位",
"ten_points": "10 分",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "仅显示一次,即使他们未回应。",
"then": "然后",
"this_action_will_remove_all_the_translations_from_this_survey": "此操作将删除该调查中的所有翻译。",
"this_will_remove_the_language_and_all_its_translations": "这将从此调查问卷中删除该语言及其所有翻译。此操作无法撤销。",
"three_points": "3 分",
"times": "次数",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "为了 保持 所有 调查 的 放置 一致,您 可以",
"translated": "已翻译",
"trigger_survey_when_one_of_the_actions_is_fired": "当 其中 一个 动作 被 触发 时 启动 调查…",
"try_lollipop_or_mountain": "尝试 'lollipop' 或 'mountain' ...",
"type_field_id": "类型 字段 ID",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "仅允许 拥有 有效 电子邮件 的 人 回应。",
"visibility_and_recontact": "可见性与重新联系",
"visibility_and_recontact_description": "控制此调查何时可以显示以及可以重新显示的频率。",
"visible": "可见",
"wait": "等待",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "触发后等待几秒再显示问卷",
"waiting_time_across_surveys": "冷却期(跨问卷)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "语言或语言 ID 重复",
"edit_languages": "编辑语言",
"identifier": "标识符(ISO",
"incomplete_translations": "翻译不完整",
"language": "语言",
"language_deleted_successfully": "语言删除成功",
"languages_updated_successfully": "语言更新成功",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "请选择一种语言",
"remove_language": "移除语言",
"remove_language_from_surveys_to_remove_it_from_workspace": "请先从这些调查中移除该语言,然后才能从工作区中删除。",
"search_items": "搜索项目",
"translate": "翻译"
"search_items": "搜索项目"
},
"look": {
"add_background_color": "添加背景色",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "设置进度条未填充部分的颜色。",
"advanced_styling_field_track_height": "轨道高度",
"advanced_styling_field_track_height_description": "控制进度条的粗细。",
"advanced_styling_field_upper_label_color": "标题标签颜色",
"advanced_styling_field_upper_label_color_description": "设置输入框上方小标签的颜色。",
"advanced_styling_field_upper_label_size": "标题标签字体大小",
"advanced_styling_field_upper_label_size_description": "调整输入框上方标签的大小。",
"advanced_styling_field_upper_label_weight": "标题标签字体粗细",
"advanced_styling_field_upper_label_weight_description": "设置标签文字的粗细。",
"advanced_styling_field_upper_label_color": "标签颜色",
"advanced_styling_field_upper_label_color_description": "输入框上方小标签和刻度标签着色。",
"advanced_styling_field_upper_label_size": "标签字体大小",
"advanced_styling_field_upper_label_size_description": "调整输入框上方的小标签和刻度标签的大小。",
"advanced_styling_field_upper_label_weight": "标签字体粗细",
"advanced_styling_field_upper_label_weight_description": "使标签更细或更粗。",
"advanced_styling_section_buttons": "按钮",
"advanced_styling_section_headlines": "标题和描述",
"advanced_styling_section_inputs": "输入项",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "帮助 我们 更好 地 了解 你 :",
"improve_trial_conversion_question_2_button_label": "下一步",
"improve_trial_conversion_question_2_headline": "很抱歉 听到。使用 $[projectName] 时 最大的 问题 是 什么?",
"improve_trial_conversion_question_3_button_label": "下一步",
"improve_trial_conversion_question_3_headline": "您期望 $[projectName] 做什么?",
"improve_trial_conversion_question_4_button_label": "获取 20% 折扣",
"improve_trial_conversion_question_4_headline": "很抱歉 听到!首年 可 获 20% 优惠。",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>我们 很 乐意 为 您 提供 年 度 计划 20% 的 折扣。</span></p>",
"improve_trial_conversion_question_5_button_label": "下一步",
"improve_trial_conversion_question_5_headline": "你 想 实现 什么?",
"improve_trial_conversion_question_5_subheader": "请选择以下选项之一:",
"improve_trial_conversion_question_5_subheader": "请在下方描述:",
"improve_trial_conversion_question_6_headline": "你 现在 如何 解决 你的 问题?",
"improve_trial_conversion_question_6_subheader": "请 列举 替代 方案:",
"integration_setup_survey_description": "评估用户 添加 集成 到 产品 的 便捷程度 。 找到 盲点 。",
+34 -21
View File
@@ -152,6 +152,7 @@
"centered_modal": "置中彈窗",
"change_organization": "變更組織",
"change_workspace": "變更工作區",
"choice_n": "選項 {{n}}",
"choices": "選項",
"choose_environment": "選擇環境",
"choose_organization": "選擇 組織",
@@ -165,6 +166,7 @@
"close": "關閉",
"code": "程式碼",
"collapse_rows": "摺疊列",
"column_n": "欄 {{n}}",
"completed": "已完成",
"configuration": "組態",
"confirm": "確認",
@@ -236,6 +238,7 @@
"failed_to_copy_to_clipboard": "無法複製到剪貼簿",
"failed_to_load_organizations": "無法載入組織",
"failed_to_load_workspaces": "載入工作區失敗",
"field_placeholder": "{{field}} 預設文字",
"filter": "篩選",
"finish": "完成",
"first_name": "名字",
@@ -247,10 +250,12 @@
"generate": "產生",
"go_back": "返回",
"go_to_dashboard": "前往儀表板",
"headline": "標題",
"hidden": "隱藏",
"hidden_field": "隱藏欄位",
"hidden_fields": "隱藏欄位",
"hide_column": "隱藏欄位",
"html": "HTML",
"id": "ID",
"image": "圖片",
"images": "圖片",
@@ -298,7 +303,6 @@
"months": "月",
"move_down": "下移",
"move_up": "上移",
"multiple_languages": "多種語言",
"my_product": "我的產品",
"name": "名稱",
"new": "新增",
@@ -313,6 +317,7 @@
"no_result_found": "找不到結果",
"no_results": "沒有結果",
"no_surveys_found": "找不到問卷。",
"no_text_found": "找不到文字",
"none_of_the_above": "以上皆非",
"not_authenticated": "您未經授權執行此操作。",
"not_authorized": "未授權",
@@ -336,6 +341,7 @@
"organization_settings": "組織設定",
"other": "其他",
"other_filters": "其他篩選條件",
"other_placeholder": "其他預設文字",
"others": "其他",
"overlay_color": "覆蓋層顏色",
"overview": "概覽",
@@ -381,6 +387,7 @@
"responses": "回應",
"restart": "重新開始",
"role": "角色",
"row_n": "列 {{n}}",
"saas": "SaaS",
"sales": "銷售",
"save": "儲存",
@@ -419,6 +426,7 @@
"storage_not_configured": "檔案儲存未設定,上傳可能會失敗",
"string": "文字",
"styling": "樣式設定",
"subheader": "副標題",
"submit": "提交",
"summary": "摘要",
"survey": "問卷",
@@ -828,9 +836,6 @@
},
"notion_integration_description": "將資料傳送至您的 Notion 資料庫",
"please_select_a_survey_error": "請選取問卷",
"reconnect_button": "重新連接",
"reconnect_button_description": "您的整合連線已過期。請重新連接以繼續同步回應。您現有的連結和資料將會保留。",
"reconnect_button_tooltip": "重新連接整合以更新您的存取權限。您現有的連結和資料將會保留。",
"select_at_least_one_question_error": "請選取至少一個問題",
"slack": {
"already_connected_another_survey": "您已將另一個問卷連線到此頻道。",
@@ -1251,8 +1256,7 @@
"unlock_two_factor_authentication": "使用更高等級的方案解鎖雙重驗證",
"update_personal_info": "更新您的個人資訊",
"warning_cannot_delete_account": "您是此組織的唯一擁有者。請先將所有權轉讓給其他成員。",
"warning_cannot_undo": "此操作無法復原",
"wrong_password": "密碼錯誤"
"warning_cannot_undo": "此操作無法復原"
},
"teams": {
"add_members_description": "將成員新增至團隊並確定其角色。",
@@ -1313,8 +1317,7 @@
"copy_survey_success": "問卷已成功複製!",
"delete_survey_and_responses_warning": "您確定要刪除此問卷及其所有回應嗎?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. 選擇此問卷的預設語言:",
"2_activate_translation_for_specific_languages": "2. 啟用特定語言的翻譯:",
"activate_translations": "啟用翻譯",
"add": "新增 +",
"add_a_delay_or_auto_close_the_survey": "新增延遲或自動關閉問卷",
"add_a_four_digit_pin": "新增四位數 PIN 碼",
@@ -1363,6 +1366,8 @@
"assign": "等於 =",
"audience": "受眾",
"auto_close_on_inactivity": "非活動時自動關閉",
"auto_progress_rating_and_nps": "自動前進評分與 NPS 問題",
"auto_progress_rating_and_nps_description": "當受訪者在評分或 NPS 問題中選擇答案時自動前進。此設定僅適用於單一問題區塊。必填問題會隱藏「下一步」按鈕;選填問題仍會顯示該按鈕以便跳過。",
"auto_save_disabled": "自動儲存已停用",
"auto_save_disabled_tooltip": "您的問卷僅在草稿狀態時自動儲存。這確保公開的問卷不會被意外更新。",
"auto_save_on": "自動儲存已啟用",
@@ -1408,6 +1413,7 @@
"caution_text": "變更會導致不一致",
"change_anyway": "仍然變更",
"change_background": "變更背景",
"change_default": "變更預設",
"change_question_type": "變更問題類型",
"change_survey_type": "切換問卷類型會影響現有訪問",
"change_the_background_to_a_color_image_or_animation": "將背景變更為顏色、圖片或動畫。",
@@ -1420,6 +1426,7 @@
"choose_where_to_run_the_survey": "選擇在哪裡執行問卷。",
"city": "城市",
"close_survey_on_response_limit": "在回應次數上限關閉問卷",
"code": "代碼",
"color": "顏色",
"column_used_in_logic_error": "此 column 用於問題 '{'questionIndex'}' 的邏輯中。請先從邏輯中移除。",
"columns": "欄位",
@@ -1444,6 +1451,7 @@
"customize_survey_logo": "自訂問卷標誌",
"darken_or_lighten_background_of_your_choice": "變暗或變亮您選擇的背景。",
"days_before_showing_this_survey_again": "距離上次顯示問卷後,需間隔指定天數才能再次顯示此問卷。",
"default_language": "預設語言",
"delete_anyways": "仍要刪除",
"delete_block": "刪除區塊",
"delete_choice": "刪除選項",
@@ -1463,7 +1471,6 @@
"duplicate_question": "複製問題",
"edit_link": "編輯 連結",
"edit_recall": "編輯回憶",
"edit_translations": "編輯 '{'language'}' 翻譯",
"element_not_found": "找不到問題",
"enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey": "允許受訪者隨時切換語言。需要至少啟用兩種語言。",
"enable_recaptcha_to_protect_your_survey_from_spam": "垃圾郵件保護使用 reCAPTCHA v3 過濾垃圾回應。",
@@ -1600,10 +1607,12 @@
"long_answer_toggle_description": "允許受訪者撰寫較長的多行回答。",
"lower_label": "下標籤",
"manage_languages": "管理語言",
"manage_translations": "管理翻譯",
"matrix_all_fields": "所有欄位",
"matrix_rows": "列",
"max_file_size": "最大檔案大小",
"max_file_size_limit_is": "最大檔案大小限制為",
"missing_first": "缺少的優先",
"move_question_to_block": "將問題移至區塊",
"multiply": "乘 *",
"needed_for_self_hosted_cal_com_instance": "自行託管 Cal.com 執行個體時需要",
@@ -1611,7 +1620,7 @@
"next_button_label": "「下一步」按鈕標籤",
"no_hidden_fields_yet_add_first_one_below": "尚無隱藏欄位。在下方新增第一個隱藏欄位。",
"no_images_found_for": "找不到「'{'query'}'」的圖片",
"no_languages_found_add_first_one_to_get_started": "找不到語言。新增一個語言以開始使用。",
"no_languages_found_add_first_one_to_get_started": "此工作區中找不到問卷語言。新增一個語言以開始使用。",
"no_option_found": "找不到選項",
"no_recall_items_found": "未找到回溯項目",
"no_variables_yet_add_first_one_below": "尚無變數。在下方新增第一個變數。",
@@ -1638,6 +1647,7 @@
"please_enter_a_valid_url": "請輸入有效的 URL(例如:https://example.com",
"please_set_a_survey_trigger": "請設定問卷觸發器",
"please_specify": "請指定",
"present_your_survey_in_multiple_languages": "以多種語言呈現你的問卷",
"prevent_double_submission": "防止重複提交",
"prevent_double_submission_description": "每個電子郵件地址僅允許 1 個回應",
"progress_saved": "進度已儲存",
@@ -1729,6 +1739,7 @@
"seven_points": "7 分",
"show_block_settings": "顯示區塊設定",
"show_button": "顯示按鈕",
"show_in_order": "依序顯示",
"show_language_switch": "顯示語言切換",
"show_multiple_times": "顯示有限次數",
"show_only_once": "僅顯示一次",
@@ -1760,7 +1771,6 @@
"survey_preview": "問卷預覽 👀",
"survey_styling": "表單樣式設定",
"survey_trigger": "問卷觸發器",
"switch_multi_language_on_to_get_started": "請開啟多語言功能以開始使用 👉",
"target_block_not_found": "找不到目標區塊",
"targeted": "目標",
"ten_points": "10 分",
@@ -1768,9 +1778,11 @@
"the_survey_will_be_shown_once_even_if_person_doesnt_respond": "僅顯示一次,即使他們未回應。",
"then": "然後",
"this_action_will_remove_all_the_translations_from_this_survey": "此操作將從此問卷中移除所有翻譯。",
"this_will_remove_the_language_and_all_its_translations": "這將會從此問卷中移除該語言及其所有翻譯。此操作無法復原。",
"three_points": "3 分",
"times": "次",
"to_keep_the_placement_over_all_surveys_consistent_you_can": "若要保持所有問卷的位置一致,您可以",
"translated": "已翻譯",
"trigger_survey_when_one_of_the_actions_is_fired": "當觸發其中一個操作時,觸發問卷...",
"try_lollipop_or_mountain": "嘗試「棒棒糖」或「山峰」...",
"type_field_id": "輸入欄位 ID",
@@ -1845,6 +1857,7 @@
"verify_email_before_submission_description": "僅允許擁有真實電子郵件的人員回應。",
"visibility_and_recontact": "可見性與重新聯絡",
"visibility_and_recontact_description": "控制此問卷何時可以顯示以及可以重新顯示的頻率。",
"visible": "可見",
"wait": "等待",
"wait_a_few_seconds_after_the_trigger_before_showing_the_survey": "在觸發後等待幾秒鐘再顯示問卷",
"waiting_time_across_surveys": "冷卻期(跨問卷)",
@@ -2229,7 +2242,6 @@
"duplicate_language_or_language_id": "語言或語言 ID 重複",
"edit_languages": "編輯語言",
"identifier": "識別碼(ISO",
"incomplete_translations": "翻譯不完整",
"language": "語言",
"language_deleted_successfully": "語言已成功刪除",
"languages_updated_successfully": "語言已成功更新",
@@ -2239,8 +2251,7 @@
"please_select_a_language": "請選擇一種語言",
"remove_language": "移除語言",
"remove_language_from_surveys_to_remove_it_from_workspace": "請先從這些問卷中移除此語言,才能從工作區移除。",
"search_items": "搜尋項目",
"translate": "翻譯"
"search_items": "搜尋項目"
},
"look": {
"add_background_color": "新增背景顏色",
@@ -2300,12 +2311,12 @@
"advanced_styling_field_track_bg_description": "設定進度條未填滿部分的顏色。",
"advanced_styling_field_track_height": "軌道高度",
"advanced_styling_field_track_height_description": "調整進度條的厚度。",
"advanced_styling_field_upper_label_color": "標題標籤顏色",
"advanced_styling_field_upper_label_color_description": "設定輸入框上方小標籤顏色。",
"advanced_styling_field_upper_label_size": "標題標籤字體大小",
"advanced_styling_field_upper_label_size_description": "調整輸入上方小標籤大小。",
"advanced_styling_field_upper_label_weight": "標題標籤字體粗細",
"advanced_styling_field_upper_label_weight_description": "讓標籤字體變細或粗。",
"advanced_styling_field_upper_label_color": "標籤顏色",
"advanced_styling_field_upper_label_color_description": "為輸入欄位上方小標籤和刻度標籤設定顏色。",
"advanced_styling_field_upper_label_size": "標籤字體大小",
"advanced_styling_field_upper_label_size_description": "調整輸入欄位上方小標籤和刻度標籤的字體大小。",
"advanced_styling_field_upper_label_weight": "標籤字體粗細",
"advanced_styling_field_upper_label_weight_description": "讓標籤變得更細或粗。",
"advanced_styling_section_buttons": "按鈕",
"advanced_styling_section_headlines": "標題與說明",
"advanced_styling_section_inputs": "輸入欄位",
@@ -2950,12 +2961,14 @@
"improve_trial_conversion_question_1_subheader": "協助我們更瞭解您:",
"improve_trial_conversion_question_2_button_label": "下一步",
"improve_trial_conversion_question_2_headline": "很抱歉聽到。使用 {projectName} 時,最大的問題是什麼?",
"improve_trial_conversion_question_3_button_label": "下一步",
"improve_trial_conversion_question_3_headline": "您期望 $[projectName] 做什麼?",
"improve_trial_conversion_question_4_button_label": "獲得 20% 折扣",
"improve_trial_conversion_question_4_headline": "很抱歉聽到!在第一年獲得 20% 的折扣。",
"improve_trial_conversion_question_4_html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>我們很樂意為您提供年度方案的 20% 折扣。</span></p>",
"improve_trial_conversion_question_5_button_label": "下一步",
"improve_trial_conversion_question_5_headline": "您想要達成什麼?",
"improve_trial_conversion_question_5_subheader": "請選取以下其中一個選項",
"improve_trial_conversion_question_5_subheader": "請在下方說明",
"improve_trial_conversion_question_6_headline": "您現在如何解決您的問題?",
"improve_trial_conversion_question_6_subheader": "請列出替代解決方案:",
"integration_setup_survey_description": "評估使用者將整合新增至您的產品的容易程度。找出盲點。",
@@ -1,89 +1,24 @@
"use server";
import { z } from "zod";
import { logger } from "@formbricks/logger";
import { AuthorizationError, InvalidInputError, OperationNotAllowedError } from "@formbricks/types/errors";
import { OperationNotAllowedError } from "@formbricks/types/errors";
import { getOrganizationsWhereUserIsSingleOwner } from "@/lib/organization/service";
import { verifyUserPassword } from "@/lib/user/password";
import { deleteUser, getUser } from "@/lib/user/service";
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { applyRateLimit } from "@/modules/core/rate-limit/helpers";
import { rateLimitConfigs } from "@/modules/core/rate-limit/rate-limit-configs";
import { withAuditLogging } from "@/modules/ee/audit-logs/lib/handler";
import { getIsMultiOrgEnabled } from "@/modules/ee/license-check/lib/utils";
import { DELETE_ACCOUNT_WRONG_PASSWORD_ERROR } from "./constants";
const DELETE_USER_CONFIRMATION_REQUIRED_ERROR =
"Password and email confirmation are required to delete your account.";
const ZDeleteUserConfirmation = z
.object({
confirmationEmail: z.string().trim().min(1).max(255),
password: z.string().max(128).optional(),
})
.strict();
const parseDeleteUserConfirmation = (input: unknown) => {
const parsedInput = ZDeleteUserConfirmation.safeParse(input);
if (!parsedInput.success) {
throw new InvalidInputError(DELETE_USER_CONFIRMATION_REQUIRED_ERROR);
}
return parsedInput.data;
};
const getPasswordOrThrow = (password?: string) => {
if (!password) {
throw new InvalidInputError(DELETE_USER_CONFIRMATION_REQUIRED_ERROR);
}
return password;
};
const logAccountDeletionError = (userId: string, error: unknown) => {
logger.error({ error, userId }, "Account deletion failed");
};
export const deleteUserAction = authenticatedActionClient.inputSchema(z.unknown()).action(
withAuditLogging("deleted", "user", async ({ ctx, parsedInput }) => {
ctx.auditLoggingCtx.userId = ctx.user.id;
try {
await applyRateLimit(rateLimitConfigs.actions.accountDeletion, ctx.user.id);
const isPasswordBackedAccount = ctx.user.identityProvider === "email";
const { confirmationEmail, password } = parseDeleteUserConfirmation(parsedInput);
if (confirmationEmail.toLowerCase() !== ctx.user.email.toLowerCase()) {
throw new AuthorizationError("Email confirmation does not match");
}
if (isPasswordBackedAccount) {
const isCorrectPassword = await verifyUserPassword(ctx.user.id, getPasswordOrThrow(password));
if (!isCorrectPassword) {
throw new AuthorizationError(DELETE_ACCOUNT_WRONG_PASSWORD_ERROR);
}
}
const isMultiOrgEnabled = await getIsMultiOrgEnabled();
if (!isMultiOrgEnabled) {
const organizationsWithSingleOwner = await getOrganizationsWhereUserIsSingleOwner(ctx.user.id);
if (organizationsWithSingleOwner.length > 0) {
throw new OperationNotAllowedError(
"You are the only owner of this organization. Please transfer ownership to another member first."
);
}
}
ctx.auditLoggingCtx.oldObject = await getUser(ctx.user.id);
await deleteUser(ctx.user.id);
return { success: true };
} catch (error) {
logAccountDeletionError(ctx.user.id, error);
throw error;
export const deleteUserAction = authenticatedActionClient.action(
withAuditLogging("deleted", "user", async ({ ctx }) => {
const isMultiOrgEnabled = await getIsMultiOrgEnabled();
const organizationsWithSingleOwner = await getOrganizationsWhereUserIsSingleOwner(ctx.user.id);
if (!isMultiOrgEnabled && organizationsWithSingleOwner.length > 0) {
throw new OperationNotAllowedError(
"You are the only owner of this organization. Please transfer ownership to another member first."
);
}
ctx.auditLoggingCtx.userId = ctx.user.id;
ctx.auditLoggingCtx.oldObject = await getUser(ctx.user.id);
const result = await deleteUser(ctx.user.id);
return result;
})
);
@@ -1 +0,0 @@
export const DELETE_ACCOUNT_WRONG_PASSWORD_ERROR = "Wrong password";
@@ -3,16 +3,12 @@
import { Dispatch, SetStateAction, useState } from "react";
import toast from "react-hot-toast";
import { Trans, useTranslation } from "react-i18next";
import { logger } from "@formbricks/logger";
import { TOrganization } from "@formbricks/types/organizations";
import { TUser } from "@formbricks/types/user";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { useSignOut } from "@/modules/auth/hooks/use-sign-out";
import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
import { Input } from "@/modules/ui/components/input";
import { PasswordInput } from "@/modules/ui/components/password-input";
import { deleteUserAction } from "./actions";
import { DELETE_ACCOUNT_WRONG_PASSWORD_ERROR } from "./constants";
interface DeleteAccountModalProps {
open: boolean;
@@ -32,57 +28,15 @@ export const DeleteAccountModal = ({
const { t } = useTranslation();
const [deleting, setDeleting] = useState(false);
const [inputValue, setInputValue] = useState("");
const [password, setPassword] = useState("");
const { signOut: signOutWithAudit } = useSignOut({ id: user.id, email: user.email });
const isPasswordBackedAccount = user.identityProvider === "email";
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
};
const handleOpenChange = (nextOpen: boolean) => {
if (!nextOpen) {
setInputValue("");
setPassword("");
}
setOpen(nextOpen);
};
const hasValidEmailConfirmation = inputValue.trim().toLowerCase() === user.email.toLowerCase();
const hasValidConfirmation = hasValidEmailConfirmation && (!isPasswordBackedAccount || password.length > 0);
const isDeleteDisabled = !hasValidConfirmation;
const deleteAccount = async () => {
try {
if (!hasValidConfirmation) {
return;
}
setDeleting(true);
const result = await deleteUserAction(
isPasswordBackedAccount
? {
confirmationEmail: inputValue,
password,
}
: {
confirmationEmail: inputValue,
}
);
if (!result?.data?.success) {
const fallbackErrorMessage = t("common.something_went_wrong_please_try_again");
let errorMessage = fallbackErrorMessage;
if (result?.serverError === DELETE_ACCOUNT_WRONG_PASSWORD_ERROR) {
errorMessage = t("environments.settings.profile.wrong_password");
} else if (result) {
errorMessage = getFormattedErrorMessage(result);
}
logger.error({ errorMessage }, "Account deletion action failed");
toast.error(errorMessage || fallbackErrorMessage);
return;
}
await deleteUserAction();
// Sign out with account deletion reason (no automatic redirect)
await signOutWithAudit({
@@ -98,22 +52,22 @@ export const DeleteAccountModal = ({
window.location.replace("/auth/login");
}
} catch (error) {
logger.error({ error }, "Account deletion failed");
toast.error(t("common.something_went_wrong_please_try_again"));
toast.error("Something went wrong");
} finally {
setDeleting(false);
setOpen(false);
}
};
return (
<DeleteDialog
open={open}
setOpen={handleOpenChange}
setOpen={setOpen}
deleteWhat={t("common.account")}
onDelete={() => deleteAccount()}
text={t("environments.settings.profile.account_deletion_consequences_warning")}
isDeleting={deleting}
disabled={isDeleteDisabled}>
disabled={inputValue !== user.email}>
<div className="py-5">
<ul className="list-disc pb-6 pl-6">
<li>
@@ -156,29 +110,11 @@ export const DeleteAccountModal = ({
value={inputValue}
onChange={handleInputChange}
placeholder={user.email}
className="mt-2"
className="mt-5"
type="text"
id="deleteAccountConfirmation"
name="deleteAccountConfirmation"
/>
{isPasswordBackedAccount && (
<>
<label htmlFor="deleteAccountPassword" className="mt-4 block">
{t("common.password")}
</label>
<PasswordInput
data-testid="deleteAccountPassword"
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete="current-password"
className="pr-10"
containerClassName="mt-2"
id="deleteAccountPassword"
name="deleteAccountPassword"
required
/>
</>
)}
</form>
</div>
</DeleteDialog>
@@ -13,10 +13,6 @@ const mockUser = {
createdAt: new Date(),
updatedAt: new Date(),
isActive: true,
password: "$2b$12$hashedPassword",
twoFactorSecret: "encrypted-2fa-secret",
backupCodes: "encrypted-backup-codes",
identityProviderAccountId: "provider-account-id",
role: "admin",
memberships: [{ organizationId: "org456", role: "admin" }],
teamUsers: [{ team: { name: "Test Team", id: "team123", projectTeams: [{ projectId: "proj789" }] } }],
@@ -64,10 +60,6 @@ describe("Users Lib", () => {
updatedAt: expect.any(Date),
},
]);
expect(result.data.data[0]).not.toHaveProperty("password");
expect(result.data.data[0]).not.toHaveProperty("twoFactorSecret");
expect(result.data.data[0]).not.toHaveProperty("backupCodes");
expect(result.data.data[0]).not.toHaveProperty("identityProviderAccountId");
}
});
@@ -92,10 +84,6 @@ describe("Users Lib", () => {
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.data.id).toBe(mockUser.id);
expect(result.data).not.toHaveProperty("password");
expect(result.data).not.toHaveProperty("twoFactorSecret");
expect(result.data).not.toHaveProperty("backupCodes");
expect(result.data).not.toHaveProperty("identityProviderAccountId");
}
});
@@ -110,14 +98,11 @@ describe("Users Lib", () => {
test("returns conflict error if user with email already exists", async () => {
(prisma.user.create as any).mockRejectedValueOnce(
new Prisma.PrismaClientKnownRequestError(
"Unique constraint failed on the fields: (`email`)",
{
code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "1.0.0",
meta: { target: ["email"] },
}
)
new Prisma.PrismaClientKnownRequestError("Unique constraint failed on the fields: (`email`)", {
code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "1.0.0",
meta: { target: ["email"] },
})
);
const result = await createUser(
{ name: "Duplicate", email: "test@example.com", role: "member" },
@@ -163,10 +148,6 @@ describe("Users Lib", () => {
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.data.name).toBe("Updated User");
expect(result.data).not.toHaveProperty("password");
expect(result.data).not.toHaveProperty("twoFactorSecret");
expect(result.data).not.toHaveProperty("backupCodes");
expect(result.data).not.toHaveProperty("identityProviderAccountId");
}
});
@@ -75,7 +75,6 @@ describe("rateLimitConfigs", () => {
const actionConfigs = Object.keys(rateLimitConfigs.actions);
expect(actionConfigs).toEqual([
"emailUpdate",
"accountDeletion",
"surveyFollowUp",
"sendLinkSurveyEmail",
"licenseRecheck",
@@ -140,7 +139,6 @@ describe("rateLimitConfigs", () => {
{ config: rateLimitConfigs.api.v3, identifier: "api-v3-key" },
{ config: rateLimitConfigs.api.client, identifier: "client-api-key" },
{ config: rateLimitConfigs.actions.emailUpdate, identifier: "user-profile" },
{ config: rateLimitConfigs.actions.accountDeletion, identifier: "user-account-delete" },
{ config: rateLimitConfigs.storage.upload, identifier: "storage-upload" },
{ config: rateLimitConfigs.storage.delete, identifier: "storage-delete" },
];
@@ -18,7 +18,6 @@ export const rateLimitConfigs = {
// Server actions - varies by action type
actions: {
emailUpdate: { interval: 3600, allowedPerInterval: 3, namespace: "action:email" }, // 3 per hour
accountDeletion: { interval: 3600, allowedPerInterval: 5, namespace: "action:account-delete" }, // 5 per hour
surveyFollowUp: { interval: 3600, allowedPerInterval: 50, namespace: "action:followup" }, // 50 per hour
sendLinkSurveyEmail: {
interval: 3600,
@@ -46,9 +46,9 @@ export const OpenIdButton = ({
type="button"
onClick={handleLogin}
variant="secondary"
className="relative w-full justify-center">
{text ? text : t("auth.continue_with_openid")}
{lastUsed && <span className="absolute right-3 text-xs opacity-50">{t("auth.last_used")}</span>}
className="w-full items-center justify-center gap-2 px-2">
<span className="truncate">{text || t("auth.continue_with_openid")}</span>
{lastUsed && <span className="shrink-0 text-xs opacity-50">{t("auth.last_used")}</span>}
</Button>
);
};
@@ -51,7 +51,7 @@ describe("SSO Providers", () => {
expect((samlProvider as any).authorization?.url).toBe("https://test-app.com/api/auth/saml/authorize");
expect(samlProvider.token).toBe("https://test-app.com/api/auth/saml/token");
expect(samlProvider.userinfo).toBe("https://test-app.com/api/auth/saml/userinfo");
expect((googleProvider as any).options?.allowDangerousEmailAccountLinking).toBe(true);
expect(samlProvider.allowDangerousEmailAccountLinking).toBe(true);
expect(googleProvider.allowDangerousEmailAccountLinking).toBeUndefined();
expect(samlProvider.allowDangerousEmailAccountLinking).toBeUndefined();
});
});
-2
View File
@@ -26,7 +26,6 @@ export const getSSOProviders = () => [
GoogleProvider({
clientId: GOOGLE_CLIENT_ID || "",
clientSecret: GOOGLE_CLIENT_SECRET || "",
allowDangerousEmailAccountLinking: true,
}),
AzureAD({
clientId: AZUREAD_CLIENT_ID || "",
@@ -81,7 +80,6 @@ export const getSSOProviders = () => [
clientId: "dummy",
clientSecret: "dummy",
},
allowDangerousEmailAccountLinking: true,
},
];
+5 -4
View File
@@ -34,6 +34,8 @@ const LINKED_SSO_LOOKUP_SELECT = {
identityProviderAccountId: true,
} as const;
const OAUTH_ACCOUNT_NOT_LINKED_ERROR = "OAuthAccountNotLinked";
const syncSsoAccount = async (userId: string, account: Account, tx?: Prisma.TransactionClient) => {
await upsertAccount(
{
@@ -217,7 +219,7 @@ export const handleSsoCallback = async ({
}
// There is no existing linked account for this identity provider / account id
// check if a user account with this email already exists and auto-link it
// check if a user account with this email already exists and fail closed if so
contextLogger.debug({ lookupType: "email" }, "No linked SSO account found, checking for user by email");
const existingUserWithEmail = await getUserByEmail(user.email);
@@ -228,10 +230,9 @@ export const handleSsoCallback = async ({
existingUserId: existingUserWithEmail.id,
existingIdentityProvider: existingUserWithEmail.identityProvider,
},
"SSO callback successful: existing user found by email"
"SSO callback blocked: existing user found by email without linked provider account"
);
await syncSsoAccount(existingUserWithEmail.id, account);
return true;
throw new Error(OAUTH_ACCOUNT_NOT_LINKED_ERROR);
}
contextLogger.debug(
@@ -338,7 +338,7 @@ describe("handleSsoCallback", () => {
);
});
test("should auto-link verified email users whose SSO provider is not already linked", async () => {
test("should reject verified email users whose SSO provider is not already linked", async () => {
vi.mocked(prisma.user.findFirst).mockResolvedValue(null);
vi.mocked(getUserByEmail).mockResolvedValue({
id: "existing-user-id",
@@ -349,26 +349,22 @@ describe("handleSsoCallback", () => {
isActive: true,
});
const result = await handleSsoCallback({
user: mockUser,
account: mockAccount,
callbackUrl: "http://localhost:3000",
});
expect(result).toBe(true);
expect(upsertAccount).toHaveBeenCalledWith(
expect.objectContaining({
userId: "existing-user-id",
provider: mockAccount.provider,
providerAccountId: mockAccount.providerAccountId,
}),
undefined
);
await expect(
handleSsoCallback({
user: mockUser,
account: mockAccount,
callbackUrl: "http://localhost:3000",
})
).rejects.toThrow("OAuthAccountNotLinked");
expect(upsertAccount).not.toHaveBeenCalled();
expect(updateUser).not.toHaveBeenCalled();
expect(createUser).not.toHaveBeenCalled();
expect(createMembership).not.toHaveBeenCalled();
expect(createBrevoCustomer).not.toHaveBeenCalled();
expect(capturePostHogEvent).not.toHaveBeenCalled();
});
test("should auto-link unverified email users whose SSO provider is not already linked", async () => {
test("should reject unverified email users whose SSO provider is not already linked", async () => {
vi.mocked(prisma.account.findUnique).mockResolvedValue(null);
vi.mocked(prisma.user.findFirst).mockResolvedValue(null);
vi.mocked(getUserByEmail).mockResolvedValue({
@@ -380,26 +376,22 @@ describe("handleSsoCallback", () => {
isActive: true,
});
const result = await handleSsoCallback({
user: mockUser,
account: mockAccount,
callbackUrl: "http://localhost:3000",
});
expect(result).toBe(true);
expect(upsertAccount).toHaveBeenCalledWith(
expect.objectContaining({
userId: "existing-user-id",
provider: mockAccount.provider,
providerAccountId: mockAccount.providerAccountId,
}),
undefined
);
await expect(
handleSsoCallback({
user: mockUser,
account: mockAccount,
callbackUrl: "http://localhost:3000",
})
).rejects.toThrow("OAuthAccountNotLinked");
expect(upsertAccount).not.toHaveBeenCalled();
expect(updateUser).not.toHaveBeenCalled();
expect(createUser).not.toHaveBeenCalled();
expect(createMembership).not.toHaveBeenCalled();
expect(createBrevoCustomer).not.toHaveBeenCalled();
expect(capturePostHogEvent).not.toHaveBeenCalled();
});
test("should auto-link existing users from a different SSO provider when no link exists", async () => {
test("should reject existing users from a different SSO provider when no link exists", async () => {
vi.mocked(prisma.account.findUnique).mockResolvedValue(null);
vi.mocked(prisma.user.findFirst).mockResolvedValue(null);
vi.mocked(getUserByEmail).mockResolvedValue({
@@ -411,53 +403,14 @@ describe("handleSsoCallback", () => {
isActive: true,
});
const result = await handleSsoCallback({
user: mockUser,
account: mockAccount,
callbackUrl: "http://localhost:3000",
});
expect(result).toBe(true);
expect(upsertAccount).toHaveBeenCalledWith(
expect.objectContaining({
userId: "existing-user-id",
provider: mockAccount.provider,
providerAccountId: mockAccount.providerAccountId,
}),
undefined
);
expect(updateUser).not.toHaveBeenCalled();
expect(createUser).not.toHaveBeenCalled();
});
test("should auto-link same-email users even when the stored legacy provider account id is stale", async () => {
vi.mocked(prisma.account.findUnique).mockResolvedValue(null);
vi.mocked(prisma.user.findFirst).mockResolvedValue(null);
vi.mocked(getUserByEmail).mockResolvedValue({
id: "existing-user-id",
email: mockUser.email,
emailVerified: new Date(),
identityProvider: "google",
identityProviderAccountId: "old-provider-id",
locale: mockUser.locale,
isActive: true,
} as any);
const result = await handleSsoCallback({
user: mockUser,
account: mockAccount,
callbackUrl: "http://localhost:3000",
});
expect(result).toBe(true);
expect(upsertAccount).toHaveBeenCalledWith(
expect.objectContaining({
userId: "existing-user-id",
provider: mockAccount.provider,
providerAccountId: mockAccount.providerAccountId,
}),
undefined
);
await expect(
handleSsoCallback({
user: mockUser,
account: mockAccount,
callbackUrl: "http://localhost:3000",
})
).rejects.toThrow("OAuthAccountNotLinked");
expect(upsertAccount).not.toHaveBeenCalled();
expect(updateUser).not.toHaveBeenCalled();
expect(createUser).not.toHaveBeenCalled();
});
@@ -76,7 +76,6 @@ export const AddWebhookModal = ({
url: testEndpointInput,
secret: webhookSecret,
});
if (!testEndpointActionResult?.data) {
const errorMessage = getFormattedErrorMessage(testEndpointActionResult);
throw new Error(errorMessage);
@@ -17,14 +17,6 @@ vi.mock("@formbricks/database", () => ({
},
}));
const constantsMock = vi.hoisted(() => ({ dangerouslyAllow: false }));
vi.mock("@/lib/constants", () => ({
get DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS() {
return constantsMock.dangerouslyAllow;
},
}));
vi.mock("@/lib/crypto", () => ({
generateStandardWebhookSignature: vi.fn(() => "signed-payload"),
generateWebhookSecret: vi.fn(() => "generated-secret"),
@@ -49,7 +41,6 @@ vi.mock("uuid", () => ({
describe("testEndpoint", () => {
beforeEach(() => {
vi.resetAllMocks();
constantsMock.dangerouslyAllow = false;
vi.mocked(generateStandardWebhookSignature).mockReturnValue("signed-payload");
vi.mocked(validateWebhookUrl).mockResolvedValue(undefined);
vi.mocked(getTranslate).mockResolvedValue((key: string) => key);
@@ -85,36 +76,6 @@ describe("testEndpoint", () => {
expect(getTranslate).toHaveBeenCalled();
});
test.each([301, 302, 303, 307, 308])(
"rejects %s redirects to prevent SSRF via redirect",
async (statusCode) => {
const fetchMock = vi.fn(async () => ({ status: statusCode }));
vi.stubGlobal("fetch", fetchMock);
await expect(testEndpoint("https://example.com/webhook")).rejects.toThrow(
"Webhook endpoint returned a redirect, which is not allowed"
);
expect(fetchMock).toHaveBeenCalledWith(
"https://example.com/webhook",
expect.objectContaining({ redirect: "manual" })
);
}
);
test("follows redirects when DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS is enabled", async () => {
constantsMock.dangerouslyAllow = true;
const fetchMock = vi.fn(async () => ({ status: 200 }));
vi.stubGlobal("fetch", fetchMock);
await expect(testEndpoint("https://example.com/webhook")).resolves.toBe(true);
expect(fetchMock).toHaveBeenCalledWith(
"https://example.com/webhook",
expect.objectContaining({ redirect: "follow" })
);
});
test("allows non-blocked non-2xx statuses", async () => {
vi.stubGlobal(
"fetch",
@@ -9,7 +9,6 @@ import {
ResourceNotFoundError,
UnknownError,
} from "@formbricks/types/errors";
import { DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS } from "@/lib/constants";
import { generateStandardWebhookSignature, generateWebhookSecret } from "@/lib/crypto";
import { validateInputs } from "@/lib/utils/validate";
import { validateWebhookUrl } from "@/lib/utils/validate-webhook-url";
@@ -192,29 +191,13 @@ export const testEndpoint = async (url: string, secret?: string): Promise<boolea
);
}
// `redirect: "manual"` prevents SSRF via redirect — validateWebhookUrl only checks the
// initial URL, so following 30x to a private/internal host (e.g. cloud metadata) would bypass it.
// Gated on the same env var as validateWebhookUrl: self-hosters who opted into trusting internal
// URLs also get the pre-patch redirect-follow behavior for consistency.
const redirectMode: RequestRedirect = DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS ? "follow" : "manual";
const response = await fetch(url, {
method: "POST",
body,
headers: requestHeaders,
signal: controller.signal,
redirect: redirectMode,
});
const statusCode = response.status;
// With `redirect: "manual"`, Node's undici returns the actual 30x response (not the spec's
// opaqueredirect filter). Treat any 30x as a redirect rejection so users get a clear error
// instead of a misleading success. With `redirect: "follow"`, fetch returns the final 2xx/4xx/5xx
// and this branch is unreachable.
if (statusCode >= 300 && statusCode < 400) {
throw new InvalidInputError("Webhook endpoint returned a redirect, which is not allowed");
}
const errorMessage = await getWebhookTestErrorMessage(statusCode);
if (errorMessage) {
@@ -1,100 +0,0 @@
"use client";
import { ReactNode, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { TI18nString } from "@formbricks/types/i18n";
import { TSurvey, TSurveyRecallItem } from "@formbricks/types/surveys/types";
import { getTextContent } from "@formbricks/types/surveys/validation";
import { TUserLocale } from "@formbricks/types/user";
import { getEnabledLanguages } from "@/lib/i18n/utils";
import { headlineToRecall, recallToHeadline } from "@/lib/utils/recall";
import { LanguageIndicator } from "@/modules/survey/multi-language-surveys/components/language-indicator";
interface MultiLangWrapperRenderProps {
value: TI18nString;
onChange: (value: string, recallItems?: TSurveyRecallItem[], fallbacks?: { [key: string]: string }) => void;
children?: ReactNode;
}
interface MultiLangWrapperProps {
isTranslationIncomplete: boolean;
value: TI18nString;
onChange: (value: TI18nString) => void;
localSurvey: TSurvey;
selectedLanguageCode: string;
setSelectedLanguageCode: (code: string) => void;
locale: TUserLocale;
render: (props: MultiLangWrapperRenderProps) => ReactNode;
}
export const MultiLangWrapper = ({
isTranslationIncomplete,
value,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
render,
onChange,
}: MultiLangWrapperProps) => {
const { t } = useTranslation();
const defaultLanguageCode =
localSurvey.languages.filter((lang) => lang.default)[0]?.language.code ?? "default";
const usedLanguageCode = selectedLanguageCode === defaultLanguageCode ? "default" : selectedLanguageCode;
const enabledLanguages = useMemo(
() => getEnabledLanguages(localSurvey.languages ?? []),
[localSurvey.languages]
);
const handleChange = (
newValue: string,
recallItems?: TSurveyRecallItem[],
fallbacks?: { [key: string]: string }
) => {
const updatedValue = {
...value,
[usedLanguageCode]:
recallItems && fallbacks ? headlineToRecall(newValue, recallItems, fallbacks) : newValue,
};
onChange(updatedValue);
};
return (
<div className="w-full">
<div>
{render({
value,
onChange: handleChange,
children:
enabledLanguages.length > 1 ? (
<LanguageIndicator
selectedLanguageCode={usedLanguageCode}
surveyLanguages={localSurvey.languages}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
/>
) : null,
})}
</div>
{enabledLanguages.length > 1 && (
<>
{usedLanguageCode !== "default" && value && typeof value["default"] !== "undefined" && (
<div className="mt-1 text-xs text-slate-500">
<strong>{t("environments.workspace.languages.translate")}:</strong>{" "}
{getTextContent(recallToHeadline(value, localSurvey, false, "default")["default"] ?? "")}
</div>
)}
{usedLanguageCode === "default" && localSurvey.languages?.length > 1 && isTranslationIncomplete && (
<div className="mt-1 text-xs text-red-400">
{t("environments.workspace.languages.incomplete_translations")}
</div>
)}
</>
)}
</div>
);
};
@@ -122,7 +122,11 @@ export const RecallItemSelect = ({
return !recallItemIds.includes(element.id) && !notAllowed && element.id !== elementId && idx > index;
})
.map((element) => {
return { id: element.id, label: element.headline[selectedLanguageCode], type: "element" as const };
return {
id: element.id,
label: element.headline[selectedLanguageCode],
type: "element" as const,
};
});
return filteredElements;
@@ -208,7 +212,7 @@ export const RecallItemSelect = ({
}}>
<div>{IconComponent && <IconComponent className="mr-2 w-4" />}</div>
<p className="max-w-full overflow-hidden text-ellipsis whitespace-nowrap text-sm">
{getTextContentWithRecallTruncated(recallItem.label)}
{getTextContentWithRecallTruncated(recallItem.label).trim() || t("common.no_text_found")}
</p>
</DropdownMenuItem>
);
@@ -14,14 +14,14 @@ import {
import {
TSurvey,
TSurveyEndScreenCard,
TSurveyRecallItem,
TSurveyRedirectUrlCard,
TSurveyWelcomeCard,
} from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { createI18nString, extractLanguageCodes } from "@/lib/i18n/utils";
import { useSyncScroll } from "@/lib/utils/hooks/useSyncScroll";
import { recallToHeadline } from "@/lib/utils/recall";
import { MultiLangWrapper } from "@/modules/survey/components/element-form-input/components/multi-lang-wrapper";
import { headlineToRecall, recallToHeadline } from "@/lib/utils/recall";
import { RecallWrapper } from "@/modules/survey/components/element-form-input/components/recall-wrapper";
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
import { LocalizedEditor } from "@/modules/survey/multi-language-surveys/components/localized-editor";
@@ -54,8 +54,7 @@ interface ElementFormInputProps {
updateChoice?: (choiceIdx: number, data: Partial<TSurveyElementChoice>) => void;
updateMatrixLabel?: (index: number, type: "row" | "column", matrixLabel: TI18nString) => void;
isInvalid: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
selectedLanguageCode?: string;
label: string;
maxLength?: number;
placeholder?: string;
@@ -81,8 +80,7 @@ export const ElementFormInput = ({
updateMatrixLabel,
isInvalid,
label,
selectedLanguageCode,
setSelectedLanguageCode,
selectedLanguageCode = "default",
maxLength,
placeholder,
onBlur,
@@ -387,6 +385,19 @@ export const ElementFormInput = ({
return false;
};
const handleMultiLangChange = useCallback(
(newValue: string, recallItems?: TSurveyRecallItem[], fallbacks?: { [key: string]: string }) => {
const updatedValue = {
...text,
[usedLanguageCode]:
recallItems && fallbacks ? headlineToRecall(newValue, recallItems, fallbacks) : newValue,
};
setText(updatedValue);
debouncedHandleUpdate(updatedValue[usedLanguageCode]);
},
[text, usedLanguageCode, debouncedHandleUpdate]
);
const useRichTextEditor = id === "headline" || id === "subheader" || id === "html";
// For rich text editor fields, we need either updateElement or updateSurvey
@@ -455,7 +466,6 @@ export const ElementFormInput = ({
isInvalid={isInvalid}
updateElement={(isWelcomeCard || isEndingCard ? updateSurvey : updateElement)!}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
firstRender={firstRender}
setFirstRender={setFirstRender}
locale={locale}
@@ -532,98 +542,69 @@ export const ElementFormInput = ({
<Label htmlFor={id}>{label}</Label>
</div>
)}
<MultiLangWrapper
isTranslationIncomplete={isTranslationIncomplete}
value={text}
<RecallWrapper
localSurvey={localSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
key={selectedLanguageCode}
onChange={(updatedText) => {
setText(updatedText);
debouncedHandleUpdate(updatedText[usedLanguageCode]);
elementId={elementId}
value={text[usedLanguageCode]}
onChange={(value, recallItems, fallbacks) => {
handleMultiLangChange(value, recallItems, fallbacks);
}}
render={({ value, onChange, children: languageIndicator }) => {
onAddFallback={() => {
inputRef.current?.focus();
}}
isRecallAllowed={false}
usedLanguageCode={usedLanguageCode}
render={({ value, onChange, highlightedJSX, children: recallComponents, isRecallSelectVisible }) => {
return (
<RecallWrapper
localSurvey={localSurvey}
elementId={elementId}
value={value[usedLanguageCode]}
onChange={(value, recallItems, fallbacks) => {
// Pass all values to MultiLangWrapper's onChange
onChange(value, recallItems, fallbacks);
}}
onAddFallback={() => {
inputRef.current?.focus();
}}
isRecallAllowed={false}
usedLanguageCode={usedLanguageCode}
render={({
value,
onChange,
highlightedJSX,
children: recallComponents,
isRecallSelectVisible,
}) => {
return (
<div className="flex flex-col gap-4 bg-white" ref={animationParent}>
<div className="flex w-full items-center space-x-2">
<div className="group relative w-full">
{languageIndicator}
{/* The highlight container is absolutely positioned behind the input */}
<div className="h-10 w-full"></div>
<div
ref={highlightContainerRef}
className={`no-scrollbar absolute top-0 z-0 mt-0.5 flex h-10 w-full overflow-scroll whitespace-nowrap px-3 py-2 text-center text-sm text-transparent ${
localSurvey.languages?.length > 1 ? "pr-24" : ""
}`}
dir="auto"
key={highlightedJSX.toString()}>
{highlightedJSX}
</div>
<Input
key={`${elementId}-${id}-${usedLanguageCode}`}
value={
recallToHeadline(
{
[usedLanguageCode]: value,
},
localSurvey,
false,
usedLanguageCode
)[usedLanguageCode]
}
dir="auto"
onChange={(e) => onChange(e.target.value)}
id={id}
name={id}
placeholder={placeholder ?? getPlaceHolderById(id, t)}
aria-label={label}
maxLength={maxLength}
ref={inputRef}
onBlur={onBlur}
className={`absolute top-0 text-black caret-black ${
localSurvey.languages?.length > 1 ? "pr-24" : ""
} ${className}`}
isInvalid={
isInvalid &&
text[usedLanguageCode]?.trim() === "" &&
localSurvey.languages?.length > 1 &&
isTranslationIncomplete
}
autoComplete={isRecallSelectVisible ? "off" : "on"}
autoFocus={false}
onKeyDown={handleKeyDown}
/>
{recallComponents}
</div>
</div>
<div className="flex flex-col gap-4 bg-white" ref={animationParent}>
<div className="flex w-full items-center space-x-2">
<div className="group relative w-full">
{/* The highlight container is absolutely positioned behind the input */}
<div className="h-10 w-full"></div>
<div
ref={highlightContainerRef}
className="no-scrollbar absolute top-0 z-0 mt-0.5 flex h-10 w-full overflow-scroll whitespace-nowrap px-3 py-2 text-center text-sm text-transparent"
dir="auto"
key={highlightedJSX.toString()}>
{highlightedJSX}
</div>
);
}}
/>
<Input
key={`${elementId}-${id}-${usedLanguageCode}`}
value={
recallToHeadline(
{
[usedLanguageCode]: value,
},
localSurvey,
false,
usedLanguageCode
)[usedLanguageCode]
}
dir="auto"
onChange={(e) => onChange(e.target.value)}
id={id}
name={id}
placeholder={placeholder ?? getPlaceHolderById(id, t)}
aria-label={label}
maxLength={maxLength}
ref={inputRef}
onBlur={onBlur}
className={`absolute top-0 text-black caret-black ${className}`}
isInvalid={
isInvalid &&
text[usedLanguageCode]?.trim() === "" &&
localSurvey.languages?.length > 1 &&
isTranslationIncomplete
}
autoComplete={isRecallSelectVisible ? "off" : "on"}
autoFocus={false}
onKeyDown={handleKeyDown}
/>
{recallComponents}
</div>
</div>
</div>
);
}}
/>
+28 -1
View File
@@ -3,6 +3,7 @@
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { ZActionClassInput } from "@formbricks/types/action-classes";
import { ZId } from "@formbricks/types/common";
import { OperationNotAllowedError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TSurvey, ZSurvey } from "@formbricks/types/surveys/types";
import { POSTHOG_KEY, UNSPLASH_ACCESS_KEY, UNSPLASH_ALLOWED_DOMAINS } from "@/lib/constants";
@@ -25,7 +26,7 @@ import { getSurveyFollowUpsPermission } from "@/modules/survey/follow-ups/lib/ut
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
import { checkSpamProtectionPermission } from "@/modules/survey/lib/permission";
import { getOrganizationBilling, getSurvey } from "@/modules/survey/lib/survey";
import { getProject } from "./lib/project";
import { getProject, getProjectLanguages } from "./lib/project";
/**
* Checks if survey follow-ups can be added for the given organization.
@@ -199,6 +200,32 @@ export const refetchProjectAction = authenticatedActionClient
return await getProject(parsedInput.projectId);
});
const ZGetProjectLanguagesAction = z.object({
projectId: ZId,
});
export const getProjectLanguagesAction = authenticatedActionClient
.inputSchema(ZGetProjectLanguagesAction)
.action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromProjectId(parsedInput.projectId),
access: [
{
type: "organization",
roles: ["owner", "manager"],
},
{
type: "projectTeam",
minPermission: "read",
projectId: parsedInput.projectId,
},
],
});
return await getProjectLanguages(parsedInput.projectId);
});
const ZGetImagesFromUnsplashAction = z.object({
searchQuery: z.string(),
page: z.number().optional(),
@@ -19,8 +19,6 @@ interface AddressElementFormProps {
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
isInvalid: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
locale: TUserLocale;
isStorageConfigured: boolean;
isExternalUrlsAllowed?: boolean;
@@ -32,8 +30,6 @@ export const AddressElementForm = ({
updateElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -101,8 +97,6 @@ export const AddressElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -121,8 +115,6 @@ export const AddressElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -154,8 +146,6 @@ export const AddressElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
/>
@@ -11,7 +11,6 @@ interface AdvancedSettingsProps {
updateElement: (elementIdx: number, updatedAttributes: any) => void;
updateBlockLogic: (elementIdx: number, logic: TSurveyBlockLogic[]) => void;
updateBlockLogicFallback: (elementIdx: number, logicFallback: string | undefined) => void;
selectedLanguageCode: string;
}
export const AdvancedSettings = ({
@@ -19,7 +18,6 @@ export const AdvancedSettings = ({
elementIdx,
localSurvey,
updateElement,
selectedLanguageCode,
}: AdvancedSettingsProps) => {
const showOptionIds =
element.type === TSurveyElementTypeEnum.PictureSelection ||
@@ -36,9 +34,7 @@ export const AdvancedSettings = ({
updateElement={updateElement}
/>
{showOptionIds && (
<OptionIds type="element" element={element} selectedLanguageCode={selectedLanguageCode} />
)}
{showOptionIds && <OptionIds type="element" element={element} />}
</div>
);
};
@@ -59,8 +59,6 @@ interface BlockCardProps {
setActiveElementId: (elementId: string | null) => void;
lastElement: boolean;
lastElementIndex: number;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
invalidElements?: string[];
addElement: (element: any, index?: number) => void;
isFormbricksCloud: boolean;
@@ -95,8 +93,6 @@ export const BlockCard = ({
setActiveElementId,
lastElement,
lastElementIndex,
selectedLanguageCode,
setSelectedLanguageCode,
invalidElements,
addElement,
isFormbricksCloud,
@@ -114,6 +110,8 @@ export const BlockCard = ({
moveElementToBlock,
totalBlocks,
}: BlockCardProps) => {
const selectedLanguageCode = "default";
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: block.id,
});
@@ -169,7 +167,6 @@ export const BlockCard = ({
elementIdx,
updateElement,
selectedLanguageCode,
setSelectedLanguageCode,
isInvalid: invalidElements ? invalidElements.includes(element.id) : false,
locale,
isStorageConfigured,
@@ -427,7 +424,6 @@ export const BlockCard = ({
updateElement={updateElement}
updateBlockLogic={updateBlockLogic}
updateBlockLogicFallback={updateBlockLogicFallback}
selectedLanguageCode={selectedLanguageCode}
/>
</Collapsible.CollapsibleContent>
</Collapsible.Root>
@@ -461,7 +457,6 @@ export const BlockCard = ({
block={block}
blockIndex={blockIdx}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
updateBlockButtonLabel={updateBlockButtonLabel}
updateBlockLogic={updateBlockLogic}
updateBlockLogicFallback={updateBlockLogicFallback}
@@ -17,7 +17,6 @@ interface BlockSettingsProps {
block: TSurveyBlock;
blockIndex: number;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
updateBlockButtonLabel: (
blockIndex: number,
labelKey: "buttonLabel" | "backButtonLabel",
@@ -35,7 +34,6 @@ export const BlockSettings = ({
block,
blockIndex,
selectedLanguageCode,
setSelectedLanguageCode,
updateBlockButtonLabel,
updateBlockLogic,
updateBlockLogicFallback,
@@ -97,8 +95,6 @@ export const BlockSettings = ({
});
}
}}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
placeholder={t("common.back")}
locale={locale}
isStorageConfigured={isStorageConfigured}
@@ -139,8 +135,6 @@ export const BlockSettings = ({
updateBlockButtonLabel(blockIndex, "buttonLabel", updatedButtonLabel);
}
}}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
placeholder={t("common.next")}
locale={locale}
isStorageConfigured={isStorageConfigured}
@@ -25,8 +25,6 @@ interface BlocksDroppableProps {
duplicateElement: (elementIdx: number) => void;
activeElementId: string | null;
setActiveElementId: (elementId: string | null) => void;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
invalidElements: string[] | null;
addElement: (element: any, index?: number) => void;
isFormbricksCloud: boolean;
@@ -52,9 +50,7 @@ export const BlocksDroppable = ({
setLocalSurvey,
moveElement,
project,
selectedLanguageCode,
setActiveElementId,
setSelectedLanguageCode,
updateElement,
updateBlockLogic,
updateBlockLogicFallback,
@@ -97,8 +93,6 @@ export const BlocksDroppable = ({
updateBlockLogicFallback={updateBlockLogicFallback}
updateBlockButtonLabel={updateBlockButtonLabel}
duplicateElement={duplicateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
deleteElement={deleteElement}
activeElementId={activeElementId}
setActiveElementId={setActiveElementId}
@@ -30,7 +30,6 @@ interface BulkEditOptionsModalProps {
onSave: (updatedChoices: TSurveyMultipleChoiceElement["choices"]) => void;
element: TSurveyMultipleChoiceElement;
localSurvey: TSurvey;
selectedLanguageCode: string;
surveyLanguageCodes: string[];
locale: TUserLocale;
}
@@ -67,10 +66,10 @@ export const BulkEditOptionsModal = ({
onSave,
element,
localSurvey,
selectedLanguageCode,
surveyLanguageCodes,
locale,
}: BulkEditOptionsModalProps): JSX.Element => {
const selectedLanguageCode = "default";
const { t } = useTranslation();
const [textareaValue, setTextareaValue] = useState("");
const [validationError, setValidationError] = useState<string | null>(null);
@@ -19,8 +19,6 @@ interface CalElementFormProps {
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
lastElement: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -32,8 +30,6 @@ export const CalElementForm = ({
element,
elementIdx,
updateElement,
selectedLanguageCode,
setSelectedLanguageCode,
isInvalid,
locale,
isStorageConfigured = true,
@@ -62,8 +58,6 @@ export const CalElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -81,8 +75,6 @@ export const CalElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -16,8 +16,6 @@ interface ConsentElementFormProps {
element: TSurveyConsentElement;
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -30,8 +28,6 @@ export const ConsentElementForm = ({
updateElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -45,8 +41,6 @@ export const ConsentElementForm = ({
elementIdx,
isInvalid,
updateElement,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured,
isExternalUrlsAllowed,
@@ -20,8 +20,6 @@ interface ContactInfoElementFormProps {
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
lastElement: boolean;
isInvalid: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
locale: TUserLocale;
isStorageConfigured: boolean;
isExternalUrlsAllowed?: boolean;
@@ -33,8 +31,6 @@ export const ContactInfoElementForm = ({
updateElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -98,8 +94,6 @@ export const ContactInfoElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -118,8 +112,6 @@ export const ContactInfoElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -151,8 +143,6 @@ export const ContactInfoElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
/>
@@ -20,8 +20,6 @@ interface CTAElementFormProps {
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
lastElement: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -35,8 +33,6 @@ export const CTAElementForm = ({
lastElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -55,8 +51,6 @@ export const CTAElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -75,8 +69,6 @@ export const CTAElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -122,8 +114,6 @@ export const CTAElementForm = ({
placeholder={lastElement ? t("common.finish") : t("common.next")}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
/>
@@ -17,8 +17,6 @@ interface IDateElementFormProps {
element: TSurveyDateElement;
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -31,8 +29,6 @@ export const DateElementForm = ({
updateElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -51,8 +47,6 @@ export const DateElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -70,8 +64,6 @@ export const DateElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -33,8 +33,6 @@ interface EditEndingCardProps {
setActiveElementId: (id: string | null) => void;
activeElementId: string | null;
isInvalid: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
addEndingCard: (index: number) => void;
isFormbricksCloud: boolean;
locale: TUserLocale;
@@ -50,8 +48,6 @@ export const EditEndingCard = ({
setActiveElementId,
activeElementId,
isInvalid,
selectedLanguageCode,
setSelectedLanguageCode,
addEndingCard,
isFormbricksCloud,
locale,
@@ -59,6 +55,7 @@ export const EditEndingCard = ({
quotas,
isExternalUrlsAllowed,
}: EditEndingCardProps) => {
const selectedLanguageCode = "default";
const { t } = useTranslation();
const endingCard = useMemo(
@@ -298,7 +295,6 @@ export const EditEndingCard = ({
endingCardIndex={endingCardIndex}
isInvalid={isInvalid}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
updateSurvey={updateSurvey}
endingCard={endingCard}
locale={locale}
@@ -23,8 +23,6 @@ interface EditWelcomeCardProps {
setActiveElementId: (id: string | null) => void;
activeElementId: string | null;
isInvalid: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
locale: TUserLocale;
isStorageConfigured: boolean;
isExternalUrlsAllowed?: boolean;
@@ -36,8 +34,6 @@ export const EditWelcomeCard = ({
setActiveElementId,
activeElementId,
isInvalid,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -155,8 +151,6 @@ export const EditWelcomeCard = ({
elementIdx={-1}
isInvalid={isInvalid}
updateSurvey={updateSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
isExternalUrlsAllowed={isExternalUrlsAllowed}
@@ -171,8 +165,6 @@ export const EditWelcomeCard = ({
elementIdx={-1}
isInvalid={isInvalid}
updateSurvey={updateSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
isExternalUrlsAllowed={isExternalUrlsAllowed}
@@ -191,8 +183,6 @@ export const EditWelcomeCard = ({
placeholder={t("common.next")}
isInvalid={isInvalid}
updateSurvey={updateSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
label={t("environments.surveys.edit.next_button_label")}
locale={locale}
isStorageConfigured={isStorageConfigured}
@@ -28,8 +28,6 @@ interface ChoiceProps {
addChoice: (choiceIdx: number) => void;
isInvalid: boolean;
localSurvey: TSurvey;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
surveyLanguages: TSurveyLanguage[];
element: TSurveyMultipleChoiceElement | TSurveyRankingElement;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
@@ -46,8 +44,6 @@ export const ElementOptionChoice = ({
isInvalid,
localSurvey,
elementIdx,
selectedLanguageCode,
setSelectedLanguageCode,
surveyLanguages,
updateChoice,
element,
@@ -104,8 +100,6 @@ export const ElementOptionChoice = ({
elementIdx={elementIdx}
value={choice.label}
updateChoice={updateChoice}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
isInvalid={
isInvalid && !isLabelValidForAllLanguages(element.choices?.[choiceIdx]?.label, surveyLanguages)
}
@@ -151,8 +145,6 @@ export const ElementOptionChoice = ({
createI18nString(t("environments.surveys.edit.please_specify"), surveyLanguageCodes)
}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
isInvalid={
isInvalid && !isLabelValidForAllLanguages(element.choices?.[choiceIdx]?.label, surveyLanguages)
}
@@ -11,7 +11,7 @@ import {
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { createId } from "@paralleldrive/cuid2";
import { Language, Project } from "@prisma/client";
import { Project } from "@prisma/client";
import React, { SetStateAction, useEffect, useMemo } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
@@ -52,7 +52,6 @@ import {
isUsedInRecall,
} from "@/modules/survey/editor/lib/utils";
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
import { MultiLanguageCard } from "@/modules/survey/multi-language-surveys/components/multi-language-card";
import { ConfirmationModal } from "@/modules/ui/components/confirmation-modal";
import { isEndingCardValid, isWelcomeCardValid, validateElement } from "../lib/validation";
@@ -62,11 +61,9 @@ interface ElementsViewProps {
activeElementId: string | null;
setActiveElementId: (elementId: string | null) => void;
project: Project;
projectLanguages: Language[];
invalidElements: string[] | null;
setInvalidElements: React.Dispatch<SetStateAction<string[] | null>>;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
isFormbricksCloud: boolean;
isCxMode: boolean;
locale: TUserLocale;
@@ -83,10 +80,8 @@ export const ElementsView = ({
localSurvey,
setLocalSurvey,
project,
projectLanguages,
invalidElements,
setInvalidElements,
setSelectedLanguageCode,
selectedLanguageCode,
isFormbricksCloud,
isCxMode,
@@ -850,8 +845,6 @@ export const ElementsView = ({
setActiveElementId={setActiveElementId}
activeElementId={activeElementId}
isInvalid={invalidElements ? invalidElements.includes("start") : false}
setSelectedLanguageCode={setSelectedLanguageCode}
selectedLanguageCode={selectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
isExternalUrlsAllowed={isExternalUrlsAllowed}
@@ -874,8 +867,6 @@ export const ElementsView = ({
updateBlockLogicFallback={updateBlockLogicFallback}
updateBlockButtonLabel={updateBlockButtonLabel}
duplicateElement={duplicateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
deleteElement={deleteElement}
activeElementId={activeElementId}
setActiveElementId={setActiveElementId}
@@ -915,8 +906,6 @@ export const ElementsView = ({
setActiveElementId={setActiveElementId}
activeElementId={activeElementId}
isInvalid={invalidElements ? invalidElements.includes(ending.id) : false}
setSelectedLanguageCode={setSelectedLanguageCode}
selectedLanguageCode={selectedLanguageCode}
addEndingCard={addEndingCard}
isFormbricksCloud={isFormbricksCloud}
locale={locale}
@@ -949,16 +938,6 @@ export const ElementsView = ({
setActiveElementId={setActiveElementId}
quotas={quotas}
/>
<MultiLanguageCard
localSurvey={localSurvey}
projectLanguages={projectLanguages}
setLocalSurvey={setLocalSurvey}
setActiveElementId={setActiveElementId}
activeElementId={activeElementId}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
/>
</>
)}
</div>
@@ -20,7 +20,6 @@ interface EndScreenFormProps {
endingCardIndex: number;
isInvalid: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
updateSurvey: (
input: Partial<TSurveyEndScreenCard & { _forceUpdate?: boolean }> | Partial<TSurveyRedirectUrlCard>
) => void;
@@ -35,7 +34,6 @@ export const EndScreenForm = ({
endingCardIndex,
isInvalid,
selectedLanguageCode,
setSelectedLanguageCode,
updateSurvey,
endingCard,
locale,
@@ -66,8 +64,6 @@ export const EndScreenForm = ({
elementIdx={questions.length + endingCardIndex}
isInvalid={isInvalid}
updateSurvey={updateSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!endingCard.headline?.default || endingCard.headline.default.trim() === ""}
@@ -85,8 +81,6 @@ export const EndScreenForm = ({
elementIdx={questions.length + endingCardIndex}
isInvalid={isInvalid}
updateSurvey={updateSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!endingCard.subheader?.default || endingCard.subheader.default.trim() === ""}
@@ -155,8 +149,6 @@ export const EndScreenForm = ({
elementIdx={questions.length + endingCardIndex}
isInvalid={isInvalid}
updateSurvey={updateSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
/>
@@ -24,8 +24,6 @@ interface FileUploadFormProps {
element: TSurveyFileUploadElement;
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
isInvalid: boolean;
isFormbricksCloud: boolean;
locale: TUserLocale;
@@ -40,8 +38,6 @@ export const FileUploadElementForm = ({
updateElement,
isInvalid,
project,
selectedLanguageCode,
setSelectedLanguageCode,
isFormbricksCloud,
locale,
isStorageConfigured = true,
@@ -91,8 +87,6 @@ export const FileUploadElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -110,8 +104,6 @@ export const FileUploadElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -2,13 +2,14 @@
import { useAutoAnimate } from "@formkit/auto-animate/react";
import * as Collapsible from "@radix-ui/react-collapsible";
import { CheckIcon } from "lucide-react";
import { CheckIcon, SparklesIcon } from "lucide-react";
import React, { useState } from "react";
import { UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { TProjectStyling } from "@formbricks/types/project";
import { TSurveyStyling } from "@formbricks/types/surveys/types";
import { cn } from "@/lib/cn";
import { Button } from "@/modules/ui/components/button";
import {
ColorField,
DimensionInput,
@@ -23,6 +24,7 @@ type FormStylingSettingsProps = {
isSettingsPage?: boolean;
disabled?: boolean;
form: UseFormReturn<TProjectStyling | TSurveyStyling>;
onSuggestColorsClick?: () => void;
};
export const FormStylingSettings = ({
@@ -31,6 +33,7 @@ export const FormStylingSettings = ({
disabled = false,
setOpen,
form,
onSuggestColorsClick,
}: FormStylingSettingsProps) => {
const { t } = useTranslation();
@@ -80,6 +83,24 @@ export const FormStylingSettings = ({
<hr className="py-1 text-slate-600" />
<div className="flex flex-col gap-6 p-6">
<div className="grid grid-cols-2 items-end gap-4">
<ColorField
form={form}
name="brandColor.light"
label={t("environments.surveys.edit.brand_color")}
description={t("environments.surveys.edit.brand_color_description")}
/>
<Button
type="button"
variant="default"
className="h-10 justify-center gap-1"
onClick={onSuggestColorsClick}
disabled={disabled || !onSuggestColorsClick}>
<SparklesIcon className="mr-2 h-4 w-4" />
{t("environments.workspace.look.suggest_colors")}
</Button>
</div>
{/* Headlines & Descriptions */}
<StylingSection
title={t("environments.workspace.look.advanced_styling_section_headlines")}
@@ -27,8 +27,6 @@ interface MatrixElementFormProps {
element: TSurveyMatrixElement;
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -41,8 +39,6 @@ export const MatrixElementForm = ({
updateElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -211,8 +207,6 @@ export const MatrixElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -230,8 +224,6 @@ export const MatrixElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -277,8 +269,6 @@ export const MatrixElementForm = ({
onDelete={(index) => handleDeleteLabel("row", index)}
onKeyDown={(e) => handleKeyDown(e, "row", index)}
canDelete={element.rows.length > 2}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
isInvalid={
isInvalid &&
!isLabelValidForAllLanguages(element.rows[index].label, localSurvey.languages)
@@ -323,8 +313,6 @@ export const MatrixElementForm = ({
onDelete={(index) => handleDeleteLabel("column", index)}
onKeyDown={(e) => handleKeyDown(e, "column", index)}
canDelete={element.columns.length > 2}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
isInvalid={
isInvalid &&
!isLabelValidForAllLanguages(element.columns[index].label, localSurvey.languages)
@@ -24,8 +24,6 @@ interface MatrixSortableItemProps {
onDelete: (index: number) => void;
onKeyDown: (e: React.KeyboardEvent) => void;
canDelete: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -41,8 +39,6 @@ export const MatrixSortableItem = ({
onDelete,
onKeyDown,
canDelete,
selectedLanguageCode,
setSelectedLanguageCode,
isInvalid,
locale,
isStorageConfigured,
@@ -73,8 +69,6 @@ export const MatrixSortableItem = ({
elementIdx={elementIdx}
value={choice.label}
updateMatrixLabel={updateMatrixLabel}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
isInvalid={isInvalid}
locale={locale}
onKeyDown={onKeyDown}
@@ -35,7 +35,6 @@ interface MultipleChoiceElementFormProps {
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -49,7 +48,6 @@ export const MultipleChoiceElementForm = ({
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -262,8 +260,6 @@ export const MultipleChoiceElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -282,8 +278,6 @@ export const MultipleChoiceElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -353,8 +347,6 @@ export const MultipleChoiceElementForm = ({
addChoice={addChoice}
isInvalid={isInvalid}
localSurvey={localSurvey}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
surveyLanguages={surveyLanguages}
element={element}
updateElement={updateElement}
@@ -443,7 +435,6 @@ export const MultipleChoiceElementForm = ({
}}
element={element}
localSurvey={localSurvey}
selectedLanguageCode={selectedLanguageCode}
surveyLanguageCodes={surveyLanguageCodes}
locale={locale}
/>
@@ -17,8 +17,6 @@ interface NPSElementFormProps {
element: TSurveyNPSElement;
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
selectedLanguageCode: string;
setSelectedLanguageCode: (languageCode: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -31,8 +29,6 @@ export const NPSElementForm = ({
updateElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -51,8 +47,6 @@ export const NPSElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -71,8 +65,6 @@ export const NPSElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -108,8 +100,6 @@ export const NPSElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
/>
@@ -123,8 +113,6 @@ export const NPSElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
/>
@@ -23,8 +23,6 @@ interface OpenElementFormProps {
elementIdx: number;
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
lastElement: boolean;
selectedLanguageCode: string;
setSelectedLanguageCode: (language: string) => void;
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
@@ -37,8 +35,6 @@ export const OpenElementForm = ({
updateElement,
isInvalid,
localSurvey,
selectedLanguageCode,
setSelectedLanguageCode,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
@@ -59,8 +55,6 @@ export const OpenElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
@@ -79,8 +73,6 @@ export const OpenElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
@@ -117,8 +109,6 @@ export const OpenElementForm = ({
elementIdx={elementIdx}
isInvalid={isInvalid}
updateElement={updateElement}
selectedLanguageCode={selectedLanguageCode}
setSelectedLanguageCode={setSelectedLanguageCode}
label={t("common.placeholder")}
locale={locale}
isStorageConfigured={isStorageConfigured}
@@ -9,7 +9,6 @@ import { Label } from "@/modules/ui/components/label";
interface OptionIdsElementProps {
type: "element";
element: TSurveyElement;
selectedLanguageCode: string;
}
interface OptionIdsVariablesProps {
@@ -21,6 +20,7 @@ type OptionIdsProps = OptionIdsElementProps | OptionIdsVariablesProps;
export const OptionIds = (props: OptionIdsProps) => {
const { t } = useTranslation();
const selectedLanguageCode = "default";
const renderChoiceIds = (element: TSurveyElement, selectedLanguageCode: string) => {
switch (element.type) {
@@ -92,7 +92,7 @@ export const OptionIds = (props: OptionIdsProps) => {
return (
<div className="space-y-3">
<Label className="text-sm font-medium text-gray-700">{t("common.option_ids")}</Label>
<div className="w-full">{renderChoiceIds(props.element, props.selectedLanguageCode)}</div>
<div className="w-full">{renderChoiceIds(props.element, selectedLanguageCode)}</div>
</div>
);
};

Some files were not shown because too many files have changed in this diff Show More